File fix-issue-235.patch of Package lftp

From 764c5a3057357abdf6c272b0e67ff26c07e272f0 Mon Sep 17 00:00:00 2001
From: "Alexander V. Lukyanov" <lavv17f@gmail.com>
Date: Tue, 10 May 2016 17:09:37 +0300
Subject: [PATCH] Squashed patches for issue 235

---
 src/FileCopy.cc  |   6 ++--
 src/FileSet.cc   |  21 +++++------
 src/FileSet.h    |  10 ++++--
 src/Http.cc      | 107 +++++++++++++++++++++++++++++++++----------------------
 src/Http.h       |   1 +
 src/MirrorJob.cc |   3 +-
 src/NetAccess.cc |  87 +++++++++++++++++++++++++++++++++++++++++---
 src/NetAccess.h  |   7 ++++
 src/xstring.h    |   3 ++
 9 files changed, 182 insertions(+), 63 deletions(-)

diff --git a/src/FileCopy.cc b/src/FileCopy.cc
index c71df2e..46de066 100644
--- a/src/FileCopy.cc
+++ b/src/FileCopy.cc
@@ -1238,8 +1238,10 @@ int FileCopyPeerFA::Get_LL(int len)
 		  file.set(loc);
 	    }
 
-	    size=NO_SIZE_YET;
-	    date=NO_DATE_YET;
+	    if(want_size || size!=NO_SIZE)
+	       WantSize();
+	    if(want_date || date!=NO_DATE)
+	       WantDate();
 
 	    upload_state.Clear();
 	    current->Timeout(0); // retry with new location.
diff --git a/src/FileSet.cc b/src/FileSet.cc
index 54afc89..2c7197e 100644
--- a/src/FileSet.cc
+++ b/src/FileSet.cc
@@ -50,10 +50,16 @@ void  FileInfo::Merge(const FileInfo& f)
 {
    if(strcmp(basename_ptr(name),basename_ptr(f.name)))
       return;
-// int sim=defined&f.defined;
-   int dif=(~defined)&f.defined;
-   if(dif&MODE)
+   MergeInfo(f,~defined);
+}
+void  FileInfo::MergeInfo(const FileInfo& f,unsigned dif)
+{
+   dif&=f.defined;
+   if(dif&MODE) {
       SetMode(f.mode);
+      if(mode!=SYMLINK && mode!=REDIRECT)
+	 symlink.unset();
+   }
    if(dif&DATE || (defined&DATE && f.defined&DATE && f.date.ts_prec<date.ts_prec))
       SetDate(f.date,f.date.ts_prec);
    if(dif&SIZE)
@@ -484,7 +490,7 @@ void FileSet::Count(int *d,int *f,int *s,int *o) const
 	 if(f) (*f)++; break;
       case(FileInfo::SYMLINK):
 	 if(s) (*s)++; break;
-      case(FileInfo::UNKNOWN):
+      default:
 	 if(o) (*o)++;
       }
    }
@@ -720,12 +726,6 @@ FileInfo::FileInfo(const FileInfo &fi)
    nlinks=fi.nlinks;
    longname.set(fi.longname);
 }
-FileInfo::FileInfo(const char *n)
-{
-   Init();
-   SetName(n);
-}
-
 FileInfo::~FileInfo()
 {
 }
@@ -1004,6 +1004,7 @@ void FileInfo::MakeLongName()
    case UNKNOWN:   break;
    case DIRECTORY: filetype_c='d'; break;
    case SYMLINK:   filetype_c='l'; break;
+   case REDIRECT:  filetype_c='L'; break;
    }
    int mode1=(defined&MODE?mode:
       (filetype_c=='d'?0755:(filetype_c=='l'?0777:0644)));
diff --git a/src/FileSet.h b/src/FileSet.h
index 6771fa9..7f456d0 100644
--- a/src/FileSet.h
+++ b/src/FileSet.h
@@ -51,6 +51,7 @@ public:
    xstring  name;
    xstring  longname;
    xstring_c symlink;
+   xstring_c uri;
    mode_t   mode;
    FileTimestamp date;
    off_t    size;
@@ -63,7 +64,8 @@ public:
       UNKNOWN=0,
       DIRECTORY,
       SYMLINK,
-      NORMAL
+      NORMAL,
+      REDIRECT,
    };
    type	 filetype;
 
@@ -85,7 +87,8 @@ public:
    void Init();
    FileInfo() { Init(); }
    FileInfo(const FileInfo &fi);
-   FileInfo(const char *n);
+   FileInfo(const char *n) { Init(); SetName(n); }
+   FileInfo(const xstring& n) { Init(); SetName(n); }
    ~FileInfo();
 
    void SetName(const char *n) { name.set(n); def(NAME); }
