Logo Search packages:      
Sourcecode: lftp version File versions

SFtp.h

/*
 * lftp - file transfer program
 *
 * Copyright (c) 2003 by Alexander V. Lukyanov (lav@yars.free.net)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* $Id: SFtp.h,v 1.16 2004/10/18 05:45:18 lav Exp $ */

#ifndef SFTP_H
#define SFTP_H

#include "NetAccess.h"
#include "StatusLine.h"
#include "PtyShell.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "FileSet.h"

class SFtp : public NetAccess
{
   int       protocol_version;

   const char *lc_to_utf8(const char *);
   const char *utf8_to_lc(const char *);

public:
enum packet_type {
   SSH_FXP_INIT     =1,
   SSH_FXP_VERSION  =2,
   SSH_FXP_OPEN     =3,
   SSH_FXP_CLOSE    =4,
   SSH_FXP_READ     =5,
   SSH_FXP_WRITE    =6,
   SSH_FXP_LSTAT    =7,
   SSH_FXP_FSTAT    =8,
   SSH_FXP_SETSTAT  =9,
   SSH_FXP_FSETSTAT =10,
   SSH_FXP_OPENDIR  =11,
   SSH_FXP_READDIR  =12,
   SSH_FXP_REMOVE   =13,
   SSH_FXP_MKDIR    =14,
   SSH_FXP_RMDIR    =15,
   SSH_FXP_REALPATH =16,
   SSH_FXP_STAT     =17,
   SSH_FXP_RENAME   =18,
   SSH_FXP_READLINK =19,
   SSH_FXP_SYMLINK  =20,
   SSH_FXP_STATUS   =101,
   SSH_FXP_HANDLE   =102,
   SSH_FXP_DATA     =103,
   SSH_FXP_NAME     =104,
   SSH_FXP_ATTRS    =105,
   SSH_FXP_EXTENDED =200,
   SSH_FXP_EXTENDED_REPLY=201
};

#define SSH_FILEXFER_ATTR_SIZE         0x00000001
#define SSH_FILEXFER_ATTR_UIDGID       0x00000002  // used in protocol v3
#define SSH_FILEXFER_ATTR_PERMISSIONS  0x00000004
#define SSH_FILEXFER_ATTR_ACCESSTIME   0x00000008
#define SSH_FILEXFER_ATTR_ACMODTIME    0x00000008  // used in protocol v3
#define SSH_FILEXFER_ATTR_CREATETIME   0x00000010
#define SSH_FILEXFER_ATTR_MODIFYTIME   0x00000020
#define SSH_FILEXFER_ATTR_ACL          0x00000040
#define SSH_FILEXFER_ATTR_OWNERGROUP   0x00000080
#define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x00000100
#define SSH_FILEXFER_ATTR_EXTENDED     0x80000000

#define SSH_FILEXFER_ATTR_MASK_V3      0x8000000F
#define SSH_FILEXFER_ATTR_MASK_V4      0x800001FD

#define SSH_FILEXFER_TYPE_REGULAR      1
#define SSH_FILEXFER_TYPE_DIRECTORY    2
#define SSH_FILEXFER_TYPE_SYMLINK      3
#define SSH_FILEXFER_TYPE_SPECIAL      4
#define SSH_FILEXFER_TYPE_UNKNOWN      5

#define SSH_FXF_READ                 0x00000001
#define SSH_FXF_WRITE                0x00000002
#define SSH_FXF_APPEND               0x00000004
#define SSH_FXF_CREAT                0x00000008
#define SSH_FXF_TRUNC                0x00000010
#define SSH_FXF_EXCL                 0x00000020

enum sftp_status_t {
   SSH_FX_OK                 =0,
   SSH_FX_EOF                =1,
   SSH_FX_NO_SUCH_FILE       =2,
   SSH_FX_PERMISSION_DENIED  =3,
   SSH_FX_FAILURE      =4,
   SSH_FX_BAD_MESSAGE        =5,
   SSH_FX_NO_CONNECTION      =6,
   SSH_FX_CONNECTION_LOST    =7,
   SSH_FX_OP_UNSUPPORTED     =8,
   SSH_FX_INVALID_HANDLE     =9,
   SSH_FX_NO_SUCH_PATH       =10,
   SSH_FX_FILE_ALREADY_EXISTS=11,
   SSH_FX_WRITE_PROTECT      =12,
   SSH_FX_NO_MEDIA           =13
};

private:
   enum state_t
   {
      DISCONNECTED,
      CONNECTING,
      CONNECTING_1,
      CONNECTING_2,
      CONNECTED,
      FILE_RECV,
      FILE_SEND,
      WAITING,
      DONE
   };

