Blame src/NetAccess.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2017 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 <errno.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <math.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
Packit 8f70b4
#include "NetAccess.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "Speedometer.h"
Packit 8f70b4
Packit 8f70b4
#define super FileAccess
Packit 8f70b4
Packit 8f70b4
xmap_p<NetAccess::SiteData> NetAccess::site_data;
Packit 8f70b4
Packit 8f70b4
void NetAccess::Init()
Packit 8f70b4
{
Packit 8f70b4
   resolver=0;
Packit 8f70b4
   idle_timer.SetResource("net:idle",0);
Packit 8f70b4
   timeout_timer.SetResource("net:timeout",0);
Packit 8f70b4
   max_persist_retries=0;
Packit 8f70b4
   persist_retries=0;
Packit 8f70b4
   socket_buffer=0;
Packit 8f70b4
   socket_maxseg=0;
Packit 8f70b4
Packit 8f70b4
   peer_curr=0;
Packit 8f70b4
Packit 8f70b4
   reconnect_interval=30;  // retry with 30 second interval
Packit 8f70b4
   reconnect_interval_multiplier=1.2;
Packit 8f70b4
   reconnect_interval_max=300;
Packit 8f70b4
Packit 8f70b4
   rate_limit=0;
Packit 8f70b4
Packit 8f70b4
   connection_limit=0;	// no limit.
Packit 8f70b4
   connection_takeover=false;
Packit 8f70b4
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
   reconnect_interval_current=reconnect_interval;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
NetAccess::NetAccess()
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
}
Packit 8f70b4
NetAccess::NetAccess(const NetAccess *o) : super(o)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   if(o->peer)
Packit 8f70b4
   {
Packit 8f70b4
      peer.set(o->peer);
Packit 8f70b4
      peer_curr=o->peer_curr;
Packit 8f70b4
      if(peer_curr>=peer.count())
Packit 8f70b4
	 peer_curr=0;
Packit 8f70b4
   }
Packit 8f70b4
   home_auto.set(o->home_auto);
