1 /*
2  * lftp - file transfer program
3  *
4  * Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef FILEACCESS_H
21 #define FILEACCESS_H
22 
23 #include <sys/types.h>
24 #include <time.h>
25 
26 #include "SMTask.h"
27 #include "trio.h"
28 #include "xstring.h"
29 #include "ResMgr.h"
30 #include "FileSet.h"
31 #include "ArgV.h"
32 #include "ProtoLog.h"
33 #include "xlist.h"
34 #include "xmap.h"
35 #include "Timer.h"
36 
37 #define FILE_END     ((off_t)-1L)
38 #define UNKNOWN_POS  ((off_t)-1L)
39 
40 class ListInfo;
41 class Glob;
42 class NoGlob;
43 class DirList;
44 class FileAccessRef;
45 class Buffer;
46 
47 class FileAccess : public SMTask, public ResClient, protected ProtoLog
48 {
49    static bool class_inited;
50 public:
51    static class LsCache *cache;
52    enum open_mode
53    {
54       CLOSED,
55       RETRIEVE,
56       STORE,
57       LONG_LIST,
58       LIST,
59       MP_LIST,
60       CHANGE_DIR,
61       MAKE_DIR,
62       REMOVE_DIR,
63       REMOVE,
64       QUOTE_CMD,
65       RENAME,
66       ARRAY_INFO,
67       CONNECT_VERIFY,
68       CHANGE_MODE,
69       LINK,
70       SYMLINK,
71    };
72 
73    class Path
74    {
75       void init();
76    public:
77       int   device_prefix_len;
78       xstring path;
79       bool  is_file;
80       xstring url;
Path()81       Path() { init(); }
Path(const Path * o)82       Path(const Path *o) { init(); Set(o); }
Path(const Path & o)83       Path(const Path &o) { init(); Set(o); }
Path(const char * new_path)84       Path(const char *new_path) { init(); Set(new_path); }
85       Path(const char *new_path,bool new_is_file,const char *new_url=0,int new_device_prefix_len=0)
86 	 { init(); Set(new_path,new_is_file,new_url,new_device_prefix_len); }
87       ~Path();
88       void Set(const Path*);
Set(const Path & o)89       void Set(const Path &o) { Set(&o); }
90       void Set(const char *new_path,bool new_is_file=false,const char *new_url=0,int device_prefix_len=0);
SetURL(const char * u)91       void SetURL(const char *u) { url.set(u); }
92       void Change(const char *new_path,bool new_is_file=false,const char *new_path_enc=0,int device_prefix_len=0);
GetDirectory()93       const xstring& GetDirectory() const { return is_file?dirname(path):path; }
94       void ExpandTilde(const Path &home);
95       static void Optimize(xstring& p,int dev_prefix=0);
Optimize()96       void Optimize() { Optimize(path,device_prefix_len); }
97       const Path& operator=(const Path &o)
98 	 {
99 	    Set(&o);
100 	    return *this;
101 	 }
102       operator const char *() const { return path; }
103       bool operator==(const Path &p2) const;
104       bool operator!=(const Path &p2) const { return !(*this==p2); }
105    };
106 
107 protected:
108    xstring_c vproto;
109    xstring_c hostname;
110    xstring_c portname;
111    xstring_c user;
112    xstring_c pass;
113    bool	 pass_open;
114 
115    const char *default_cwd;
116    Path	 home;
117    Path	 cwd;
118    Ref<Path> new_cwd;
119    xstring file;
120    xstring file_url;
121    xstring file1;
122    int	 mode;
123    off_t pos;
124    off_t real_pos;
125    off_t limit;
126 
127    FileTimestamp *opt_date;
128    off_t  *opt_size;
129 
130    FileSet *fileset_for_info;
131 
132    Timer reconnect_timer;
133    int retries;
134    int max_retries;
135 
136    bool	 mkdir_p;
137    bool	 rename_f;
138 
139    int	 saved_errno;
140 
141    void	 ExpandTildeInCWD();
142    const char *ExpandTildeStatic(const char *s) const;
143 
144    xstring real_cwd;
set_real_cwd(const char * c)145    void set_real_cwd(const char *c)
146       {
147 	 real_cwd.set(c);
148       }
149    void set_home(const char *h);
150 
151    off_t  entity_size; // size of file to be sent
152    time_t entity_date; // date of file to be sent
153 
154    xstring_c closure;
155    const char *res_prefix;
ResPrefix()156    const char *ResPrefix() const { return res_prefix?res_prefix:GetProto(); }
ResClosure()157    const char *ResClosure() const { return closure?closure.get():GetHostName(); }
158 
159    int chmod_mode;
160    bool ascii;
161    bool norest_manual;
162    bool fragile;
163 
164    int	priority;   // higher priority can take over other session.
165    int	last_priority;
166 
Error()167    bool Error() { return error_code!=OK; }
168    void ClearError();
169    void SetError(int code,const char *mess=0);
170    void Fatal(const char *mess);
171    xstring error;
172    int error_code;
173 
174    xstring_c location;
175    xstring_c location_file;
176    int location_mode;
177    bool location_permanent;
178 
179    xstring_c suggested_filename;
180    void SetSuggestedFileName(const char *fn);
181 
182    xstring_c entity_content_type;
183    xstring_c entity_charset;
184 
185    xstring_c last_disconnect_cause;
186 
187    xlist<FileAccess> all_fa_node;
188    static xlist_head<FileAccess> all_fa;
189 
FirstSameSite()190    FileAccess *FirstSameSite() const { return NextSameSite(0); }
191    FileAccess *NextSameSite(FileAccess *) const;
192 
193    StringSet *MkdirMakeSet() const; // splits the path for mkdir -p
194 
195    int device_prefix_len(const char *path) const;
196 
197    virtual ~FileAccess();
198 
199 public:
200    virtual const char *GetProto() const = 0; // http, ftp, file etc
SameProtoAs(const FileAccess * fa)201    bool SameProtoAs(const FileAccess *fa) const { return !strcmp(GetProto(),fa->GetProto()); }
202    virtual FileAccess *Clone() const = 0;
ProtocolSubstitution(const char * host)203    virtual const char *ProtocolSubstitution(const char *host) { return 0; }
204 
GetVisualProto()205    const char *GetVisualProto() const { return vproto?vproto.get():GetProto(); }
SetVisualProto(const char * p)206    void SetVisualProto(const char *p) { vproto.set(p); }
GetHome()207    const char  *GetHome() const { return home; }
GetHostName()208    const char  *GetHostName() const { return hostname; }
GetUser()209    const char  *GetUser() const { return user; }
GetPassword()210    const char  *GetPassword() const { return pass; }
GetPort()211    const char  *GetPort() const { return portname; }
212    const xstring& GetConnectURL(int flags=0) const;
213    const xstring& GetFileURL(const char *file,int flags=0) const;
214    enum { NO_PATH=1,WITH_PASSWORD=2,NO_PASSWORD=4,NO_USER=8 };
GetLastDisconnectCause()215    const char *GetLastDisconnectCause() const { return last_disconnect_cause; }
216 
217    void Connect(const char *h,const char *p);
218    void ConnectVerify();
219    void PathVerify(const Path &);
220    virtual void Login(const char *u,const char *p);
AnonymousLogin()221    void AnonymousLogin() { Login(0,0); }
222 
223    // reset location-related state on Connect/Login/AnonymousLogin
224    virtual void ResetLocationData();
225 
226    virtual void Open(const char *file,int mode,off_t pos=0);
227    void Open2(const char *f1,const char *f2,open_mode m);
228    void SetFileURL(const char *u);
SetLimit(off_t lim)229    void SetLimit(off_t lim) { limit=lim; }
SetSize(off_t s)230    void SetSize(off_t s) { entity_size=s; }
SetDate(time_t d)231    void SetDate(time_t d) { entity_date=d; }
WantDate(FileTimestamp * d)232    void WantDate(FileTimestamp *d) { opt_date=d; }
WantSize(off_t * s)233    void WantSize(off_t *s) { opt_size=s; }
AsciiTransfer()234    void AsciiTransfer() { ascii=true; }
235    virtual void Close();
236 
237    void Rename(const char *rfile,const char *to,bool clobber=false);
Link(const char * f1,const char * f2)238    void Link(const char *f1,const char *f2) { Open2(f1,f2,LINK); }
Symlink(const char * f1,const char * f2)239    void Symlink(const char *f1,const char *f2) { Open2(f1,f2,SYMLINK); }
240    void Mkdir(const char *rfile,bool allpath=false);
241    void Chdir(const char *dir,bool verify=true);
ChdirAccept()242    void ChdirAccept() { cwd=*new_cwd; }
SetCwd(const Path & new_cwd)243    void SetCwd(const Path &new_cwd) { cwd=new_cwd; }
Remove(const char * rfile)244    void Remove(const char *rfile)    { Open(rfile,REMOVE); }
RemoveDir(const char * dir)245    void RemoveDir(const char *dir)  { Open(dir,REMOVE_DIR); }
246    void Chmod(const char *file,int m);
247 
248    void	 GetInfoArray(FileSet *info);
InfoArrayPercentDone()249    int	 InfoArrayPercentDone() { return fileset_for_info->curr_pct(); }
250 
251    virtual const char *CurrentStatus();
252 
253    virtual int Read(Buffer *buf,int size) = 0;
254    virtual int Write(const void *buf,int size) = 0;
255    virtual int Buffered();
256    virtual int StoreStatus() = 0;
257    virtual bool IOReady();
GetPos()258    off_t GetPos() const { return pos; }
GetRealPos()259    off_t GetRealPos() const { return real_pos<0?pos:real_pos; }
SeekReal()260    void SeekReal() { pos=GetRealPos(); }
RereadManual()261    void RereadManual() { norest_manual=true; }
SetFragile()262    void SetFragile() { fragile=true; }
263 
GetCwd()264    const Path& GetCwd() const { return cwd; }
GetNewCwd()265    const Path& GetNewCwd() const { return *new_cwd; }
GetFile()266    const char *GetFile() const { return file; }
267 
268    virtual int Do() = 0;
269    virtual int Done() = 0;
270 
271    virtual bool SameLocationAs(const FileAccess *fa) const;
272    virtual bool SameSiteAs(const FileAccess *fa) const;
273    bool IsBetterThan(const FileAccess *fa) const;
274 
275    void Init();
FileAccess()276    FileAccess() : all_fa_node(this) { Init(); }
277    FileAccess(const FileAccess *);
278 
DontSleep()279    void	 DontSleep() { reconnect_timer.Stop(); }
280 
IsClosed()281    bool	 IsClosed() { return mode==CLOSED; }
IsOpen()282    bool	 IsOpen() { return !IsClosed(); }
OpenMode()283    int	 OpenMode() { return mode; }
284 
285    virtual int  IsConnected() const; // level of connection (0 - not connected).
286    void Disconnect(const char *dc=0) { last_disconnect_cause.set(dc); DisconnectLL(); }
DisconnectLL()287    virtual void DisconnectLL() {}
288    virtual void UseCache(bool);
289    virtual bool NeedSizeDateBeforehand();
290 
GetErrorCode()291    int GetErrorCode() { return error_code; }
292 
293    enum status
294    {
295       IN_PROGRESS=1,	// is returned only by *Status() or Done()
296       OK=0,
297       SEE_ERRNO=-100,
298       LOOKUP_ERROR,
299       NOT_OPEN,
300       NO_FILE,
301       NO_HOST,
302       FILE_MOVED,
303       FATAL,
304       STORE_FAILED,
305       LOGIN_FAILED,
306       DO_AGAIN,
307       NOT_SUPP,
308       FRAGILE_FAILED,
309    };
310 
311    virtual const char *StrError(int err);
312    virtual void Cleanup();
313    virtual void CleanupThis();
314    void CleanupAll();
315       // ^^ close idle connections, etc.
316    virtual ListInfo *MakeListInfo(const char *path=0);
317    virtual Glob *MakeGlob(const char *pattern);
318    virtual DirList *MakeDirList(ArgV *a);
319    virtual FileSet *ParseLongList(const char *buf,int len,int *err=0) const { return 0; }
320 
NotSerious(int err)321    static bool NotSerious(int err) { return temporary_network_error(err); }
322 
GetNewLocation()323    const char *GetNewLocation() const { return location; }
GetNewLocationFile()324    const char *GetNewLocationFile() const { return location_file; }
GetNewLocationMode()325    int GetNewLocationMode() const { return location_mode; }
IsNewLocationPermanent()326    bool IsNewLocationPermanent() const { return location_permanent; }
327    virtual FileAccess *GetNewLocationFA() const;
328 
GetSuggestedFileName()329    const char *GetSuggestedFileName() { return suggested_filename; }
GetEntityContentType()330    const char *GetEntityContentType() { return entity_content_type; }
GetEntityCharset()331    const char *GetEntityCharset() { return entity_charset; }
332 
333    void Reconfig(const char *);
334 
335 
336    typedef FileAccess *SessionCreator();
337    class Protocol
338    {
339       static xmap_p<Protocol> proto_by_name;
340       const char *proto;
341       SessionCreator *New;
342 
343       static Protocol *FindProto(const char *proto);
344    public:
345       static FileAccess *NewSession(const char *proto);
346       Protocol(const char *proto,SessionCreator *creator);
ClassCleanup()347       static void ClassCleanup() { proto_by_name.empty(); }
348    };
349 
Register(const char * proto,SessionCreator * creator)350    static void Register(const char *proto,SessionCreator *creator)
351       {
352 	 (void)new Protocol(proto,creator);
353       }
354 
355    static FileAccess *New(const char *proto,const char *host=0,const char *port=0);
356    static FileAccess *New(const class ParsedURL *u,bool dummy=true);
357 
358    void SetPasswordGlobal(const char *p);
InsecurePassword(bool i)359    void InsecurePassword(bool i)
360       {
361 	 pass_open=i;
362       }
SetPriority(int p)363    void SetPriority(int p)
364       {
365 	 if(p==priority)
366 	    return;
367 	 priority=p;
368 	 current->Timeout(0);
369       }
GetPriority()370    int GetPriority() const { return priority; }
371 
372    // not pretty (FIXME)
GetRetries()373    int GetRetries() const { return retries; }
SetRetries(int r)374    void SetRetries(int r) { retries=r; }
GetMaxRetries()375    int GetMaxRetries() const { return max_retries; }
GetTryTime()376    time_t GetTryTime() const { return reconnect_timer.GetStartTime(); }
377    void SetTryTime(time_t t);
378 
GetLogContext()379    const char *GetLogContext() { return hostname; }
380 
381    static void ClassInit();
382    static void ClassCleanup();
383 };
384 
385 // shortcut
386 #define FA FileAccess
387 
388 // cache of used sessions
389 class SessionPool
390 {
391    enum { pool_size=64 };
392    static FileAccess *pool[pool_size];
393 
394 public:
395    static void Reuse(FileAccess *);
396    static void Print(FILE *f);
397    static FileAccess *GetSession(int n);
398 
399    // start with n==0, then increase n; returns 0 when no more
400    static FileAccess *Walk(int *n,const char *proto);
401 
402    static void ClearAll();
403 };
404 
405 class FileAccessRef : public SMTaskRef<FileAccess>
406 {
407    FileAccessRef(const FileAccessRef&);  // disable cloning
408    void operator=(const FileAccessRef&);   // and assignment
409 
reuse()410    void reuse() { if(ptr) { ptr->DecRefCount(); SessionPool::Reuse(ptr); ptr=0; } }
411 
412 public:
FileAccessRef()413    FileAccessRef() {}
FileAccessRef(FileAccess * p)414    FileAccessRef(FileAccess *p) : SMTaskRef<FileAccess>(p) {}
~FileAccessRef()415    ~FileAccessRef() { reuse(); }
416    const FileAccessRef& operator=(FileAccess *p);
417 
Cast()418    template<class T> const SMTaskRef<T>& Cast() const
419       { void(static_cast<T*>(this->ptr)); return *(const SMTaskRef<T>*)this; }
420 
421    static const FileAccessRef null;
422 };
423 
424 // constant ref (ref clone)
425 class FileAccessRefC
426 {
close()427    void close() { if(*ref) (*ref)->Close(); }
428 
429 protected:
430    const FileAccessRef *ref;
431 
432 public:
FileAccessRefC(const FileAccessRef & p)433    FileAccessRefC(const FileAccessRef& p) { ref=&p; }
434    const FileAccessRef& operator=(const FileAccessRef& p) { close(); ref=&p; return p; }
435    operator const FileAccess*() const { return *ref; }
436    FileAccess *operator->() const { return ref->operator->(); }
437    operator const FileAccessRef&() { return *ref; }
438 };
439 
440 // static reference
441 class FileAccessRefS : public FileAccessRef
442 {
443    FileAccessRefS(const FileAccessRefS&);  // disable cloning
444    void operator=(const FileAccessRefS&);   // and assignment
445 
446 public:
FileAccessRefS()447    FileAccessRefS() {}
FileAccessRefS(FileAccess * p)448    FileAccessRefS(FileAccess *p) { ptr=p; }
~FileAccessRefS()449    ~FileAccessRefS() { ptr=0; }
450    const FileAccessRefS& operator=(FileAccess *p) { ptr=p; return *this; }
451 };
452 
453 class FileAccessOperation : public SMTask
454 {
455 protected:
456    FileAccessRefS session;
457    bool done;
458    xstring error_text;
459    void SetError(const char *);
460    void SetErrorCached(const char *);
461 
462    bool use_cache;
463 
PrepareToDie()464    void PrepareToDie() { if(session) session->Close(); }
465 
466 public:
FileAccessOperation(FileAccess * s)467    FileAccessOperation(FileAccess *s) : session(s), done(false), use_cache(true) {}
468 
469    virtual int Do() = 0;
Done()470    bool Done() { return done; }
Error()471    bool Error() { return error_text!=0; }
ErrorText()472    const char *ErrorText() { return error_text; }
473 
474    virtual const char *Status() = 0;
475 
476    void UseCache(bool y=true) { use_cache=y; }
477 };
478 
479 #include "PatternSet.h"
480 class ListInfo : public FileAccessOperation
481 {
482 protected:
483    FileAccess::Path saved_cwd;
484    Ref<FileSet> result;
485    Ref<FileSet> excluded;
486 
487    const char *exclude_prefix;
488    const PatternSet *exclude;
489 
490    unsigned need;
491    bool follow_symlinks;
492    bool try_recursive;
493    bool is_recursive;
494 
495    void PrepareToDie();
496    ~ListInfo();
497 
498 public:
499    ListInfo(FileAccess *session,const char *path);
500 
SetExclude(const char * p,const PatternSet * e)501    void SetExclude(const char *p,const PatternSet *e) { exclude_prefix=p; exclude=e; excluded=new FileSet(); }
502    void TryRecursive(bool y=true) { try_recursive=y; }
503 
504    // caller has to delete the resulting FileSet itself.
GetResult()505    FileSet *GetResult() { return result.borrow(); }
GetExcluded()506    FileSet *GetExcluded() { return excluded.borrow(); }
IsRecursive()507    bool IsRecursive() const { return is_recursive; }
508 
Need(unsigned mask)509    void Need(unsigned mask) { need|=mask; }
NoNeed(unsigned mask)510    void NoNeed(unsigned mask) { need&=~mask; }
FollowSymlinks()511    void FollowSymlinks() { follow_symlinks=true; }
512 };
513 
514 #include "buffer.h"
515 class LsOptions
516 {
517 public:
518    bool append_type:1;
519    bool multi_column:1;
520    bool show_all:1;
LsOptions()521    LsOptions()
522       {
523 	 append_type=false;
524 	 multi_column=false;
525 	 show_all=false;
526       }
527 };
528 
529 class DirList : public FileAccessOperation
530 {
531 protected:
532    Ref<Buffer> buf;
533    Ref<ArgV> args;
534    bool color;
535    ~DirList();
536 
537 public:
538    DirList(FileAccess *s,ArgV *a);
539 
540    virtual int Do() = 0;
541    virtual const char *Status() = 0;
542 
Size()543    int Size() { return buf->Size(); }
Eof()544    bool Eof() { return buf->Eof();  }
Get(const char ** b,int * size)545    void Get(const char **b,int *size) { buf->Get(b,size); }
Skip(int len)546    void Skip(int len) { buf->Skip(len); }
547 
548    void UseColor(bool c=true) { color=c; }
549 };
550 
551 class UploadState
552 {
553    time_t try_time;
554    off_t pos_watermark;
555    int retries;
556 public:
UploadState()557    UploadState() : try_time(NO_DATE), pos_watermark(0),retries(-1) {}
Clear()558    void Clear() {
559       try_time=NO_DATE;
560       pos_watermark=0;
561       retries=-1;
562    }
Save(const FileAccess * session)563    void Save(const FileAccess *session) {
564       try_time=session->GetTryTime();
565       retries=session->GetRetries();
566       off_t pos=session->GetRealPos();
567       int max_retries=session->GetMaxRetries();
568       if(max_retries>0 && retries>=max_retries)
569 	 pos=0;
570       if(pos_watermark<pos) {
571 	 pos_watermark=pos;
572 	 retries=-1;
573       }
574    }
Restore(const FileAccessRef & session)575    void Restore(const FileAccessRef& session) {
576       if(try_time!=NO_DATE)
577 	 session->SetTryTime(try_time);
578       if(retries>=0)
579 	 session->SetRetries(retries+1);
580    }
581 };
582 
583 #endif /* FILEACCESS_H */
584