   state_t state;
   bool received_greeting;
   int  password_sent;
   unsigned ssh_id;
   char *handle;
   int handle_len;

   void Init();

   void      SendMethod();
   void      SendArrayInfoRequests();

   IOBuffer *send_buf;
   IOBuffer *recv_buf;
   bool recv_buf_suspended;
   IOBuffer *pty_send_buf;
   IOBuffer *pty_recv_buf;
   DirectedBuffer *send_translate;
   DirectedBuffer *recv_translate;

   Buffer   *file_buf;
   FileSet  *file_set;

   PtyShell *ssh;

   void Disconnect();
   int IsConnected()
      {
       if(state==DISCONNECTED)
          return 0;
       if(state==CONNECTING)
          return 1;
       return 2;
      }

   const char *SkipHome(const char *path);
   const char *WirePath(const char *path);

public:
   enum unpack_status_t
   {
      UNPACK_SUCCESS=0,
      UNPACK_WRONG_FORMAT=-1,
      UNPACK_PREMATURE_EOF=-2,
      UNPACK_NO_DATA_YET=1
   };
   class Packet
   {
      static bool is_valid_reply(int p)
      {
       return p==SSH_FXP_VERSION
          || p>=101 && p<=105
          || p==SSH_FXP_EXTENDED_REPLY;
      }
   protected:
      int length;
      int unpacked;
      packet_type type;
      unsigned id;
      Packet(packet_type t)
       {
          type=t;
          length=1;
          if(HasID())
             length+=4;
       }
      bool HasID();
   public:
      Packet() { length=0; }
      virtual void ComputeLength() { length=1+4*HasID(); }
      virtual void Pack(Buffer *b)
       {
          b->PackUINT32BE(length);
          b->PackUINT8(type);
          if(HasID())
             b->PackUINT32BE(id);
       }
      virtual unpack_status_t Unpack(Buffer *b);
      virtual ~Packet() {}
      int GetLength() { return length; }
      packet_type GetPacketType() { return type; }
      const char *GetPacketTypeText();
      unsigned GetID() { return id; }
      void SetID(unsigned new_id) { id=new_id; }
      void DropData(Buffer *b) { b->Skip(4+(length>0?length:0)); }
      bool TypeIs(packet_type t) const { return type==t; }
      static unpack_status_t UnpackString(Buffer *b,int *offset,int limit,char **str_out,int *len_out=0);
      static void PackString(Buffer *b,const char *str,int len=-1);
   };
private:
   unpack_status_t UnpackPacket(Buffer *,Packet **);
   class PacketUINT32 : public Packet
   {
   protected:
      unsigned data;
      PacketUINT32(packet_type t,unsigned d=0) : Packet(t)
       { data=d; length+=4; }
      unpack_status_t Unpack(Buffer *b)
       {
          unpack_status_t res;
          res=Packet::Unpack(b);
          if(res!=UNPACK_SUCCESS)
             return res;
          data=b->UnpackUINT32BE(unpacked);
          unpacked+=4;
          return UNPACK_SUCCESS;
       }
      void ComputeLength() { Packet::ComputeLength(); length+=4; }
      void Pack(Buffer *b) { Packet::Pack(b); b->PackUINT32BE(data); }
   };
   class PacketSTRING : public Packet
   {
   protected:
      int string_len;
      char *string;
      PacketSTRING(packet_type t) : Packet(t)
       {
          string_len=0;
          string=0;
          length=4;
       }
      PacketSTRING(packet_type t,const char *s,int l=-1) : Packet(t)
       {
          if(l==-1)
             l=strlen(s);
          string_len=l;
          string=(char*)xmalloc(l+1);
          memcpy(string,s,l);
          string[l]=0;
          length+=4+l;
       }
      ~PacketSTRING()
       {
          xfree(string);
       }
      unpack_status_t Unpack(Buffer *b)
       {
          unpack_status_t res;
          res=Packet::Unpack(b);
          if(res!=UNPACK_SUCCESS)
             return res;
          res=UnpackString(b,&unpacked,length+4,&string,&string_len);
          return res;
       }
      void ComputeLength() { Packet::ComputeLength(); length+=4+string_len; }
      void Pack(Buffer *b)
       {
          Packet::Pack(b);
          Packet::PackString(b,string,string_len);
       }
      const char *GetString() { return string; }
      int GetStringLength() { return string_len; }
   };
   class Request_INIT : public PacketUINT32
   {
   public:
      Request_INIT(int v) : PacketUINT32(SSH_FXP_INIT,v) {}
   };
   class Reply_VERSION : public PacketUINT32
   {
      char **extension_name;
      char **extension_data;
   public:
      Reply_VERSION() : PacketUINT32(SSH_FXP_VERSION) {}
      unpack_status_t Unpack(Buffer *b)
       {
          unpack_status_t res;
          res=PacketUINT32::Unpack(b);
          if(res!=UNPACK_SUCCESS)
             return res;
          // FIXME: unpack extensions.
          return res;
       }
      unsigned GetVersion() { return data; }
   };
   class Request_REALPATH : public PacketSTRING
   {
   public:
      Request_REALPATH(const char *p) : PacketSTRING(SSH_FXP_REALPATH,p) {}
   };
   class Request_FSTAT : public PacketSTRING
   {
      unsigned flags;
      int protocol_version;
   public:
      Request_FSTAT(const char *h,int len,unsigned f,int pv) : PacketSTRING(SSH_FXP_FSTAT,h,len)
       {
          flags=f;
          protocol_version=pv;
       }
      void ComputeLength()
       {
          PacketSTRING::ComputeLength();
          if(protocol_version>=4)
             length+=4;
       }
      void Pack(Buffer *b)
       {
          PacketSTRING::Pack(b);
          if(protocol_version>=4)
             b->PackUINT32BE(flags);
       }
   };
   class Request_STAT : public Request_FSTAT
   {
   public:
      Request_STAT(const char *p,unsigned f,int pv) : Request_FSTAT(p,strlen(p),f,pv)
       {
          type=SSH_FXP_STAT;
       }
      const char *GetName() { return string; }
   };
public:
   struct FileAttrs
   {
      struct ExtFileAttr
      {
       char *extended_type;
       char *extended_data;
       ExtFileAttr() { extended_type=extended_data=0; }
       ~ExtFileAttr() { xfree(extended_type); xfree(extended_data); }
       unpack_status_t Unpack(Buffer *b,int *offset,int limit);
       void Pack(Buffer *b);
      };
      struct FileACE
      {
       unsigned ace_type;
       unsigned ace_flag;
       unsigned ace_mask;
       char     *who;
       FileACE() { ace_type=ace_flag=ace_mask=0; who=0; }
       ~FileACE() { xfree(who); }
       unpack_status_t Unpack(Buffer *b,int *offset,int limit);
       void Pack(Buffer *b);
      };

