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