@@ -100,10 +103,13 @@ public:
    void SetDate(time_t t,int prec) { date.set(t,prec); def(DATE); }
    void SetType(type t) { filetype=t; def(TYPE); }
    void SetSymlink(const char *s) { symlink.set(s); filetype=SYMLINK; def(TYPE|SYMLINK_DEF); }
+   void SetRedirect(const char *s) { symlink.set(s); filetype=REDIRECT; def(TYPE|SYMLINK_DEF); }
+   const char *GetRedirect() const { return symlink; }
    void	SetSize(off_t s) { size=s; def(SIZE); }
    void	SetNlink(int n) { nlinks=n; def(NLINKS); }
 
    void	 Merge(const FileInfo&);
+   void  MergeInfo(const FileInfo& f,unsigned mask);
 
    bool	 SameAs(const FileInfo *,int ignore) const;
    bool	 OlderThan(time_t t) const;
diff --git a/src/Http.cc b/src/Http.cc
index 6591fc2..4ca83bd 100644
--- a/src/Http.cc
+++ b/src/Http.cc
@@ -222,6 +222,7 @@ void Http::MoveConnectionHere(Http *o)
 
 void Http::DisconnectLL()
 {
+   Enter(this);
    rate_limit=0;
    if(conn)
    {
@@ -247,6 +248,7 @@ void Http::DisconnectLL()
    last_url.unset();
    ResetRequestData();
    state=DISCONNECTED;
+   Leave(this);
 }
 
 void Http::ResetRequestData()
@@ -877,6 +879,10 @@ int Http::SendArrayInfoRequest()
 	 name=&xstring::get_tmp(*name);
 	 name->append('/');
       }
+      if(fi->uri)
+	 file_url.set(dir_file(GetConnectURL(),fi->uri));
+      else
+	 file_url.unset();
       SendRequest(array_send==fileset_for_info->count()-1 ? 0 : "keep-alive", *name);
       req_count++;
    }
@@ -1578,6 +1584,14 @@ int Http::Do()
 		     }
 		     goto pre_RECEIVING_BODY;
 		  }
+		  FileInfo *fi=fileset_for_info->curr();
+		  if(H_REDIRECTED(status_code)) {
+		     HandleRedirection();
+		     if(location)
+			fi->SetRedirect(location);
+		  } else if(H_2XX(status_code) && !fi->Has(fi->TYPE)) {
+		     fi->SetType(last_uri.last_char()=='/'?fi->DIRECTORY:fi->NORMAL);
+		  }
 		  ProceedArrayInfo();
 		  return MOVED;
 	       }
@@ -1736,49 +1750,7 @@ int Http::Do()
 
 	 if(H_REDIRECTED(status_code))
 	 {
-	    bool is_url=(location && url::is_url(location));
-	    if(location && !is_url
-	    && mode==QUOTE_CMD && !strncasecmp(file,"POST ",5)
-	    && tunnel_state!=TUNNEL_WAITING)
-	    {
-	       const char *the_file=file;
-
-	       const char *scan=file+5;
-	       while(*scan==' ')
-		  scan++;
-	       char *the_post_file=alloca_strdup(scan);
-	       char *space=strchr(the_post_file,' ');
-	       if(space)
-		  *space=0;
-	       the_file=the_post_file;
-
-	       char *new_location=alloca_strdup2(GetConnectURL(),
-				    strlen(the_file)+strlen(location));
-	       int p_ind=url::path_index(new_location);
-	       if(location[0]=='/')
-		  strcpy(new_location+p_ind,location);
-	       else
-	       {
-		  if(the_file[0]=='/')
-		     strcpy(new_location+p_ind,the_file);
-		  else
-		  {
-		     char *slash=strrchr(new_location,'/');
-		     strcpy(slash+1,the_file);
-		  }
-		  char *slash=strrchr(new_location,'/');
-		  strcpy(slash+1,location);
-	       }
-	       location.set(new_location);
-	    } else if(is_url && !hftp) {
-	       ParsedURL url(location);
-	       if(url.proto.eq(GetProto()) && !xstrcasecmp(url.host,hostname)
-	       && user && !url.user) {
-		  // use the same user name after redirect to the same site.
-		  url.user.set(user);
-		  location.set_allocated(url.Combine());
-	       }
-	    }
+	    HandleRedirection();
 	    err.setf("%s (%s -> %s)",status+status_consumed,file.get(),
 				    location?location.get():"nowhere");
 	    code=FILE_MOVED;
@@ -1927,6 +1899,53 @@ system_error:
    return MOVED;
 }
 