      unsigned flags;
      int      type;              // v4
      off_t    size;              // present only if flag SIZE
      char     *owner;            // present only if flag OWNERGROUP // v4
      char     *group;            // present only if flag OWNERGROUP // v4
      uid_t    uid;               // present only if flag UIDGID // v3
      gid_t    gid;               // present only if flag UIDGID // v3
      unsigned permissions;       // present only if flag PERMISSIONS
      time_t   atime;             // present only if flag ACCESSTIME (ACMODTIME)
      unsigned atime_nseconds;          // present only if flag SUBSECOND_TIMES
      time_t   createtime;        // present only if flag CREATETIME
      unsigned createtime_nseconds; // present only if flag SUBSECOND_TIMES
      time_t   mtime;             // present only if flag MODIFYTIME (ACMODTIME)
      unsigned mtime_nseconds;          // present only if flag SUBSECOND_TIMES
      unsigned ace_count;         // present only if flag ACL
      FileACE  *ace;
      unsigned extended_count;          // present only if flag EXTENDED
      ExtFileAttr *extended_attrs;

      FileAttrs()
      {
       flags=0; type=0; size=NO_SIZE; owner=group=0; uid=gid=0;
       permissions=0;
       atime=createtime=mtime=NO_DATE;
       atime_nseconds=createtime_nseconds=mtime_nseconds=0;
       extended_count=0; extended_attrs=0;
       ace_count=0; ace=0;
      }
      ~FileAttrs()
      {
       xfree(owner); xfree(group);
       delete[] extended_attrs;
       delete[] ace;
      }
      unpack_status_t Unpack(Buffer *b,int *offset,int limit,int proto_version);
      void Pack(Buffer *b,int proto_version);
      int ComputeLength(int v);
   };
   struct NameAttrs
   {
      char *name;
      char *longname;
      FileAttrs attrs;
      NameAttrs() { name=0; longname=0; }
      ~NameAttrs() { xfree(name); xfree(longname); }
      unpack_status_t Unpack(Buffer *b,int *offset,int limit,int proto_version);
   };
private:
   class Reply_NAME : public Packet
   {
      int protocol_version;
      int count;
      NameAttrs *names;
   public:
      Reply_NAME(int pv) : Packet(SSH_FXP_NAME) { protocol_version=pv; }
      ~Reply_NAME() { delete[] names; }
      unpack_status_t Unpack(Buffer *b);
      int GetCount() { return count; }
      const NameAttrs *GetNameAttrs(int index)
       {
          if(index>count)
             return 0;
          return &names[index];
       }
   };
   class Reply_ATTRS : public Packet
   {
      int protocol_version;
      FileAttrs attrs;
   public:
      Reply_ATTRS(int pv) : Packet(SSH_FXP_ATTRS) { protocol_version=pv; }
      unpack_status_t Unpack(Buffer *b);
      const FileAttrs *GetAttrs() { return &attrs; }
   };
   class PacketSTRING_ATTRS : public PacketSTRING
   {
   protected:
      int protocol_version;
   public:
      FileAttrs attrs;
      PacketSTRING_ATTRS(packet_type type,const char *s,int len,int pv)
       : PacketSTRING(type,s,len)
       {
          protocol_version=pv;
       }
      void ComputeLength()
       {
          PacketSTRING::ComputeLength();
          length+=attrs.ComputeLength(protocol_version);
       }
      void Pack(Buffer *b)
       {
          PacketSTRING::Pack(b);
          attrs.Pack(b,protocol_version);
       }
   };
   class Request_FSETSTAT : public PacketSTRING_ATTRS
   {
   public:
      Request_FSETSTAT(const char *h,int h_len,int pv)
       : PacketSTRING_ATTRS(SSH_FXP_FSETSTAT,h,h_len,pv) {}
   };
   class Request_OPEN : public PacketSTRING_ATTRS
   {
      unsigned pflags;
   public:
      Request_OPEN(const char *fn,unsigned fl,int pv)
       : PacketSTRING_ATTRS(SSH_FXP_OPEN,fn,strlen(fn),pv)
       {
          pflags=fl;
       }
      void ComputeLength()
       {
          PacketSTRING_ATTRS::ComputeLength();
          length+=4;
       }
      void Pack(Buffer *b)
       {
          PacketSTRING::Pack(b);
          b->PackUINT32BE(pflags);
          attrs.Pack(b,protocol_version);
       }
   };
   class Reply_HANDLE : public PacketSTRING
   {
   public:
      Reply_HANDLE() : PacketSTRING(SSH_FXP_HANDLE) {}
      char *GetHandle(int *out_len=0)
       {
          char *out=(char*)xmemdup(string,string_len+1);
          if(out_len)
             *out_len=string_len;
          return out;
       }
   };
   class Request_CLOSE : public PacketSTRING
   {
   public:
      Request_CLOSE(const char *h,int len) : PacketSTRING(SSH_FXP_CLOSE,h,len) {}
   };
   class Request_OPENDIR : public PacketSTRING
   {
   public:
      Request_OPENDIR(const char *name) : PacketSTRING(SSH_FXP_OPENDIR,name) {}
   };
   class Request_READDIR : public PacketSTRING
   {
   public:
      Request_READDIR(const char *h,int len) : PacketSTRING(SSH_FXP_READDIR,h,len) {}
   };
   class Reply_STATUS : public Packet
   {
      int protocol_version;
      unsigned code;
      char *message;
      char *language;
   public:
      Reply_STATUS(int pv) { protocol_version=pv; code=0; message=0; language=0; }
      ~Reply_STATUS() { xfree(message); xfree(language); }
      unpack_status_t Unpack(Buffer *b);
      int GetCode() { return code; }
      const char *GetCodeText();
      const char *GetMessage() { return message; }
   };
   class Request_READ : public PacketSTRING
   {
   public:
      off_t pos;
      unsigned len;
      Request_READ(const char *h,int hlen,off_t p,unsigned l)
       : PacketSTRING(SSH_FXP_READ,h,hlen) { pos=p; len=l; }
      void ComputeLength() { PacketSTRING::ComputeLength(); length+=8+4; }
      void Pack(Buffer *b);
   };
   class Reply_DATA : public PacketSTRING
   {
   public:
      Reply_DATA() : PacketSTRING(SSH_FXP_DATA) {}
      void GetData(const char **b,int *s) { *b=string; *s=string_len; }
   };
   class Request_WRITE : public PacketSTRING
   {
   public:
      off_t pos;
      unsigned len;
      char *data;
      Request_WRITE(const char *h,int hlen,off_t p,const char *d,unsigned l)
       : PacketSTRING(SSH_FXP_WRITE,h,hlen) { pos=p; len=l; data=(char*)xmemdup(d,l); }
      ~Request_WRITE() { xfree(data); }
      void ComputeLength() { PacketSTRING::ComputeLength(); length+=8+4+len; }
      void Pack(Buffer *b);
   };
   class Request_MKDIR : public PacketSTRING_ATTRS
   {
   public:
      Request_MKDIR(const char *fn,int pv)
       : PacketSTRING_ATTRS(SSH_FXP_MKDIR,fn,strlen(fn),pv) {}
   };
   class Request_SETSTAT : public PacketSTRING_ATTRS
   {
   public:
      Request_SETSTAT(const char *fn,int pv)
       : PacketSTRING_ATTRS(SSH_FXP_SETSTAT,fn,strlen(fn),pv) {}
   };
   class Request_RMDIR : public PacketSTRING
   {
   public:
      Request_RMDIR(const char *fn) : PacketSTRING(SSH_FXP_RMDIR,fn) {}
   };
   class Request_REMOVE : public PacketSTRING
   {
   public:
      Request_REMOVE(const char *fn) : PacketSTRING(SSH_FXP_REMOVE,fn) {}
   };
   class Request_RENAME : public Packet
   {
      char *oldpath;
      char *newpath;
   public:
      Request_RENAME(const char *o,const char *n) : Packet(SSH_FXP_RENAME)
       {
          oldpath=xstrdup(o);
          newpath=xstrdup(n);
       }
      ~Request_RENAME()
       {
          xfree(oldpath);
          xfree(newpath);
       }
      void ComputeLength()
       {
          Packet::ComputeLength();
          length+=4+strlen(oldpath)+4+strlen(newpath);
       }
      void Pack(Buffer *b)
       {
          Packet::Pack(b);
          Packet::PackString(b,oldpath);
          Packet::PackString(b,newpath);
       }
   };