Packit 8f70b4
}
Packit 8f70b4
NetAccess::~NetAccess()
Packit 8f70b4
{
Packit 8f70b4
   ClearPeer();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::Cleanup()
Packit 8f70b4
{
Packit 8f70b4
   if(hostname==0)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
Packit 8f70b4
      fo->CleanupThis();
Packit 8f70b4
Packit 8f70b4
   CleanupThis();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::CleanupThis()
Packit 8f70b4
{
Packit 8f70b4
   if(!IsConnected() || mode!=CLOSED)
Packit 8f70b4
      return;
Packit 8f70b4
   Disconnect();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::Reconfig(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   super::Reconfig(name);
Packit 8f70b4
Packit 8f70b4
   const char *c=hostname;
Packit 8f70b4
Packit 8f70b4
   reconnect_interval = ResMgr::Query("net:reconnect-interval-base",c);
Packit 8f70b4
   reconnect_interval_multiplier = ResMgr::Query("net:reconnect-interval-multiplier",c);
Packit 8f70b4
   if(reconnect_interval_multiplier<1)
Packit 8f70b4
      reconnect_interval_multiplier=1;
Packit 8f70b4
   reconnect_interval_max = ResMgr::Query("net:reconnect-interval-max",c);
Packit 8f70b4
   if(reconnect_interval_max
Packit 8f70b4
      reconnect_interval_max=reconnect_interval;
Packit 8f70b4
   max_retries = ResMgr::Query("net:max-retries",c);
Packit 8f70b4
   max_persist_retries = ResMgr::Query("net:persist-retries",c);
Packit 8f70b4
   socket_buffer = ResMgr::Query("net:socket-buffer",c);
Packit 8f70b4
   socket_maxseg = ResMgr::Query("net:socket-maxseg",c);
Packit 8f70b4
   connection_limit = ResMgr::Query("net:connection-limit",c);
Packit 8f70b4
   connection_takeover = ResMgr::QueryBool("net:connection-takeover",c);
Packit 8f70b4
Packit 8f70b4
   if(rate_limit)
Packit 8f70b4
      rate_limit->Reconfig(name,c);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *NetAccess::CheckHangup(const struct pollfd *pfd,int num)
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
   {
Packit 8f70b4
#ifdef SO_ERROR
Packit 8f70b4
      int s_errno=0;
Packit 8f70b4
      errno=0;
Packit 8f70b4
      socklen_t len=sizeof(s_errno);
Packit 8f70b4
      getsockopt(pfd[i].fd,SOL_SOCKET,SO_ERROR,(char*)&s_errno,&len;;
Packit 8f70b4
// Where does the error number go - to errno or to the pointer?
Packit 8f70b4
// It seems that to errno, but if the pointer is NULL it dumps core.
Packit 8f70b4
// (solaris 2.5)
Packit 8f70b4
// It seems to be different on glibc 2.0 - check both errno and s_errno
Packit 8f70b4
      if((errno!=0 || s_errno!=0) && errno!=ENOTSOCK)
Packit 8f70b4
	 return strerror(errno?errno:s_errno);
Packit 8f70b4
#endif /* SO_ERROR */
Packit 8f70b4
      if(pfd[i].revents&POLLERR)
Packit 8f70b4
	 return "POLLERR";
Packit 8f70b4
   } /* end for */
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
int NetAccess::Poll(int fd,int ev,const char **err)
Packit 8f70b4
{
Packit 8f70b4
   struct pollfd pfd;
Packit 8f70b4
   pfd.fd=fd;
Packit 8f70b4
   pfd.events=ev;
Packit 8f70b4
   pfd.revents=0;
Packit 8f70b4
   int res=poll(&pfd,1,0);
Packit 8f70b4
   if(res<1)
Packit 8f70b4
      return 0;
Packit 8f70b4
   *err=CheckHangup(&pfd,1);
Packit 8f70b4
   if(*err)
Packit 8f70b4
      return -1;
Packit 8f70b4
   if(pfd.revents)
Packit 8f70b4
      timeout_timer.Reset();
Packit 8f70b4
   return pfd.revents;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::SayConnectingTo()
Packit 8f70b4
{
Packit 8f70b4
   assert(peer_curr
Packit 8f70b4
   const char *h=(proxy?proxy:hostname);
Packit 8f70b4
   LogNote(1,_("Connecting to %s%s (%s) port %u"),proxy?"proxy ":"",
Packit 8f70b4
      h,SocketNumericAddress(&peer[peer_curr]),SocketPort(&peer[peer_curr]));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::SetProxy(const char *px)
Packit 8f70b4
{
Packit 8f70b4
   bool was_proxied=(proxy!=0);
Packit 8f70b4
Packit 8f70b4
   proxy.set(0);
Packit 8f70b4
   proxy_port.set(0);
Packit 8f70b4
   proxy_user.set(0);
Packit 8f70b4
   proxy_pass.set(0);
Packit 8f70b4
   proxy_proto.set(0);
Packit 8f70b4
Packit 8f70b4
   if(!px)
Packit 8f70b4
      px="";
Packit 8f70b4
Packit 8f70b4
   ParsedURL url(px);
Packit 8f70b4
   if(!url.host || url.host[0]==0)
Packit 8f70b4
   {
Packit 8f70b4
      if(was_proxied)
Packit 8f70b4
	 ClearPeer();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   proxy.set(url.host);
Packit 8f70b4
   proxy_port.set(url.port);
Packit 8f70b4
   proxy_user.set(url.user);
Packit 8f70b4
   proxy_pass.set(url.pass);
Packit 8f70b4
   proxy_proto.set(url.proto);
Packit 8f70b4
   ClearPeer();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool NetAccess::NoProxy(const char *hostname)
Packit 8f70b4
{
Packit 8f70b4
   // match hostname against no-proxy var.
Packit 8f70b4
   if(!hostname)
Packit 8f70b4
      return false;
Packit 8f70b4
   const char *no_proxy_c=ResMgr::Query("net:no-proxy",0);
Packit 8f70b4
   if(!no_proxy_c)
Packit 8f70b4
      return false;
Packit 8f70b4
   char *no_proxy=alloca_strdup(no_proxy_c);
Packit 8f70b4
   int h_len=strlen(hostname);
Packit 8f70b4
   for(char *p=strtok(no_proxy," ,"); p; p=strtok(0," ,"))
Packit 8f70b4
   {
Packit 8f70b4
      int p_len=strlen(p);
Packit 8f70b4
      if(p_len>h_len || p_len==0)
Packit 8f70b4
	 continue;
Packit 8f70b4
      if(!strcasecmp(hostname+h_len-p_len,p))
Packit 8f70b4
	 return true;
Packit 8f70b4
   }
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::HandleTimeout()
Packit 8f70b4
{
Packit 8f70b4
   LogError(0,_("Timeout - reconnecting"));
Packit 8f70b4
   Disconnect();
Packit 8f70b4
   timeout_timer.Reset();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool NetAccess::CheckTimeout()
Packit 8f70b4
{
Packit 8f70b4
   if(timeout_timer.Stopped())
Packit 8f70b4
   {
Packit 8f70b4
      HandleTimeout();
Packit 8f70b4
      return(true);
Packit 8f70b4
   }
Packit 8f70b4
   return(false);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::ClearPeer()
Packit 8f70b4
{
Packit 8f70b4
   peer.unset();
Packit 8f70b4
   peer_curr=0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::NextPeer()
Packit 8f70b4
{
Packit 8f70b4
   peer_curr++;
Packit 8f70b4
   if(peer_curr>=peer.count())
Packit 8f70b4
      peer_curr=0;
Packit 8f70b4
   else
Packit 8f70b4
      DontSleep(); // try next address immediately
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::ResetLocationData()
Packit 8f70b4
{
Packit 8f70b4
   Disconnect();
Packit 8f70b4
   ClearPeer();
Packit 8f70b4
   super::ResetLocationData();
Packit 8f70b4
   timeout_timer.SetResource("net:timeout",hostname);
Packit 8f70b4
   idle_timer.SetResource("net:idle",hostname);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::Open(const char *fn,int mode,off_t offs)
Packit 8f70b4
{
Packit 8f70b4
   timeout_timer.Reset();
Packit 8f70b4
   super::Open(fn,mode,offs);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int NetAccess::Resolve(const char *defp,const char *ser,const char *pr)
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
Packit 8f70b4
   if(!resolver)
Packit 8f70b4
   {
Packit 8f70b4
      peer.unset();
Packit 8f70b4
      if(proxy)
Packit 8f70b4
	 resolver=new Resolver(proxy,proxy_port,defp);
Packit 8f70b4
      else
Packit 8f70b4
	 resolver=new Resolver(hostname,portname,defp,ser,pr);
Packit 8f70b4
      if(!resolver)
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      resolver->Roll();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!resolver->Done())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(resolver->Error())
Packit 8f70b4
   {
Packit 8f70b4
      SetError(LOOKUP_ERROR,resolver->ErrorMsg());
Packit 8f70b4
      return(MOVED);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   peer.set(resolver->Result());
Packit 8f70b4
   if(peer_curr>=peer.count())
Packit 8f70b4
      peer_curr=0;
Packit 8f70b4
Packit 8f70b4
   resolver=0;
Packit 8f70b4
   return MOVED;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool NetAccess::ReconnectAllowed()
Packit 8f70b4
{
Packit 8f70b4
   if(max_retries>0 && retries>=max_retries)
Packit 8f70b4
      return true; // it will fault later - no need to wait.
Packit 8f70b4
   int connection_limit=GetConnectionLimit();
Packit 8f70b4
   if(connection_limit>0 && connection_limit<=CountConnections())
Packit 8f70b4
      return false;
Packit 8f70b4
   if(reconnect_timer.Stopped())
Packit 8f70b4
      return true;
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *NetAccess::DelayingMessage()
Packit 8f70b4
{
Packit 8f70b4
   int connection_limit=GetConnectionLimit();
Packit 8f70b4
   if(connection_limit>0 && connection_limit<=CountConnections())
Packit 8f70b4
      return _("Connection limit reached");
Packit 8f70b4
   long remains=reconnect_timer.TimeLeft();
Packit 8f70b4
   if(remains<=0)
Packit 8f70b4
      return "";
Packit 8f70b4
   current->TimeoutS(1);
Packit 8f70b4
   if(last_disconnect_cause && reconnect_timer.TimePassed()<5)
Packit 8f70b4
      return last_disconnect_cause;
Packit 8f70b4
   return xstring::format("%s: %ld",_("Delaying before reconnect"),remains);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool NetAccess::NextTry()
Packit 8f70b4
{
Packit 8f70b4
   if(!CheckRetries())
Packit 8f70b4
      return false;
Packit 8f70b4
   if(retries==0)
Packit 8f70b4
      reconnect_interval_current=reconnect_interval;
Packit 8f70b4
   else if(reconnect_interval_multiplier>1)
Packit 8f70b4
   {
Packit 8f70b4
      reconnect_interval_current*=reconnect_interval_multiplier;
Packit 8f70b4
      if(reconnect_interval_current>reconnect_interval_max)
Packit 8f70b4
	 reconnect_interval_current=reconnect_interval_max;
Packit 8f70b4
   }
Packit 8f70b4
   retries++;
Packit 8f70b4
   LogNote(10,"attempt number %d (max_retries=%d)",retries,max_retries);
Packit 8f70b4
   return CheckRetries();
Packit 8f70b4
}
Packit 8f70b4
bool NetAccess::CheckRetries()
Packit 8f70b4
{
Packit 8f70b4
   if(max_retries>0 && retries>max_retries)
Packit 8f70b4
   {
Packit 8f70b4
      if(!IsConnected() && last_disconnect_cause)
Packit 8f70b4
	 Fatal(xstring::cat(_("max-retries exceeded")," (",last_disconnect_cause.get(),")",NULL));
Packit 8f70b4
      else
Packit 8f70b4
	 Fatal(_("max-retries exceeded"));
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
   reconnect_timer.Set(reconnect_interval_current);
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
void NetAccess::TrySuccess()
Packit 8f70b4
{
Packit 8f70b4
   retries=0;
Packit 8f70b4
   persist_retries=0;
Packit 8f70b4
   reconnect_interval_current=reconnect_interval;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::Close()
Packit 8f70b4
{
Packit 8f70b4
   if(mode!=CLOSED)
Packit 8f70b4
      idle_timer.Reset();
Packit 8f70b4
Packit 8f70b4
   TrySuccess();
Packit 8f70b4
   resolver=0;
Packit 8f70b4
   super::Close();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int NetAccess::CountConnections()
Packit 8f70b4
{
Packit 8f70b4
   int count=0;
Packit 8f70b4
   for(FileAccess *o=FirstSameSite(); o!=0; o=NextSameSite(o))
Packit 8f70b4
   {
Packit 8f70b4
      if(o->IsConnected())
Packit 8f70b4
	 count++;
Packit 8f70b4
   }
Packit 8f70b4
   return count;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NetAccess::PropagateHomeAuto()
Packit 8f70b4
{
Packit 8f70b4
   if(!home_auto)
Packit 8f70b4
      return;
Packit 8f70b4
   for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
Packit 8f70b4
   {
Packit 8f70b4
      NetAccess *o=(NetAccess*)fo; // we are sure it is NetAccess.
Packit 8f70b4
      if(!o->home_auto)
Packit 8f70b4
      {
Packit 8f70b4
	 o->home_auto.set(home_auto);
Packit 8f70b4
	 if(!o->home)
Packit 8f70b4
	    o->set_home(home_auto);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
const char *NetAccess::FindHomeAuto()
Packit 8f70b4
{
Packit 8f70b4
   for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
Packit 8f70b4
   {
Packit 8f70b4
      NetAccess *o=(NetAccess*)fo; // we are sure it is NetAccess.
Packit 8f70b4
      if(o->home_auto)
Packit 8f70b4
	 return o->home_auto;
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
// GenericParseListInfo implementation
Packit 8f70b4
int GenericParseListInfo::Do()
Packit 8f70b4
{
Packit 8f70b4
#define need_size (need&FileInfo::SIZE)
Packit 8f70b4
#define need_time (need&FileInfo::DATE)
Packit 8f70b4
Packit 8f70b4
   FileInfo *file;
Packit 8f70b4
   int res;
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   int old_mode=mode;
Packit 8f70b4
   Ref<FileSet> set;
Packit 8f70b4
Packit 8f70b4
do_again:
Packit 8f70b4
   if(done)
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(redir_resolution) {
Packit 8f70b4
      if(redir_session && redir_session->OpenMode()==FA::ARRAY_INFO) {
Packit 8f70b4
	 res=redir_session->Done();
Packit 8f70b4
	 if(res==FA::DO_AGAIN || res==FA::IN_PROGRESS)
Packit 8f70b4
	    return m;
Packit 8f70b4
	 redir_session->Close();
Packit 8f70b4
	 redir_fs->rewind();
Packit 8f70b4
	 FileInfo *fi=redir_fs->curr();
Packit 8f70b4
	 if(ResolveRedirect(fi))
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 result->curr()->MergeInfo(*fi,~0U);
Packit 8f70b4
	 result->next();
Packit 8f70b4
      }
Packit 8f70b4
      redir_count=0;
Packit 8f70b4
      for(FileInfo *fi=result->curr(); fi; fi=result->next()) {
Packit 8f70b4
	 if(ResolveRedirect(fi))
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      FileAccess::cache->UpdateFileSet(session,"",FA::MP_LIST,result);
Packit 8f70b4
      FileAccess::cache->UpdateFileSet(session,"",FA::LONG_LIST,result);
Packit 8f70b4
      done=true;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(session->OpenMode()==FA::ARRAY_INFO)
Packit 8f70b4
   {
Packit 8f70b4
      res=session->Done();
Packit 8f70b4
      if(res==FA::DO_AGAIN)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(res==FA::IN_PROGRESS)
Packit 8f70b4
	 return m;
Packit 8f70b4
      session->Close();
Packit 8f70b4
      // start redirection resolution.
Packit 8f70b4
      redir_resolution=true;
Packit 8f70b4
      result->rewind();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      goto do_again;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!ubuf)
Packit 8f70b4
   {
Packit 8f70b4
      const char *cache_buffer=0;
Packit 8f70b4
      int cache_buffer_size=0;
Packit 8f70b4
      const FileSet *cache_fset=0;
Packit 8f70b4
      int err;
Packit 8f70b4
      if(use_cache && FileAccess::cache->Find(session,"",mode,&err,
Packit 8f70b4
				    &cache_buffer,&cache_buffer_size,&cache_fset))
Packit 8f70b4
      {
Packit 8f70b4
	 if(err)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(mode==FA::MP_LIST)
Packit 8f70b4
	    {
Packit 8f70b4
	       mode=FA::LONG_LIST;
Packit 8f70b4
	       goto do_again;
Packit 8f70b4
	    }
Packit 8f70b4
	    SetErrorCached(cache_buffer);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(cache_fset) {
Packit 8f70b4
	    Log::global->Write(11,"ListInfo: using cached file set\n");
Packit 8f70b4
	    set=new FileSet(cache_fset);
Packit 8f70b4
	    old_mode=mode;
Packit 8f70b4
	    goto got_fileset;
Packit 8f70b4
	 }
Packit 8f70b4
	 ubuf=new IOBuffer(IOBuffer::GET);
Packit 8f70b4
	 ubuf->Put(cache_buffer,cache_buffer_size);
Packit 8f70b4
	 ubuf->PutEOF();
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 session->Open("",mode);
Packit 8f70b4
	 session->UseCache(use_cache);
Packit 8f70b4
	 ubuf=new IOBufferFileAccess(session);
Packit 8f70b4
	 ubuf->SetSpeedometer(new Speedometer());
Packit 8f70b4
	 if(FileAccess::cache->IsEnabled(session->GetHostName()))
Packit 8f70b4
	    ubuf->Save(FileAccess::cache->SizeLimit());
Packit 8f70b4
	 session->Roll();
Packit 8f70b4
	 ubuf->Roll();
Packit 8f70b4
      }
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(ubuf)
Packit 8f70b4
   {
Packit 8f70b4
      if(ubuf->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 FileAccess::cache->Add(session,"",mode,session->GetErrorCode(),ubuf);
Packit 8f70b4
	 if(mode==FA::MP_LIST)
Packit 8f70b4
	 {
Packit 8f70b4
	    mode=FA::LONG_LIST;
Packit 8f70b4
	    ubuf=0;
Packit 8f70b4
	    m=MOVED;
Packit 8f70b4
	    goto do_again;
Packit 8f70b4
	 }
Packit 8f70b4
	 SetError(ubuf->ErrorText());
Packit 8f70b4
	 ubuf=0;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(!ubuf->Eof())
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      // now we have all the index in ubuf; parse it.
Packit 8f70b4
      const char *b;
Packit 8f70b4
      int len;
Packit 8f70b4
      ubuf->Get(&b,&len;;
Packit 8f70b4
      old_mode=mode;
Packit 8f70b4
      set=Parse(b,len);
Packit 8f70b4
Packit 8f70b4
      // cache the list and the set.
Packit 8f70b4
      FileAccess::cache->Add(session,"",old_mode,FA::OK,ubuf,set);
Packit 8f70b4
Packit 8f70b4
got_fileset:
Packit 8f70b4
      if(set)
Packit 8f70b4
      {
Packit 8f70b4
	 bool need_resort=false;
Packit 8f70b4
	 set->rewind();
Packit 8f70b4
	 for(file=set->curr(); file!=0; file=set->next())
Packit 8f70b4
	 {
Packit 8f70b4
	    // tilde is special.
Packit 8f70b4
	    if(file->name[0]=='~')
Packit 8f70b4
	    {
Packit 8f70b4
	       file->name.set_substr(0,0,"./");
Packit 8f70b4
	       need_resort=true;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 if(need_resort && !result)
Packit 8f70b4
	    result=new FileSet; // Merge will sort the names
Packit 8f70b4
	 if(result)
Packit 8f70b4
	 {
Packit 8f70b4
	    result->Merge(set);
Packit 8f70b4
	    set=0; // free it now.
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	    result=set.borrow();
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      ubuf=0;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
      // try another mode? Parse() can set mode to indicate it wants to try it.
Packit 8f70b4
      if(mode!=old_mode)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(!result)
Packit 8f70b4
	 result=new FileSet;
Packit 8f70b4
Packit 8f70b4
      if(exclude)
Packit 8f70b4
	 result->Exclude(exclude_prefix,exclude,excluded.get_non_const());
Packit 8f70b4
      result->rewind();
Packit 8f70b4
      for(file=result->curr(); file!=0; file=result->next())
Packit 8f70b4
      {
Packit 8f70b4
	 file->need=0;
Packit 8f70b4
	 if(need_size && !file->Has(file->SIZE))
Packit 8f70b4
	    file->Need(file->SIZE);
Packit 8f70b4
	 if(need_time && (!file->Has(file->DATE)
Packit 8f70b4
                          || (file->date.ts_prec>0 && can_get_prec_time)))
Packit 8f70b4
	    file->Need(file->DATE);
Packit 8f70b4
Packit 8f70b4
	 if(file->defined & file->TYPE)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(file->filetype==file->SYMLINK && follow_symlinks)
Packit 8f70b4
	    {
Packit 8f70b4
	       file->filetype=file->UNKNOWN;
Packit 8f70b4
	       file->defined &= ~(file->SIZE|file->SYMLINK_DEF|file->MODE|file->DATE|file->TYPE);
Packit 8f70b4
	       file->Need(file->SIZE|file->DATE);
Packit 8f70b4
	    }
Packit 8f70b4
	    else if(file->filetype==file->SYMLINK)
Packit 8f70b4
	    {
Packit 8f70b4
	       // don't need these for symlinks
Packit 8f70b4
	       file->NoNeed(file->SIZE|file->DATE);
Packit 8f70b4
	       // but need the link target
Packit 8f70b4
	       if(!file->Has(file->SYMLINK_DEF))
Packit 8f70b4
		  file->Need(file->SYMLINK_DEF);
Packit 8f70b4
	    }
Packit 8f70b4
	    else if(file->filetype==file->DIRECTORY)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(!get_time_for_dirs)
Packit 8f70b4
		  continue;
Packit 8f70b4
	       // don't need size for directories
Packit 8f70b4
	       file->NoNeed(file->SIZE);
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      session->GetInfoArray(result.get_non_const());
Packit 8f70b4
      session->Roll();
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool GenericParseListInfo::ResolveRedirect(const FileInfo *fi)
Packit 8f70b4
{
Packit 8f70b4
   if(fi->filetype!=fi->REDIRECT || redir_count>=max_redir)
Packit 8f70b4
      return false;
Packit 8f70b4
Packit 8f70b4
   redir_count++;
Packit 8f70b4
   Log::global->Format(9,"ListInfo: resolving redirection %s -> %s\n",fi->name.get(),fi->GetRedirect());
Packit 8f70b4
Packit 8f70b4
   Ref<FileInfo> redir_fi(new FileInfo());
Packit 8f70b4
   redir_fi->Need(fi->need);
Packit 8f70b4
Packit 8f70b4
   xstring loc(fi->GetRedirect());
Packit 8f70b4
   ParsedURL u(loc,true);
Packit 8f70b4
   if(!u.proto) {
Packit 8f70b4
      // relative URI
Packit 8f70b4
      redir_session=session->Clone();
Packit 8f70b4
      if(loc[0]=='/' || fi->uri) {
Packit 8f70b4
	 if(loc[0]!='/') {
Packit 8f70b4
	    const char *slash=strrchr(fi->uri,'/');
Packit 8f70b4
	    if(slash)
Packit 8f70b4
	       loc.prepend(fi->uri,slash+1-fi->uri);
Packit 8f70b4
	 }
Packit 8f70b4
	 redir_fi->uri.set(loc);
Packit 8f70b4
	 redir_fi->name.set(loc);
Packit 8f70b4
	 redir_fi->name.url_decode();
Packit 8f70b4
      } else {
Packit 8f70b4
	 loc.url_decode();
Packit 8f70b4
	 const char *slash=strrchr(fi->name,'/');
Packit 8f70b4
	 if(slash)
Packit 8f70b4
	    redir_fi->name.nset(fi->name,slash+1-fi->name);
Packit 8f70b4
	 redir_fi->name.append(loc);
Packit 8f70b4
      }
Packit 8f70b4
   } else { // u.proto
Packit 8f70b4
      // absolute URL
Packit 8f70b4
      redir_session=FileAccess::New(&u);
Packit 8f70b4
      redir_fi->name.set(u.path?u.path.get():"/");
Packit 8f70b4
      redir_fi->uri.set(url::path_ptr(u.orig_url));
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!redir_fs)
Packit 8f70b4
      redir_fs=new FileSet();
Packit 8f70b4
   else
Packit 8f70b4
      redir_fs->Empty();
Packit 8f70b4
   redir_fs->Add(redir_fi.borrow());
Packit 8f70b4
   redir_session->GetInfoArray(redir_fs.get_non_const());
Packit 8f70b4
   redir_session->Roll();
Packit 8f70b4
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
GenericParseListInfo::GenericParseListInfo(FileAccess *s,const char *p)
Packit 8f70b4
   : ListInfo(s,p), redir_resolution(false), redir_count(0),
Packit 8f70b4
     max_redir(ResMgr::Query("xfer:max-redirections",0))
Packit 8f70b4
{
Packit 8f70b4
   get_time_for_dirs=true;
Packit 8f70b4
   can_get_prec_time=true;
Packit 8f70b4
   mode=FA::MP_LIST;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *GenericParseListInfo::Status()
Packit 8f70b4
{
Packit 8f70b4
   if(ubuf && !ubuf->Eof() && session->IsOpen())
Packit 8f70b4
      return xstring::format("%s (%lld) %s[%s]",_("Getting directory contents"),
Packit 8f70b4
		     (long long)session->GetPos(),
Packit 8f70b4
		     ubuf->GetRateStrS(),session->CurrentStatus());
Packit 8f70b4
   if(session->OpenMode()==FA::ARRAY_INFO)
Packit 8f70b4
      return xstring::format("%s (%d%%) [%s]",_("Getting files information"),
Packit 8f70b4
		     session->InfoArrayPercentDone(),
Packit 8f70b4
		     session->CurrentStatus());
Packit 8f70b4
   return "";
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
CDECL void lftp_network_cleanup()
Packit 8f70b4
{
Packit 8f70b4
   NetAccess::ClassCleanup();
Packit 8f70b4
   RateLimit::ClassCleanup();
Packit 8f70b4
}