+void Http::HandleRedirection()
+{
+   bool is_url=(location && url::is_url(location));
+   if(location && !is_url
+   && mode==QUOTE_CMD && !strncasecmp(file,"POST ",5)
+   && tunnel_state!=TUNNEL_WAITING)
+   {
+      const char *the_file=file;
+
+      const char *scan=file+5;
+      while(*scan==' ')
+	 scan++;
+      char *the_post_file=alloca_strdup(scan);
+      char *space=strchr(the_post_file,' ');
+      if(space)
+	 *space=0;
+      the_file=the_post_file;
+
+      char *new_location=alloca_strdup2(GetConnectURL(),
+			   strlen(the_file)+strlen(location));
+      int p_ind=url::path_index(new_location);
+      if(location[0]=='/')
+	 strcpy(new_location+p_ind,location);
+      else
+      {
+	 if(the_file[0]=='/')
+	    strcpy(new_location+p_ind,the_file);
+	 else
+	 {
+	    char *slash=strrchr(new_location,'/');
+	    strcpy(slash+1,the_file);
+	 }
+	 char *slash=strrchr(new_location,'/');
+	 strcpy(slash+1,location);
+      }
+      location.set(new_location);
+   } else if(is_url && !hftp) {
+      ParsedURL url(location);
+      if(url.proto.eq(GetProto()) && !xstrcasecmp(url.host,hostname)
+      && user && !url.user) {
+	 // use the same user name after redirect to the same site.
+	 url.user.set(user);
+	 location.set_allocated(url.Combine());
+      }
+   }
+}
+
 FileAccess *Http::New() { return new Http(); }
 FileAccess *HFtp::New() { return new HFtp(); }
 
@@ -1964,6 +1983,7 @@ int Http::Read(Buffer *buf,int size)
    int res=DO_AGAIN;
    if(state==RECEIVING_BODY && real_pos>=0)
    {
+      Enter(this);
       res=_Read(buf,size);
       if(res>0)
       {
@@ -1972,6 +1992,7 @@ int Http::Read(Buffer *buf,int size)
 	    rate_limit->BytesGot(res);
 	 TrySuccess();
       }
+      Leave(this);
    }
    return res;
 }
diff --git a/src/Http.h b/src/Http.h
index ad19912..6633f52 100644
--- a/src/Http.h
+++ b/src/Http.h
@@ -109,6 +109,7 @@ class Http : public NetAccess
    int status_code;
    void HandleHeaderLine(const char *name,const char *value);
    static const xstring& extract_quoted_header_value(const char *value,const char **end=0);
+   void HandleRedirection();
    void GetBetterConnection(int level);
    void SetCookie(const char *val);
    void MakeCookie(xstring &cookie,const char *host,const char *path);