   struct Expect;
   friend struct SFtp::Expect; // grant access to Packet.
   struct Expect
   {
      enum expect_t
      {
       HOME_PATH,
       FXP_VERSION,
       CWD,
       HANDLE,
       HANDLE_STALE,
       DATA,
       INFO,
       DEFAULT,
       WRITE_STATUS,
       IGNORE
      };

      Packet *request;
      Packet *reply;
      Expect *next;
      int i;
      expect_t tag;
      Expect(Packet *req,expect_t t,int j=0) { request=req; tag=t; reply=0; i=j; }
      ~Expect() { delete request; delete reply; }
   };

   void PushExpect(Expect *);
   int HandleReplies();
   int HandlePty();
   void HandleExpect(Expect *);
   void CloseExpectQueue();
   void CloseHandle(Expect::expect_t e);
   int ReplyLogPriority(int);

   int expect_queue_size;
   Expect *expect_chain;
   Expect **expect_chain_end;
   Expect **FindExpect(Packet *reply);
   void DeleteExpect(Expect **);
   Expect *FindExpectExclusive(Packet *reply);
   Expect *ooo_chain;   // out of order replies buffered

   int   RespQueueIsEmpty() { return expect_chain==0; }
   int       RespQueueSize() { return expect_queue_size; }
   void  EmptyRespQueue()
      {
       while(expect_chain)
          DeleteExpect(&expect_chain);
       while(ooo_chain)
          DeleteExpect(&ooo_chain);
      }

