Blame src/FileAccess.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2016 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
Packit 8f70b4
#include "FileAccess.h"
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/socket.h>
Packit 8f70b4
#include <netinet/in.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <stddef.h>
Packit 8f70b4
#include "ascii_ctype.h"
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "DummyProto.h"
Packit 8f70b4
#include "netrc.h"
Packit 8f70b4
#include "ArgV.h"
Packit 8f70b4
#include "ConnectionSlot.h"
Packit 8f70b4
#include "SignalHook.h"
Packit 8f70b4
#include "FileGlob.h"
Packit 8f70b4
#ifdef WITH_MODULES
Packit 8f70b4
# include "module.h"
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
xlist_head<FileAccess> FileAccess::all_fa;
Packit 8f70b4
const FileAccessRef FileAccessRef::null;
Packit 8f70b4
Packit 8f70b4
void FileAccess::Init()
Packit 8f70b4
{
Packit 8f70b4
   ClassInit();
Packit 8f70b4
Packit 8f70b4
   pass_open=false;
Packit 8f70b4
Packit 8f70b4
   default_cwd="~";
Packit 8f70b4
   cwd.Set(default_cwd,false,0);
Packit 8f70b4
   limit=FILE_END;
Packit 8f70b4
   real_pos=UNKNOWN_POS;
Packit 8f70b4
   pos=0;
Packit 8f70b4
   mode=CLOSED;
Packit 8f70b4
   retries=0;
Packit 8f70b4
   max_retries=0;
Packit 8f70b4
   opt_date=0;
Packit 8f70b4
   opt_size=0;
Packit 8f70b4
   fileset_for_info=0;
Packit 8f70b4
   error_code=OK;
Packit 8f70b4
   saved_errno=0;
Packit 8f70b4
   mkdir_p=false;
Packit 8f70b4
   rename_f=false;
Packit 8f70b4
   ascii=false;
Packit 8f70b4
   norest_manual=false;
Packit 8f70b4
Packit 8f70b4
   entity_size=NO_SIZE;
Packit 8f70b4
   entity_date=NO_DATE;
Packit 8f70b4
Packit 8f70b4
   res_prefix=0;
Packit 8f70b4
Packit 8f70b4
   chmod_mode=0644;
Packit 8f70b4
Packit 8f70b4
   priority=last_priority=0;
Packit 8f70b4
Packit 8f70b4
   all_fa.add(all_fa_node);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess::FileAccess(const FileAccess *fa)
Packit 8f70b4
   : all_fa_node(this)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
Packit 8f70b4
   cwd=fa->cwd;
Packit 8f70b4
   home=fa->home;
Packit 8f70b4
   user.set(fa->user);
Packit 8f70b4
   pass.set(fa->pass);
Packit 8f70b4
   pass_open=fa->pass_open;
Packit 8f70b4
   hostname.set(fa->hostname);
Packit 8f70b4
   portname.set(fa->portname);
Packit 8f70b4
   vproto.set(fa->vproto);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess::~FileAccess()
Packit 8f70b4
{
Packit 8f70b4
   all_fa_node.remove();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void  FileAccess::Open(const char *fn,int mode,off_t offs)
Packit 8f70b4
{
Packit 8f70b4
#ifdef OPEN_DEBUG
Packit 8f70b4
   printf("%p->FA::Open(%s,%d)\n",this,fn?fn:"NULL",mode);
Packit 8f70b4
#endif
Packit 8f70b4
   if(IsOpen())
Packit 8f70b4
      Close();
Packit 8f70b4
   Resume();
Packit 8f70b4
   file.set(fn);
Packit 8f70b4
   real_pos=UNKNOWN_POS;
Packit 8f70b4
   pos=offs;
Packit 8f70b4
   this->mode=mode;
Packit 8f70b4
   mkdir_p=false;
Packit 8f70b4
   rename_f=false;
Packit 8f70b4
   Timeout(0);
Packit 8f70b4
Packit 8f70b4
   switch((open_mode)mode)
Packit 8f70b4
   {
Packit 8f70b4
   case STORE:
Packit 8f70b4
   case REMOVE:
Packit 8f70b4
   case MAKE_DIR:
Packit 8f70b4
   case CHANGE_MODE:
Packit 8f70b4
      cache->FileChanged(this,file);
Packit 8f70b4
      break;
Packit 8f70b4
   case REMOVE_DIR:
Packit 8f70b4
      cache->FileChanged(this,file);
Packit 8f70b4
      cache->TreeChanged(this,file);
Packit 8f70b4
      break;
Packit 8f70b4
   default:
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *FileAccess::StrError(int err)
Packit 8f70b4
{
Packit 8f70b4
   static xstring str;
Packit 8f70b4
Packit 8f70b4
   // note to translators: several errors should not be displayed to user;
Packit 8f70b4
   // so no need to translate them.
Packit 8f70b4
   switch(err)
Packit 8f70b4
   {
Packit 8f70b4
   case(IN_PROGRESS):
Packit 8f70b4
      return("Operation is in progress");
Packit 8f70b4
   case(OK):
Packit 8f70b4
      return("Error 0");
Packit 8f70b4
   case(SEE_ERRNO):
Packit 8f70b4
      if(error)
Packit 8f70b4
	 return str.vset(error.get(),": ",strerror(saved_errno),NULL);
Packit 8f70b4
      return(strerror(saved_errno));
Packit 8f70b4
   case(LOOKUP_ERROR):
Packit 8f70b4
      return(error);
Packit 8f70b4
   case(NOT_OPEN):   // Actually this means an error in application
Packit 8f70b4
      return("Class is not Open()ed");
Packit 8f70b4
   case(NO_FILE):
Packit 8f70b4
      if(error)
Packit 8f70b4
	 return str.vset(_("Access failed: "),error.get(),NULL);
Packit 8f70b4
      return(_("File cannot be accessed"));
Packit 8f70b4
   case(NO_HOST):
Packit 8f70b4
      return(_("Not connected"));
Packit 8f70b4
   case(FATAL):
Packit 8f70b4
      if(error)
Packit 8f70b4
	 return str.vset(_("Fatal error"),": ",error.get(),NULL);
Packit 8f70b4
      return(_("Fatal error"));
Packit 8f70b4
   case(STORE_FAILED):
Packit 8f70b4
      return(_("Store failed - you have to reput"));
Packit 8f70b4
   case(LOGIN_FAILED):
Packit 8f70b4
      if(error)
Packit 8f70b4
	 return str.vset(_("Login failed"),": ",error.get(),NULL);
Packit 8f70b4
      return(_("Login failed"));
Packit 8f70b4
   case(NOT_SUPP):
Packit 8f70b4
      if(error)
Packit 8f70b4
	 return str.vset(_("Operation not supported"),": ",error.get(),NULL);
Packit 8f70b4
      return(_("Operation not supported"));
Packit 8f70b4
   case(FILE_MOVED):
Packit 8f70b4
      if(error)
Packit 8f70b4
	 return str.vset(_("File moved"),": ",error.get(),NULL);
Packit 8f70b4
      else
Packit 8f70b4
	 return str.vset(_("File moved to `"),location?location.get():"?","'",NULL);
Packit 8f70b4
   }
Packit 8f70b4
   return("");
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Close()
Packit 8f70b4
{
Packit 8f70b4
   file.set(0);
Packit 8f70b4
   file_url.set(0);
Packit 8f70b4
   file1.set(0);
Packit 8f70b4
   new_cwd=0;
Packit 8f70b4
   mode=CLOSED;
Packit 8f70b4
   opt_date=0;
Packit 8f70b4
   opt_size=0;
Packit 8f70b4
   fileset_for_info=0;
Packit 8f70b4
   retries=0;
Packit 8f70b4
   entity_size=NO_SIZE;
Packit 8f70b4
   entity_date=NO_DATE;
Packit 8f70b4
   ascii=false;
Packit 8f70b4
   norest_manual=false;
Packit 8f70b4
   location.set(0);
Packit 8f70b4
   entity_content_type.set(0);
Packit 8f70b4
   entity_charset.set(0);
Packit 8f70b4
   ClearError();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Open2(const char *f,const char *f1,open_mode o)
Packit 8f70b4
{
Packit 8f70b4
   Close();
Packit 8f70b4
   file1.set(f1);
Packit 8f70b4
   Open(f,o);
Packit 8f70b4
Packit 8f70b4
   cache->TreeChanged(this,file);
Packit 8f70b4
   cache->FileChanged(this,file);
Packit 8f70b4
   cache->FileChanged(this,file1);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Rename(const char *rfile,const char *to,bool clobber)
Packit 8f70b4
{
Packit 8f70b4
   Open2(rfile,to,RENAME);
Packit 8f70b4
   rename_f=clobber;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Mkdir(const char *fn,bool allp)
Packit 8f70b4
{
Packit 8f70b4
   Open(fn,MAKE_DIR);
Packit 8f70b4
   mkdir_p=allp;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
StringSet *FileAccess::MkdirMakeSet() const
Packit 8f70b4
{
Packit 8f70b4
   StringSet *set=new StringSet;
Packit 8f70b4
   const char *sl=strchr(file,'/');
Packit 8f70b4
   while(sl)
Packit 8f70b4
   {
Packit 8f70b4
      if(sl>file)
Packit 8f70b4
      {
Packit 8f70b4
	 xstring& tmp=xstring::get_tmp(file,sl-file);
Packit 8f70b4
	 if(tmp.ne(".") && tmp.ne(".."))
Packit 8f70b4
	    set->Append(tmp);
Packit 8f70b4
      }
Packit 8f70b4
      sl=strchr(sl+1,'/');
Packit 8f70b4
   }
Packit 8f70b4
   return set;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool FileAccess::SameLocationAs(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   return SameSiteAs(fa);
Packit 8f70b4
}
Packit 8f70b4
bool FileAccess::SameSiteAs(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   return SameProtoAs(fa);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const xstring& FileAccess::GetFileURL(const char *f,int flags) const
Packit 8f70b4
{
Packit 8f70b4
   const char *proto=GetVisualProto();
Packit 8f70b4
   if(proto[0]==0)
Packit 8f70b4
      return xstring::get_tmp("");
Packit 8f70b4
Packit 8f70b4
   ParsedURL u;
Packit 8f70b4
Packit 8f70b4
   u.proto.set(proto);
Packit 8f70b4
   if(!(flags&NO_USER))
Packit 8f70b4
      u.user.set(user);
Packit 8f70b4
   if((pass_open || (flags&WITH_PASSWORD)) && !(flags&NO_PASSWORD))
Packit 8f70b4
      u.pass.set(pass);
Packit 8f70b4
   u.host.set(hostname);
Packit 8f70b4
   u.port.set(portname);
Packit 8f70b4
   if(!(flags&NO_PATH))
Packit 8f70b4
   {
Packit 8f70b4
      if(cwd.url)
Packit 8f70b4
      {
Packit 8f70b4
	 Path f_path(cwd);
Packit 8f70b4
	 if(f)
Packit 8f70b4
	    f_path.Change(f,true);
Packit 8f70b4
	 if(f_path.url)
Packit 8f70b4
	 {
Packit 8f70b4
	    int f_path_index=url::path_index(f_path.url);
Packit 8f70b4
	    return u.CombineTo(xstring::get_tmp(""),home)
Packit 8f70b4
	       .append(f_path.url+f_path_index);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      bool is_dir=((!f || !*f) && !cwd.is_file);
Packit 8f70b4
Packit 8f70b4
      if(!f || (f[0]!='/' && f[0]!='~'))
Packit 8f70b4
	 f=dir_file(cwd.path?cwd.path.get():"~",f);
Packit 8f70b4
      u.path.set(f);
Packit 8f70b4
      if(is_dir && url::dir_needs_trailing_slash(proto) && u.path.last_char()!='/')
Packit 8f70b4
	 u.path.append('/');
Packit 8f70b4
   }
Packit 8f70b4
   return u.CombineTo(xstring::get_tmp(""),home);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const xstring& FileAccess::GetConnectURL(int flags) const
Packit 8f70b4
{
Packit 8f70b4
   return GetFileURL(0,flags);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Connect(const char *host1,const char *port1)
Packit 8f70b4
{
Packit 8f70b4
   Close();
Packit 8f70b4
   hostname.set(host1);
Packit 8f70b4
   portname.set(port1);
Packit 8f70b4
   DontSleep();
Packit 8f70b4
   ResetLocationData();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Login(const char *user1,const char *pass1)
Packit 8f70b4
{
Packit 8f70b4
   Close();
Packit 8f70b4
   user.set(user1);
Packit 8f70b4
   pass.set(pass1);
Packit 8f70b4
   pass_open=false;
Packit 8f70b4
Packit 8f70b4
   if(user && pass==0)
Packit 8f70b4
   {
Packit 8f70b4
      xlist_for_each(FileAccess,all_fa,node,o)
Packit 8f70b4
      {
Packit 8f70b4
	 pass.set(o->pass);
Packit 8f70b4
	 if(SameSiteAs(o) && o->pass)
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
      if(!o)
Packit 8f70b4
	 pass.set(0);
Packit 8f70b4
      if(pass==0 && hostname) // still no pass? Try .netrc
Packit 8f70b4
      {
Packit 8f70b4
	 NetRC::Entry *nrc=NetRC::LookupHost(hostname,user);
Packit 8f70b4
	 if(nrc)
Packit 8f70b4
	    pass.set(nrc->pass);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   ResetLocationData();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::ResetLocationData()
Packit 8f70b4
{
Packit 8f70b4
   cwd.Set(default_cwd,false,0);
Packit 8f70b4
   home.Set((char*)0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::SetPasswordGlobal(const char *p)
Packit 8f70b4
{
Packit 8f70b4
   pass.set(p);
Packit 8f70b4
   xstring save_pass;
Packit 8f70b4
   xlist_for_each(FileAccess,all_fa,node,o)
Packit 8f70b4
   {
Packit 8f70b4
      if(o==this)
Packit 8f70b4
	 continue;
Packit 8f70b4
      save_pass.set(o->pass);	 // cheat SameSiteAs.
Packit 8f70b4
      o->pass.set(pass);
Packit 8f70b4
      if(!SameSiteAs(o))
Packit 8f70b4
	 o->pass.set(save_pass);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::GetInfoArray(FileSet *info)
Packit 8f70b4
{
Packit 8f70b4
   Open(0,ARRAY_INFO);
Packit 8f70b4
   fileset_for_info=info;
Packit 8f70b4
   fileset_for_info->rewind();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static void expand_tilde(xstring &path, const char *home, int i=0)
Packit 8f70b4
{
Packit 8f70b4
   if(!(path[i]=='~' && (path[i+1]==0 || path[i+1]=='/')))
Packit 8f70b4
      return;
Packit 8f70b4
   char prefix_len=(last_char(home)=='/' ? 2 : 1);
Packit 8f70b4
   if(home[0]=='/' && i>0 && path[i-1]=='/')
Packit 8f70b4
      home++;
Packit 8f70b4
   path.set_substr(i,prefix_len,home);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void  FileAccess::ExpandTildeInCWD()
Packit 8f70b4
{
Packit 8f70b4
   if(home)
Packit 8f70b4
   {
Packit 8f70b4
      cwd.ExpandTilde(home);
Packit 8f70b4
      if(new_cwd)
Packit 8f70b4
	 new_cwd->ExpandTilde(home);
Packit 8f70b4
      if(real_cwd)
Packit 8f70b4
	 expand_tilde(real_cwd,home);
Packit 8f70b4
      if(file)
Packit 8f70b4
	 expand_tilde(file,home);
Packit 8f70b4
      if(file1)
Packit 8f70b4
	 expand_tilde(file1,home);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void FileAccess::set_home(const char *h)
Packit 8f70b4
{
Packit 8f70b4
   home.Set(h);
Packit 8f70b4
   ExpandTildeInCWD();
Packit 8f70b4
}
Packit 8f70b4
const char *FileAccess::ExpandTildeStatic(const char *s) const
Packit 8f70b4
{
Packit 8f70b4
   if(!home || !(s[0]=='~' && (s[1]=='/' || s[1]==0)))
Packit 8f70b4
      return s;
Packit 8f70b4
Packit 8f70b4
   static xstring buf;
Packit 8f70b4
   buf.set(s);
Packit 8f70b4
   expand_tilde(buf,home);
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static inline
Packit 8f70b4
bool last_element_is_doubledot(const char *path,const char *end)
Packit 8f70b4
{
Packit 8f70b4
   return((end==path+2 && !strncmp(path,"..",2))
Packit 8f70b4
        || (end>path+2 && !strncmp(end-3,"/..",3)));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileAccess::device_prefix_len(const char *path) const
Packit 8f70b4
{
Packit 8f70b4
   ResValue dp=Query("device-prefix",hostname);
Packit 8f70b4
   if(dp.is_nil() || !dp.to_bool())
Packit 8f70b4
      return 0;
Packit 8f70b4
   int i=0;
Packit 8f70b4
   while(path[i] && (is_ascii_alnum(path[i]) || strchr("$_-",path[i])))
Packit 8f70b4
      i++;
Packit 8f70b4
   if(i>0 && path[i]==':')
Packit 8f70b4
      return i+1+(path[i+1]=='/');
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Path::Optimize(xstring& path,int device_prefix_len)
Packit 8f70b4
{
Packit 8f70b4
   int prefix_size=0;
Packit 8f70b4
Packit 8f70b4
   if(path[0]=='/' && path[1]=='~' && device_prefix_len==1)
Packit 8f70b4
   {
Packit 8f70b4
      prefix_size=2;
Packit 8f70b4
      while(path[prefix_size]!='/' && path[prefix_size]!='\0')
Packit 8f70b4
	 prefix_size++;
Packit 8f70b4
   }
Packit 8f70b4
   else if(path[0]=='/')
Packit 8f70b4
   {
Packit 8f70b4
      prefix_size=1;
Packit 8f70b4
      if(path[1]=='/' && (!path[2] || path[2]!='/'))
Packit 8f70b4
	 prefix_size=2;
Packit 8f70b4
   }
Packit 8f70b4
   else if(path[0]=='~')
Packit 8f70b4
   {
Packit 8f70b4
      prefix_size=1;
Packit 8f70b4
      while(path[prefix_size]!='/' && path[prefix_size]!='\0')
Packit 8f70b4
	 prefix_size++;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      // handle VMS and DOS devices.
Packit 8f70b4
      prefix_size=device_prefix_len;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   char	 *in;
Packit 8f70b4
   char	 *out;
Packit 8f70b4
Packit 8f70b4
   in=out=path.get_non_const()+prefix_size;
Packit 8f70b4
Packit 8f70b4
   while((in[0]=='.' && (in[1]=='/' || in[1]==0))
Packit 8f70b4
   || (in>path && in[-1]=='/' && (in[0]=='/'
Packit 8f70b4
	 || (in[0]=='.' && in[1]=='.' && (in[2]=='/' || in[2]==0)))))
Packit 8f70b4
   {
Packit 8f70b4
      if(in[0]=='.' && in[1]=='.')
Packit 8f70b4
	 in++;
Packit 8f70b4
      in++;
Packit 8f70b4
      if(*in=='/')
Packit 8f70b4
	 in++;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   while(*in)
Packit 8f70b4
   {
Packit 8f70b4
      if(in[0]=='/')
Packit 8f70b4
      {
Packit 8f70b4
	 // double slash
Packit 8f70b4
	 if(in[1]=='/')
Packit 8f70b4
	 {
Packit 8f70b4
	    in++;
Packit 8f70b4
	    continue;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(in[1]=='.')
Packit 8f70b4
	 {
Packit 8f70b4
	    // . - cur dir
Packit 8f70b4
	    if(in[2]=='/' || in[2]=='\0')
Packit 8f70b4
	    {
Packit 8f70b4
	       in+=2;
Packit 8f70b4
	       continue;
Packit 8f70b4
	    }
Packit 8f70b4
	    // .. - prev dir
Packit 8f70b4
	    if(in[2]=='.' && (in[3]=='/' || in[3]=='\0'))
Packit 8f70b4
	    {
Packit 8f70b4
	       if(last_element_is_doubledot(path+prefix_size,out)
Packit 8f70b4
	       || out==path
Packit 8f70b4
	       || (out==path+prefix_size && out[-1]!='/'))
Packit 8f70b4
	       {
Packit 8f70b4
		  if(out>path && out[-1]!='/')
Packit 8f70b4
		     *out++='/';
Packit 8f70b4
		  *out++='.';
Packit 8f70b4
		  *out++='.';
Packit 8f70b4
	       }
Packit 8f70b4
	       else
Packit 8f70b4
	       {
Packit 8f70b4
		  while(out>path+prefix_size && *--out!='/')
Packit 8f70b4
		     ;
Packit 8f70b4
	       }
Packit 8f70b4
	       in+=3;
Packit 8f70b4
	       continue;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 // don't add slash after prefix with slash
Packit 8f70b4
	 if(out>path && out[-1]=='/')
Packit 8f70b4
	 {
Packit 8f70b4
	    in++;
Packit 8f70b4
	    continue;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      *out++=*in++;
Packit 8f70b4
   }
Packit 8f70b4
   path.truncate(path.length()-(in-out));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Chdir(const char *path,bool verify)
Packit 8f70b4
{
Packit 8f70b4
   cwd.ExpandTilde(home);
Packit 8f70b4
Packit 8f70b4
   Close();
Packit 8f70b4
   new_cwd=new Path(&cwd;;
Packit 8f70b4
   new_cwd->Change(path,false);
Packit 8f70b4
Packit 8f70b4
   if(verify)
Packit 8f70b4
      Open(new_cwd->path,CHANGE_DIR);
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      cwd.Set(new_cwd);
Packit 8f70b4
      new_cwd=0;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::PathVerify(const Path &p)
Packit 8f70b4
{
Packit 8f70b4
   Close();
Packit 8f70b4
   new_cwd=new Path(p);
Packit 8f70b4
   Open(new_cwd->path,CHANGE_DIR);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Chmod(const char *file,int m)
Packit 8f70b4
{
Packit 8f70b4
   chmod_mode=m;
Packit 8f70b4
   Open(file,CHANGE_MODE);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::SetError(int ec,const char *e)
Packit 8f70b4
{
Packit 8f70b4
   if(ec==SEE_ERRNO && !saved_errno)
Packit 8f70b4
      saved_errno=errno;
Packit 8f70b4
   if(ec==NO_FILE && file && file[0] && !strstr(e,file))
Packit 8f70b4
      error.vset(e," (",file.get(),")",NULL);
Packit 8f70b4
   else
Packit 8f70b4
      error.set(e);
Packit 8f70b4
   error_code=ec;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::ClearError()
Packit 8f70b4
{
Packit 8f70b4
   saved_errno=0;
Packit 8f70b4
   error_code=OK;
Packit 8f70b4
   error.set(0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Fatal(const char *e)
Packit 8f70b4
{
Packit 8f70b4
   SetError(FATAL,e);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::SetSuggestedFileName(const char *fn)
Packit 8f70b4
{
Packit 8f70b4
   suggested_filename.set(0);
Packit 8f70b4
   if(fn==0)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   // don't allow subdirectories.
Packit 8f70b4
   if(strchr(fn,'/') || strchr(fn,'\\') || strchr(fn,':'))
Packit 8f70b4
      return;
Packit 8f70b4
   for(int i=0; fn[i]; i++)
Packit 8f70b4
   {
Packit 8f70b4
      // don't allow control chars.
Packit 8f70b4
      if(iscntrl((unsigned char)fn[i]))
Packit 8f70b4
	 return;
Packit 8f70b4
   }
Packit 8f70b4
   if(!*fn || *fn=='.')
Packit 8f70b4
      return;
Packit 8f70b4
   suggested_filename.set(fn);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::SetFileURL(const char *u)
Packit 8f70b4
{
Packit 8f70b4
   file_url.set(u);
Packit 8f70b4
   if(new_cwd && mode==CHANGE_DIR)
Packit 8f70b4
      new_cwd->SetURL(u);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess *SessionPool::pool[pool_size];
Packit 8f70b4
Packit 8f70b4
void SessionPool::Reuse(FileAccess *f)
Packit 8f70b4
{
Packit 8f70b4
   if(f==0)
Packit 8f70b4
      return;
Packit 8f70b4
   if(f->GetHostName()==0)
Packit 8f70b4
   {
Packit 8f70b4
      SMTask::Delete(f);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   f->Close();
Packit 8f70b4
   f->SetPriority(0);
Packit 8f70b4
   int i;
Packit 8f70b4
   for(i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      assert(pool[i]!=f);
Packit 8f70b4
      if(pool[i]==0)
Packit 8f70b4
      {
Packit 8f70b4
	 pool[i]=f;
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   for(i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      if(f->IsBetterThan(pool[i]))
Packit 8f70b4
      {
Packit 8f70b4
	 SMTask::Delete(pool[i]);
Packit 8f70b4
	 pool[i]=f;
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   SMTask::Delete(f);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SessionPool::Print(FILE *f)
Packit 8f70b4
{
Packit 8f70b4
   int arr[pool_size];
Packit 8f70b4
   int n=0;
Packit 8f70b4
   int i;
Packit 8f70b4
Packit 8f70b4
   for(i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      if(pool[i]==0)
Packit 8f70b4
	 continue;
Packit 8f70b4
      int j;
Packit 8f70b4
      for(j=0; j
Packit 8f70b4
	 if(pool[arr[j]]->SameLocationAs(pool[i]))
Packit 8f70b4
	    break;
Packit 8f70b4
      if(j==n)
Packit 8f70b4
	 arr[n++]=i;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   // sort?
Packit 8f70b4
Packit 8f70b4
   for(i=0; i
Packit 8f70b4
      fprintf(f,"%d\t%s\n",arr[i],pool[arr[i]]->GetConnectURL().get());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess *SessionPool::GetSession(int n)
Packit 8f70b4
{
Packit 8f70b4
   if(n<0 || n>=pool_size)
Packit 8f70b4
      return 0;
Packit 8f70b4
   FileAccess *s=pool[n];
Packit 8f70b4
   pool[n]=0;
Packit 8f70b4
   return s;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess *SessionPool::Walk(int *n,const char *proto)
Packit 8f70b4
{
Packit 8f70b4
   for( ; *n
Packit 8f70b4
   {
Packit 8f70b4
      if(pool[*n] && !strcmp(pool[*n]->GetProto(),proto))
Packit 8f70b4
	 return pool[*n];
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void SessionPool::ClearAll()
Packit 8f70b4
{
Packit 8f70b4
   int pass=0;
Packit 8f70b4
   for(;;) {
Packit 8f70b4
      int left=0;
Packit 8f70b4
      for(int n=0; n
Packit 8f70b4
	 if(!pool[n])
Packit 8f70b4
	    continue;
Packit 8f70b4
	 if(pass==0)
Packit 8f70b4
	    pool[n]->Disconnect();
Packit 8f70b4
	 if(!pool[n]->IsConnected()) {
Packit 8f70b4
	    SMTask::Delete(pool[n]);
Packit 8f70b4
	    pool[n]=0;
Packit 8f70b4
	 } else {
Packit 8f70b4
	    left++;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(left==0)
Packit 8f70b4
	 break;
Packit 8f70b4
      SMTask::Schedule();
Packit 8f70b4
      SMTask::Block();
Packit 8f70b4
      pass++;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::SetTryTime(time_t t)
Packit 8f70b4
{
Packit 8f70b4
   if(t)
Packit 8f70b4
      reconnect_timer.Reset(Time(t));
Packit 8f70b4
   else
Packit 8f70b4
      reconnect_timer.Stop();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool FileAccess::IsBetterThan(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   return(SameProtoAs(fa) && this->IsConnected() > fa->IsConnected());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileAccess::Reconfig(const char *) {}
Packit 8f70b4
void FileAccess::ConnectVerify() { mode=CONNECT_VERIFY; }
Packit 8f70b4
const char *FileAccess::CurrentStatus() { return ""; }
Packit 8f70b4
int FileAccess::Buffered() { return 0; }
Packit 8f70b4
bool FileAccess::IOReady() { return IsOpen(); }
Packit 8f70b4
int FileAccess::IsConnected() const { return 0; }
Packit 8f70b4
void FileAccess::UseCache(bool) {}
Packit 8f70b4
bool FileAccess::NeedSizeDateBeforehand() { return false; }
Packit 8f70b4
void FileAccess::Cleanup() {}
Packit 8f70b4
void FileAccess::CleanupThis() {}
Packit 8f70b4
ListInfo *FileAccess::MakeListInfo(const char *path) { return 0; }
Packit 8f70b4
Glob *FileAccess::MakeGlob(const char *pattern) { return new NoGlob(pattern); }
Packit 8f70b4
DirList *FileAccess::MakeDirList(ArgV *a) { delete a; return 0; }
Packit 8f70b4
Packit 8f70b4
void FileAccess::CleanupAll()
Packit 8f70b4
{
Packit 8f70b4
   xlist_for_each(FileAccess,all_fa,node,o)
Packit 8f70b4
   {
Packit 8f70b4
      Enter(o);
Packit 8f70b4
      o->CleanupThis();
Packit 8f70b4
      Leave(o);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess *FileAccess::NextSameSite(FA *scan) const
Packit 8f70b4
{
Packit 8f70b4
   if(scan==0)
Packit 8f70b4
      scan=all_fa.first_obj();
Packit 8f70b4
   else
Packit 8f70b4
      scan=scan->all_fa_node.next_obj();
Packit 8f70b4
   for( ; scan; scan=scan->all_fa_node.next_obj())
Packit 8f70b4
      if(scan!=this && SameSiteAs(scan))
Packit 8f70b4
	 return scan;
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess *FileAccess::New(const char *proto,const char *host,const char *port)
Packit 8f70b4
{
Packit 8f70b4
   ClassInit();
Packit 8f70b4
Packit 8f70b4
   if(proto==0)
Packit 8f70b4
      proto="file";
Packit 8f70b4
Packit 8f70b4
   if(!strcmp(proto,"slot"))
Packit 8f70b4
   {
Packit 8f70b4
      const FA *session=ConnectionSlot::FindSession(host);
Packit 8f70b4
      return session?session->Clone():0;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   FA *session=Protocol::NewSession(proto);
Packit 8f70b4
   if(!session)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   const char *n_proto=session->ProtocolSubstitution(host);
Packit 8f70b4
   if(n_proto && strcmp(n_proto,proto))
Packit 8f70b4
   {
Packit 8f70b4
      FA *n_session=Protocol::NewSession(n_proto);
Packit 8f70b4
      if(n_session)
Packit 8f70b4
      {
Packit 8f70b4
	 Delete(session);
Packit 8f70b4
	 session=n_session;
Packit 8f70b4
	 session->SetVisualProto(proto);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(host)
Packit 8f70b4
      session->Connect(host,port);
Packit 8f70b4
Packit 8f70b4
   return session;
Packit 8f70b4
}
Packit 8f70b4
FileAccess *FileAccess::New(const ParsedURL *u,bool dummy)
Packit 8f70b4
{
Packit 8f70b4
   const char *proto=u->proto?u->proto.get():"file";
Packit 8f70b4
   FileAccess *s=New(proto,u->host);
Packit 8f70b4
   if(!s)
Packit 8f70b4
   {
Packit 8f70b4
      if(!dummy)
Packit 8f70b4
	 return 0;
Packit 8f70b4
      return new DummyNoProto(proto);
Packit 8f70b4
   }
Packit 8f70b4
   if(strcmp(proto,"slot"))
Packit 8f70b4
      s->Connect(u->host,u->port);
Packit 8f70b4
   if(u->user)
Packit 8f70b4
      s->Login(u->user,u->pass);
Packit 8f70b4
   // path?
Packit 8f70b4
   return s;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileAccess::Protocol implementation
Packit 8f70b4
xmap_p<FileAccess::Protocol> FileAccess::Protocol::proto_by_name;
Packit 8f70b4
Packit 8f70b4
FileAccess::Protocol::Protocol(const char *proto, SessionCreator *creator)
Packit 8f70b4
{
Packit 8f70b4
   this->proto=proto;
Packit 8f70b4
   this->New=creator;
Packit 8f70b4
   proto_by_name.add(proto,this);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess::Protocol *FileAccess::Protocol::FindProto(const char *proto)
Packit 8f70b4
{
Packit 8f70b4
   return proto_by_name.lookup(proto);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileAccess *FileAccess::Protocol::NewSession(const char *proto)
Packit 8f70b4
{
Packit 8f70b4
   Protocol *p;
Packit 8f70b4
Packit 8f70b4
   p=FindProto(proto);
Packit 8f70b4
   if(p)
Packit 8f70b4
      return p->New();
Packit 8f70b4
Packit 8f70b4
#ifdef WITH_MODULES
Packit 8f70b4
#define PROTO_PREFIX "proto-"
Packit 8f70b4
   const char *mod=xstring::cat(PROTO_PREFIX,proto,NULL);
Packit 8f70b4
   void *map=module_load(mod,0,0);
Packit 8f70b4
   if(map==0)
Packit 8f70b4
   {
Packit 8f70b4
      fprintf(stderr,"%s\n",module_error_message());
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   p=FindProto(proto);
Packit 8f70b4
   if(p)
Packit 8f70b4
      return p->New();
Packit 8f70b4
#endif
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileAccessOperation implementation
Packit 8f70b4
void FileAccessOperation::SetError(const char *e)
Packit 8f70b4
{
Packit 8f70b4
   error_text.set(e);
Packit 8f70b4
   done=true;
Packit 8f70b4
}
Packit 8f70b4
void FileAccessOperation::SetErrorCached(const char *e)
Packit 8f70b4
{
Packit 8f70b4
   SetError(e);
Packit 8f70b4
   error_text.append(_(" [cached]"));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
DirList::DirList(FileAccess *s,ArgV *a)
Packit 8f70b4
   : FileAccessOperation(s), buf(new Buffer()), args(a), color(false)
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
DirList::~DirList()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// ListInfo implementation
Packit 8f70b4
ListInfo::ListInfo(FileAccess *s,const char *p)
Packit 8f70b4
   : FileAccessOperation(s), exclude_prefix(0), exclude(0), need(0),
Packit 8f70b4
   follow_symlinks(false), try_recursive(false), is_recursive(false)
Packit 8f70b4
{
Packit 8f70b4
   if(session && p)
Packit 8f70b4
   {
Packit 8f70b4
      saved_cwd=session->GetCwd();
Packit 8f70b4
      session->Chdir(p,false);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void ListInfo::PrepareToDie()
Packit 8f70b4
{
Packit 8f70b4
   if(session)
Packit 8f70b4
      session->Close();
Packit 8f70b4
   if(session && saved_cwd)
Packit 8f70b4
      session->SetCwd(saved_cwd);
Packit 8f70b4
}
Packit 8f70b4
ListInfo::~ListInfo() {}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
// Path implementation
Packit 8f70b4
void FileAccess::Path::init()
Packit 8f70b4
{
Packit 8f70b4
   device_prefix_len=0;
Packit 8f70b4
   is_file=false;
Packit 8f70b4
}
Packit 8f70b4
FileAccess::Path::~Path()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
void FileAccess::Path::Set(const char *new_path,bool new_is_file,const char *new_url,int new_device_prefix_len)
Packit 8f70b4
{
Packit 8f70b4
   path.set(new_path);
Packit 8f70b4
   is_file=new_is_file;
Packit 8f70b4
   url.set(new_url);
Packit 8f70b4
   device_prefix_len=new_device_prefix_len;
Packit 8f70b4
}
Packit 8f70b4
void FileAccess::Path::Set(const Path *o)
Packit 8f70b4
{
Packit 8f70b4
   Set(o->path,o->is_file,o->url,o->device_prefix_len);
Packit 8f70b4
}
Packit 8f70b4
void FileAccess::Path::Change(const char *new_path,bool new_is_file,const char *new_path_enc,int new_device_prefix_len)
Packit 8f70b4
{
Packit 8f70b4
   if(!new_path && new_path_enc)
Packit 8f70b4
      new_path=url::decode(new_path_enc);
Packit 8f70b4
   if(!new_path || !*new_path)
Packit 8f70b4
      return;
Packit 8f70b4
   const char *bn=basename_ptr(new_path);
Packit 8f70b4
   if(!strcmp(bn,".") || !strcmp(bn,".."))
Packit 8f70b4
      new_is_file=false;
Packit 8f70b4
Packit 8f70b4
   int path_index=0;
Packit 8f70b4
   if(url)
Packit 8f70b4
   {
Packit 8f70b4
      path_index=url::path_index(url);
Packit 8f70b4
      xstring new_url_path(url+path_index);
Packit 8f70b4
      if(is_file)
Packit 8f70b4
      {
Packit 8f70b4
	 dirname_modify(new_url_path);
Packit 8f70b4
	 if(!new_url_path[0])
Packit 8f70b4
	    new_url_path.set("/~");
Packit 8f70b4
      }
Packit 8f70b4
      if(new_url_path.last_char()!='/')
Packit 8f70b4
	 new_url_path.append('/');
Packit 8f70b4
      if(new_path[0]=='/' || new_path[0]=='~' || new_device_prefix_len!=0)
Packit 8f70b4
      {
Packit 8f70b4
	 bool have_slash=((new_path_enc?new_path_enc:new_path)[0]=='/');
Packit 8f70b4
	 new_url_path.set(have_slash?"":"/");
Packit 8f70b4
      }
Packit 8f70b4
      if(new_path_enc)
Packit 8f70b4
	 new_url_path.append(new_path_enc);
Packit 8f70b4
      else
Packit 8f70b4
	 new_url_path.append(url::encode(new_path,URL_PATH_UNSAFE));
Packit 8f70b4
      if(!new_is_file && url::dir_needs_trailing_slash(url) && new_url_path.last_char()!='/')
Packit 8f70b4
	 new_url_path.append('/');
Packit 8f70b4
      Optimize(new_url_path,!strncmp(new_url_path,"/~",2));
Packit 8f70b4
      url.truncate(path_index);
Packit 8f70b4
      url.append(new_url_path);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(new_path[0]!='/' && new_path[0]!='~' && new_device_prefix_len==0
Packit 8f70b4
   && path && path[0])
Packit 8f70b4
   {
Packit 8f70b4
      if(is_file)
Packit 8f70b4
      {
Packit 8f70b4
	 dirname_modify(path);
Packit 8f70b4
	 if(!path[0])
Packit 8f70b4
	    path.set("~");
Packit 8f70b4
      }
Packit 8f70b4
      if(last_char(path)=='/')
Packit 8f70b4
	 new_path=xstring::format("%s%s",path.get(),new_path);
Packit 8f70b4
      else
Packit 8f70b4
	 new_path=xstring::format("%s/%s",path.get(),new_path);
Packit 8f70b4
   }
Packit 8f70b4
   path.set(new_path);
Packit 8f70b4
   device_prefix_len=new_device_prefix_len;
Packit 8f70b4
   Optimize();
Packit 8f70b4
   strip_trailing_slashes(path);
Packit 8f70b4
   is_file=new_is_file;
Packit 8f70b4
   if(!strcmp(path,"/") || !strcmp(path,"//"))
Packit 8f70b4
      is_file=false;
Packit 8f70b4
Packit 8f70b4
   // sanity check
Packit 8f70b4
   if(url)
Packit 8f70b4
   {
Packit 8f70b4
      ParsedURL u(url);
Packit 8f70b4
      if(u.path.length()>1)
Packit 8f70b4
	 u.path.chomp('/');
Packit 8f70b4
      if(!u.path.eq(path))
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(0,"URL mismatch %s [%s] vs %s, dropping URL\n",url.get(),u.path.get(),path.get());
Packit 8f70b4
	 url.set(0);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
bool FileAccess::Path::operator==(const Path &p2) const
Packit 8f70b4
{
Packit 8f70b4
   const Path &p1=*this;
Packit 8f70b4
   if(p1.is_file!=p2.is_file)
Packit 8f70b4
      return false;
Packit 8f70b4
   if(xstrcmp(p1.path,p2.path))
Packit 8f70b4
      return false;
Packit 8f70b4
   if(xstrcmp(p1.url,p2.url))
Packit 8f70b4
      return false;
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
void FileAccess::Path::ExpandTilde(const Path &home)
Packit 8f70b4
{
Packit 8f70b4
   if(!home.path)
Packit 8f70b4
      return;
Packit 8f70b4
   if(path && path[0]=='~' && (path[1]=='/' || path[1]=='\0'))
Packit 8f70b4
   {
Packit 8f70b4
      device_prefix_len=home.device_prefix_len;
Packit 8f70b4
      if(path[1]=='\0')
Packit 8f70b4
	 is_file=home.is_file;
Packit 8f70b4
   }
Packit 8f70b4
   if(url)
Packit 8f70b4
   {
Packit 8f70b4
      int pi=url::path_index(url);
Packit 8f70b4
      if(url[pi]=='/' && url[pi+1]=='~')
Packit 8f70b4
	 pi++;
Packit 8f70b4
      expand_tilde(url,home.url?home.url.get():url::encode(home.path,URL_PATH_UNSAFE).get(),pi);
Packit 8f70b4
   }
Packit 8f70b4
   expand_tilde(path,home.path);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#include "DirColors.h"
Packit 8f70b4
#include "LocalDir.h"
Packit 8f70b4
#include "FileCopy.h"
Packit 8f70b4
#include "modconfig.h"
Packit 8f70b4
#ifndef MODULE_PROTO_FTP
Packit 8f70b4
# include "ftpclass.h"
Packit 8f70b4
# define _ftp Ftp::ClassInit()
Packit 8f70b4
#else
Packit 8f70b4
# define _ftp
Packit 8f70b4
#endif
Packit 8f70b4
#ifndef MODULE_PROTO_FILE
Packit 8f70b4
# include "LocalAccess.h"
Packit 8f70b4
# define _file LocalAccess::ClassInit()
Packit 8f70b4
#else
Packit 8f70b4
# define _file
Packit 8f70b4
#endif
Packit 8f70b4
#ifndef MODULE_PROTO_HTTP
Packit 8f70b4
# include "Http.h"
Packit 8f70b4
# define _http Http::ClassInit()
Packit 8f70b4
#else
Packit 8f70b4
# define _http
Packit 8f70b4
#endif
Packit 8f70b4
#ifndef MODULE_PROTO_FISH
Packit 8f70b4
# include "Fish.h"
Packit 8f70b4
# define _fish Fish::ClassInit()
Packit 8f70b4
#else
Packit 8f70b4
# define _fish
Packit 8f70b4
#endif
Packit 8f70b4
#ifndef MODULE_PROTO_SFTP
Packit 8f70b4
# include "SFtp.h"
Packit 8f70b4
# define _sftp SFtp::ClassInit()
Packit 8f70b4
#else
Packit 8f70b4
# define _sftp
Packit 8f70b4
#endif
Packit 8f70b4
bool FileAccess::class_inited;
Packit 8f70b4
LsCache *FileAccess::cache;
Packit 8f70b4
void FileAccess::ClassInit()
Packit 8f70b4
{
Packit 8f70b4
   if(class_inited)
Packit 8f70b4
      return;
Packit 8f70b4
   class_inited=true;
Packit 8f70b4
   cache=new LsCache();
Packit 8f70b4
Packit 8f70b4
   SignalHook::ClassInit();
Packit 8f70b4
   ResMgr::ClassInit();
Packit 8f70b4
Packit 8f70b4
   if(!Log::global)
Packit 8f70b4
      Log::global=new Log("debug");
Packit 8f70b4
Packit 8f70b4
   _ftp;
Packit 8f70b4
   _file;
Packit 8f70b4
   _http;
Packit 8f70b4
   _fish;
Packit 8f70b4
   _sftp;
Packit 8f70b4
Packit 8f70b4
   // make it link in classes required by modules.
Packit 8f70b4
   LocalDirectory ld;
Packit 8f70b4
}
Packit 8f70b4
void FileAccess::ClassCleanup()
Packit 8f70b4
{
Packit 8f70b4
   Protocol::ClassCleanup();
Packit 8f70b4
   call_dynamic_hook("lftp_network_cleanup");
Packit 8f70b4
   DirColors::DeleteInstance();
Packit 8f70b4
   delete cache;
Packit 8f70b4
   cache=0;
Packit 8f70b4
   FileCopy::fxp_create=0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const FileAccessRef& FileAccessRef::operator=(FileAccess *p)
Packit 8f70b4
{
Packit 8f70b4
   reuse();
Packit 8f70b4
   ptr=SMTask::MakeRef(p);
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// hook-up gnulib...
Packit 8f70b4
CDECL_BEGIN
Packit 8f70b4
#include "md5.h"
Packit 8f70b4
#include "glob.h"
Packit 8f70b4
CDECL_END
Packit 8f70b4
void *_md5_hook=(void*)md5_init_ctx;
Packit 8f70b4
void *_glob_hook=(void*)glob;