Blame src/SFtp.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include "SFtp.h"
Packit 8f70b4
#include "ArgV.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "ascii_ctype.h"
Packit 8f70b4
#include "FileGlob.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <stddef.h>
Packit 8f70b4
Packit 8f70b4
#define max_buf 0x10000
Packit 8f70b4
Packit 8f70b4
#define super SSH_Access
Packit 8f70b4
Packit 8f70b4
bool SFtp::GetBetterConnection(int level,bool limit_reached)
Packit 8f70b4
{
Packit 8f70b4
   bool need_sleep=false;
Packit 8f70b4
Packit 8f70b4
   for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
Packit 8f70b4
   {
Packit 8f70b4
      SFtp *o=(SFtp*)fo; // we are sure it is SFtp.
Packit 8f70b4
Packit 8f70b4
      if(!o->recv_buf)
Packit 8f70b4
	 continue;
Packit 8f70b4
Packit 8f70b4
      if(o->state!=CONNECTED || o->mode!=CLOSED)
Packit 8f70b4
      {
Packit 8f70b4
	 if(level<2)
Packit 8f70b4
	    continue;
Packit 8f70b4
	 if(!connection_takeover || (o->priority>=priority && !o->IsSuspended()))
Packit 8f70b4
	    continue;
Packit 8f70b4
	 o->Disconnect();
Packit 8f70b4
	 return need_sleep;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(level==0 && xstrcmp(real_cwd,o->real_cwd))
Packit 8f70b4
	 continue;
Packit 8f70b4
Packit 8f70b4
      // borrow the connection
Packit 8f70b4
      MoveConnectionHere(o);
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   return need_sleep;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int SFtp::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int s;
Packit 8f70b4
Packit 8f70b4
   // check if idle time exceeded
Packit 8f70b4
   if(mode==CLOSED && send_buf && idle_timer.Stopped())
Packit 8f70b4
   {
Packit 8f70b4
      LogNote(1,_("Closing idle connection"));
Packit 8f70b4
      Disconnect();
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(!hostname)
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(send_buf && send_buf->Error())
Packit 8f70b4
   {
Packit 8f70b4
      LogError(0,"send: %s",send_buf->ErrorText());
Packit 8f70b4
      Disconnect(send_buf->ErrorText());
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(state!=CONNECTING_1 && state!=CONNECTING_2)
Packit 8f70b4
      m|=HandleReplies();
Packit 8f70b4
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(send_buf)
Packit 8f70b4
      timeout_timer.Reset(send_buf->EventTime());
Packit 8f70b4
   if(recv_buf)
Packit 8f70b4
      timeout_timer.Reset(recv_buf->EventTime());
Packit 8f70b4
   if(pty_send_buf)
Packit 8f70b4
      timeout_timer.Reset(pty_send_buf->EventTime());
Packit 8f70b4
   if(pty_recv_buf)
Packit 8f70b4
      timeout_timer.Reset(pty_recv_buf->EventTime());
Packit 8f70b4
Packit 8f70b4
   // check for timeout only if there should be connection activity.
Packit 8f70b4
   if(state!=DISCONNECTED && state!=CONNECTED
Packit 8f70b4
   && mode!=CLOSED && CheckTimeout())
Packit 8f70b4
      return MOVED;
Packit 8f70b4
Packit 8f70b4
   if((state==FILE_RECV || state==FILE_SEND)
Packit 8f70b4
   && rate_limit==0)
Packit 8f70b4
      rate_limit=new RateLimit(hostname);
Packit 8f70b4
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case DISCONNECTED:
Packit 8f70b4
   {
Packit 8f70b4
      if(mode==CLOSED)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(mode==CONNECT_VERIFY)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      // walk through SFtp classes and try to find identical idle session
Packit 8f70b4
      // first try "easy" cases of session take-over.
Packit 8f70b4
      for(int i=0; i<3; i++)
Packit 8f70b4
      {
Packit 8f70b4
	 bool limit_reached=(connection_limit>0
Packit 8f70b4
			    && connection_limit<=CountConnections());
Packit 8f70b4
	 if(i>=2 && !limit_reached)
Packit 8f70b4
	    break;
Packit 8f70b4
	 bool need_sleep=GetBetterConnection(i,limit_reached);
Packit 8f70b4
	 if(state!=DISCONNECTED)
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 if(need_sleep)
Packit 8f70b4
	    return m;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(!ReconnectAllowed())
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(!NextTry())
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
Packit 8f70b4
      const char *init=Query("server-program",hostname);
Packit 8f70b4
      const char *prog=Query("connect-program",hostname);
Packit 8f70b4
      if(!prog || !prog[0])
Packit 8f70b4
	 prog="ssh -a -x";
Packit 8f70b4
      ArgV args;
Packit 8f70b4
      if(!strchr(init,'/'))
Packit 8f70b4
      {
Packit 8f70b4
	 if(init[0])
Packit 8f70b4
	    args.Add("-s");   // run ssh2 subsystem
Packit 8f70b4
	 // sftpd does not have a greeting
Packit 8f70b4
	 received_greeting=true;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
	 init=xstring::cat("echo SFTP: >&2;",init,NULL);
Packit 8f70b4
      if(user)
Packit 8f70b4
      {
Packit 8f70b4
	 args.Add("-l");
Packit 8f70b4
	 args.Add(user);
Packit 8f70b4
      }
Packit 8f70b4
      if(portname)
Packit 8f70b4
      {
Packit 8f70b4
	 args.Add("-p");
Packit 8f70b4
	 args.Add(portname);
Packit 8f70b4
      }
Packit 8f70b4
      args.Add(hostname);
Packit 8f70b4
      if(init[0])
Packit 8f70b4
	 args.Add(init);
Packit 8f70b4
      xstring_ca cmd_q(args.CombineShellQuoted(0));
Packit 8f70b4
      xstring& cmd_str=xstring::cat(prog," ",cmd_q.get(),NULL);
Packit 8f70b4
      LogNote(9,"%s (%s)",_("Running connect program"),cmd_str.get());
Packit 8f70b4
      ssh=new PtyShell(cmd_str);
Packit 8f70b4
      ssh->UsePipes();
Packit 8f70b4
      state=CONNECTING;
Packit 8f70b4
      timeout_timer.Reset();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   case CONNECTING:
Packit 8f70b4
   {
Packit 8f70b4
      int fd=ssh->getfd();
Packit 8f70b4
      if(fd==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 if(ssh->error())
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(FATAL,ssh->error_text);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 TimeoutS(1);
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      MakePtyBuffers();
Packit 8f70b4
      set_real_cwd("~");
Packit 8f70b4
      state=CONNECTING_1;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   case CONNECTING_1:
Packit 8f70b4
      m|=HandleSSHMessage();
Packit 8f70b4
      if(state!=CONNECTING_1)
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      if(!received_greeting)
Packit 8f70b4
	 return m;
Packit 8f70b4
      SendRequest(new Request_INIT(Query("protocol-version",hostname)),Expect::FXP_VERSION);
Packit 8f70b4
      state=CONNECTING_2;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
Packit 8f70b4
   case CONNECTING_2:
Packit 8f70b4
      m|=HandleSSHMessage();
Packit 8f70b4
      if(state!=CONNECTING_2)
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      m|=HandleReplies();
Packit 8f70b4
      if(protocol_version==0)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(home_auto==0)
Packit 8f70b4
	 SendRequest(new Request_REALPATH("."),Expect::HOME_PATH);
Packit 8f70b4
      state=CONNECTED;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
   case CONNECTED:
Packit 8f70b4
      if(home.path==0 && !RespQueueIsEmpty())
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(mode==CLOSED)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      SendRequest();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
Packit 8f70b4
   case FILE_RECV:
Packit 8f70b4
      if(file_buf->Size()>=rate_limit->BytesAllowedToGet())
Packit 8f70b4
      {
Packit 8f70b4
	 recv_buf->Suspend();
Packit 8f70b4
	 Timeout(1000);
Packit 8f70b4
      }
Packit 8f70b4
      else if(file_buf->Size()>=max_buf)
Packit 8f70b4
      {
Packit 8f70b4
	 recv_buf->Suspend();
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      else if(recv_buf->IsSuspended())
Packit 8f70b4
      {
Packit 8f70b4
	 recv_buf->Resume();
Packit 8f70b4
	 if(recv_buf->Size()>0 || (recv_buf->Size()==0 && recv_buf->Eof()))
Packit 8f70b4
	    m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case FILE_SEND:
Packit 8f70b4
      // pack data from file_buf.
Packit 8f70b4
      file_buf->Get(&b,&s);
Packit 8f70b4
      if(s==0 && !eof)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(s
Packit 8f70b4
	 return m;   // wait for more data before sending.
Packit 8f70b4
      if(RespQueueSize()>max_packets_in_flight)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(s==0)
Packit 8f70b4
      {
Packit 8f70b4
	 // no more data, set attributes and close the file.
Packit 8f70b4
	 Request_FSETSTAT *req=new Request_FSETSTAT(handle,protocol_version);
Packit 8f70b4
	 if(entity_date!=NO_DATE) {
Packit 8f70b4
	    req->attrs.mtime=entity_date;
Packit 8f70b4
	    req->attrs.flags|=SSH_FILEXFER_ATTR_MODIFYTIME;
Packit 8f70b4
	 }
Packit 8f70b4
	 req->attrs.size=pos;
Packit 8f70b4
	 req->attrs.flags|=SSH_FILEXFER_ATTR_SIZE;
Packit 8f70b4
	 SendRequest(req,Expect::IGNORE);
Packit 8f70b4
	 CloseHandle(Expect::DEFAULT);
Packit 8f70b4
	 state=WAITING;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      if(s>size_write)
Packit 8f70b4
	 s=size_write;
Packit 8f70b4
      SendRequest(new Request_WRITE(handle,request_pos,b,s),Expect::WRITE_STATUS);
Packit 8f70b4
      file_buf->Skip(s);
Packit 8f70b4
      request_pos+=s;
Packit 8f70b4
      flush_timer.Reset();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      break;
Packit 8f70b4
   case WAITING:
Packit 8f70b4
      if(mode==ARRAY_INFO)
Packit 8f70b4
	 SendArrayInfoRequests();
Packit 8f70b4
      break;
Packit 8f70b4
   case DONE:
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::MoveConnectionHere(SFtp *o)
Packit 8f70b4
{
Packit 8f70b4
   super::MoveConnectionHere(o);
Packit 8f70b4
   protocol_version=o->protocol_version;
Packit 8f70b4
   recv_translate=o->recv_translate.borrow();
Packit 8f70b4
   send_translate=o->send_translate.borrow();
Packit 8f70b4
   rate_limit=o->rate_limit.borrow();
Packit 8f70b4
   expect_queue.move_here(o->expect_queue);
Packit 8f70b4
   timeout_timer.Reset(o->timeout_timer);
Packit 8f70b4
   ssh_id=o->ssh_id;
Packit 8f70b4
   state=CONNECTED;
Packit 8f70b4
   o->Disconnect();
Packit 8f70b4
   if(!home)
Packit 8f70b4
      set_home(home_auto);
Packit 8f70b4
   ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::DisconnectLL()
Packit 8f70b4
{
Packit 8f70b4
   super::DisconnectLL();
Packit 8f70b4
   handle.set(0);
Packit 8f70b4
   file_buf=0;
Packit 8f70b4
   EmptyRespQueue();
Packit 8f70b4
   state=DISCONNECTED;
Packit 8f70b4
   if(mode==STORE)
Packit 8f70b4
      SetError(STORE_FAILED);
Packit 8f70b4
   protocol_version=0;
Packit 8f70b4
   send_translate=0;
Packit 8f70b4
   recv_translate=0;
Packit 8f70b4
   ssh_id=0;
Packit 8f70b4
   home_auto.set(FindHomeAuto());
Packit 8f70b4
   // may have to resend file info queries.
Packit 8f70b4
   if(fileset_for_info)
Packit 8f70b4
      fileset_for_info->rewind();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::Init()
Packit 8f70b4
{
Packit 8f70b4
   state=DISCONNECTED;
Packit 8f70b4
   ssh_id=0;
Packit 8f70b4
   eof=false;
Packit 8f70b4
   received_greeting=false;
Packit 8f70b4
   password_sent=0;
Packit 8f70b4
   protocol_version=0;
Packit 8f70b4
   send_translate=0;
Packit 8f70b4
   recv_translate=0;
Packit 8f70b4
   max_packets_in_flight=16;
Packit 8f70b4
   max_packets_in_flight_slow_start=1;
Packit 8f70b4
   size_read=0x8000;
Packit 8f70b4
   size_write=0x8000;
Packit 8f70b4
   use_full_path=false;
Packit 8f70b4
   flush_timer.Set(0,500);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::SFtp() : SSH_Access("SFTP:")
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::~SFtp()
Packit 8f70b4
{
Packit 8f70b4
   Disconnect();
Packit 8f70b4
   Close();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::SFtp(const SFtp *o) : super(o)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
bool SFtp::Packet::HasID()
Packit 8f70b4
{
Packit 8f70b4
   return(type!=SSH_FXP_INIT && type!=SSH_FXP_VERSION);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::Packet::PackString(Buffer *b,const char *str,int len)
Packit 8f70b4
{
Packit 8f70b4
   if(len==-1)
Packit 8f70b4
      len=strlen(str);
Packit 8f70b4
   b->PackUINT32BE(len);
Packit 8f70b4
   b->Put(str,len);
Packit 8f70b4
}
Packit 8f70b4
SFtp::unpack_status_t SFtp::Packet::UnpackString(const Buffer *b,int *offset,int limit,xstring *str_out)
Packit 8f70b4
{
Packit 8f70b4
   if(limit-*offset<4)
Packit 8f70b4
   {
Packit 8f70b4
      // We unpack strings when we have already received complete packet,
Packit 8f70b4
      // so it is not possible to receive any more data.
Packit 8f70b4
      LogError(2,"bad string in reply (truncated length field)");
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   int len=b->UnpackUINT32BE(*offset);
Packit 8f70b4
   if(len>limit-*offset-4)
Packit 8f70b4
   {
Packit 8f70b4
      LogError(2,"bad string in reply (invalid length field)");
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   }
Packit 8f70b4
   *offset+=4;
Packit 8f70b4
Packit 8f70b4
   const char *data;
Packit 8f70b4
   int data_len;
Packit 8f70b4
   b->Get(&data,&data_len);
Packit 8f70b4
Packit 8f70b4
   str_out->nset(data+*offset,len);
Packit 8f70b4
Packit 8f70b4
   *offset+=len;
Packit 8f70b4
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::Packet::Unpack(const Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   unpacked=0;
Packit 8f70b4
   if(b->Size()<4)
Packit 8f70b4
      return b->Eof()?UNPACK_PREMATURE_EOF:UNPACK_NO_DATA_YET;
Packit 8f70b4
   length=b->UnpackUINT32BE(0);
Packit 8f70b4
   unpacked+=4;
Packit 8f70b4
   if(length<1)
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   if(b->Size()
Packit 8f70b4
      return b->Eof()?UNPACK_PREMATURE_EOF:UNPACK_NO_DATA_YET;
Packit 8f70b4
   int t=b->UnpackUINT8(4);
Packit 8f70b4
   unpacked++;
Packit 8f70b4
   if(!is_valid_reply(t))
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   type=(packet_type)t;
Packit 8f70b4
   if(HasID())
Packit 8f70b4
   {
Packit 8f70b4
      if(length<5)
Packit 8f70b4
	 return UNPACK_WRONG_FORMAT;
Packit 8f70b4
      id=b->UnpackUINT32BE(5);
Packit 8f70b4
      unpacked+=4;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      id=0;
Packit 8f70b4
   }
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::UnpackPacket(Buffer *b,SFtp::Packet **p)
Packit 8f70b4
{
Packit 8f70b4
   Packet *&pp=*p;
Packit 8f70b4
   pp=0;
Packit 8f70b4
Packit 8f70b4
   Packet probe;
Packit 8f70b4
   unpack_status_t res=probe.Unpack(b);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
Packit 8f70b4
   LogRecvF(9,"got a packet, length=%d, type=%d(%s), id=%u\n",
Packit 8f70b4
      probe.GetLength(),probe.GetPacketType(),probe.GetPacketTypeText(),probe.GetID());
Packit 8f70b4
Packit 8f70b4
   switch(probe.GetPacketType())
Packit 8f70b4
   {
Packit 8f70b4
   case SSH_FXP_VERSION:
Packit 8f70b4
      pp=new Reply_VERSION();
Packit 8f70b4
      break;
Packit 8f70b4
   case SSH_FXP_NAME:
Packit 8f70b4
      pp=new Reply_NAME(protocol_version);
Packit 8f70b4
      break;
Packit 8f70b4
   case SSH_FXP_ATTRS:
Packit 8f70b4
      pp=new Reply_ATTRS(protocol_version);
Packit 8f70b4
      break;
Packit 8f70b4
   case SSH_FXP_STATUS:
Packit 8f70b4
      pp=new Reply_STATUS(protocol_version);
Packit 8f70b4
      break;
Packit 8f70b4
   case SSH_FXP_HANDLE:
Packit 8f70b4
      pp=new Reply_HANDLE();
Packit 8f70b4
      break;
Packit 8f70b4
   case SSH_FXP_DATA:
Packit 8f70b4
      pp=new Reply_DATA();
Packit 8f70b4
      break;
Packit 8f70b4
   case SSH_FXP_INIT:
Packit 8f70b4
   case SSH_FXP_OPEN:
Packit 8f70b4
   case SSH_FXP_CLOSE:
Packit 8f70b4
   case SSH_FXP_READ:
Packit 8f70b4
   case SSH_FXP_WRITE:
Packit 8f70b4
   case SSH_FXP_LSTAT:
Packit 8f70b4
   case SSH_FXP_FSTAT:
Packit 8f70b4
   case SSH_FXP_SETSTAT:
Packit 8f70b4
   case SSH_FXP_FSETSTAT:
Packit 8f70b4
   case SSH_FXP_OPENDIR:
Packit 8f70b4
   case SSH_FXP_READDIR:
Packit 8f70b4
   case SSH_FXP_REMOVE:
Packit 8f70b4
   case SSH_FXP_MKDIR:
Packit 8f70b4
   case SSH_FXP_RMDIR:
Packit 8f70b4
   case SSH_FXP_REALPATH:
Packit 8f70b4
   case SSH_FXP_STAT:
Packit 8f70b4
   case SSH_FXP_RENAME:
Packit 8f70b4
   case SSH_FXP_READLINK:
Packit 8f70b4
   case SSH_FXP_SYMLINK:
Packit 8f70b4
   case SSH_FXP_LINK:
Packit 8f70b4
   case SSH_FXP_BLOCK:
Packit 8f70b4
   case SSH_FXP_UNBLOCK:
Packit 8f70b4
   case SSH_FXP_EXTENDED:
Packit 8f70b4
      LogError(0,"request in reply??");
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   case SSH_FXP_EXTENDED_REPLY:
Packit 8f70b4
      LogError(0,"unexpected SSH_FXP_EXTENDED_REPLY");
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   }
Packit 8f70b4
   res=pp->Unpack(b);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
   {
Packit 8f70b4
      switch(res)
Packit 8f70b4
      {
Packit 8f70b4
      case UNPACK_PREMATURE_EOF:
Packit 8f70b4
	 LogError(0,"premature eof");
Packit 8f70b4
	 break;
Packit 8f70b4
      case UNPACK_WRONG_FORMAT:
Packit 8f70b4
	 LogError(0,"wrong packet format");
Packit 8f70b4
	 break;
Packit 8f70b4
      case UNPACK_NO_DATA_YET:
Packit 8f70b4
      case UNPACK_SUCCESS:
Packit 8f70b4
	 ;
Packit 8f70b4
      }
Packit 8f70b4
      probe.DropData(b);
Packit 8f70b4
      delete pp;
Packit 8f70b4
      pp=0;
Packit 8f70b4
   }
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::SendRequest(Packet *request,Expect::expect_t tag,int i)
Packit 8f70b4
{
Packit 8f70b4
   request->SetID(ssh_id++);
Packit 8f70b4
   request->ComputeLength();
Packit 8f70b4
   LogSendF(9,"sending a packet, length=%d, type=%d(%s), id=%u\n",
Packit 8f70b4
      request->GetLength(),request->GetPacketType(),request->GetPacketTypeText(),request->GetID());
Packit 8f70b4
   request->Pack(send_buf.get_non_const());
Packit 8f70b4
   PushExpect(new Expect(request,tag,i));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *SFtp::SkipHome(const char *path)
Packit 8f70b4
{
Packit 8f70b4
   if(path[0]=='~' && path[1]=='/' && path[2])
Packit 8f70b4
      return path+2;
Packit 8f70b4
   if(path[0]=='~' && !path[1])
Packit 8f70b4
      return ".";
Packit 8f70b4
   if(!home)
Packit 8f70b4
      return path;
Packit 8f70b4
   int home_len=home.path.length();
Packit 8f70b4
   if(strncmp(home,path,home_len))
Packit 8f70b4
      return path;
Packit 8f70b4
   if(path[home_len]=='/' && path[home_len+1] && path[home_len+1]!='/')
Packit 8f70b4
      return path+home_len+1;
Packit 8f70b4
   if(!path[home_len])
Packit 8f70b4
      return ".";
Packit 8f70b4
   return path;
Packit 8f70b4
}
Packit 8f70b4
const char *SFtp::WirePath(const char *path)
Packit 8f70b4
{
Packit 8f70b4
   path=dir_file(cwd,path);
Packit 8f70b4
   if(!use_full_path || path[0]=='~')
Packit 8f70b4
      path=SkipHome(path);
Packit 8f70b4
   LogNote(9,"path on wire is `%s'",path);
Packit 8f70b4
   return lc_to_utf8(path);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::SendRequest()
Packit 8f70b4
{
Packit 8f70b4
   max_packets_in_flight_slow_start=1;
Packit 8f70b4
   ExpandTildeInCWD();
Packit 8f70b4
   switch((open_mode)mode)
Packit 8f70b4
   {
Packit 8f70b4
   case CHANGE_DIR:
Packit 8f70b4
      LogNote(9,"checking directory `%s'",file.get());
Packit 8f70b4
      SendRequest(new Request_STAT(lc_to_utf8(file),0,protocol_version),Expect::CWD);
Packit 8f70b4
      SendRequest(new Request_STAT(lc_to_utf8(dir_file(file,".")),0,protocol_version),Expect::CWD);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case RETRIEVE:
Packit 8f70b4
      SendRequest(new Request_OPEN(WirePath(file),SSH_FXF_READ,
Packit 8f70b4
	 ACE4_READ_DATA|ACE4_READ_ATTRIBUTES,SSH_FXF_OPEN_EXISTING,protocol_version),Expect::HANDLE);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case LIST:
Packit 8f70b4
   case LONG_LIST:
Packit 8f70b4
      SendRequest(new Request_OPENDIR(WirePath(file)),Expect::HANDLE);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case STORE:
Packit 8f70b4
      SendRequest(
Packit 8f70b4
	 new Request_OPEN(WirePath(file),
Packit 8f70b4
	    SSH_FXF_WRITE|SSH_FXF_CREAT|(pos==0?SSH_FXF_TRUNC:0),
Packit 8f70b4
	    ACE4_WRITE_DATA|ACE4_WRITE_ATTRIBUTES,
Packit 8f70b4
	    pos==0?SSH_FXF_CREATE_TRUNCATE:SSH_FXF_OPEN_OR_CREATE,
Packit 8f70b4
	    protocol_version),
Packit 8f70b4
	 Expect::HANDLE);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case ARRAY_INFO:
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case RENAME:
Packit 8f70b4
   {
Packit 8f70b4
      if(protocol_version<3)
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(NOT_SUPP);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      unsigned options=0;
Packit 8f70b4
      if(rename_f) {
Packit 8f70b4
	 options=SSH_FXF_RENAME_OVERWRITE;
Packit 8f70b4
	 if(protocol_version<5) {
Packit 8f70b4
	    // overwrite is not supported, remove the target explicitly
Packit 8f70b4
	    SendRequest(new Request_REMOVE(WirePath(file1)),Expect::IGNORE);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      SendRequest(new Request_RENAME(WirePath(file),WirePath(file1),
Packit 8f70b4
			options,protocol_version),Expect::DEFAULT);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   case CHANGE_MODE:
Packit 8f70b4
   {
Packit 8f70b4
      Request_SETSTAT *req=new Request_SETSTAT(WirePath(file),protocol_version);
Packit 8f70b4
      req->attrs.permissions=chmod_mode;
Packit 8f70b4
      req->attrs.flags|=SSH_FILEXFER_ATTR_PERMISSIONS;
Packit 8f70b4
      SendRequest(req,Expect::DEFAULT);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   case MAKE_DIR:
Packit 8f70b4
      if(mkdir_p)
Packit 8f70b4
      {
Packit 8f70b4
	 Ref<StringSet> dirs(MkdirMakeSet());
Packit 8f70b4
	 for(int i=0; i<dirs->Count(); i++)
Packit 8f70b4
	    SendRequest(new Request_MKDIR(WirePath(dirs->String(i)),protocol_version),Expect::IGNORE);
Packit 8f70b4
      }
Packit 8f70b4
      SendRequest(new Request_MKDIR(WirePath(file),protocol_version),Expect::DEFAULT);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case REMOVE_DIR:
Packit 8f70b4
      SendRequest(new Request_RMDIR(WirePath(file)),Expect::DEFAULT);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case REMOVE:
Packit 8f70b4
      SendRequest(new Request_REMOVE(WirePath(file)),Expect::DEFAULT);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case LINK:
Packit 8f70b4
      if(protocol_version<6) {
Packit 8f70b4
	 SetError(NOT_SUPP);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   case SYMLINK:
Packit 8f70b4
      if(protocol_version<3) {
Packit 8f70b4
	 SetError(NOT_SUPP);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      if(protocol_version>=6)
Packit 8f70b4
	 SendRequest(new Request_LINK(mode==SYMLINK?lc_to_utf8(file):WirePath(file),WirePath(file1),mode==SYMLINK),Expect::DEFAULT);
Packit 8f70b4
      else
Packit 8f70b4
	 SendRequest(new Request_SYMLINK(lc_to_utf8(file),WirePath(file1)),Expect::DEFAULT);
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      break;
Packit 8f70b4
   case QUOTE_CMD:
Packit 8f70b4
   case MP_LIST:
Packit 8f70b4
      SetError(NOT_SUPP);
Packit 8f70b4
      break;
Packit 8f70b4
   case CONNECT_VERIFY:
Packit 8f70b4
   case CLOSED:
Packit 8f70b4
      abort();
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::SendArrayInfoRequests()
Packit 8f70b4
{
Packit 8f70b4
   for(FileInfo *fi=fileset_for_info->curr();
Packit 8f70b4
      fi && RespQueueSize()
Packit 8f70b4
      fi=fileset_for_info->next())
Packit 8f70b4
   {
Packit 8f70b4
      if(fi->need&(fi->SIZE|fi->DATE|fi->MODE|fi->TYPE|fi->USER|fi->GROUP)) {
Packit 8f70b4
	 unsigned flags=0;
Packit 8f70b4
	 if(fi->need&fi->SIZE) flags|=SSH_FILEXFER_ATTR_SIZE;
Packit 8f70b4
	 if(fi->need&fi->DATE) flags|=SSH_FILEXFER_ATTR_MODIFYTIME;
Packit 8f70b4
	 if(fi->need&fi->MODE) flags|=SSH_FILEXFER_ATTR_PERMISSIONS;
Packit 8f70b4
	 if(fi->need&(fi->USER|fi->GROUP)) flags|=SSH_FILEXFER_ATTR_OWNERGROUP;
Packit 8f70b4
	 SendRequest(new Request_STAT(WirePath(fi->name),flags,
Packit 8f70b4
	    protocol_version),Expect::INFO,fileset_for_info->curr_index());
Packit 8f70b4
      }
Packit 8f70b4
      if(fi->need&fi->SYMLINK_DEF && protocol_version>=3)
Packit 8f70b4
	 SendRequest(new Request_READLINK(WirePath(fi->name)),
Packit 8f70b4
	    Expect::INFO_READLINK,fileset_for_info->curr_index());
Packit 8f70b4
   }
Packit 8f70b4
   if(RespQueueIsEmpty())
Packit 8f70b4
      state=DONE;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::CloseHandle(Expect::expect_t c)
Packit 8f70b4
{
Packit 8f70b4
   if(handle)
Packit 8f70b4
   {
Packit 8f70b4
      SendRequest(new Request_CLOSE(handle),c);
Packit 8f70b4
      handle.set(0);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::Close()
Packit 8f70b4
{
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case(DISCONNECTED):
Packit 8f70b4
   case(WAITING):
Packit 8f70b4
   case(CONNECTED):
Packit 8f70b4
   case(DONE):
Packit 8f70b4
   case(FILE_RECV):
Packit 8f70b4
   case(FILE_SEND):
Packit 8f70b4
      break;
Packit 8f70b4
   case(CONNECTING):
Packit 8f70b4
   case(CONNECTING_1):
Packit 8f70b4
   case(CONNECTING_2):
Packit 8f70b4
      Disconnect();
Packit 8f70b4
   }
Packit 8f70b4
   CloseExpectQueue();
Packit 8f70b4
   state=(recv_buf?CONNECTED:DISCONNECTED);
Packit 8f70b4
   eof=false;
Packit 8f70b4
   file_buf=0;
Packit 8f70b4
   file_set=0;
Packit 8f70b4
   CloseHandle(Expect::IGNORE);
Packit 8f70b4
   super::Close();
Packit 8f70b4
   // don't need these out-of-order packets anymore
Packit 8f70b4
   ooo_chain.truncate();
Packit 8f70b4
   if(recv_buf)
Packit 8f70b4
      recv_buf->Resume();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int SFtp::HandlePty()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(pty_recv_buf==0)
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int s;
Packit 8f70b4
   pty_recv_buf->Get(&b,&s);
Packit 8f70b4
   const char *eol=(const char*)memchr(b,'\n',s);
Packit 8f70b4
   if(!eol)
Packit 8f70b4
   {
Packit 8f70b4
      if(pty_recv_buf->Eof())
Packit 8f70b4
	 LogError(0,_("Peer closed connection"));
Packit 8f70b4
      if(pty_recv_buf->Error())
Packit 8f70b4
	 LogError(0,"pty read: %s",pty_recv_buf->ErrorText());
Packit 8f70b4
      if(pty_recv_buf->Eof() || pty_recv_buf->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 Disconnect(pty_recv_buf->ErrorText());
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
   m=MOVED;
Packit 8f70b4
   s=eol-b+1;
Packit 8f70b4
   char *line=string_alloca(s);
Packit 8f70b4
   memcpy(line,b,s-1);
Packit 8f70b4
   line[s-1]=0;
Packit 8f70b4
   pty_recv_buf->Skip(s);
Packit 8f70b4
Packit 8f70b4
   LogRecv(4,line);
Packit 8f70b4
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::HandleExpect(Expect *e)
Packit 8f70b4
{
Packit 8f70b4
   const Packet *reply=e->reply;
Packit 8f70b4
   if(reply->TypeIs(SSH_FXP_STATUS))
Packit 8f70b4
   {
Packit 8f70b4
      Reply_STATUS *r=(Reply_STATUS*)reply;
Packit 8f70b4
      const char *message=r->GetMessage();
Packit 8f70b4
      LogNote(9,"status code=%d(%s), message=%s",r->GetCode(),r->GetCodeText(),
Packit 8f70b4
	 message?message:"NULL");
Packit 8f70b4
   }
Packit 8f70b4
   switch(e->tag)
Packit 8f70b4
   {
Packit 8f70b4
   case Expect::FXP_VERSION:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_VERSION))
Packit 8f70b4
      {
Packit 8f70b4
	 protocol_version=((Reply_VERSION*)reply)->GetVersion();
Packit 8f70b4
	 LogNote(9,"protocol version set to %d",protocol_version);
Packit 8f70b4
	 const char *charset=0;
Packit 8f70b4
	 if(protocol_version>=4)
Packit 8f70b4
	    charset="UTF-8";
Packit 8f70b4
	 else
Packit 8f70b4
	    charset=ResMgr::Query("sftp:charset",hostname);
Packit 8f70b4
	 if(charset && *charset)
Packit 8f70b4
	 {
Packit 8f70b4
	    send_translate=new DirectedBuffer(DirectedBuffer::PUT);
Packit 8f70b4
	    recv_translate=new DirectedBuffer(DirectedBuffer::GET);
Packit 8f70b4
	    send_translate->SetTranslation(charset,false);
Packit 8f70b4
	    recv_translate->SetTranslation(charset,true);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 SetError(FATAL,"cannot negotiate protocol version");
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::HOME_PATH:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_NAME))
Packit 8f70b4
      {
Packit 8f70b4
	 Reply_NAME *r=(Reply_NAME*)reply;
Packit 8f70b4
	 const NameAttrs *a=r->GetNameAttrs(0);
Packit 8f70b4
	 if(a && !home_auto)
Packit 8f70b4
	 {
Packit 8f70b4
	    home_auto.set(utf8_to_lc(a->name));
Packit 8f70b4
	    LogNote(9,"home set to %s",home_auto.get());
Packit 8f70b4
	    PropagateHomeAuto();
Packit 8f70b4
	    if(!home)
Packit 8f70b4
	       set_home(home_auto);
Packit 8f70b4
	    cache->SetDirectory(this, home, true);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::CWD:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_ATTRS))
Packit 8f70b4
      {
Packit 8f70b4
	 const FileAttrs *a=((Reply_ATTRS*)reply)->GetAttrs();
Packit 8f70b4
	 if(a->type!=SSH_FILEXFER_TYPE_DIRECTORY
Packit 8f70b4
	 && a->type!=SSH_FILEXFER_TYPE_SYMLINK)	// workaround for RouterOS v6
Packit 8f70b4
	 {
Packit 8f70b4
	    LogError(1,"got file type %d",a->type);
Packit 8f70b4
	    cache->SetDirectory(this,cwd,false);
Packit 8f70b4
	    SetError(NO_FILE,strerror(ENOTDIR));
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(mode==CHANGE_DIR && GetExpectCount(Expect::CWD)==0)
Packit 8f70b4
	 {
Packit 8f70b4
	    cwd.Set(file);
Packit 8f70b4
	    eof=true;
Packit 8f70b4
	    cache->SetDirectory(this,cwd,true);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
	 SetError(NO_FILE,reply);
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::HANDLE:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_HANDLE))
Packit 8f70b4
      {
Packit 8f70b4
	 handle.set(((Reply_HANDLE*)reply)->GetHandle());
Packit 8f70b4
	 state=(mode==STORE?FILE_SEND:FILE_RECV);
Packit 8f70b4
	 file_buf=new Buffer;
Packit 8f70b4
	 xstring handle_x("");
Packit 8f70b4
	 int handle_len=handle.length();
Packit 8f70b4
	 for(int i=0; i
Packit 8f70b4
	    handle_x.appendf("%02X",(unsigned char)handle[i]);
Packit 8f70b4
	 LogNote(9,"got file handle %s (%d)",handle_x.get(),handle_len);
Packit 8f70b4
	 request_pos=real_pos=pos;
Packit 8f70b4
	 if(mode==RETRIEVE) {
Packit 8f70b4
	    SendRequest(new Request_FSTAT(handle,
Packit 8f70b4
	       SSH_FILEXFER_ATTR_SIZE|SSH_FILEXFER_ATTR_MODIFYTIME|SSH_FILEXFER_ATTR_PERMISSIONS,
Packit 8f70b4
	       protocol_version),Expect::INFO);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
	 SetError(NO_FILE,reply);
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::HANDLE_STALE:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_HANDLE))
Packit 8f70b4
      {
Packit 8f70b4
	 // close the handle immediately.
Packit 8f70b4
	 const xstring &handle=((Reply_HANDLE*)reply)->GetHandle();
Packit 8f70b4
	 SendRequest(new Request_CLOSE(handle),Expect::IGNORE);
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::DATA:
Packit 8f70b4
      if(max_packets_in_flight_slow_start
Packit 8f70b4
	 max_packets_in_flight_slow_start++;
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_DATA))
Packit 8f70b4
      {
Packit 8f70b4
	 const Request_READ *r=e->request.Cast<Request_READ>();
Packit 8f70b4
	 Reply_DATA *d=(Reply_DATA*)reply;
Packit 8f70b4
	 if(r->pos==pos+file_buf->Size())
Packit 8f70b4
	 {
Packit 8f70b4
	    const char *b; int s;
Packit 8f70b4
	    d->GetData(&b,&s);
Packit 8f70b4
	    LogNote(9,"data packet: pos=%lld, size=%d",(long long)r->pos,s);
Packit 8f70b4
	    file_buf->Put(b,s);
Packit 8f70b4
	    if(d->Eof())
Packit 8f70b4
	       goto eof;
Packit 8f70b4
	    if(r->len > unsigned(s))   // received less than requested?
Packit 8f70b4
	    {
Packit 8f70b4
	       // if we have not yet requested next chunk of data,
Packit 8f70b4
	       // then adjust request position, else re-request missed data.
Packit 8f70b4
	       if(r->pos+r->len==request_pos)
Packit 8f70b4
		  request_pos=r->pos+s;
Packit 8f70b4
	       else
Packit 8f70b4
		  SendRequest(new Request_READ(handle,r->pos+s,r->len-s),Expect::DATA);
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    LogNote(9,"put a packet with id=%d on out-of-order chain (need_pos=%lld packet_pos=%lld)",
Packit 8f70b4
	       reply->GetID(),(long long)(pos+file_buf->Size()),(long long)r->pos);
Packit 8f70b4
	    if(ooo_chain.count()>=64)
Packit 8f70b4
	    {
Packit 8f70b4
	       LogError(0,"Too many out-of-order packets");
Packit 8f70b4
	       Disconnect();
Packit 8f70b4
	       return;
Packit 8f70b4
	    }
Packit 8f70b4
	    ooo_chain.append(e);
Packit 8f70b4
	    return;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else if(reply->TypeIs(SSH_FXP_NAME))
Packit 8f70b4
      {
Packit 8f70b4
	 Reply_NAME *r=(Reply_NAME*)reply;
Packit 8f70b4
	 LogNote(9,"file name count=%d",r->GetCount());
Packit 8f70b4
	 for(int i=0; i<r->GetCount(); i++)
Packit 8f70b4
	 {
Packit 8f70b4
	    const NameAttrs *a=r->GetNameAttrs(i);
Packit 8f70b4
	    FileInfo *info=MakeFileInfo(a);
Packit 8f70b4
	    if(mode==LIST)
Packit 8f70b4
	    {
Packit 8f70b4
	       file_buf->Put(a->name);
Packit 8f70b4
	       if(a->attrs.type==SSH_FILEXFER_TYPE_DIRECTORY)
Packit 8f70b4
		  file_buf->Put("/");
Packit 8f70b4
	       file_buf->Put("\n");
Packit 8f70b4
	    }
Packit 8f70b4
	    else if(mode==LONG_LIST)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(a->longname)
Packit 8f70b4
	       {
Packit 8f70b4
		  file_buf->Put(a->longname);
Packit 8f70b4
		  file_buf->Put("\n");
Packit 8f70b4
	       }
Packit 8f70b4
	       else if(info)
Packit 8f70b4
	       {
Packit 8f70b4
		  info->MakeLongName();
Packit 8f70b4
		  file_buf->Put(info->longname);
Packit 8f70b4
		  file_buf->Put("\n");
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	    if(info)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(!file_set)
Packit 8f70b4
		  file_set=new FileSet;
Packit 8f70b4
	       file_set->Add(info);
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 if(r->Eof())
Packit 8f70b4
	    goto eof;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 if(reply->TypeIs(SSH_FXP_STATUS))
Packit 8f70b4
	 {
Packit 8f70b4
	    if(((Reply_STATUS*)reply)->GetCode()==SSH_FX_EOF)
Packit 8f70b4
	    {
Packit 8f70b4
	    eof:
Packit 8f70b4
	       if(!eof)
Packit 8f70b4
		  LogNote(9,"eof");
Packit 8f70b4
	       eof=true;
Packit 8f70b4
	       state=DONE;
Packit 8f70b4
	       if(file_buf && ooo_chain.count()==0)
Packit 8f70b4
		  file_buf->PutEOF();
Packit 8f70b4
	       break;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 SetError(NO_FILE,reply);
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::INFO:
Packit 8f70b4
      if(mode==ARRAY_INFO)
Packit 8f70b4
      {
Packit 8f70b4
	 if(reply->TypeIs(SSH_FXP_ATTRS)) {
Packit 8f70b4
	    FileInfo *fi=(*fileset_for_info)[e->i];
Packit 8f70b4
	    MergeAttrs(fi,((Reply_ATTRS*)reply)->GetAttrs());
Packit 8f70b4
	 }
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      entity_size=NO_SIZE;
Packit 8f70b4
      entity_date=NO_DATE;
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_ATTRS))
Packit 8f70b4
      {
Packit 8f70b4
	 const FileAttrs *a=((Reply_ATTRS*)reply)->GetAttrs();
Packit 8f70b4
	 if(a->flags&SSH_FILEXFER_ATTR_SIZE)
Packit 8f70b4
	    entity_size=a->size;
Packit 8f70b4
	 if(a->flags&SSH_FILEXFER_ATTR_MODIFYTIME)
Packit 8f70b4
	    entity_date=a->mtime;
Packit 8f70b4
	 LogNote(9,"file info: size=%lld, date=%s",(long long)entity_size,ctime(&entity_date));
Packit 8f70b4
      }
Packit 8f70b4
      if(opt_size)
Packit 8f70b4
	 *opt_size=entity_size;
Packit 8f70b4
      if(opt_date)
Packit 8f70b4
	 *opt_date=entity_date;
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::INFO_READLINK:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_NAME)) {
Packit 8f70b4
	 Reply_NAME *r=(Reply_NAME*)reply;
Packit 8f70b4
	 const NameAttrs *a=r->GetNameAttrs(0);
Packit 8f70b4
	 LogNote(9,"file info: symlink=%s",a->name.get());
Packit 8f70b4
	 if(mode==ARRAY_INFO)
Packit 8f70b4
	 {
Packit 8f70b4
	    FileInfo *fi=(*fileset_for_info)[e->i];
Packit 8f70b4
	    fi->SetSymlink(a->name);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::WRITE_STATUS:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_STATUS))
Packit 8f70b4
      {
Packit 8f70b4
	 if(((Reply_STATUS*)reply)->GetCode()==SSH_FX_OK) {
Packit 8f70b4
	    TrySuccess();
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      SetError(NO_FILE,reply);
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::DEFAULT:
Packit 8f70b4
      if(reply->TypeIs(SSH_FXP_STATUS))
Packit 8f70b4
      {
Packit 8f70b4
	 if(((Reply_STATUS*)reply)->GetCode()==SSH_FX_OK)
Packit 8f70b4
	 {
Packit 8f70b4
	    state=DONE;
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      SetError(NO_FILE,reply);
Packit 8f70b4
      break;
Packit 8f70b4
   case Expect::IGNORE:
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   delete e;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::RequestMoreData()
Packit 8f70b4
{
Packit 8f70b4
   Enter(this);
Packit 8f70b4
   if(mode==RETRIEVE) {
Packit 8f70b4
      int req_len=size_read;
Packit 8f70b4
      SendRequest(new Request_READ(handle,request_pos,req_len),Expect::DATA);
Packit 8f70b4
      request_pos+=req_len;
Packit 8f70b4
   } else if(mode==LIST || mode==LONG_LIST) {
Packit 8f70b4
      SendRequest(new Request_READDIR(handle),Expect::DATA);
Packit 8f70b4
   }
Packit 8f70b4
   Leave(this);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int SFtp::HandleReplies()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(recv_buf==0)
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(state!=CONNECTING_2)
Packit 8f70b4
      m|=HandlePty();
Packit 8f70b4
Packit 8f70b4
   if(!recv_buf)
Packit 8f70b4
      return MOVED;
Packit 8f70b4
Packit 8f70b4
   if(file_buf) {
Packit 8f70b4
      off_t need_pos=pos+file_buf->Size();
Packit 8f70b4
      // there are usually a few of out-of-order packets, no need for fast search
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 if(ooo_chain[i]->has_data_at_pos(need_pos)) {
Packit 8f70b4
	    Expect *e=ooo_chain[i];
Packit 8f70b4
	    ooo_chain[i]=0; // to keep the Expect
Packit 8f70b4
	    ooo_chain.remove(i);
Packit 8f70b4
	    HandleExpect(e);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(ooo_chain.count()==0 && eof && file_buf && !file_buf->Eof())
Packit 8f70b4
      file_buf->PutEOF();
Packit 8f70b4
Packit 8f70b4
   if(recv_buf->Size()<4)
Packit 8f70b4
   {
Packit 8f70b4
      if(recv_buf->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(0,"receive: %s",recv_buf->ErrorText());
Packit 8f70b4
	 Disconnect(recv_buf->ErrorText());
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(recv_buf->Eof() && pty_recv_buf->Size()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(0,_("Peer closed connection"));
Packit 8f70b4
	 Disconnect(last_ssh_message?last_ssh_message.get():_("Peer closed connection"));
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(recv_buf->IsSuspended())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   Packet *reply=0;
Packit 8f70b4
   unpack_status_t st=UnpackPacket(recv_buf.get_non_const(),&reply);
Packit 8f70b4
   if(st==UNPACK_NO_DATA_YET)
Packit 8f70b4
      return m;
Packit 8f70b4
   if(st!=UNPACK_SUCCESS)
Packit 8f70b4
   {
Packit 8f70b4
      LogError(2,_("invalid server response format"));
Packit 8f70b4
      Disconnect(_("invalid server response format"));
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   reply->DropData(recv_buf.get_non_const());
Packit 8f70b4
   Expect *e=FindExpectExclusive(reply);
Packit 8f70b4
   if(e==0)
Packit 8f70b4
   {
Packit 8f70b4
      LogError(3,_("extra server response"));
Packit 8f70b4
      delete reply;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   HandleExpect(e);
Packit 8f70b4
   return MOVED;
Packit 8f70b4
}
Packit 8f70b4
void SFtp::PushExpect(Expect *e)
Packit 8f70b4
{
Packit 8f70b4
   expect_queue.add(e->request->GetKey(),e);
Packit 8f70b4
}
Packit 8f70b4
SFtp::Expect *SFtp::FindExpectExclusive(Packet *p)
Packit 8f70b4
{
Packit 8f70b4
   Expect *e=expect_queue.borrow(p->GetKey());
Packit 8f70b4
   if(e)
Packit 8f70b4
      e->reply=p;
Packit 8f70b4
   return e;
Packit 8f70b4
}
Packit 8f70b4
void SFtp::CloseExpectQueue()
Packit 8f70b4
{
Packit 8f70b4
   for(Expect *e=expect_queue.each_begin(); e; e=expect_queue.each_next())
Packit 8f70b4
   {
Packit 8f70b4
      switch(e->tag)
Packit 8f70b4
      {
Packit 8f70b4
      case Expect::IGNORE:
Packit 8f70b4
      case Expect::HANDLE_STALE:
Packit 8f70b4
      case Expect::HOME_PATH:
Packit 8f70b4
      case Expect::FXP_VERSION:
Packit 8f70b4
	 break;
Packit 8f70b4
      case Expect::CWD:
Packit 8f70b4
      case Expect::INFO:
Packit 8f70b4
      case Expect::INFO_READLINK:
Packit 8f70b4
      case Expect::DEFAULT:
Packit 8f70b4
      case Expect::DATA:
Packit 8f70b4
      case Expect::WRITE_STATUS:
Packit 8f70b4
	 e->tag=Expect::IGNORE;
Packit 8f70b4
	 break;
Packit 8f70b4
      case Expect::HANDLE:
Packit 8f70b4
	 e->tag=Expect::HANDLE_STALE;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int SFtp::GetExpectCount(Expect::expect_t tag)
Packit 8f70b4
{
Packit 8f70b4
   int count=0;
Packit 8f70b4
   for(Expect *e=expect_queue.each_begin(); e; e=expect_queue.each_next())
Packit 8f70b4
      count+=(e->tag==tag);
Packit 8f70b4
   return count;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Glob *SFtp::MakeGlob(const char *pat)
Packit 8f70b4
{
Packit 8f70b4
   return new GenericGlob(this,pat);
Packit 8f70b4
}
Packit 8f70b4
ListInfo *SFtp::MakeListInfo(const char *dir)
Packit 8f70b4
{
Packit 8f70b4
   return new SFtpListInfo(this,dir);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int SFtp::Read(Buffer *buf,int size)
Packit 8f70b4
{
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return error_code;
Packit 8f70b4
   if(mode==CLOSED)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(state==DONE && !(file_buf && file_buf->Size()>0))
Packit 8f70b4
      return 0;	  // eof
Packit 8f70b4
   if(state==FILE_RECV)
Packit 8f70b4
   {
Packit 8f70b4
      // keep some packets in flight.
Packit 8f70b4
      int limit=(entity_size>=0?max_packets_in_flight:max_packets_in_flight_slow_start);
Packit 8f70b4
      if(RespQueueSize()<limit && !file_buf->Eof())
Packit 8f70b4
      {
Packit 8f70b4
	 // but don't request much after possible EOF.
Packit 8f70b4
	 if(entity_size<0 || request_pos
Packit 8f70b4
	    RequestMoreData();
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(file_buf && file_buf->Size()>0)
Packit 8f70b4
   {
Packit 8f70b4
      const char *buf1;
Packit 8f70b4
      int size1;
Packit 8f70b4
      file_buf->Get(&buf1,&size1);
Packit 8f70b4
      if(buf1==0)
Packit 8f70b4
	 return 0;
Packit 8f70b4
Packit 8f70b4
      int bytes_allowed=rate_limit->BytesAllowedToGet();
Packit 8f70b4
      if(size1>bytes_allowed)
Packit 8f70b4
	 size1=bytes_allowed;
Packit 8f70b4
      if(size1==0)
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      if(size>size1)
Packit 8f70b4
	 size=size1;
Packit 8f70b4
      size=buf->MoveDataHere(file_buf,size);
Packit 8f70b4
      if(size<=0)
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      pos+=size;
Packit 8f70b4
      real_pos+=size;
Packit 8f70b4
      rate_limit->BytesGot(size);
Packit 8f70b4
      TrySuccess();
Packit 8f70b4
      return size;
Packit 8f70b4
   }
Packit 8f70b4
   return DO_AGAIN;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int SFtp::Write(const void *buf,int size)
Packit 8f70b4
{
Packit 8f70b4
   if(mode!=STORE)
Packit 8f70b4
      return(0);
Packit 8f70b4
Packit 8f70b4
   Resume();
Packit 8f70b4
   Enter(this);
Packit 8f70b4
   Do();
Packit 8f70b4
   Leave(this);
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return(error_code);
Packit 8f70b4
Packit 8f70b4
   if(state!=FILE_SEND || rate_limit==0
Packit 8f70b4
   || send_buf->Size()>2*max_buf)
Packit 8f70b4
      return DO_AGAIN;
Packit 8f70b4
Packit 8f70b4
   {
Packit 8f70b4
      int allowed=rate_limit->BytesAllowedToPut();
Packit 8f70b4
      if(allowed==0)
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      if(size+file_buf->Size()>allowed)
Packit 8f70b4
	 size=allowed-send_buf->Size();
Packit 8f70b4
   }
Packit 8f70b4
   if(size+file_buf->Size()>max_buf)
Packit 8f70b4
      size=max_buf-file_buf->Size();
Packit 8f70b4
   if(entity_size>=0 && pos+size>entity_size)
Packit 8f70b4
      size=entity_size-pos;
Packit 8f70b4
   if(size<=0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   file_buf->Put(static_cast<const char*>(buf),size);
Packit 8f70b4
   rate_limit->BytesPut(size);
Packit 8f70b4
   pos+=size;
Packit 8f70b4
   real_pos+=size;
Packit 8f70b4
   return(size);
Packit 8f70b4
}
Packit 8f70b4
int SFtp::Buffered()
Packit 8f70b4
{
Packit 8f70b4
   if(file_buf==0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   off_t b=file_buf->Size()+send_buf->Size()*size_write/(size_write+20);
Packit 8f70b4
   if(b<0)
Packit 8f70b4
      b=0;
Packit 8f70b4
   else if(b>real_pos)
Packit 8f70b4
      b=real_pos;
Packit 8f70b4
   return b;
Packit 8f70b4
}
Packit 8f70b4
int SFtp::StoreStatus()
Packit 8f70b4
{
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return error_code;
Packit 8f70b4
   if(state==FILE_SEND && !eof)
Packit 8f70b4
   {
Packit 8f70b4
      eof=true;
Packit 8f70b4
      return IN_PROGRESS;
Packit 8f70b4
   }
Packit 8f70b4
   if(state==DONE)
Packit 8f70b4
      return OK;
Packit 8f70b4
   return IN_PROGRESS;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int SFtp::Done()
Packit 8f70b4
{
Packit 8f70b4
   if(mode==CLOSED)
Packit 8f70b4
      return OK;
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return error_code;
Packit 8f70b4
   if(eof || state==DONE)
Packit 8f70b4
      return OK;
Packit 8f70b4
   if(mode==CONNECT_VERIFY)
Packit 8f70b4
      return OK;
Packit 8f70b4
   return IN_PROGRESS;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::SuspendInternal()
Packit 8f70b4
{
Packit 8f70b4
   super::SuspendInternal();
Packit 8f70b4
   if(recv_buf)
Packit 8f70b4
      recv_buf->SuspendSlave();
Packit 8f70b4
   if(send_buf)
Packit 8f70b4
      send_buf->SuspendSlave();
Packit 8f70b4
   if(pty_send_buf)
Packit 8f70b4
      pty_send_buf->SuspendSlave();
Packit 8f70b4
   if(pty_recv_buf)
Packit 8f70b4
      pty_recv_buf->SuspendSlave();
Packit 8f70b4
}
Packit 8f70b4
void SFtp::ResumeInternal()
Packit 8f70b4
{
Packit 8f70b4
   if(recv_buf)
Packit 8f70b4
      recv_buf->ResumeSlave();
Packit 8f70b4
   if(send_buf)
Packit 8f70b4
      send_buf->ResumeSlave();
Packit 8f70b4
   if(pty_send_buf)
Packit 8f70b4
      pty_send_buf->ResumeSlave();
Packit 8f70b4
   if(pty_recv_buf)
Packit 8f70b4
      pty_recv_buf->ResumeSlave();
Packit 8f70b4
   super::ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *SFtp::CurrentStatus()
Packit 8f70b4
{
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case DISCONNECTED:
Packit 8f70b4
      if(!ReconnectAllowed())
Packit 8f70b4
	 return DelayingMessage();
Packit 8f70b4
      return _("Not connected");
Packit 8f70b4
   case CONNECTING:
Packit 8f70b4
      if(ssh && ssh->status)
Packit 8f70b4
	 return ssh->status;
Packit 8f70b4
   case CONNECTING_1:
Packit 8f70b4
   case CONNECTING_2:
Packit 8f70b4
      return _("Connecting...");
Packit 8f70b4
   case CONNECTED:
Packit 8f70b4
      return _("Connected");
Packit 8f70b4
   case WAITING:
Packit 8f70b4
      return _("Waiting for response...");
Packit 8f70b4
   case FILE_RECV:
Packit 8f70b4
      return _("Receiving data");
Packit 8f70b4
   case FILE_SEND:
Packit 8f70b4
      return _("Sending data");
Packit 8f70b4
   case DONE:
Packit 8f70b4
      return _("Done");
Packit 8f70b4
   }
Packit 8f70b4
   return "";
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool SFtp::SameSiteAs(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   if(!SameProtoAs(fa))
Packit 8f70b4
      return false;
Packit 8f70b4
   SFtp *o=(SFtp*)fa;
Packit 8f70b4
   return(!xstrcasecmp(hostname,o->hostname) && !xstrcmp(portname,o->portname)
Packit 8f70b4
   && !xstrcmp(user,o->user) && !xstrcmp(pass,o->pass));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool SFtp::SameLocationAs(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   if(!SameSiteAs(fa))
Packit 8f70b4
      return false;
Packit 8f70b4
   SFtp *o=(SFtp*)fa;
Packit 8f70b4
   if(xstrcmp(cwd,o->cwd))
Packit 8f70b4
      return false;
Packit 8f70b4
   if(xstrcmp(home,o->home))
Packit 8f70b4
      return false;
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::Reconfig(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   super::Reconfig(name);
Packit 8f70b4
   const char *c=hostname;
Packit 8f70b4
   max_packets_in_flight=Query("max-packets-in-flight",c);
Packit 8f70b4
   if(max_packets_in_flight<1)
Packit 8f70b4
      max_packets_in_flight=1;
Packit 8f70b4
   if(max_packets_in_flight_slow_start>max_packets_in_flight)
Packit 8f70b4
      max_packets_in_flight_slow_start=max_packets_in_flight;
Packit 8f70b4
   size_read=Query("size-read",c);
Packit 8f70b4
   size_write=Query("size-write",c);
Packit 8f70b4
   if(size_read<16)
Packit 8f70b4
      size_read=16;
Packit 8f70b4
   if(size_write<16)
Packit 8f70b4
      size_write=16;
Packit 8f70b4
   use_full_path=QueryBool("use-full-path",c);
Packit 8f70b4
   if(!xstrcmp(name,"sftp:charset") && protocol_version && protocol_version<4)
Packit 8f70b4
   {
Packit 8f70b4
      if(!IsSuspended())
Packit 8f70b4
	 cache->TreeChanged(this,"/");
Packit 8f70b4
      const char *charset=ResMgr::Query("sftp:charset",hostname);
Packit 8f70b4
      if(charset && *charset)
Packit 8f70b4
      {
Packit 8f70b4
	 if(!send_translate)
Packit 8f70b4
	    send_translate=new DirectedBuffer(DirectedBuffer::PUT);
Packit 8f70b4
	 if(!recv_translate)
Packit 8f70b4
	    recv_translate=new DirectedBuffer(DirectedBuffer::GET);
Packit 8f70b4
	 send_translate->SetTranslation(charset,false);
Packit 8f70b4
	 recv_translate->SetTranslation(charset,true);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 send_translate=0;
Packit 8f70b4
	 recv_translate=0;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::ClassInit()
Packit 8f70b4
{
Packit 8f70b4
   // register the class
Packit 8f70b4
   Register("sftp",SFtp::New);
Packit 8f70b4
}
Packit 8f70b4
FileAccess *SFtp::New() { return new SFtp(); }
Packit 8f70b4
Packit 8f70b4
DirList *SFtp::MakeDirList(ArgV *args)
Packit 8f70b4
{
Packit 8f70b4
  return new SFtpDirList(this,args);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
struct code_text { int code; const char *text; };
Packit 8f70b4
Packit 8f70b4
const char *SFtp::Packet::GetPacketTypeText()
Packit 8f70b4
{
Packit 8f70b4
   struct code_text text_table[]={
Packit 8f70b4
      { SSH_FXP_INIT,          "INIT"		},
Packit 8f70b4
      { SSH_FXP_VERSION,       "VERSION"	},
Packit 8f70b4
      { SSH_FXP_OPEN,          "OPEN"		},
Packit 8f70b4
      { SSH_FXP_CLOSE,         "CLOSE"		},
Packit 8f70b4
      { SSH_FXP_READ,          "READ"		},
Packit 8f70b4
      { SSH_FXP_WRITE,         "WRITE"		},
Packit 8f70b4
      { SSH_FXP_LSTAT,         "LSTAT"		},
Packit 8f70b4
      { SSH_FXP_FSTAT,         "FSTAT"		},
Packit 8f70b4
      { SSH_FXP_SETSTAT,       "SETSTAT"	},
Packit 8f70b4
      { SSH_FXP_FSETSTAT,      "FSETSTAT"	},
Packit 8f70b4
      { SSH_FXP_OPENDIR,       "OPENDIR"	},
Packit 8f70b4
      { SSH_FXP_READDIR,       "READDIR"	},
Packit 8f70b4
      { SSH_FXP_REMOVE,        "REMOVE"		},
Packit 8f70b4
      { SSH_FXP_MKDIR,         "MKDIR"		},
Packit 8f70b4
      { SSH_FXP_RMDIR,         "RMDIR"		},
Packit 8f70b4
      { SSH_FXP_REALPATH,      "REALPATH"	},
Packit 8f70b4
      { SSH_FXP_STAT,          "STAT"		},
Packit 8f70b4
      { SSH_FXP_RENAME,        "RENAME"		},
Packit 8f70b4
      { SSH_FXP_READLINK,      "READLINK"	},
Packit 8f70b4
      { SSH_FXP_SYMLINK,       "SYMLINK"	},
Packit 8f70b4
      { SSH_FXP_LINK,          "LINK"		},
Packit 8f70b4
      { SSH_FXP_BLOCK,         "BLOCK"		},
Packit 8f70b4
      { SSH_FXP_UNBLOCK,       "UNBLOCK"	},
Packit 8f70b4
      { SSH_FXP_STATUS,        "STATUS"		},
Packit 8f70b4
      { SSH_FXP_HANDLE,        "HANDLE"		},
Packit 8f70b4
      { SSH_FXP_DATA,          "DATA"		},
Packit 8f70b4
      { SSH_FXP_NAME,          "NAME"		},
Packit 8f70b4
      { SSH_FXP_ATTRS,         "ATTRS"		},
Packit 8f70b4
      { SSH_FXP_EXTENDED,      "EXTENDED"	},
Packit 8f70b4
      { SSH_FXP_EXTENDED_REPLY,"EXTENDED_REPLY"	},
Packit 8f70b4
      {0,0}
Packit 8f70b4
   };
Packit 8f70b4
   for(int i=0; text_table[i].text; i++)
Packit 8f70b4
      if(text_table[i].code==type)
Packit 8f70b4
	 return text_table[i].text;
Packit 8f70b4
   return "UNKNOWN";
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *SFtp::Reply_STATUS::GetCodeText()
Packit 8f70b4
{
Packit 8f70b4
   static const char *text_table[]={
Packit 8f70b4
      "OK",
Packit 8f70b4
      "EOF",
Packit 8f70b4
      "No such file",
Packit 8f70b4
      "Permission denied",
Packit 8f70b4
      "Failure",
Packit 8f70b4
      "Bad message",
Packit 8f70b4
      "No connection",
Packit 8f70b4
      "Connection lost",
Packit 8f70b4
      "Operation not supported",
Packit 8f70b4
      "Invalid handle",
Packit 8f70b4
      "No such path",
Packit 8f70b4
      "File already exists",
Packit 8f70b4
      "Write protect",
Packit 8f70b4
      "No media",
Packit 8f70b4
      "No space on filesystem",
Packit 8f70b4
      "Quota exceeded",
Packit 8f70b4
      "Unknown principal",
Packit 8f70b4
      "Lock conflict",
Packit 8f70b4
      "Directory not empty",
Packit 8f70b4
      "Not a directory",
Packit 8f70b4
      "Invalid file name",
Packit 8f70b4
      "Link loop",
Packit 8f70b4
      "Cannot delete",
Packit 8f70b4
      "Invalid parameter",
Packit 8f70b4
      "File is a directory",
Packit 8f70b4
      "Byte range lock conflict",
Packit 8f70b4
      "Byte range lock refused",
Packit 8f70b4
      "Delete pending",
Packit 8f70b4
      "File corrupt",
Packit 8f70b4
      "Owner invalid",
Packit 8f70b4
      "Group invalid"
Packit 8f70b4
   };
Packit 8f70b4
   if(code>=0 && code
Packit 8f70b4
      return text_table[code];
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::SetError(int code,const Packet *reply)
Packit 8f70b4
{
Packit 8f70b4
   if(!reply->TypeIs(SSH_FXP_STATUS))
Packit 8f70b4
   {
Packit 8f70b4
      SetError(code);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   Reply_STATUS *status=(Reply_STATUS*)reply;
Packit 8f70b4
   const char *message=status->GetMessage();
Packit 8f70b4
   if(message && *message)
Packit 8f70b4
   {
Packit 8f70b4
      SetError(code,utf8_to_lc(message));
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   message=status->GetCodeText();
Packit 8f70b4
   if(message)
Packit 8f70b4
   {
Packit 8f70b4
      SetError(code,_(message));
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   SetError(code);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
#define UNPACK_GENERIC(out,size,fun)   \
Packit 8f70b4
   do {				       \
Packit 8f70b4
      if(limit-*offset<(size))	       \
Packit 8f70b4
	 return UNPACK_WRONG_FORMAT;   \
Packit 8f70b4
      out=b->fun(*offset);	       \
Packit 8f70b4
      *offset+=(size);		       \
Packit 8f70b4
   } while(0)
Packit 8f70b4
#define UNPACK8(out)	UNPACK_GENERIC(out,1,UnpackUINT8)
Packit 8f70b4
#define UNPACK32(out)	UNPACK_GENERIC(out,4,UnpackUINT32BE)
Packit 8f70b4
#define UNPACK64(out)	UNPACK_GENERIC(out,8,UnpackUINT64BE)
Packit 8f70b4
#define UNPACK32_SIGNED(out)	UNPACK_GENERIC(out,4,UnpackINT32BE)
Packit 8f70b4
#define UNPACK64_SIGNED(out)	UNPACK_GENERIC(out,8,UnpackINT64BE)
Packit 8f70b4
#define PACK8(data)	b->PackUINT8(data)
Packit 8f70b4
#define PACK32(data)	b->PackUINT32BE(data)
Packit 8f70b4
#define PACK64(data)	b->PackUINT64BE(data)
Packit 8f70b4
#define PACK32_SIGNED(data)	b->PackINT32BE(data)
Packit 8f70b4
#define PACK64_SIGNED(data)	b->PackINT64BE(data)
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::PacketSTRING::Unpack(const Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res;
Packit 8f70b4
   res=Packet::Unpack(b);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   res=UnpackString(b,&unpacked,length+4,&string);
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::Reply_NAME::Unpack(const Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res=Packet::Unpack(b);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   int *offset=&unpacked;
Packit 8f70b4
   int limit=length+4;
Packit 8f70b4
   UNPACK32(count);
Packit 8f70b4
   names=new NameAttrs[count];
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      res=names[i].Unpack(b,offset,limit,protocol_version);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
   }
Packit 8f70b4
   if(*offset
Packit 8f70b4
      UNPACK8(eof);
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
SFtp::unpack_status_t SFtp::Reply_DATA::Unpack(const Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res=PacketSTRING::Unpack(b);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   int *offset=&unpacked;
Packit 8f70b4
   int limit=length+4;
Packit 8f70b4
   if(*offset
Packit 8f70b4
      UNPACK8(eof);
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
SFtp::unpack_status_t SFtp::NameAttrs::Unpack(const Buffer *b,int *offset,int limit,int protocol_version)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res;
Packit 8f70b4
Packit 8f70b4
   res=Packet::UnpackString(b,offset,limit,&name);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   if(protocol_version<=3)
Packit 8f70b4
   {
Packit 8f70b4
      res=Packet::UnpackString(b,offset,limit,&longname);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
   }
Packit 8f70b4
   res=attrs.Unpack(b,offset,limit,protocol_version);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::FileAttrs::Unpack(const Buffer *b,int *offset,int limit,int protocol_version)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res;
Packit 8f70b4
Packit 8f70b4
   UNPACK32(flags);
Packit 8f70b4
   if(protocol_version>=4)
Packit 8f70b4
      UNPACK8(type);
Packit 8f70b4
   if(flags & SSH_FILEXFER_ATTR_SIZE)
Packit 8f70b4
      UNPACK64(size);
Packit 8f70b4
   if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_UIDGID))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK32(uid);
Packit 8f70b4
      UNPACK32(gid);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_OWNERGROUP))
Packit 8f70b4
   {
Packit 8f70b4
      res=Packet::UnpackString(b,offset,limit,&owner);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
      res=Packet::UnpackString(b,offset,limit,&group);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
   }
Packit 8f70b4
   if(flags & SSH_FILEXFER_ATTR_PERMISSIONS)
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK32(permissions);
Packit 8f70b4
      if(protocol_version<=3)
Packit 8f70b4
      {
Packit 8f70b4
	 switch(permissions&S_IFMT)
Packit 8f70b4
	 {
Packit 8f70b4
	 case S_IFREG: type=SSH_FILEXFER_TYPE_REGULAR;	 break;
Packit 8f70b4
	 case S_IFDIR: type=SSH_FILEXFER_TYPE_DIRECTORY; break;
Packit 8f70b4
	 case S_IFLNK: type=SSH_FILEXFER_TYPE_SYMLINK;	 break;
Packit 8f70b4
	 case S_IFIFO:
Packit 8f70b4
	 case S_IFCHR:
Packit 8f70b4
	 case S_IFBLK: type=SSH_FILEXFER_TYPE_SPECIAL;	 break;
Packit 8f70b4
	 default:      type=SSH_FILEXFER_TYPE_UNKNOWN;	 break;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_ACMODTIME))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK32_SIGNED(atime);
Packit 8f70b4
      UNPACK32_SIGNED(mtime);
Packit 8f70b4
      flags|=SSH_FILEXFER_ATTR_MODIFYTIME;
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACCESSTIME))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK64_SIGNED(atime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 UNPACK32(atime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_CREATETIME))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK64_SIGNED(createtime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 UNPACK32(createtime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_MODIFYTIME))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK64_SIGNED(mtime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 UNPACK32(mtime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_CTIME))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK64_SIGNED(ctime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 UNPACK32(ctime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(atime_nseconds>999999999 || createtime_nseconds>999999999 || mtime_nseconds>999999999 || ctime_nseconds>999999999)
Packit 8f70b4
      return UNPACK_WRONG_FORMAT;
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACL))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK32(ace_count);
Packit 8f70b4
      ace=new FileACE[ace_count];
Packit 8f70b4
      for(unsigned i=0; i
Packit 8f70b4
      {
Packit 8f70b4
	 res=ace[i].Unpack(b,offset,limit);
Packit 8f70b4
	 if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	    return res;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_BITS))
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK32(attrib_bits);
Packit 8f70b4
      if(protocol_version>=6)
Packit 8f70b4
	 UNPACK32(attrib_bits_valid);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_TEXT_HINT))
Packit 8f70b4
      UNPACK8(text_hint);
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_MIME_TYPE))
Packit 8f70b4
   {
Packit 8f70b4
      res=Packet::UnpackString(b,offset,limit,&mime_type);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_LINK_COUNT))
Packit 8f70b4
      UNPACK32(link_count);
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_UNTRANSLATED_NAME))
Packit 8f70b4
   {
Packit 8f70b4
      res=Packet::UnpackString(b,offset,limit,&untranslated_name);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
   }
Packit 8f70b4
   if(flags & SSH_FILEXFER_ATTR_EXTENDED)
Packit 8f70b4
   {
Packit 8f70b4
      UNPACK32(extended_count);
Packit 8f70b4
      extended_attrs=new ExtFileAttr[extended_count];
Packit 8f70b4
      for(unsigned i=0; i
Packit 8f70b4
      {
Packit 8f70b4
	 res=extended_attrs[i].Unpack(b,offset,limit);
Packit 8f70b4
	 if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	    return res;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
void SFtp::FileAttrs::Pack(Buffer *b,int protocol_version)
Packit 8f70b4
{
Packit 8f70b4
   if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_MODIFYTIME)
Packit 8f70b4
   && !(flags & SSH_FILEXFER_ATTR_ACCESSTIME))
Packit 8f70b4
   {
Packit 8f70b4
      flags|=SSH_FILEXFER_ATTR_ACMODTIME;
Packit 8f70b4
      atime=mtime;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   unsigned flags_mask=SSH_FILEXFER_ATTR_MASK_V3;
Packit 8f70b4
   if(protocol_version==4) flags_mask=SSH_FILEXFER_ATTR_MASK_V4;
Packit 8f70b4
   if(protocol_version==5) flags_mask=SSH_FILEXFER_ATTR_MASK_V5;
Packit 8f70b4
   if(protocol_version>=6) flags_mask=SSH_FILEXFER_ATTR_MASK_V6;
Packit 8f70b4
   PACK32(flags&flags_mask);
Packit 8f70b4
Packit 8f70b4
   if(protocol_version>=4)
Packit 8f70b4
   {
Packit 8f70b4
      if(type==0)
Packit 8f70b4
      {
Packit 8f70b4
	 switch(permissions&S_IFMT)
Packit 8f70b4
	 {
Packit 8f70b4
	 case S_IFREG: type=SSH_FILEXFER_TYPE_REGULAR;	 break;
Packit 8f70b4
	 case S_IFDIR: type=SSH_FILEXFER_TYPE_DIRECTORY; break;
Packit 8f70b4
	 case S_IFLNK: type=SSH_FILEXFER_TYPE_SYMLINK;	 break;
Packit 8f70b4
	 case S_IFIFO:
Packit 8f70b4
	 case S_IFCHR:
Packit 8f70b4
	 case S_IFBLK: type=SSH_FILEXFER_TYPE_SPECIAL;	 break;
Packit 8f70b4
	 default:      type=SSH_FILEXFER_TYPE_UNKNOWN;	 break;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      PACK8(type);
Packit 8f70b4
   }
Packit 8f70b4
   if(flags & SSH_FILEXFER_ATTR_SIZE)
Packit 8f70b4
      PACK64(size);
Packit 8f70b4
   if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_UIDGID))
Packit 8f70b4
   {
Packit 8f70b4
      PACK32(uid);
Packit 8f70b4
      PACK32(gid);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_OWNERGROUP))
Packit 8f70b4
   {
Packit 8f70b4
      Packet::PackString(b,owner);
Packit 8f70b4
      Packet::PackString(b,group);
Packit 8f70b4
   }
Packit 8f70b4
   if(flags & SSH_FILEXFER_ATTR_PERMISSIONS)
Packit 8f70b4
      PACK32(permissions);
Packit 8f70b4
   if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_ACMODTIME))
Packit 8f70b4
   {
Packit 8f70b4
      PACK32_SIGNED(atime);
Packit 8f70b4
      PACK32_SIGNED(mtime);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACCESSTIME))
Packit 8f70b4
   {
Packit 8f70b4
      PACK64_SIGNED(atime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 PACK32(atime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_CREATETIME))
Packit 8f70b4
   {
Packit 8f70b4
      PACK64_SIGNED(createtime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 PACK32(createtime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_MODIFYTIME))
Packit 8f70b4
   {
Packit 8f70b4
      PACK64_SIGNED(mtime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 PACK32(mtime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_CTIME))
Packit 8f70b4
   {
Packit 8f70b4
      PACK64_SIGNED(ctime);
Packit 8f70b4
      if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
Packit 8f70b4
	 PACK32(ctime_nseconds);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACL))
Packit 8f70b4
   {
Packit 8f70b4
      PACK32(ace_count);
Packit 8f70b4
      for(unsigned i=0; i
Packit 8f70b4
	 ace[i].Pack(b);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_BITS))
Packit 8f70b4
   {
Packit 8f70b4
      PACK32(attrib_bits);
Packit 8f70b4
      if(protocol_version>=6)
Packit 8f70b4
	 PACK32(attrib_bits_valid);
Packit 8f70b4
   }
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_TEXT_HINT))
Packit 8f70b4
      PACK8(text_hint);
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_MIME_TYPE))
Packit 8f70b4
      Packet::PackString(b,mime_type);
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_LINK_COUNT))
Packit 8f70b4
      PACK32(link_count);
Packit 8f70b4
   if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_UNTRANSLATED_NAME))
Packit 8f70b4
      Packet::PackString(b,untranslated_name);
Packit 8f70b4
   if(flags & SSH_FILEXFER_ATTR_EXTENDED)
Packit 8f70b4
   {
Packit 8f70b4
      PACK32(extended_count);
Packit 8f70b4
      for(unsigned i=0; i
Packit 8f70b4
	 extended_attrs[i].Pack(b);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
int SFtp::FileAttrs::ComputeLength(int protocol_version)
Packit 8f70b4
{
Packit 8f70b4
   Buffer b;
Packit 8f70b4
   Pack(&b,protocol_version);
Packit 8f70b4
   return b.Size();
Packit 8f70b4
}
Packit 8f70b4
SFtp::FileAttrs::~FileAttrs()
Packit 8f70b4
{
Packit 8f70b4
   delete[] extended_attrs;
Packit 8f70b4
   delete[] ace;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::FileAttrs::FileACE::Unpack(const Buffer *b,int *offset,int limit)
Packit 8f70b4
{
Packit 8f70b4
   UNPACK32(ace_type);
Packit 8f70b4
   UNPACK32(ace_flag);
Packit 8f70b4
   UNPACK32(ace_mask);
Packit 8f70b4
   return Packet::UnpackString(b,offset,limit,&who);
Packit 8f70b4
}
Packit 8f70b4
void SFtp::FileAttrs::FileACE::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   PACK32(ace_type);
Packit 8f70b4
   PACK32(ace_flag);
Packit 8f70b4
   PACK32(ace_mask);
Packit 8f70b4
   Packet::PackString(b,who);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::FileAttrs::ExtFileAttr::Unpack(const Buffer *b,int *offset,int limit)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res;
Packit 8f70b4
   res=Packet::UnpackString(b,offset,limit,&extended_type);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   res=Packet::UnpackString(b,offset,limit,&extended_data);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
void SFtp::FileAttrs::ExtFileAttr::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   Packet::PackString(b,extended_type);
Packit 8f70b4
   Packet::PackString(b,extended_data);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::Reply_ATTRS::Unpack(const Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res=Packet::Unpack(b);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   return attrs.Unpack(b,&unpacked,length+4,protocol_version);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtp::unpack_status_t SFtp::Reply_STATUS::Unpack(const Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   unpack_status_t res=Packet::Unpack(b);
Packit 8f70b4
   if(res!=UNPACK_SUCCESS)
Packit 8f70b4
      return res;
Packit 8f70b4
   int *offset=&unpacked;
Packit 8f70b4
   int limit=length+4;
Packit 8f70b4
   UNPACK32(code);
Packit 8f70b4
   if(protocol_version>=3)
Packit 8f70b4
   {
Packit 8f70b4
      if(unpacked>=limit)
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(2,"Status reply lacks `error message' field");
Packit 8f70b4
	 return UNPACK_SUCCESS;
Packit 8f70b4
      }
Packit 8f70b4
      res=Packet::UnpackString(b,offset,limit,&message);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
      if(unpacked>=limit)
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(2,"Status reply lacks `language tag' field");
Packit 8f70b4
	 return UNPACK_SUCCESS;
Packit 8f70b4
      }
Packit 8f70b4
      res=Packet::UnpackString(b,offset,limit,&language);
Packit 8f70b4
      if(res!=UNPACK_SUCCESS)
Packit 8f70b4
	 return res;
Packit 8f70b4
   }
Packit 8f70b4
   return UNPACK_SUCCESS;
Packit 8f70b4
}
Packit 8f70b4
void SFtp::Request_READ::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   PacketSTRING::Pack(b);
Packit 8f70b4
   PACK64(pos);
Packit 8f70b4
   PACK32(len);
Packit 8f70b4
}
Packit 8f70b4
void SFtp::Request_WRITE::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   PacketSTRING::Pack(b);
Packit 8f70b4
   PACK64(pos);
Packit 8f70b4
   int len=data.length();
Packit 8f70b4
   PACK32(len);
Packit 8f70b4
   b->Put(data,len);
Packit 8f70b4
}
Packit 8f70b4
void SFtp::Request_OPEN::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   PacketSTRING::Pack(b);
Packit 8f70b4
   if(protocol_version<=4)
Packit 8f70b4
      PACK32(pflags);
Packit 8f70b4
   if(protocol_version>=5)
Packit 8f70b4
   {
Packit 8f70b4
      PACK32(desired_access);
Packit 8f70b4
      PACK32(flags);
Packit 8f70b4
   }
Packit 8f70b4
   attrs.Pack(b,protocol_version);
Packit 8f70b4
}
Packit 8f70b4
void SFtp::Request_RENAME::ComputeLength()
Packit 8f70b4
{
Packit 8f70b4
   Packet::ComputeLength();
Packit 8f70b4
   length+=4+strlen(oldpath)+4+strlen(newpath);
Packit 8f70b4
   if(protocol_version>=5)
Packit 8f70b4
      length+=4; // flags
Packit 8f70b4
}
Packit 8f70b4
void SFtp::Request_RENAME::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   Packet::Pack(b);
Packit 8f70b4
   Packet::PackString(b,oldpath);
Packit 8f70b4
   Packet::PackString(b,newpath);
Packit 8f70b4
   if(protocol_version>=5)
Packit 8f70b4
      PACK32(flags);
Packit 8f70b4
}
Packit 8f70b4
void SFtp::Request_SYMLINK::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   Packet::Pack(b);
Packit 8f70b4
   Packet::PackString(b,oldpath);
Packit 8f70b4
   Packet::PackString(b,newpath);
Packit 8f70b4
}
Packit 8f70b4
void SFtp::Request_LINK::Pack(Buffer *b)
Packit 8f70b4
{
Packit 8f70b4
   Packet::Pack(b);
Packit 8f70b4
   Packet::PackString(b,newpath);
Packit 8f70b4
   Packet::PackString(b,oldpath);
Packit 8f70b4
   PACK8(symbolic);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *SFtp::utf8_to_lc(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   if(!recv_translate || !s)
Packit 8f70b4
      return s;
Packit 8f70b4
Packit 8f70b4
   recv_translate->ResetTranslation();
Packit 8f70b4
   recv_translate->PutTranslated(s);
Packit 8f70b4
   recv_translate->Buffer::Put("",1);
Packit 8f70b4
   int len;
Packit 8f70b4
   recv_translate->Get(&s,&len;;
Packit 8f70b4
   recv_translate->Skip(len);
Packit 8f70b4
   return xstring::get_tmp(s,len);
Packit 8f70b4
}
Packit 8f70b4
const char *SFtp::lc_to_utf8(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   if(!send_translate || !s)
Packit 8f70b4
      return s;
Packit 8f70b4
Packit 8f70b4
   send_translate->ResetTranslation();
Packit 8f70b4
   send_translate->PutTranslated(s);
Packit 8f70b4
   send_translate->Buffer::Put("",1);
Packit 8f70b4
   int len;
Packit 8f70b4
   send_translate->Get(&s,&len;;
Packit 8f70b4
   send_translate->Skip(len);
Packit 8f70b4
   return xstring::get_tmp(s,len);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileSet *SFtp::GetFileSet()
Packit 8f70b4
{
Packit 8f70b4
   FileSet *fset=file_set.borrow();
Packit 8f70b4
   return fset?fset:new FileSet;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtp::MergeAttrs(FileInfo *fi,const FileAttrs *a)
Packit 8f70b4
{
Packit 8f70b4
   switch(a->type)
Packit 8f70b4
   {
Packit 8f70b4
   case SSH_FILEXFER_TYPE_REGULAR:  fi->SetType(fi->NORMAL);    break;
Packit 8f70b4
   case SSH_FILEXFER_TYPE_DIRECTORY:fi->SetType(fi->DIRECTORY); break;
Packit 8f70b4
   case SSH_FILEXFER_TYPE_SYMLINK:  fi->SetType(fi->SYMLINK);   break;
Packit 8f70b4
   default: break;
Packit 8f70b4
   }
Packit 8f70b4
   if(a->flags&SSH_FILEXFER_ATTR_SIZE)
Packit 8f70b4
      fi->SetSize(a->size);
Packit 8f70b4
   if(a->flags&SSH_FILEXFER_ATTR_UIDGID)
Packit 8f70b4
   {
Packit 8f70b4
      char id[24];
Packit 8f70b4
      snprintf(id,sizeof(id),"%u",a->uid);
Packit 8f70b4
      fi->SetUser(id);
Packit 8f70b4
      snprintf(id,sizeof(id),"%u",a->gid);
Packit 8f70b4
      fi->SetGroup(id);
Packit 8f70b4
   }
Packit 8f70b4
   if(a->flags&SSH_FILEXFER_ATTR_OWNERGROUP)
Packit 8f70b4
   {
Packit 8f70b4
      fi->SetUser (utf8_to_lc(a->owner));
Packit 8f70b4
      fi->SetGroup(utf8_to_lc(a->group));
Packit 8f70b4
   }
Packit 8f70b4
   if(a->flags&SSH_FILEXFER_ATTR_PERMISSIONS)
Packit 8f70b4
      fi->SetMode(a->permissions&07777);
Packit 8f70b4
   if(a->flags&SSH_FILEXFER_ATTR_MODIFYTIME)
Packit 8f70b4
      fi->SetDate(a->mtime,0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileInfo *SFtp::MakeFileInfo(const NameAttrs *na)
Packit 8f70b4
{
Packit 8f70b4
   const FileAttrs *a=&na->attrs;
Packit 8f70b4
   const char *name=utf8_to_lc(na->name);
Packit 8f70b4
   const char *longname=utf8_to_lc(na->longname);
Packit 8f70b4
Packit 8f70b4
   LogNote(10,"NameAttrs(name=\"%s\",type=%d,longname=\"%s\")\n",name?name:"",a->type,longname?longname:"");
Packit 8f70b4
Packit 8f70b4
   if(!name || !name[0])
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(name[0]=='~')
Packit 8f70b4
      name=dir_file(".",name);
Packit 8f70b4
   Ref<FileInfo> fi(new FileInfo(name));
Packit 8f70b4
   switch(a->type)
Packit 8f70b4
   {
Packit 8f70b4
   case SSH_FILEXFER_TYPE_REGULAR:
Packit 8f70b4
   case SSH_FILEXFER_TYPE_DIRECTORY:
Packit 8f70b4
   case SSH_FILEXFER_TYPE_SYMLINK:
Packit 8f70b4
   case SSH_FILEXFER_TYPE_UNKNOWN: break;
Packit 8f70b4
   default: return 0;
Packit 8f70b4
   }
Packit 8f70b4
   if(longname)
Packit 8f70b4
      fi->SetLongName(longname);
Packit 8f70b4
   MergeAttrs(fi.get_non_const(),a);
Packit 8f70b4
   if(fi->longname && !a->owner)
Packit 8f70b4
   {
Packit 8f70b4
      // try to extract owner/group from long name.
Packit 8f70b4
      Ref<FileInfo> ls(FileInfo::parse_ls_line(fi->longname,0));
Packit 8f70b4
      if(ls)
Packit 8f70b4
      {
Packit 8f70b4
	 if(ls->user)
Packit 8f70b4
	    fi->SetUser(ls->user);
Packit 8f70b4
	 if(ls->group)
Packit 8f70b4
	    fi->SetGroup(ls->group);
Packit 8f70b4
	 if(ls->nlinks>0)
Packit 8f70b4
	    fi->SetNlink(ls->nlinks);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   return fi.borrow();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
#undef super
Packit 8f70b4
#define super DirList
Packit 8f70b4
#include "ArgV.h"
Packit 8f70b4
Packit 8f70b4
int SFtpDirList::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
Packit 8f70b4
   if(done)
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(buf->Eof())
Packit 8f70b4
   {
Packit 8f70b4
      done=true;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!ubuf)
Packit 8f70b4
   {
Packit 8f70b4
      const char *cache_buffer=0;
Packit 8f70b4
      int cache_buffer_size=0;
Packit 8f70b4
      int err;
Packit 8f70b4
      const FileSet *fset_c;
Packit 8f70b4
      if(use_cache && FileAccess::cache->Find(session,dir,FA::LONG_LIST,&err,
Packit 8f70b4
				    &cache_buffer,&cache_buffer_size,&fset_c))
Packit 8f70b4
      {
Packit 8f70b4
	 if(err)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetErrorCached(cache_buffer);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 ubuf=new IOBuffer(IOBuffer::GET);
Packit 8f70b4
	 ubuf->Put(cache_buffer,cache_buffer_size);
Packit 8f70b4
	 ubuf->PutEOF();
Packit 8f70b4
	 fset=new FileSet(fset_c);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 session->Open(dir,FA::LONG_LIST);
Packit 8f70b4
	 ubuf=new IOBufferFileAccess(session);
Packit 8f70b4
	 if(FileAccess::cache->IsEnabled(session->GetHostName()))
Packit 8f70b4
	    ubuf->Save(FileAccess::cache->SizeLimit());
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int len;
Packit 8f70b4
   ubuf->Get(&b,&len;;
Packit 8f70b4
   if(b==0) // eof
Packit 8f70b4
   {
Packit 8f70b4
      if(!fset && session->IsOpen())
Packit 8f70b4
	 fset=session.Cast<SFtp>()->GetFileSet();
Packit 8f70b4
      FileAccess::cache->Add(session,dir,FA::LONG_LIST,FA::OK,ubuf,fset);
Packit 8f70b4
      if(use_file_set)
Packit 8f70b4
      {
Packit 8f70b4
	 fset->Sort(fset->BYNAME,false);
Packit 8f70b4
	 for(fset->rewind(); fset->curr(); fset->next())
Packit 8f70b4
	 {
Packit 8f70b4
	    FileInfo *fi=fset->curr();
Packit 8f70b4
	    buf->Put(fi->GetLongName());
Packit 8f70b4
	    buf->Put("\n");
Packit 8f70b4
	 }
Packit 8f70b4
	 fset=0;
Packit 8f70b4
      }
Packit 8f70b4
      ubuf=0;
Packit 8f70b4
      dir=args->getnext();
Packit 8f70b4
      if(!dir)
Packit 8f70b4
	 buf->PutEOF();
Packit 8f70b4
      else
Packit 8f70b4
	 buf->Format("\n%s:\n",dir);
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(len>0)
Packit 8f70b4
   {
Packit 8f70b4
      if(!use_file_set)
Packit 8f70b4
	 buf->Put(b,len);
Packit 8f70b4
      ubuf->Skip(len);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(ubuf->Error())
Packit 8f70b4
   {
Packit 8f70b4
      SetError(ubuf->ErrorText());
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
SFtpDirList::SFtpDirList(SFtp *s,ArgV *a)
Packit 8f70b4
   : DirList(s,a)
Packit 8f70b4
{
Packit 8f70b4
   use_file_set=true;
Packit 8f70b4
   args->rewind();
Packit 8f70b4
   int opt;
Packit 8f70b4
   while((opt=args->getopt("fCFl"))!=EOF)
Packit 8f70b4
   {
Packit 8f70b4
      switch(opt)
Packit 8f70b4
      {
Packit 8f70b4
      case('a'):
Packit 8f70b4
	 ls_options.show_all=true;
Packit 8f70b4
	 break;
Packit 8f70b4
      case('C'):
Packit 8f70b4
	 ls_options.multi_column=true;
Packit 8f70b4
	 break;
Packit 8f70b4
      case('F'):
Packit 8f70b4
	 ls_options.append_type=true;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   while(args->getindex()>1)
Packit 8f70b4
      args->delarg(1);	// remove options.
Packit 8f70b4
   if(args->count()<2)
Packit 8f70b4
      args->Append("");
Packit 8f70b4
   args->rewind();
Packit 8f70b4
   dir=args->getnext();
Packit 8f70b4
   if(args->getindex()+1<args->count())
Packit 8f70b4
      buf->Format("%s:\n",dir);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *SFtpDirList::Status()
Packit 8f70b4
{
Packit 8f70b4
   if(ubuf && !ubuf->Eof() && session->IsOpen())
Packit 8f70b4
      return xstring::format(_("Getting file list (%lld) [%s]"),
Packit 8f70b4
		     (long long)session->GetPos(),session->CurrentStatus());
Packit 8f70b4
   return "";
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SFtpDirList::SuspendInternal()
Packit 8f70b4
{
Packit 8f70b4
   super::SuspendInternal();
Packit 8f70b4
   if(ubuf)
Packit 8f70b4
      ubuf->SuspendSlave();
Packit 8f70b4
}
Packit 8f70b4
void SFtpDirList::ResumeInternal()
Packit 8f70b4
{
Packit 8f70b4
   if(ubuf)
Packit 8f70b4
      ubuf->ResumeSlave();
Packit 8f70b4
   super::ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
#undef super
Packit 8f70b4
#define super ListInfo
Packit 8f70b4
int SFtpListInfo::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(done)
Packit 8f70b4
      return m;
Packit 8f70b4
   if(!ubuf && !result)
Packit 8f70b4
   {
Packit 8f70b4
      const char *cache_buffer=0;
Packit 8f70b4
      int cache_buffer_size=0;
Packit 8f70b4
      int err;
Packit 8f70b4
      const FileSet *fset_c;
Packit 8f70b4
      if(use_cache && FileAccess::cache->Find(session,"",FA::LONG_LIST,&err,
Packit 8f70b4
				    &cache_buffer,&cache_buffer_size,&fset_c))
Packit 8f70b4
      {
Packit 8f70b4
	 if(err)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetErrorCached(cache_buffer);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 ubuf=new IOBuffer(IOBuffer::GET);
Packit 8f70b4
	 ubuf->Put(cache_buffer,cache_buffer_size);
Packit 8f70b4
	 ubuf->PutEOF();
Packit 8f70b4
	 result=new FileSet(fset_c);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 session->Open("",FA::LONG_LIST);
Packit 8f70b4
	 ubuf=new IOBufferFileAccess(session);
Packit 8f70b4
	 if(FileAccess::cache->IsEnabled(session->GetHostName()))
Packit 8f70b4
	    ubuf->Save(FileAccess::cache->SizeLimit());
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(!result) {
Packit 8f70b4
      const char *b;
Packit 8f70b4
      int len;
Packit 8f70b4
      ubuf->Get(&b,&len;;
Packit 8f70b4
      if(len>0)
Packit 8f70b4
      {
Packit 8f70b4
	 ubuf->Skip(len);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(ubuf->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(ubuf->ErrorText());
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(b)
Packit 8f70b4
	 return m;
Packit 8f70b4
      // eof
Packit 8f70b4
      if(!result && session->IsOpen())
Packit 8f70b4
	 result=session.Cast<SFtp>()->GetFileSet();
Packit 8f70b4
      FileAccess::cache->Add(session,"",FA::LONG_LIST,FA::OK,ubuf,result);
Packit 8f70b4
      result->Exclude(exclude_prefix,exclude);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(result && session->OpenMode()!=FA::ARRAY_INFO)
Packit 8f70b4
   {
Packit 8f70b4
      ubuf=0;
Packit 8f70b4
      result->ExcludeCompound();
Packit 8f70b4
      result->rewind();
Packit 8f70b4
      for(FileInfo *file=result->curr(); file!=0; file=result->next())
Packit 8f70b4
      {
Packit 8f70b4
	 file->need=0;
Packit 8f70b4
	 if(file->defined & file->TYPE)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(file->filetype==file->SYMLINK && follow_symlinks)
Packit 8f70b4
	    {
Packit 8f70b4
	       file->filetype=file->UNKNOWN;
Packit 8f70b4
	       file->defined &= ~(file->SIZE|file->DATE|file->SYMLINK_DEF|file->MODE|file->TYPE|file->USER|file->GROUP);
Packit 8f70b4
	       file->Need(file->SIZE|file->DATE|file->MODE|file->TYPE|file->USER|file->GROUP);
Packit 8f70b4
	    }
Packit 8f70b4
	    else if(file->filetype==file->SYMLINK)
Packit 8f70b4
	    {
Packit 8f70b4
	       // need the link target
Packit 8f70b4
	       if(!file->Has(file->SYMLINK_DEF))
Packit 8f70b4
		  file->Need(file->SYMLINK_DEF);
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      session->GetInfoArray(result.get_non_const());
Packit 8f70b4
      session->Roll();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(session->OpenMode()==FA::ARRAY_INFO)
Packit 8f70b4
   {
Packit 8f70b4
      int res=session->Done();
Packit 8f70b4
      if(res==FA::DO_AGAIN)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(res==FA::IN_PROGRESS)
Packit 8f70b4
	 return m;
Packit 8f70b4
      session->Close();
Packit 8f70b4
      done=true;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
const char *SFtpListInfo::Status()
Packit 8f70b4
{
Packit 8f70b4
   if(ubuf && !ubuf->Eof() && session->IsOpen())
Packit 8f70b4
      return xstring::format(_("Getting file list (%lld) [%s]"),
Packit 8f70b4
		     (long long)session->GetPos(),session->CurrentStatus());
Packit 8f70b4
   return "";
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#include "modconfig.h"
Packit 8f70b4
#ifdef MODULE_PROTO_SFTP
Packit 8f70b4
void module_init()
Packit 8f70b4
{
Packit 8f70b4
   SFtp::ClassInit();
Packit 8f70b4
}
Packit 8f70b4
#endif