   bool GetBetterConnection(int level,bool limit_reached);
   void MoveConnectionHere(SFtp *o);

   bool      eof;

   void      SendRequest();
   void      SendRequest(Packet *req,Expect::expect_t exp,int i=0);
   void      SendRequestGeneric(int type);
   void      RequestMoreData();
   off_t request_pos;

   FileInfo *MakeFileInfo(const NameAttrs *a);

   int max_packets_in_flight;
   int max_packets_in_flight_slow_start;
   int size_read;
   int size_write;

protected:
   void SetError(int code,const Packet *reply);
   void SetError(int code,const char *mess=0) { FA::SetError(code,mess); }

public:
   static void ClassInit();

   SFtp();
   SFtp(const SFtp*);
   ~SFtp();

   const char *GetProto() { return "sftp"; }

   FileAccess *Clone() { return new SFtp(this); }
   static FileAccess *New();

   int Do();
   int Done();
   int Read(void *,int);
   int Write(const void *,int);
   int StoreStatus();
   int Buffered();

   void Close();
   const char *CurrentStatus();

   void Reconfig(const char *name=0);

   bool SameSiteAs(FileAccess *fa);
   bool SameLocationAs(FileAccess *fa);

   DirList *MakeDirList(ArgV *args);
   Glob *MakeGlob(const char *pattern);
   ListInfo *MakeListInfo(const char *dir);

   bool NeedSizeDateBeforehand() { return true; }

   void Suspend();
   void Resume();

   void Cleanup();
   void CleanupThis();

   FileSet *GetFileSet() { FileSet *fset=file_set; file_set=0; return fset; }
};

class SFtpDirList : public DirList
{
   FileAccess *session;
   IOBuffer *ubuf;
   const char *dir;
   bool use_file_set;
   FileSet *fset;
   LsOptions ls_options;

public:
   SFtpDirList(ArgV *a,FileAccess *fa);
   ~SFtpDirList();
   const char *Status();
   int Do();

   void Suspend();
   void Resume();
};

class SFtpListInfo : public ListInfo
{
   IOBuffer *ubuf;
public:
   SFtpListInfo(SFtp *session,const char *dir)
      : ListInfo(session,dir)
      {
       ubuf=0;
      }
   ~SFtpListInfo() { Delete(ubuf); }
   int Do();
   const char *Status();
};

#endif

Generated by  Doxygen 1.6.0   Back to index