lftp  4.4.6
About: lftp is a command line ftp client (FTP, HTTP, ssl support, background transfer, reget, reput, ...)
  Fossies Dox: lftp-4.4.6.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups
FileAccess.cc
Go to the documentation of this file.
1 /*
2  * lftp - file transfer program
3  *
4  * Copyright (c) 1996-2012 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 #include <config.h>
21 
22 #include "FileAccess.h"
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <assert.h>
29 #include <stddef.h>
30 #include "ascii_ctype.h"
31 #include <fcntl.h>
32 #include "LsCache.h"
33 #include "log.h"
34 #include "url.h"
35 #include "misc.h"
36 #include "DummyProto.h"
37 #include "netrc.h"
38 #include "ArgV.h"
39 #include "ConnectionSlot.h"
40 #include "SignalHook.h"
41 #include "FileGlob.h"
42 #ifdef WITH_MODULES
43 # include "module.h"
44 #endif
45 
48 
50 {
51  ClassInit();
52 
53  pass_open=false;
54 
55  default_cwd="~";
56  cwd.Set(default_cwd,false,0);
57  new_cwd=0;
60  pos=0;
61  mode=CLOSED;
62  try_time=0;
63  retries=0;
64  opt_date=0;
65  opt_size=0;
67  error_code=OK;
68  saved_errno=0;
69  mkdir_p=false;
70  ascii=false;
71  norest_manual=false;
72 
75 
76  res_prefix=0;
77 
78  chmod_mode=0644;
79 
81 
82  next=chain;
83  chain=this;
84 }
85 
87 {
88  Init();
89 
90  cwd=fa->cwd;
91  home=fa->home;
92  user.set(fa->user);
93  pass.set(fa->pass);
94  pass_open=fa->pass_open;
95  hostname.set(fa->hostname);
96  portname.set(fa->portname);
97  vproto.set(fa->vproto);
98 }
99 
101 {
103 }
104 
105 void FileAccess::Open(const char *fn,int mode,off_t offs)
106 {
107 #ifdef OPEN_DEBUG
108  printf("%p->FA::Open(%s,%d)\n",this,fn?fn:"NULL",mode);
109 #endif
110  if(IsOpen())
111  Close();
112  Resume();
113  file.set(fn);
115  pos=offs;
116  this->mode=mode;
117  mkdir_p=false;
118  Timeout(0);
119 
120  switch((open_mode)mode)
121  {
122  case STORE:
123  case REMOVE:
124  case MAKE_DIR:
125  case CHANGE_MODE:
126  cache->FileChanged(this,file);
127  break;
128  case REMOVE_DIR:
129  cache->FileChanged(this,file);
130  cache->TreeChanged(this,file);
131  break;
132  default:
133  break;
134  }
135 }
136 
137 const char *FileAccess::StrError(int err)
138 {
139  static xstring str;
140 
141  // note to translators: several errors should not be displayed to user;
142  // so no need to translate them.
143  switch(err)
144  {
145  case(IN_PROGRESS):
146  return("Operation is in progress");
147  case(OK):
148  return("Error 0");
149  case(SEE_ERRNO):
150  if(error)
151  return str.vset(error.get(),": ",strerror(saved_errno),NULL);
152  return(strerror(saved_errno));
153  case(LOOKUP_ERROR):
154  return(error);
155  case(NOT_OPEN): // Actually this means an error in application
156  return("Class is not Open()ed");
157  case(NO_FILE):
158  if(error)
159  return str.vset(_("Access failed: "),error.get(),NULL);
160  return(_("File cannot be accessed"));
161  case(NO_HOST):
162  return(_("Not connected"));
163  case(FATAL):
164  if(error)
165  return str.vset(_("Fatal error"),": ",error.get(),NULL);
166  return(_("Fatal error"));
167  case(STORE_FAILED):
168  return(_("Store failed - you have to reput"));
169  case(LOGIN_FAILED):
170  if(error)
171  return str.vset(_("Login failed"),": ",error.get(),NULL);
172  return(_("Login failed"));
173  case(NOT_SUPP):
174  if(error)
175  return str.vset(_("Operation not supported"),": ",error.get(),NULL);
176  return(_("Operation not supported"));
177  case(FILE_MOVED):
178  if(error)
179  return str.vset(_("File moved"),": ",error.get(),NULL);
180  else
181  return str.vset(_("File moved to `"),location?location.get():"?","'",NULL);
182  }
183  return("");
184 }
185 
187 {
188  file.set(0);
189  file_url.set(0);
190  file1.set(0);
191  delete new_cwd; new_cwd=0;
192  mode=CLOSED;
193  opt_date=0;
194  opt_size=0;
198  ascii=false;
199  norest_manual=false;
200  location.set(0);
202  entity_charset.set(0);
203  ClearError();
204 }
205 
206 void FileAccess::Open2(const char *f,const char *f1,open_mode o)
207 {
208  Close();
209  file1.set(f1);
210  Open(f,o);
211 
212  cache->TreeChanged(this,file);
213  cache->FileChanged(this,file);
214  cache->FileChanged(this,file1);
215 }
216 
217 void FileAccess::Mkdir(const char *fn,bool allp)
218 {
219  Open(fn,MAKE_DIR);
220  mkdir_p=allp;
221 }
222 
224 {
225  StringSet *set=new StringSet;
226  const char *sl=strchr(file,'/');
227  while(sl)
228  {
229  if(sl>file)
230  {
232  if(tmp.ne(".") && tmp.ne(".."))
233  set->Append(tmp);
234  }
235  sl=strchr(sl+1,'/');
236  }
237  return set;
238 }
239 
241 {
242  return SameSiteAs(fa);
243 }
244 bool FileAccess::SameSiteAs(const FileAccess *fa) const
245 {
246  return SameProtoAs(fa);
247 }
248 
249 const char *FileAccess::GetFileURL(const char *f,int flags) const
250 {
251  static xstring url;
252 
253  const char *proto=GetVisualProto();
254  if(proto[0]==0)
255  return "";
256 
257  ParsedURL u("");
258 
259  u.proto.set(proto);
260  if(!(flags&NO_USER))
261  u.user.set(user);
262  if((pass_open || (flags&WITH_PASSWORD)) && !(flags&NO_PASSWORD))
263  u.pass.set(pass);
264  u.host.set(hostname);
265  u.port.set(portname);
266  if(!(flags&NO_PATH))
267  {
268  if(cwd.url)
269  {
270  Path f_path(cwd);
271  if(f)
272  f_path.Change(f,true);
273  if(f_path.url)
274  {
275  int f_path_index=url::path_index(f_path.url);
276  url.set_allocated(u.Combine(home));
277  url.append(f_path.url+f_path_index);
278  return url;
279  }
280  }
281 
282  bool is_dir=((!f || !*f) && !cwd.is_file);
283 
284  if(!f || (f[0]!='/' && f[0]!='~'))
285  f=dir_file(cwd.path?cwd.path.get():"~",f);
286  u.path.set(f);
287  if(is_dir && url::dir_needs_trailing_slash(proto))
288  u.path.append('/');
289  }
290  return url.set_allocated(u.Combine(home));
291 }
292 
293 const char *FileAccess::GetConnectURL(int flags) const
294 {
295  return GetFileURL(0,flags);
296 }
297 
298 void FileAccess::Connect(const char *host1,const char *port1)
299 {
300  Close();
301  hostname.set(host1);
302  portname.set(port1);
303  DontSleep();
305 }
306 
307 void FileAccess::Login(const char *user1,const char *pass1)
308 {
309  Close();
310  user.set(user1);
311  pass.set(pass1);
312  pass_open=false;
313 
314  if(user && pass==0)
315  {
316  FileAccess *o;
317  for(o=chain; o!=0; o=o->next)
318  {
319  pass.set(o->pass);
320  if(SameSiteAs(o) && o->pass)
321  break;
322  }
323  if(!o)
324  pass.set(0);
325  if(pass==0 && hostname) // still no pass? Try .netrc
326  {
328  if(nrc)
329  pass.set(nrc->pass);
330  }
331  }
333 }
334 
336 {
337  Close();
338  user.set(0);
339  pass.set(0);
340  pass_open=false;
342 }
343 
345 {
346  cwd.Set(default_cwd,false,0);
347  home.Set((char*)0);
348 }
349 
351 {
352  pass.set(p);
353  xstring save_pass;
354  for(FileAccess *o=chain; o!=0; o=o->next)
355  {
356  if(o==this)
357  continue;
358  save_pass.set(o->pass); // cheat SameSiteAs.
359  o->pass.set(pass);
360  if(!SameSiteAs(o))
361  o->pass.set(save_pass);
362  }
363 }
364 
366 {
367  Open(0,ARRAY_INFO);
368  fileset_for_info=info;
370 }
371 
372 static void expand_tilde(xstring &path, const char *home, int i=0)
373 {
374  if(!(path[i]=='~' && (path[i+1]==0 || path[i+1]=='/')))
375  return;
376  char prefix_len=(last_char(home)=='/' ? 2 : 1);
377  if(home[0]=='/' && i>0 && path[i-1]=='/')
378  home++;
379  path.set_substr(i,prefix_len,home);
380 }
381 
383 {
384  if(home)
385  {
386  cwd.ExpandTilde(home);
387  if(new_cwd)
388  new_cwd->ExpandTilde(home);
389  if(real_cwd)
390  expand_tilde(real_cwd,home);
391  if(file)
392  expand_tilde(file,home);
393  if(file1)
394  expand_tilde(file1,home);
395  }
396 }
397 void FileAccess::set_home(const char *h)
398 {
399  home.Set(h);
401 }
402 const char *FileAccess::ExpandTildeStatic(const char *s) const
403 {
404  if(!home || !(s[0]=='~' && (s[1]=='/' || s[1]==0)))
405  return s;
406 
407  static xstring buf;
408  buf.set(s);
409  expand_tilde(buf,home);
410  return buf;
411 }
412 
413 static inline
414 bool last_element_is_doubledot(const char *path,const char *end)
415 {
416  return((end==path+2 && !strncmp(path,"..",2))
417  || (end>path+2 && !strncmp(end-3,"/..",3)));
418 }
419 
420 int FileAccess::device_prefix_len(const char *path) const
421 {
422  ResValue dp=Query("device-prefix",hostname);
423  if(dp.is_nil() || !dp.to_bool())
424  return 0;
425  int i=0;
426  while(path[i] && (is_ascii_alnum(path[i]) || strchr("$_-",path[i])))
427  i++;
428  if(i>0 && path[i]==':')
429  return i+1+(path[i+1]=='/');
430  return 0;
431 }
432 
433 void FileAccess::Path::Optimize(xstring& path,int device_prefix_len)
434 {
435  int prefix_size=0;
436 
437  if(path[0]=='/' && path[1]=='~' && device_prefix_len==1)
438  {
439  prefix_size=2;
440  while(path[prefix_size]!='/' && path[prefix_size]!='\0')
441  prefix_size++;
442  }
443  else if(path[0]=='/')
444  {
445  prefix_size=1;
446  if(path[1]=='/' && (!path[2] || path[2]!='/'))
447  prefix_size=2;
448  }
449  else if(path[0]=='~')
450  {
451  prefix_size=1;
452  while(path[prefix_size]!='/' && path[prefix_size]!='\0')
453  prefix_size++;
454  }
455  else
456  {
457  // handle VMS and DOS devices.
458  prefix_size=device_prefix_len;
459  }
460 
461  char *in;
462  char *out;
463 
464  in=out=path.get_non_const()+prefix_size;
465 
466  while((in[0]=='.' && (in[1]=='/' || in[1]==0))
467  || (in>path && in[-1]=='/' && (in[0]=='/'
468  || (in[0]=='.' && in[1]=='.' && (in[2]=='/' || in[2]==0)))))
469  {
470  if(in[0]=='.' && in[1]=='.')
471  in++;
472  in++;
473  if(*in=='/')
474  in++;
475  }
476 
477  while(*in)
478  {
479  if(in[0]=='/')
480  {
481  // double slash
482  if(in[1]=='/')
483  {
484  in++;
485  continue;
486  }
487  if(in[1]=='.')
488  {
489  // . - cur dir
490  if(in[2]=='/' || in[2]=='\0')
491  {
492  in+=2;
493  continue;
494  }
495  // .. - prev dir
496  if(in[2]=='.' && (in[3]=='/' || in[3]=='\0'))
497  {
498  if(last_element_is_doubledot(path+prefix_size,out)
499  || out==path
500  || (out==path+prefix_size && out[-1]!='/'))
501  {
502  if(out>path && out[-1]!='/')
503  *out++='/';
504  *out++='.';
505  *out++='.';
506  }
507  else
508  {
509  while(out>path+prefix_size && *--out!='/')
510  ;
511  }
512  in+=3;
513  continue;
514  }
515  }
516  // don't add slash after prefix with slash
517  if(out>path && out[-1]=='/')
518  {
519  in++;
520  continue;
521  }
522  }
523  *out++=*in++;
524  }
525  path.truncate(path.length()-(in-out));
526 }
527 
528 void FileAccess::Chdir(const char *path,bool verify)
529 {
530  cwd.ExpandTilde(home);
531 
532  Close();
533  new_cwd=new Path(&cwd);
534  new_cwd->Change(path,false);
535 
536  if(verify)
538  else
539  {
540  cwd.Set(new_cwd);
541  delete new_cwd;
542  new_cwd=0;
543  }
544 }
545 
547 {
548  Close();
549  new_cwd=new Path(p);
551 }
552 
553 void FileAccess::Chmod(const char *file,int m)
554 {
555  chmod_mode=m;
556  Open(file,CHANGE_MODE);
557 }
558 
559 void FileAccess::SetError(int ec,const char *e)
560 {
561  if(ec==SEE_ERRNO && !saved_errno)
562  saved_errno=errno;
563  if(ec==NO_FILE && file && file[0] && !strstr(e,file))
564  error.vset(e," (",file.get(),")",NULL);
565  else
566  error.set(e);
567  error_code=ec;
568 }
569 
571 {
572  saved_errno=0;
573  error_code=OK;
574  error.set(0);
575 }
576 
577 void FileAccess::Fatal(const char *e)
578 {
579  SetError(FATAL,e);
580 }
581 
583 {
585  if(fn==0)
586  return;
587 
588  // don't allow subdirectories.
589  if(strchr(fn,'/') || strchr(fn,'\\') || strchr(fn,':'))
590  return;
591  for(int i=0; fn[i]; i++)
592  {
593  // don't allow control chars.
594  if(iscntrl((unsigned char)fn[i]))
595  return;
596  }
597  if(!*fn || *fn=='.')
598  return;
600 }
601 
602 void FileAccess::SetFileURL(const char *u)
603 {
604  file_url.set(u);
605  if(new_cwd && mode==CHANGE_DIR)
606  new_cwd->SetURL(u);
607 }
608 
609 FileAccess *SessionPool::pool[pool_size];
610 
612 {
613  if(f==0)
614  return;
615  if(f->GetHostName()==0)
616  {
617  SMTask::Delete(f);
618  return;
619  }
620  f->Close();
621  f->SetPriority(0);
622  int i;
623  for(i=0; i<pool_size; i++)
624  {
625  assert(pool[i]!=f);
626  if(pool[i]==0)
627  {
628  pool[i]=f;
629  return;
630  }
631  }
632  for(i=0; i<pool_size; i++)
633  {
634  if(f->IsBetterThan(pool[i]))
635  {
636  SMTask::Delete(pool[i]);
637  pool[i]=f;
638  return;
639  }
640  }
641  SMTask::Delete(f);
642 }
643 
644 void SessionPool::Print(FILE *f)
645 {
646  int arr[pool_size];
647  int n=0;
648  int i;
649 
650  for(i=0; i<pool_size; i++)
651  {
652  if(pool[i]==0)
653  continue;
654  int j;
655  for(j=0; j<n; j++)
656  if(pool[arr[j]]->SameLocationAs(pool[i]))
657  break;
658  if(j==n)
659  arr[n++]=i;
660  }
661 
662  // sort?
663 
664  for(i=0; i<n; i++)
665  fprintf(f,"%d\t%s\n",arr[i],pool[arr[i]]->GetConnectURL());
666 }
667 
669 {
670  if(n<0 || n>=pool_size)
671  return 0;
672  FileAccess *s=pool[n];
673  pool[n]=0;
674  return s;
675 }
676 
677 FileAccess *SessionPool::Walk(int *n,const char *proto)
678 {
679  for( ; *n<pool_size; (*n)++)
680  {
681  if(pool[*n] && !strcmp(pool[*n]->GetProto(),proto))
682  return pool[*n];
683  }
684  return 0;
685 }
686 
688 {
689  for(int n=0; n<pool_size; n++)
690  {
691  if(pool[n])
692  {
693  SMTask::Delete(pool[n]);
694  pool[n]=0;
695  }
696  }
697 }
698 
700 {
701  switch(e)
702  {
703  case(EPIPE):
704  case(ETIMEDOUT):
705 #ifdef ECONNRESET
706  case(ECONNRESET):
707 #endif
708  case(ECONNREFUSED):
709 #ifdef EHOSTUNREACH
710  case(EHOSTUNREACH):
711 #endif
712 #ifdef EHOSTDOWN
713  case(EHOSTDOWN):
714 #endif
715 #ifdef ENETRESET
716  case(ENETRESET):
717 #endif
718 #ifdef ENETUNREACH
719  case(ENETUNREACH):
720 #endif
721 #ifdef ENETDOWN
722  case(ENETDOWN):
723 #endif
724 #ifdef ECONNABORTED
725  case(ECONNABORTED):
726 #endif
727  return true;
728  }
729  return false;
730 }
731 
733 {
734  try_time=t;
735 }
736 
738 {
739  return(SameProtoAs(fa) && this->IsConnected() > fa->IsConnected());
740 }
741 
742 void FileAccess::Reconfig(const char *) {}
744 const char *FileAccess::CurrentStatus() { return ""; }
745 int FileAccess::Buffered() { return 0; }
746 bool FileAccess::IOReady() { return IsOpen(); }
747 int FileAccess::IsConnected() const { return 0; }
749 void FileAccess::UseCache(bool) {}
750 bool FileAccess::NeedSizeDateBeforehand() { return false; }
753 ListInfo *FileAccess::MakeListInfo(const char *path) { return 0; }
754 Glob *FileAccess::MakeGlob(const char *pattern) { return new NoGlob(pattern); }
755 DirList *FileAccess::MakeDirList(ArgV *a) { delete a; return 0; }
756 
758 {
759  for(FileAccess *o=chain; o!=0; o=o->next)
760  {
761  Enter(o);
762  o->CleanupThis();
763  Leave(o);
764  }
765 }
766 
768 {
769  if(scan==0)
770  scan=chain;
771  else
772  scan=scan->next;
773  for( ; scan; scan=scan->next)
774  if(scan!=this && SameSiteAs(scan))
775  return scan;
776  return 0;
777 }
778 
779 FileAccess *FileAccess::New(const char *proto,const char *host,const char *port)
780 {
781  ClassInit();
782 
783  if(proto==0)
784  proto="file";
785 
786  if(!strcmp(proto,"slot"))
787  {
789  return session?session->Clone():0;
790  }
791 
793  if(!session)
794  return 0;
795 
796  const char *n_proto=session->ProtocolSubstitution(host);
797  if(n_proto && strcmp(n_proto,proto))
798  {
799  FA *n_session=Protocol::NewSession(n_proto);
800  if(n_session)
801  {
802  delete session;
803  session=n_session;
804  session->SetVisualProto(proto);
805  }
806  }
807 
808  if(host)
809  session->Connect(host,port);
810 
811  return session;
812 }
814 {
815  const char *proto=u->proto?u->proto.get():"file";
816  FileAccess *s=New(proto,u->host);
817  if(!s)
818  {
819  if(!dummy)
820  return 0;
821  return new DummyNoProto(proto);
822  }
823  if(strcmp(proto,"slot"))
824  s->Connect(u->host,u->port);
825  if(u->user)
826  s->Login(u->user,u->pass);
827  // path?
828  return s;
829 }
830 
831 // FileAccess::Protocol implementation
832 FileAccess::Protocol *FileAccess::Protocol::chain=0;
833 
834 FileAccess::Protocol::Protocol(const char *proto, SessionCreator *creator)
835 {
836  this->proto=proto;
837  this->New=creator;
838  this->next=chain;
839  chain=this;
840 }
841 
842 FileAccess::Protocol *FileAccess::Protocol::FindProto(const char *proto)
843 {
844  for(Protocol *scan=chain; scan; scan=scan->next)
845  if(!strcasecmp(scan->proto,proto))
846  return scan;
847  return 0;
848 }
849 
851 {
852  Protocol *p;
853 
854  p=FindProto(proto);
855  if(p)
856  return p->New();
857 
858 #ifdef WITH_MODULES
859 #define PROTO_PREFIX "proto-"
860  const char *mod=xstring::cat(PROTO_PREFIX,proto,NULL);
861  void *map=module_load(mod,0,0);
862  if(map==0)
863  {
864  fprintf(stderr,"%s\n",module_error_message());
865  return 0;
866  }
867  p=FindProto(proto);
868  if(p)
869  return p->New();
870 #endif
871  return 0;
872 }
873 
874 // FileAccessOperation implementation
875 void FileAccessOperation::SetError(const char *e)
876 {
877  error_text.set(e);
878  done=true;
879 }
881 {
882  SetError(e);
883  error_text.append(_(" [cached]"));
884 }
885 
887  : FileAccessOperation(s), buf(new Buffer()), args(a), color(false)
888 {
889 }
891 {
892 }
893 
894 // ListInfo implementation
897 {
898  exclude=0;
899  exclude_prefix=0;
900 
901  need=0;
902 
903  follow_symlinks=false;
904 
905  if(session && p)
906  {
907  saved_cwd=session->GetCwd();
908  session->Chdir(p,false);
909  }
910 }
911 
913 {
914  if(session)
915  session->Close();
916  if(session && saved_cwd)
917  session->SetCwd(saved_cwd);
918 }
920 
921 
922 // Path implementation
923 void FileAccess::Path::init()
924 {
926  is_file=false;
927 }
929 {
930 }
931 void FileAccess::Path::Set(const char *new_path,bool new_is_file,const char *new_url,int new_device_prefix_len)
932 {
933  path.set(new_path);
934  is_file=new_is_file;
935  url.set(new_url);
936  device_prefix_len=new_device_prefix_len;
937 }
939 {
940  Set(o->path,o->is_file,o->url,o->device_prefix_len);
941 }
942 void FileAccess::Path::Change(const char *new_path,bool new_is_file,const char *new_path_enc,int new_device_prefix_len)
943 {
944  if(!new_path && new_path_enc)
945  new_path=url::decode(new_path_enc);
946  if(!new_path || !*new_path)
947  return;
948  const char *bn=basename_ptr(new_path);
949  if(!strcmp(bn,".") || !strcmp(bn,".."))
950  new_is_file=false;
951 
952  int path_index=0;
953  if(url)
954  {
955  path_index=url::path_index(url);
956  xstring new_url_path(url+path_index);
957  if(is_file)
958  {
959  dirname_modify(new_url_path);
960  if(!new_url_path[0])
961  new_url_path.set("/~");
962  }
963  if(new_url_path.last_char()!='/')
964  new_url_path.append("/");
965  if(new_path[0]=='/' || new_path[0]=='~' || new_device_prefix_len!=0)
966  {
967  bool have_slash=((new_path_enc?new_path_enc:new_path)[0]=='/');
968  new_url_path.set(have_slash?"":"/");
969  }
970  if(new_path_enc)
971  new_url_path.append(new_path_enc);
972  else
973  new_url_path.append(url::encode(new_path,URL_PATH_UNSAFE));
974  if(!new_is_file && url::dir_needs_trailing_slash(url) && new_url_path.last_char()!='/')
975  new_url_path.append('/');
976  Optimize(new_url_path,!strncmp(new_url_path,"/~",2));
977  url.truncate(path_index);
978  url.append(new_url_path);
979  }
980 
981  if(new_path[0]!='/' && new_path[0]!='~' && new_device_prefix_len==0
982  && path && path[0])
983  {
984  if(is_file)
985  {
986  dirname_modify(path);
987  if(!path[0])
988  path.set("~");
989  }
990  if(last_char(path)=='/')
991  new_path=xstring::format("%s%s",path.get(),new_path);
992  else
993  new_path=xstring::format("%s/%s",path.get(),new_path);
994  }
995  path.set(new_path);
996  device_prefix_len=new_device_prefix_len;
997  Optimize();
999  is_file=new_is_file;
1000  if(!strcmp(path,"/") || !strcmp(path,"//"))
1001  is_file=false;
1002 
1003  // sanity check
1004  if(url)
1005  {
1006  ParsedURL u(url);
1007  if(u.path.length()>1)
1008  u.path.chomp('/');
1009  if(!u.path.eq(path))
1010  {
1011  LogError(0,"URL mismatch %s [%s] vs %s, dropping URL\n",url.get(),u.path.get(),path.get());
1012  url.set(0);
1013  }
1014  }
1015 }
1016 bool FileAccess::Path::operator==(const Path &p2) const
1017 {
1018  const Path &p1=*this;
1019  if(p1.is_file!=p2.is_file)
1020  return false;
1021  if(xstrcmp(p1.path,p2.path))
1022  return false;
1023  if(xstrcmp(p1.url,p2.url))
1024  return false;
1025  return true;
1026 }
1028 {
1029  if(!home.path)
1030  return;
1031  if(path && path[0]=='~' && (path[1]=='/' || path[1]=='\0'))
1032  {
1034  if(path[1]=='\0')
1035  is_file=home.is_file;
1036  }
1037  if(url)
1038  {
1039  int pi=url::path_index(url);
1040  if(url[pi]=='/' && url[pi+1]=='~')
1041  pi++;
1042  expand_tilde(url,home.url?home.url.get():url::encode(home.path,URL_PATH_UNSAFE).get(),pi);
1043  }
1044  expand_tilde(path,home.path);
1045 }
1046 
1047 #include "DirColors.h"
1048 #include "LocalDir.h"
1049 #include "FileCopy.h"
1050 #include "modconfig.h"
1051 #ifndef MODULE_PROTO_FTP
1052 # include "ftpclass.h"
1053 # define _ftp Ftp::ClassInit()
1054 #else
1055 # define _ftp
1056 #endif
1057 #ifndef MODULE_PROTO_FILE
1058 # include "LocalAccess.h"
1059 # define _file LocalAccess::ClassInit()
1060 #else
1061 # define _file
1062 #endif
1063 #ifndef MODULE_PROTO_HTTP
1064 # include "Http.h"
1065 # define _http Http::ClassInit()
1066 #else
1067 # define _http
1068 #endif
1069 #ifndef MODULE_PROTO_FISH
1070 # include "Fish.h"
1071 # define _fish Fish::ClassInit()
1072 #else
1073 # define _fish
1074 #endif
1075 #ifndef MODULE_PROTO_SFTP
1076 # include "SFtp.h"
1077 # define _sftp SFtp::ClassInit()
1078 #else
1079 # define _sftp
1080 #endif
1081 bool FileAccess::class_inited;
1084 {
1085  if(class_inited)
1086  return;
1087  class_inited=true;
1088  cache=new LsCache();
1089 
1092 
1093  _ftp;
1094  _file;
1095  _http;
1096  _fish;
1097  _sftp;
1098 
1099  // make it link in classes required by modules.
1100  LocalDirectory ld;
1101 }
1103 {
1105  delete cache;
1106  cache=0;
1108 }
1109 
1110 const FileAccessRef& FileAccessRef::operator=(FileAccess *p)
1111 {
1112  reuse();
1113  ptr=SMTask::MakeRef(p);
1114  return *this;
1115 }
1116 
1117 // hook-up gnulib...
1118 CDECL_BEGIN
1119 #include "md5.h"
1120 #include "glob.h"
1121 CDECL_END
1123 void *_glob_hook=(void*)glob;