diff --git a/src/MirrorJob.cc b/src/MirrorJob.cc
index 776e350..8697259 100644
--- a/src/MirrorJob.cc
+++ b/src/MirrorJob.cc
@@ -273,6 +273,7 @@ void  MirrorJob::HandleFile(FileInfo *file)
    switch(filetype)
    {
       case(FileInfo::NORMAL):
+      case(FileInfo::REDIRECT):
       {
 	 bool remove_target=false;
 	 bool cont_this=false;
@@ -1215,7 +1216,7 @@ int   MirrorJob::Do()
 	 break;
 
       // all jobs finished.
-      if(remove_source_dirs) {
+      if(remove_source_dirs && source_dir.last_char()!='/') {
 	 // remove source directory once.
 	 remove_source_dirs=false;
 	 if(script)
diff --git a/src/NetAccess.cc b/src/NetAccess.cc
index 18a9fdd..9d55473 100644
--- a/src/NetAccess.cc
+++ b/src/NetAccess.cc
@@ -404,6 +404,31 @@ do_again:
    if(done)
       return m;
 
+   if(redir_resolution) {
+      if(redir_session && redir_session->OpenMode()==FA::ARRAY_INFO) {
+	 res=redir_session->Done();
+	 if(res==FA::DO_AGAIN || res==FA::IN_PROGRESS)
+	    return m;
+	 redir_session->Close();
+	 redir_fs->rewind();
+	 FileInfo *fi=redir_fs->curr();
+	 if(ResolveRedirect(fi))
+	    return MOVED;
+	 result->curr()->MergeInfo(*fi,~0U);
+	 result->next();
+      }
+      redir_count=0;
+      for(FileInfo *fi=result->curr(); fi; fi=result->next()) {
+	 if(ResolveRedirect(fi))
+	    return MOVED;
+      }
+
+      FileAccess::cache->UpdateFileSet(session,"",FA::MP_LIST,result);
+      FileAccess::cache->UpdateFileSet(session,"",FA::LONG_LIST,result);
+      done=true;
+      return MOVED;
+   }
+
    if(session->OpenMode()==FA::ARRAY_INFO)
    {
       res=session->Done();
@@ -412,10 +437,11 @@ do_again:
       if(res==FA::IN_PROGRESS)
 	 return m;
       session->Close();
-      FileAccess::cache->UpdateFileSet(session,"",FA::MP_LIST,result);
-      FileAccess::cache->UpdateFileSet(session,"",FA::LONG_LIST,result);
-      done=true;
-      return MOVED;
+      // start redirection resolution.
+      redir_resolution=true;
+      result->rewind();
+      m=MOVED;
+      goto do_again;
    }
 
    if(!ubuf)
@@ -567,8 +593,59 @@ got_fileset:
    return m;
 }
 
+bool GenericParseListInfo::ResolveRedirect(const FileInfo *fi)
+{
+   if(fi->filetype!=fi->REDIRECT || redir_count>=max_redir)
+      return false;
+
+   redir_count++;
+   Log::global->Format(9,"ListInfo: resolving redirection %s -> %s\n",fi->name.get(),fi->GetRedirect());
+
+   Ref<FileInfo> redir_fi(new FileInfo());
+   redir_fi->Need(fi->need);
+
+   xstring loc(fi->GetRedirect());
+   ParsedURL u(loc,true);
+   if(!u.proto) {
+      // relative URI
+      redir_session=session->Clone();
+      if(loc[0]=='/' || fi->uri) {
+	 if(loc[0]!='/') {
+	    const char *slash=strrchr(fi->uri,'/');
+	    if(slash)
+	       loc.prepend(fi->uri,slash+1-fi->uri);
+	 }
+	 redir_fi->uri.set(loc);
+	 redir_fi->name.set(loc);
+	 redir_fi->name.url_decode();
+      } else {
+	 loc.url_decode();
+	 const char *slash=strrchr(fi->name,'/');
+	 if(slash)
+	    redir_fi->name.nset(fi->name,slash+1-fi->name);
+	 redir_fi->name.append(loc);
+      }
+   } else { // u.proto
+      // absolute URL
+      redir_session=FileAccess::New(&u);
+      redir_fi->name.set(u.path?u.path.get():"/");
+      redir_fi->uri.set(url::path_ptr(u.orig_url));
+   }
+
+   if(!redir_fs)
+      redir_fs=new FileSet();
+   else
+      redir_fs->Empty();
+   redir_fs->Add(redir_fi.borrow());
+   redir_session->GetInfoArray(redir_fs.get_non_const());
+   redir_session->Roll();
+
+   return true;
+}
+
 GenericParseListInfo::GenericParseListInfo(FileAccess *s,const char *p)
-   : ListInfo(s,p)
+   : ListInfo(s,p), redir_resolution(false), redir_count(0),
+     max_redir(ResMgr::Query("xfer:max-redirections",0))
 {
    get_time_for_dirs=true;
    can_get_prec_time=true;
diff --git a/src/NetAccess.h b/src/NetAccess.h
index 28e33ab..4972849 100644
--- a/src/NetAccess.h
+++ b/src/NetAccess.h
@@ -109,6 +109,13 @@ public:
 
 class GenericParseListInfo : public ListInfo
 {
+   bool redir_resolution;
+   int redir_count;
+   int max_redir;
+   FileAccessRef redir_session;
+   Ref<FileSet> redir_fs;
+   bool ResolveRedirect(const FileInfo *fi);
+
 protected:
    int mode;
    SMTaskRef<IOBuffer> ubuf;
diff --git a/src/xstring.h b/src/xstring.h
index 8bba5db..55918ba 100644
--- a/src/xstring.h
+++ b/src/xstring.h
@@ -145,6 +145,7 @@ public:
    bool eq_nc(const char *s) const { return !xstrcasecmp(buf,s); }
    size_t length() const { return xstrlen(buf); }
    void set_length(size_t n) { if(buf) buf[n]=0; }
+   char last_char() const { size_t len=length(); return len>0?buf[len-1]:0; }
 
    void unset() { xfree(buf); buf=0; }
    void _set(const char *s) { buf=xstrdup(s); }
@@ -205,6 +206,8 @@ public:
    xstring& set_substr(int start,size_t sublen,const char *,size_t);
    xstring& set_substr(int start,size_t sublen,const char *);
    xstring& set_substr(int start,size_t sublen,const xstring &s) { return set_substr(start,sublen,s.get(),s.length()); }
+   xstring& prepend(const char *s,size_t len) { return set_substr(0,0,s,len); }
+   xstring& prepend(const xstring &s) { return prepend(s.get(),s.length()); }
 
    xstring& append(const char *s);
    xstring& append(char c);
-- 
2.8.2

openSUSE Build Service is sponsored by