]> git.pld-linux.org Git - packages/lftp.git/blobdiff - lftp-git.patch
- up to 4.6.2; fixes CVE-2014-0139
[packages/lftp.git] / lftp-git.patch
index b5621c385ac4aa8e645ce2159adb0b60530f1822..6da827744049b7c6cdb2019242fd1d1e790d8c65 100644 (file)
 diff --git a/doc/lftp.1 b/doc/lftp.1
-index f81ad01..0ad75be 100644
+index d5d408f..9222795 100644
 --- a/doc/lftp.1
 +++ b/doc/lftp.1
-@@ -41,7 +41,7 @@
- .fi
- .in
- ..
--.TH lftp 1 "08 Oct 2013"
-+.TH lftp 1 "15 Nov 2013"
- .SH NAME
- lftp \- Sophisticated file transfer program
- .SH SYNTAX
-@@ -58,7 +58,7 @@ lftp \- Sophisticated file transfer program
- .B lftp \-\-help
- .SH VERSION
--This man page documents lftp version 4.4.10.
-+This man page documents lftp version 4.4.12.
- .SH "DESCRIPTION"
- \fBlftp\fR is a file transfer program that allows sophisticated ftp, http
-@@ -261,12 +261,19 @@ command is used.
- .PP
- Alias for `wait'.
--.B find
--.RI " [" directory "] "
-+.BR find " [" OPTS "] " \fIdirectory...\fP
- .PP
- List files in the directory (current directory by default) recursively.
- This can help with servers lacking ls \-R support. You can redirect output
--of this command.
-+of this command. Options:
-+.Sp
-+.in +0.5i
-+.TS
-+l1    l       lx      .
-+\-d \fIMD\fP, \-\-max\-depth=\fIMD\fP specify maximum scan depth
-+\-l,  \-\-list        use long listing format
-+.TE
-+.PP
- .BR ftpcopy
- .PP
-diff --git a/src/ChangeLog b/src/ChangeLog
-index dbd6879..dde2382 100644
---- a/src/ChangeLog
-+++ b/src/ChangeLog
-@@ -1,3 +1,18 @@
-+2013-11-25  Alexander V. Lukyanov <lavv17f@gmail.com>
+@@ -1748,10 +1748,15 @@ is applied. It is never used if mirror:exclude-regex is empty.
+ .BR mirror:no-empty-dirs " (boolean)"
+ when true, mirror doesn't create empty directories (like \-\-no\-empty\-dirs option).
+ .TP
++.BR mirror:sort-by " (string)"
++specifies order of file transfers. Valid values are: name, name-desc, size, size-desc,
++date, date-desc. When the value is name or name-desc, then mirror:order setting also
++affects the order or transfers.
++.TP
+ .BR mirror:order " (list of patterns)"
+-specifies order of file transfers. E.g. setting this to "*.sfv *.sum" makes mirror to
++specifies order of file transfers when sorting by name. E.g. setting this to "*.sfv *.sum" makes mirror to
+ transfer files matching *.sfv first, then ones matching *.sum and then all other
+-files. To process directories after other files, add "*/" to end of pattern list.
++files. To process directories after other files, add "*/" to the end of pattern list.
+ .TP
+ .BR mirror:parallel-directories " (boolean)"
+ if true, mirror will start processing of several directories in parallel
+diff --git a/po/pl.po b/po/pl.po
+index d569428..89e8b11 100644
+--- a/po/pl.po
++++ b/po/pl.po
+@@ -7,10 +7,10 @@
+ #
+ msgid ""
+ msgstr ""
+-"Project-Id-Version: lftp 4.4.16\n"
++"Project-Id-Version: lftp 4.5.3\n"
+ "Report-Msgid-Bugs-To: lftp-bugs@lftp.yar.ru\n"
+ "POT-Creation-Date: 2014-07-06 16:15+0400\n"
+-"PO-Revision-Date: 2014-05-18 06:54+0200\n"
++"PO-Revision-Date: 2014-07-13 14:15+0200\n"
+ "Last-Translator: Jakub Bogusz <qboosh@pld-linux.org>\n"
+ "Language-Team: Polish <translation-team-pl@lists.sourceforge.net>\n"
+ "Language: pl\n"
+@@ -1152,12 +1152,10 @@ msgstr ""
+ "uczyni dopełnianie nazw plików nie rozpoznającym wielkości liter.\n"
+ #: src/commands.cc:216
+-#, fuzzy
+ msgid "debug [OPTS] [<level>|off]"
+-msgstr "debug [<poziom>|off] [-o <plik>]"
++msgstr "debug [OPCJE] [<poziom>|off]"
+ #: src/commands.cc:217
+-#, fuzzy
+ msgid ""
+ "Set debug level to given value or turn debug off completely.\n"
+ " -o <file>  redirect debug output to the file\n"
+@@ -1165,8 +1163,11 @@ msgid ""
+ " -p  show PID\n"
+ " -t  show timestamps\n"
+ msgstr ""
+-"Ustaw poziom debugowania na daną wartość lub wyłącz zupełnie.\n"
+-" -o <plik>  przekieruj wyjście debugowania do pliku.\n"
++"Ustaw poziom diagnostyki na daną wartość lub wyłącz zupełnie.\n"
++" -o <plik>  przekieruj wyjście diagnostyki do pliku.\n"
++" -c  wypisuj kontekst komunikatu\n"
++" -p  wypisuj PID\n"
++" -t  wypisuj znaczniki czasu\n"
+ #: src/commands.cc:222
+ msgid "du [options] <dirs>"
+@@ -1477,12 +1478,10 @@ msgstr ""
+ "podano nazw obu katalogów, to będą użyte bieżące katalogi lokalny i zdalny.\n"
+ #: src/commands.cc:339
+-#, fuzzy
+ msgid "mkdir [OPTS] <dirs>"
+-msgstr "mkdir [-p] <katalogi>"
++msgstr "mkdir [OPCJE] <katalogi>"
+ #: src/commands.cc:340
+-#, fuzzy
+ msgid ""
+ "Make remote directories\n"
+ " -p  make all levels of path\n"
+@@ -1490,6 +1489,7 @@ msgid ""
+ msgstr ""
+ "Utwórz zdalne katalogi\n"
+ " -p  tworzy wszystkie poziomy ścieżki\n"
++" -f  ciche działanie, bez komunikatów\n"
+ #: src/commands.cc:343
+ msgid "module name [args]"
+@@ -2378,9 +2378,9 @@ msgid "unsupported network protocol"
+ msgstr "protokół nie jest obsługiwany"
+ #: src/ftpclass.cc:2133 src/ftpclass.cc:2228
+-#, fuzzy, c-format
++#, c-format
+ msgid "Data socket error (%s) - reconnecting"
+-msgstr "Błąd połączenia (%s) - powtórne łączenie"
++msgstr "Błąd gniazda danych (%s) - powtórne łączenie"
+ #: src/ftpclass.cc:2161
+ #, c-format
+@@ -2700,13 +2700,12 @@ msgid "peer unexpectedly closed connection after %s"
+ msgstr "druga strona nieoczekiwanie zamknęła połączenie po %s"
+ #: src/Torrent.cc:2903 src/Torrent.cc:3020
+-#, fuzzy
+ msgid "peer unexpectedly closed connection"
+-msgstr "druga strona nieoczekiwanie zamknęła połączenie po %s"
++msgstr "druga strona nieoczekiwanie zamknęła połączenie"
+ #: src/Torrent.cc:2905 src/Torrent.cc:2906
+ msgid "peer closed connection (before handshake)"
+-msgstr "druga strona zamknęła połączenie (przed jego ustanowieniem)"
++msgstr "druga strona zamknęła połączenie (przed przywitaniem)"
+ #: src/Torrent.cc:2909 src/Torrent.cc:3022 src/Torrent.cc:3023
+ msgid "invalid peer response format"
+@@ -2732,15 +2731,15 @@ msgstr "Przyjęto połączenie od [%s]:%d"
+ #: src/Torrent.cc:3552
+ #, c-format
+ msgid "peer sent unknown info_hash=%s in handshake"
+-msgstr ""
++msgstr "druga strona wysłała nieznane info_hash=%s przy przywitaniu"
+ #: src/Torrent.cc:3576
+ msgid "peer handshake timeout"
+-msgstr "upłynął limit czasu nawiązywania połączenia z drugą stroną"
++msgstr "upłynął limit czasu przywitania z drugą stroną"
+ #: src/Torrent.cc:3588
+ msgid "peer short handshake"
+-msgstr "nawiązanie połączenia z drugą stroną zostało ucięte"
++msgstr "przywitanie z drugą stroną zostało ucięte"
+ #: src/Torrent.cc:3590
+ msgid "peer closed just accepted connection"
+diff --git a/src/FileSet.cc b/src/FileSet.cc
+index 389578a..6380a68 100644
+--- a/src/FileSet.cc
++++ b/src/FileSet.cc
+@@ -232,6 +232,21 @@ void FileSet::Sort(sort_e newsort, bool casefold, bool reverse)
+    }
+ }
++// reverse current sort order
++void FileSet::ReverseSort()
++{
++   if(!sorted) {
++      Sort(BYNAME,false,true);
++      return;
++   }
++   int i=0;
++   int j=sorted.length()-1;
++   while(i<j) {
++      sorted[i]=replace_value(sorted[j],sorted[i]);
++      ++i,--j;
++   }
++}
 +
-+      * Http.cc: add debug message on chunk sizes; handle 400/501 codes
-+        to turn off PROPFIND in ARRAY_INFO mode.
-+      * FileCopy.cc: clean up GetArrayInfo call condition, don't call it
-+        if not needed.
-+      * OutputJob.cc: free input and output in PrepareToDie.
-+      * FileCopy.cc, FileCopy.h: add check for file size at EOF.
+ /* Remove the current sort, allowing new entries to be added.
+  * (Nothing uses this ... */
+ void FileSet::Unsort()
+diff --git a/src/FileSet.h b/src/FileSet.h
+index 7c3ac2a..155536f 100644
+--- a/src/FileSet.h
++++ b/src/FileSet.h
+@@ -180,6 +180,7 @@ public:
+    void  Sort(sort_e newsort, bool casefold=false, bool reverse=false);
+    void  Unsort();
+    void        SortByPatternList(const char *list_c);
++   void        ReverseSort();
+    void        Exclude(const char *prefix,const PatternSet *x);
+    void        ExcludeDots();
+diff --git a/src/Fish.cc b/src/Fish.cc
+index d172a20..29ba4c6 100644
+--- a/src/Fish.cc
++++ b/src/Fish.cc
+@@ -98,6 +98,15 @@ int Fish::Do()
+       timeout_timer.Reset(send_buf->EventTime());
+    if(recv_buf)
+       timeout_timer.Reset(recv_buf->EventTime());
++   if(pty_send_buf)
++      timeout_timer.Reset(pty_send_buf->EventTime());
++   if(pty_recv_buf)
++      timeout_timer.Reset(pty_recv_buf->EventTime());
 +
-+2013-11-15  Alexander V. Lukyanov <lav@yars.free.net>
++   // check for timeout only if there should be connection activity.
++   if(state!=DISCONNECTED && (state!=CONNECTED || !RespQueueIsEmpty())
++   && mode!=CLOSED && CheckTimeout())
++      return MOVED;
+    if((state==FILE_RECV || state==FILE_SEND)
+    && rate_limit==0)
+@@ -275,14 +284,6 @@ int Fish::Do()
+    case DONE:
+       break;
+    }
+-   if(m==MOVED)
+-      return MOVED;
+-   if(send_buf)
+-      timeout_timer.Reset(send_buf->EventTime());
+-   if(recv_buf)
+-      timeout_timer.Reset(recv_buf->EventTime());
+-   if(CheckTimeout())
+-      return MOVED;
+    return m;
+ }
+diff --git a/src/MirrorJob.cc b/src/MirrorJob.cc
+index a19ff8e..eb148f1 100644
+--- a/src/MirrorJob.cc
++++ b/src/MirrorJob.cc
+@@ -589,10 +589,16 @@ void  MirrorJob::InitSets(const FileSet *source,const FileSet *dest)
+    if(!(flags&DELETE))
+       to_transfer->SubtractAny(to_rm_mismatched);
+-   to_transfer->SortByPatternList(ResMgr::Query("mirror:order",0));
+-   to_transfer->CountBytes(&bytes_to_transfer);
+-   if(parent_mirror)
+-      parent_mirror->AddBytesToTransfer(bytes_to_transfer);
++   const char *sort_by=ResMgr::Query("mirror:sort-by",0);
++   bool desc=strstr(sort_by,"-desc");
++   if(!strncmp(sort_by,"name",4))
++      to_transfer->SortByPatternList(ResMgr::Query("mirror:order",0));
++   else if(!strncmp(sort_by,"date",4))
++      to_transfer->Sort(FileSet::BYDATE);
++   else if(!strncmp(sort_by,"size",4))
++      to_transfer->Sort(FileSet::BYSIZE,false,true);
++   if(desc)
++      to_transfer->ReverseSort();
+    int dir_count=0;
+    to_transfer->Count(&dir_count,NULL,NULL,NULL);
+@@ -851,10 +857,16 @@ int   MirrorJob::Do()
+       stats.dirs++;
+       InitSets(source_set,target_set);
 +
-+      * FileCopy.cc: fixed spinning when GetArrayInfo gives an error.
-+      * commands.cc, FindJob.cc, FindJob.h, FileSet.h: add find -l (--ls)
-+        option to show long listing.
++      to_transfer->CountBytes(&bytes_to_transfer);
++      if(parent_mirror)
++       parent_mirror->AddBytesToTransfer(bytes_to_transfer);
 +
- 2013-11-11  Alexander V. Lukyanov <lav@yars.free.net>
-       * Torrent.cc: don't show verbose 100% peer completeness.
-diff --git a/src/FileCopy.cc b/src/FileCopy.cc
-index f195b95..8c0b977 100644
---- a/src/FileCopy.cc
-+++ b/src/FileCopy.cc
-@@ -345,6 +345,11 @@ int FileCopy::Do()
-       put_eof_pos=put->GetRealPos();
-       debug((10,"copy: waiting for put confirmation\n"));
-       set_state(CONFIRM_WAIT);
-+      if(!CheckFileSizeAtEOF())
-+      {
-+       SetError(_("file size decreased during transfer"));
-+       return MOVED;
-+      }
-       m=MOVED;
-    case(CONFIRM_WAIT):
-       if(put->Error())
-@@ -475,7 +480,7 @@ void FileCopy::LineBuffered(int s)
-    line_buffer_max=s;
- }
+       to_rm->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files);
+       to_rm->rewind();
+       to_rm_mismatched->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files);
+       to_rm_mismatched->rewind();
++
+       set_state(TARGET_REMOVE_OLD_FIRST);
+       goto TARGET_REMOVE_OLD_FIRST_label;
  
--off_t FileCopy::GetPos()
-+off_t FileCopy::GetPos() const
+diff --git a/src/PollVec.cc b/src/PollVec.cc
+index a18b517..e1d72fb 100644
+--- a/src/PollVec.cc
++++ b/src/PollVec.cc
+@@ -31,7 +31,7 @@ static inline bool operator<(const timeval& a,const timeval& b)
+ void PollVec::AddTimeoutU(unsigned t)
  {
-    if(put)
-       return put->GetRealPos() - put->Buffered();
-@@ -484,14 +489,14 @@ off_t FileCopy::GetPos()
-    return 0;
+    struct timeval new_timeout={t/1000000,t%1000000};
+-   if(new_timeout<tv_timeout)
++   if(tv_timeout.tv_sec<0 || new_timeout<tv_timeout)
+       SetTimeout(new_timeout);
  }
  
--off_t FileCopy::GetSize()
-+off_t FileCopy::GetSize() const
- {
-    if(get)
-       return get->GetSize();
-    return NO_SIZE;
- }
+diff --git a/src/ProcWait.cc b/src/ProcWait.cc
+index 0a065ad..d3dfec4 100644
+--- a/src/ProcWait.cc
++++ b/src/ProcWait.cc
+@@ -24,7 +24,14 @@
+ #include "ProcWait.h"
+ #include "SignalHook.h"
  
--int FileCopy::GetPercentDone()
-+int FileCopy::GetPercentDone() const
- {
-    if(!get || !put)
-       return 100;
-@@ -512,7 +517,7 @@ int FileCopy::GetPercentDone()
-       return -1;
-    return percent(ppos,psize);
- }
--const char *FileCopy::GetPercentDoneStr()
-+const char *FileCopy::GetPercentDoneStr() const
+-ProcWait *ProcWait::chain=0;
++xmap<ProcWait*> ProcWait::all_proc;
++
++const xstring& ProcWait::proc_key(pid_t p)
++{
++   static xstring tmp_key;
++   tmp_key.nset((const char*)&p,sizeof(p));
++   return tmp_key;
++}
+ int ProcWait::Do()
  {
-    int pct=GetPercentDone();
-    if(pct==-1)
-@@ -532,19 +537,19 @@ void FileCopy::RateReset()
-    rate->Reset();
-    rate_for_eta->Reset();
+@@ -100,27 +107,19 @@ int ProcWait::Kill(int sig)
  }
--float FileCopy::GetRate()
-+float FileCopy::GetRate() const
+ ProcWait::ProcWait(pid_t p)
++   : pid(p)
  {
-    if(!rate->Valid() || !put)
-       return 0;
-    return rate->Get();
+    auto_die=false;
+-   pid=p;
+    status=RUNNING;
+    term_info=-1;
+    saved_errno=0;
+-   next=chain;
+-   chain=this;
++   all_proc.add(proc_key(pid),this);
  }
--const char *FileCopy::GetRateStr()
-+const char *FileCopy::GetRateStr() const
+ ProcWait::~ProcWait()
  {
-    if(!rate->Valid() || !put)
-       return "";
-    return rate->GetStrS();
+-   for(ProcWait **scan=&chain; *scan; scan=&(*scan)->next)
+-   {
+-      if(*scan==this)
+-      {
+-       *scan=next;
+-       return;
+-      }
+-   }
++   all_proc.remove(proc_key(pid));
  }
--off_t FileCopy::GetBytesRemaining()
-+off_t FileCopy::GetBytesRemaining() const
- {
-    if(!get)
-       return 0;
-@@ -557,14 +562,14 @@ off_t FileCopy::GetBytesRemaining()
-    }
-    return get->range_limit-GetPos();
+ void ProcWait::SIGCHLD_handler(int sig)
+@@ -130,16 +129,9 @@ void ProcWait::SIGCHLD_handler(int sig)
+    pid_t pp=waitpid(-1,&info,WUNTRACED|WNOHANG);
+    if(pp==-1)
+       return;
+-   for(ProcWait *scan=chain; scan; scan=scan->next)
+-   {
+-      if(scan->pid==pp)
+-      {
+-       scan->handle_info(info);
+-       return;
+-      }
+-   }
+-   // no WaitProc for the pid. Probably the process died too fast,
+-   // but next waitpid should take care of it.
++   ProcWait *w=all_proc.lookup(proc_key(pp));
++   if(w && w->handle_info(info))
++      w->Timeout(0);
  }
--const char *FileCopy::GetETAStr()
-+const char *FileCopy::GetETAStr() const
+ void ProcWait::Signal(bool yes)
+@@ -156,6 +148,6 @@ void ProcWait::Signal(bool yes)
+ void ProcWait::DeleteAll()
  {
-    off_t b=GetBytesRemaining();
-    if(b<0 || !put)
-       return "";
-    return rate_for_eta->GetETAStrSFromSize(b);
+    Signal(false);
+-   for(ProcWait *scan=chain; scan; scan=scan->next)
+-      Delete(scan);
++   for(ProcWait *w=all_proc.each_begin(); w; w=all_proc.each_next())
++      Delete(w);
  }
--long FileCopy::GetETA(off_t b)
-+long FileCopy::GetETA(off_t b) const
+diff --git a/src/ProcWait.h b/src/ProcWait.h
+index d6f0bad..651bc21 100644
+--- a/src/ProcWait.h
++++ b/src/ProcWait.h
+@@ -23,6 +23,7 @@
+ #include <sys/types.h>
+ #include <signal.h>
+ #include "SMTask.h"
++#include "xmap.h"
+ class ProcWait : public SMTask
  {
-    if(b<0 || !rate_for_eta->Valid())
-       return -1;
-@@ -658,6 +663,23 @@ void FileCopy::SetRange(off_t s,off_t lim)
-    put->SetRange(s,lim);
- }
+@@ -35,10 +36,10 @@ public:
+    };
+ protected:
+-   static ProcWait *chain;
+-   ProcWait *next;
++   static xmap<ProcWait*> all_proc;
++   static const xstring& proc_key(pid_t p); // make key for xmap
+-   pid_t pid;
++   const pid_t pid;
+    State status;
+    int         term_info;
+    int         saved_errno;
+diff --git a/src/Ref.h b/src/Ref.h
+index 8731089..c96ee96 100644
+--- a/src/Ref.h
++++ b/src/Ref.h
+@@ -51,4 +51,22 @@ public:
  
-+bool FileCopy::CheckFileSizeAtEOF() const
+ template<typename T> const Ref<T> Ref<T>::null;
++template<typename T> class RefToArray : public Ref<T>
 +{
-+   const long long size=GetSize();
-+   if(size==NO_SIZE || size==NO_SIZE_YET)
-+      return true;   // nothing to compare with.
++   RefToArray<T>(const RefToArray<T>&);  // disable cloning
++   void operator=(const RefToArray<T>&);   // and assignment
++
++public:
++   RefToArray<T>() {}
++   RefToArray<T>(T *p) : Ref<T>(p) {}
++   ~RefToArray<T>() { delete[] Ref<T>::ptr; Ref<T>::ptr=0; }
++   void operator=(T *p) { delete[] Ref<T>::ptr; Ref<T>::ptr=p; }
++   T& operator[](unsigned i) const { return Ref<T>::ptr[i]; }
++
++   static const RefToArray<T> null;
++};
 +
-+   long long range_limit=GetRangeLimit();
-+   if(range_limit==FILE_END)
-+      range_limit=size;
++template<typename T> const RefToArray<T> RefToArray<T>::null;
 +
-+   const long long pos=put_eof_pos;
-+   if(pos>=range_limit)
-+      return true;
 +
-+   debug((0,"expected pos=%lld, actual pos=%lld\n",range_limit,pos));
-+   return false;
+ #endif
+diff --git a/src/SFtp.cc b/src/SFtp.cc
+index 8e6d0d5..3225701 100644
+--- a/src/SFtp.cc
++++ b/src/SFtp.cc
+@@ -915,11 +915,7 @@ void SFtp::HandleExpect(Expect *e)
+        for(int i=0; i<r->GetCount(); i++)
+        {
+           const NameAttrs *a=r->GetNameAttrs(i);
+-          if(!file_set)
+-             file_set=new FileSet;
+           FileInfo *info=MakeFileInfo(a);
+-          if(info)
+-             file_set->Add(info);
+           if(mode==LIST)
+           {
+              file_buf->Put(a->name);
+@@ -941,6 +937,12 @@ void SFtp::HandleExpect(Expect *e)
+                 file_buf->Put("\n");
+              }
+           }
++          if(info)
++          {
++             if(!file_set)
++                file_set=new FileSet;
++             file_set->Add(info);
++          }
+        }
+        if(r->Eof())
+           goto eof;
+diff --git a/src/Torrent.cc b/src/Torrent.cc
+index ffe6433..270bbd6 100644
+--- a/src/Torrent.cc
++++ b/src/Torrent.cc
+@@ -278,6 +278,10 @@ Torrent::Torrent(const char *mf,const char *c,const char *od)
+    dht_announce_timer.Stop();
+ }
++Torrent::~Torrent()
++{
 +}
++
+ bool Torrent::TrackersDone() const
+ {
+    if(shutting_down && shutting_down_timer.Stopped())
+@@ -318,6 +322,8 @@ void Torrent::PrepareToDie()
+       RemoveTorrent(this);
+       if(GetTorrentsCount()==0) {
+        StopListener();
++       StopDHT();
++       StopListenerUDP();
+        fd_cache=0;
+        black_list=0;
+       }
+@@ -348,9 +354,7 @@ double Torrent::GetRatio() const
  
- // FileCopyPeer implementation
- #undef super
-@@ -786,21 +808,23 @@ int FileCopyPeerFA::Do()
-       return m;
+ void Torrent::SetDownloader(unsigned piece,unsigned block,const TorrentPeer *o,const TorrentPeer *n)
+ {
+-   const TorrentPeer*& downloader=piece_info[piece]->downloader[block];
+-   if(downloader==o)
+-      downloader=n;
++   piece_info[piece].set_downloader(block,o,n,BlocksInPiece(piece));
+ }
+ BeNode *Torrent::Lookup(xmap_p<BeNode>& dict,const char *name,BeNode::be_type_t type)
+@@ -421,25 +425,18 @@ void Torrent::ValidatePiece(unsigned p)
+        complete_pieces--;
+        my_bitfield->set_bit(p,0);
+       }
+-      piece_info[p]->block_map.clear();
++      SetBlocksAbsent(p);
+    } else {
+       LogNote(11,"piece %u ok",p);
+       if(!my_bitfield->get_bit(p)) {
+        total_left-=PieceLength(p);
+        complete_pieces++;
+        my_bitfield->set_bit(p,1);
++       piece_info[p].free_block_map();
+       }
     }
+ }
  
--   if((want_size && size==NO_SIZE_YET && (mode==PUT || !start_transfer))
--   || (want_date && date==NO_DATE_YET))
-+   // if we need some info and cannot start the transfer (yet),
-+   // then use ARRAY_INFO to fetch the file information.
-+   if(((want_size && size==NO_SIZE_YET) || (want_date && date==NO_DATE_YET))
-+   && (mode==PUT || !start_transfer) && session->IsClosed())
-+   {
-+      FileInfo *fi=new FileInfo(file);
-+      if(want_size)
-+       fi->Need(fi->SIZE);
-+      if(want_date)
-+       fi->Need(fi->DATE);
-+      info.Empty();
-+      info.Add(fi);
-+      session->GetInfoArray(&info);
-+      m=MOVED;
-+   }
-+   if(session->OpenMode()==FA::ARRAY_INFO)
-    {
--      if(session->IsClosed())
--      {
--       FileInfo *fi=new FileInfo(file);
--       if(want_size)
--          fi->Need(fi->SIZE);
--       if(want_date)
--          fi->Need(fi->DATE);
--       info.Empty();
--       info.Add(fi);
--       session->GetInfoArray(&info);
--       m=MOVED;
--      }
-       res=session->Done();
-       if(res==FA::IN_PROGRESS)
-        return m;
-@@ -808,6 +832,7 @@ int FileCopyPeerFA::Do()
-       {
-        session->Close();
-        SetSize(NO_SIZE);
-+       SetDate(NO_DATE);
-        return MOVED;
+-bool TorrentPiece::has_a_downloader() const
+-{
+-   for(int i=0; i<downloader.count(); i++)
+-      if(downloader[i])
+-       return true;
+-   return false;
+-}
+-
+ template<typename T>
+ static inline int cmp(T a,T b)
+ {
+@@ -453,8 +450,8 @@ static inline int cmp(T a,T b)
+ static Torrent *cmp_torrent;
+ int Torrent::PiecesNeededCmp(const unsigned *a,const unsigned *b)
+ {
+-   int ra=cmp_torrent->piece_info[*a]->sources_count;
+-   int rb=cmp_torrent->piece_info[*b]->sources_count;
++   int ra=cmp_torrent->piece_info[*a].get_sources_count();
++   int rb=cmp_torrent->piece_info[*b].get_sources_count();
+    int c=cmp(ra,rb);
+    if(c) return c;
+    return cmp(*a,*b);
+@@ -721,7 +718,7 @@ void Torrent::SetMetadata(const xstring& md)
+    }
+    BeNode *b_piece_length=Lookup(info,"piece length",BeNode::BE_INT);
+-   if(!b_piece_length)
++   if(!b_piece_length || b_piece_length->num<1024 || b_piece_length->num>INT_MAX/4)
+       return;
+    piece_length=b_piece_length->num;
+    LogNote(4,"Piece length is %u",piece_length);
+@@ -744,7 +741,7 @@ void Torrent::SetMetadata(const xstring& md)
+    if(!files) {
+       single_file=true;
+       BeNode *length=Lookup(info,"length",BeNode::BE_INT);
+-      if(!length)
++      if(!length || length->num<0)
+        return;
+       total_length=length->num;
+    } else {
+@@ -764,9 +761,12 @@ void Torrent::SetMetadata(const xstring& md)
+           return;
+        if(!Lookup(files->list[i]->dict,"path",BeNode::BE_LIST))
+           return;
++       if(f->num<0)
++          return;
+        total_length+=f->num;
        }
-       FileInfo *fi=info[0];
-diff --git a/src/FileCopy.h b/src/FileCopy.h
-index 3304365..86e80fd 100644
---- a/src/FileCopy.h
-+++ b/src/FileCopy.h
-@@ -187,23 +187,25 @@ private:
-    Ref<Buffer> line_buffer;
-    int  line_buffer_max;
-+   bool CheckFileSizeAtEOF() const;
-+
- protected:
-    void RateAdd(int a);
-    void RateReset();
-    off_t bytes_count;
+    }
++   this->files=new TorrentFiles(files,this);
+    LogNote(4,"Total length is %llu",total_length);
+    total_left=total_length;
  
- public:
--   off_t GetPos();
--   off_t GetSize();
--   int  GetPercentDone();
--   const char *GetPercentDoneStr();
--   float GetRate();
--   const char *GetRateStr();
--   off_t GetBytesRemaining();
--   long GetETA() { return GetETA(GetBytesRemaining()); }
--   long GetETA(off_t b);
--   const char *GetETAStr();
--   const char *GetETAStrSFromTime(time_t t) { return rate_for_eta->GetETAStrSFromTime(t); }
-+   off_t GetPos() const;
-+   off_t GetSize() const;
-+   int  GetPercentDone() const;
-+   const char *GetPercentDoneStr() const;
-+   float GetRate() const;
-+   const char *GetRateStr() const;
-+   off_t GetBytesRemaining() const;
-+   long GetETA() const { return GetETA(GetBytesRemaining()); }
-+   long GetETA(off_t b) const;
-+   const char *GetETAStr() const;
-+   const char *GetETAStrSFromTime(time_t t) const { return rate_for_eta->GetETAStrSFromTime(t); }
-    const char *GetStatus();
-    FgData *GetFgData(bool fg);
-    pid_t GetProcGroup();
-@@ -229,8 +231,8 @@ public:
-    void FailIfCannotSeek() { fail_if_cannot_seek=true; }
-    void SetRange(off_t s,off_t lim);
-    void SetRangeLimit(off_t lim) { get->range_limit=lim; }
--   off_t GetRangeStart() { return get->range_start; }
--   off_t GetRangeLimit() { return get->range_limit; }
-+   off_t GetRangeStart() const { return get->range_start; }
-+   off_t GetRangeLimit() const { return get->range_limit; }
-    void RemoveSourceLater() { remove_source_later=true; }
-    void RemoveTargetFirst() { remove_target_first=true; put->Resume(); put->RemoveFile(); }
-    void LineBuffered(int size=0x1000);
-diff --git a/src/FileSet.h b/src/FileSet.h
-index 51ccdf1..a2d69ae 100644
---- a/src/FileSet.h
-+++ b/src/FileSet.h
-@@ -89,6 +89,7 @@ public:
-    ~FileInfo();
-    void SetName(const char *n) { name.set(n); def(NAME); }
-+   void SetName(const xstring& n) { name.set(n); def(NAME); }
-    void SetUser(const char *n);
-    void SetGroup(const char *n);
-    void LocalFile(const char *name, bool follow_symlinks);
-diff --git a/src/FindJob.cc b/src/FindJob.cc
-index b42f0b0..8275b0d 100644
---- a/src/FindJob.cc
-+++ b/src/FindJob.cc
-@@ -360,24 +360,36 @@ FinderJob::prf_res FinderJob_List::ProcessFile(const char *d,const FileInfo *fi)
-       fg_data=buf->GetFgData(fg);
-    if(buf->Size()>0x10000)
-       return PRF_LATER;
+@@ -800,16 +800,18 @@ void Torrent::SetMetadata(const xstring& md)
+    SaveMetadata();
+    my_bitfield=new BitField(total_pieces);
+-   for(unsigned p=0; p<total_pieces; p++)
+-      piece_info.append(new TorrentPiece(BlocksInPiece(p)));
 +
-+   xstring path_to_show;
-    if(ProcessingURL())
-    {
-       FileAccess::Path old_cwd=session->GetCwd();
-       session->SetCwd(init_dir);
-       session->Chdir(dir_file(d,fi->name),false);
--      buf->Put(session->GetConnectURL());
-+      path_to_show.set(session->GetConnectURL());
-       session->SetCwd(old_cwd);
++   blocks_in_piece=(piece_length+BLOCK_SIZE-1)/BLOCK_SIZE;
++   blocks_in_last_piece=(last_piece_length+BLOCK_SIZE-1)/BLOCK_SIZE;
++
++   piece_info=new TorrentPiece[total_pieces]();
+    if(!force_valid) {
+       validate_index=0;
+       validating=true;
+       recv_rate.Reset();
+    } else {
+-      for(unsigned i=0; i<total_pieces; i++)
+-       my_bitfield->set_bit(i,1);
++      my_bitfield->set_range(0,total_pieces,1);
+       complete_pieces=total_pieces;
+       total_left=0;
+       complete=true;
+@@ -927,7 +929,7 @@ void Torrent::CalcPiecesStats()
+    for(unsigned i=0; i<total_pieces; i++) {
+       if(my_bitfield->get_bit(i))
+        continue;
+-      unsigned sc=piece_info[i]->sources_count;
++      unsigned sc=piece_info[i].get_sources_count();
+       if(min_piece_sources>sc)
+        min_piece_sources=sc;
+       if(sc==0)
+@@ -945,12 +947,13 @@ void Torrent::RebuildPiecesNeeded()
+    bool enter_end_game=true;
+    for(unsigned i=0; i<total_pieces; i++) {
+       if(!my_bitfield->get_bit(i)) {
+-       if(!piece_info[i]->has_a_downloader())
++       if(!piece_info[i].has_a_downloader())
+           enter_end_game=false;
+-       if(piece_info[i]->sources_count==0)
++       if(piece_info[i].has_no_sources())
+           continue;
+        pieces_needed.append(i);
+       }
++      piece_info[i].cleanup();
     }
-    else
--      buf->Put(dir_file(d,fi->name));
-+      path_to_show.set(dir_file(d,fi->name));
-    if((fi->defined&fi->TYPE) && fi->filetype==fi->DIRECTORY && strcmp(fi->name,"/"))
--      buf->Put("/");
-+      path_to_show.append('/');
+    if(!end_game && enter_end_game) {
+       LogNote(1,"entering End Game mode");
+@@ -1084,6 +1087,8 @@ int Torrent::Do()
+              a.sa.sa_family=AF_INET6;
+              if(inet_pton(AF_INET6,b_ip->str,&a.in6.sin6_addr)<=0)
+                 continue;
++             if(b_port->num<=0 || b_port->num>=0x10000)
++                continue;
+              a.set_port(b_port->num);
+              dht_ipv6->SendPing(a);
+           } else
+@@ -1092,6 +1097,8 @@ int Torrent::Do()
+              a.sa.sa_family=AF_INET;
+              if(!inet_aton(b_ip->str,&a.in.sin_addr))
+                 continue;
++             if(b_port->num<=0 || b_port->num>=0x10000)
++                continue;
+              a.set_port(b_port->num);
+              dht->SendPing(a);
+           }
+@@ -1249,24 +1256,51 @@ const char *Torrent::MakePath(BeNode *p) const
+ }
+ const char *Torrent::FindFileByPosition(unsigned piece,unsigned begin,off_t *f_pos,off_t *f_tail) const
+ {
+-   const BeNode *files=info->lookup("files",BeNode::BE_LIST);
+    off_t target_pos=(off_t)piece*piece_length+begin;
++   TorrentFile *file=files->FindByPosition(target_pos);
++   if(!file)
++      return 0;
 +
-+   if(long_listing) {
-+      FileInfo n(*fi);
-+      n.SetName(path_to_show);
-+      n.MakeLongName();
-+      buf->Put(n.longname);
-+   } else {
-+      buf->Put(path_to_show);
-+   }
++   *f_pos=target_pos-file->pos;
++   *f_tail=file->length-*f_pos;
 +
-    buf->Put("\n");
-    return FinderJob::ProcessFile(d,fi);
++   return file->path;
++}
++
++TorrentFiles::TorrentFiles(const BeNode *files,const Torrent *t)
++{
+    if(!files) {
+-      *f_pos=target_pos;
+-      *f_tail=total_length-target_pos;
+-      return name;
++      grow_space(1);
++      set_length(1);
++      file(0)->set(t->GetName(),0,t->TotalLength());
+    } else {
++      int count=files->list.length();
++      grow_space(count);
++      set_length(count);
+       off_t scan_pos=0;
+-      for(int i=0; i<files->list.length(); i++) {
+-       off_t file_length=files->list[i]->lookup_int("length");
+-       if(scan_pos<=target_pos && scan_pos+file_length>target_pos) {
+-          *f_pos=target_pos-scan_pos;
+-          *f_tail=file_length-*f_pos;
+-          return MakePath(files->list[i]);
+-       }
++      for(int i=0; i<count; i++) {
++       BeNode *node=files->list[i];
++       off_t file_length=node->lookup_int("length");
++       file(i)->set(t->MakePath(node),scan_pos,file_length);
+        scan_pos+=file_length;
+       }
+    }
++   qsort(pos_cmp);
++}
++TorrentFile *TorrentFiles::FindByPosition(off_t pos)
++{
++   int i=0;
++   int j=length()-1;
++   while(i<=j) {
++      // invariant: the target element is in the range [i,j]
++      int m=(i+j)/2;
++      if(file(m)->contains_pos(pos))
++       return file(m);
++      if(file(m)->pos>pos)
++       j=m-1;
++      else
++       i=m+1;
++   }
+    return 0;
  }
  
- FinderJob_List::FinderJob_List(FileAccess *s,ArgV *a,FDStream *o)
--   : FinderJob(s), args(a)
-+   : FinderJob(s), args(a), long_listing(false)
+@@ -1485,9 +1519,9 @@ void Torrent::StoreBlock(unsigned piece,unsigned begin,unsigned len,const char *
+    }
+    while(bc-->0) {
+-      piece_info[piece]->block_map.set_bit(b++,1);
++      SetBlockPresent(piece,b++);
+    }
+-   if(piece_info[piece]->block_map.has_all_set() && !my_bitfield->get_bit(piece)) {
++   if(AllBlocksPresent(piece) && !my_bitfield->get_bit(piece)) {
+       ValidatePiece(piece);
+       if(!my_bitfield->get_bit(piece)) {
+        LogError(0,"new piece %u digest mismatch",piece);
+@@ -2021,12 +2055,12 @@ int TorrentPeer::SendDataRequests(unsigned p)
+    unsigned blocks=parent->BlocksInPiece(p);
+    unsigned bytes_allowed=BytesAllowed(RateLimit::GET);
+    for(unsigned b=0; b<blocks; b++) {
+-      if(parent->piece_info[p]->block_map.get_bit(b))
++      if(parent->BlockPresent(p,b))
+        continue;
+-      if(parent->piece_info[p]->downloader[b]) {
++      if(parent->piece_info[p].downloader_for(b)) {
+        if(!parent->end_game)
+           continue;
+-       if(parent->piece_info[p]->downloader[b]==this)
++       if(parent->piece_info[p].downloader_for(b)==this)
+           continue;
+        if(FindRequest(p,b*Torrent::BLOCK_SIZE)>=0)
+           continue;
+@@ -2118,8 +2152,7 @@ void TorrentPeer::SendDataRequests()
+        if(parent->my_bitfield->get_bit(p))
+           continue;
+        // add some randomness, so that different instances don't synchronize
+-       if(!parent->piece_info[p]->block_map.has_any_set()
+-       && random()/13%16==0)
++       if(parent->AllBlocksAbsent(p) && random()/13%16==0)
+           continue;
+        if(SendDataRequests(p)>0)
+           return;
+@@ -2233,7 +2266,7 @@ unsigned TorrentPeer::GetLastPiece() const
+    unsigned p=last_piece;
+    // continue if have any blocks already
+    if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p)
+-   && parent->piece_info[p]->block_map.has_any_set()
++   && parent->AnyBlocksPresent(p)
+    && peer_bitfield->get_bit(p))
+       return p;
+    p=parent->last_piece;
+@@ -2302,11 +2335,11 @@ void TorrentPeer::SetPieceHaving(unsigned p,bool have)
+    int diff = (have - peer_bitfield->get_bit(p));
+    if(!diff)
+       return;
+-   parent->piece_info[p]->sources_count+=diff;
++   parent->piece_info[p].add_sources_count(diff);
+    peer_complete_pieces+=diff;
+    peer_bitfield->set_bit(p,have);
+-   if(parent->piece_info[p]->sources_count==0)
++   if(parent->piece_info[p].get_sources_count()==0)
+       parent->SetPieceNotWanted(p);
+    if(have && send_buf && !am_interested && !parent->my_bitfield->get_bit(p)
+    && parent->NeedMoreUploaders()) {
+@@ -2641,7 +2674,7 @@ void TorrentPeer::HandleExtendedMessage(PacketExtended *pp)
+        return;
+       }
+       BeNode *piece=pp->data->lookup("piece",BeNode::BE_INT);
+-      if(!piece) {
++      if(!piece || piece->num<0 || piece->num>=INT_MAX/Torrent::BLOCK_SIZE) {
+        SetError("ut_metadata piece bad or missing");
+        return;
+       }
+@@ -2692,6 +2725,9 @@ void TorrentPeer::HandleExtendedMessage(PacketExtended *pp)
+        }
+        case UT_METADATA_REJECT:
+           break;
++       default:
++          SetError("ut_metadata msg_type invalid value");
++          return;
+       }
+    } else if(pp->code==MSG_EXT_PEX) {
+       if(!pex.recv_timer.Stopped())
+@@ -3322,14 +3358,16 @@ bool BitField::has_all_set(int from,int to) const {
+        return false;
+    return true;
+ }
+-
++void BitField::set_range(int from,int to,bool value) {
++   for(int i=from; i<to; i++)
++      set_bit(i,value);
++}
+ void TorrentBlackList::check_expire()
  {
-    if(o)
-       buf=new IOBufferFDStream(o,IOBuffer::PUT);
-diff --git a/src/FindJob.h b/src/FindJob.h
-index a6767a3..6d88b49 100644
---- a/src/FindJob.h
-+++ b/src/FindJob.h
-@@ -117,12 +117,14 @@ class FinderJob_List : public FinderJob
+    for(Timer *e=bl.each_begin(); e; e=bl.each_next()) {
+       if(e->Stopped()) {
+        Log::global->Format(4,"---- black-delisting peer %s\n",bl.each_key().get());
+-       delete e;
+        bl.remove(bl.each_key());
+       }
+    }
+diff --git a/src/Torrent.h b/src/Torrent.h
+index a9943c6..ef5d75c 100644
+--- a/src/Torrent.h
++++ b/src/Torrent.h
+@@ -53,20 +53,105 @@ public:
+    int get_bit_length() const { return bit_length; }
+    void set_bit_length(int b) { bit_length=b; set_length((b+7)/8); }
+    void clear() { memset(buf,0,length()); }
++   void set_range(int from,int to,bool value);
+ };
+-struct TorrentPiece
++class TorrentPiece
  {
-    SMTaskRef<IOBuffer> buf;
-    Ref<ArgV> args;
-+   bool long_listing;
- protected:
-    prf_res ProcessFile(const char *d,const FileInfo *fi);
-    void Finish();
+    unsigned sources_count;        // how many peers have the piece
++   unsigned downloader_count;     // how many downloaders of the piece are there
++   RefToArray<const TorrentPeer*> downloader; // which peers download the blocks
++   Ref<BitField> block_map;       // which blocks are present.
  
- public:
-    FinderJob_List(FileAccess *s,ArgV *a,FDStream *o);
-+   void DoLongListing(bool yes=true) { long_listing=yes; }
+-   BitField block_map;                    // which blocks are present
+-   xarray<const TorrentPeer*> downloader; // which peers download the blocks
++public:
++   TorrentPiece() : sources_count(0), downloader_count(0) {}
++   ~TorrentPiece() {}
++
++   unsigned get_sources_count() const { return sources_count; }
++   void add_sources_count(int diff) { sources_count+=diff; }
++   bool has_no_sources() const { return sources_count==0; }
++
++   bool has_a_downloader() const { return downloader_count>0; }
++   void set_downloader(unsigned block,const TorrentPeer *o,const TorrentPeer *n,unsigned blk_count) {
++      if(!downloader) {
++       if(o || !n)
++          return;
++       downloader=new const TorrentPeer*[blk_count]();
++      }
++      const TorrentPeer*& d=downloader[block];
++      if(d==o) {
++       d=n;
++       downloader_count+=(n!=0)-(o!=0);
++      }
++   }
++   void cleanup() {
++      if(downloader_count==0 && downloader)
++       downloader=0;
++   }
++   const TorrentPeer *downloader_for(unsigned block) {
++      return downloader ? downloader[block] : 0;
++   }
+-   TorrentPiece(unsigned b)
+-      : sources_count(0), block_map(b)
+-      { downloader.allocate(b,0); }
++   void set_block_present(unsigned block,unsigned blk_count) {
++      if(!block_map)
++       block_map=new BitField(blk_count);
++      block_map->set_bit(block,1);
++   }
++   void set_blocks_absent() {
++      block_map=0;
++   }
++   void free_block_map() {
++      block_map=0;
++   }
++   bool block_present(unsigned block) const {
++      return block_map && block_map->get_bit(block);
++   }
++   bool all_blocks_present(unsigned blk_count) const {
++      return block_map && block_map->has_all_set(0,blk_count);
++   }
++   bool any_blocks_present() const {
++      return block_map; // it's allocated when setting any bit
++   }
++};
++
++struct TorrentFile
++{
++   char *path;
++   off_t pos;
++   off_t length;
++   void set(const char *n,off_t p,off_t l) {
++      path=xstrdup(n);
++      pos=p;
++      length=l;
++   }
++   void unset() {
++      xfree(path); path=0;
++   }
++   bool contains_pos(off_t p) const {
++      return p>=pos && p<pos+length;
++   }
++};
  
-    int Done() { return FinderJob::Done() && buf->Done(); }
+-   bool has_a_downloader() const;
++class TorrentFiles : public xarray<TorrentFile>
++{
++   static int pos_cmp(const TorrentFile *a, const TorrentFile *b) {
++      if(a->pos < b->pos)
++       return -1;
++      if(a->pos > b->pos)
++       return 1;
++      // we want zero-sized files to placed before non-zero ones.
++      if(a->length != b->length)
++       return a->length < b->length ? -1 : 1;
++      return 0;
++   }
++public:
++   TorrentFile *file(int i) { return get_non_const()+i; }
++   TorrentFiles(const BeNode *f_node,const Torrent *t);
++   ~TorrentFiles() {
++      for(int i=0; i<length(); i++)
++       file(i)->unset();
++   }
++   TorrentFile *FindByPosition(off_t p);
  };
-diff --git a/src/Http.cc b/src/Http.cc
-index 1b6495d..d0eb857 100644
---- a/src/Http.cc
-+++ b/src/Http.cc
-@@ -64,6 +64,8 @@ CDECL char *strptime(const char *buf, const char *format, struct tm *tm);
- #define EINPROGRESS -1
- #endif
  
-+enum { CHUNK_SIZE_UNKNOWN=-1 };
+ class TorrentListener : public SMTask, protected ProtoLog, protected Networker
+@@ -99,6 +184,7 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient
+    friend class TorrentPeer;
+    friend class TorrentDispatcher;
+    friend class TorrentListener;
++   friend class TorrentFiles;
+    friend class DHT;
+    bool shutting_down;
+@@ -171,6 +257,7 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient
+    xstring info_hash;
+    const xstring *pieces;
+    xstring name;
++   Ref<TorrentFiles> files;
+    Ref<DirectedBuffer> recv_translate;
+    Ref<DirectedBuffer> recv_translate_utf8;
+@@ -214,11 +301,32 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient
+    BeNode *Lookup(Ref<BeNode>& d,const char *name,BeNode::be_type_t type) { return Lookup(d->dict,name,type); }
+    TaskRefArray<TorrentPeer> peers;
+-   RefArray<TorrentPiece> piece_info;
+    static int PeersCompareActivity(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2);
+    static int PeersCompareRecvRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2);
+    static int PeersCompareSendRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2);
++   RefToArray<TorrentPiece> piece_info;
++   unsigned blocks_in_piece;
++   unsigned blocks_in_last_piece;
++   bool BlockPresent(unsigned piece,unsigned block) const {
++      return piece_info[piece].block_present(block);
++   }
++   bool AllBlocksPresent(unsigned piece) const {
++      return piece_info[piece].all_blocks_present(BlocksInPiece(piece));
++   }
++   bool AnyBlocksPresent(unsigned piece) const {
++      return piece_info[piece].any_blocks_present();
++   }
++   bool AllBlocksAbsent(unsigned piece) const {
++      return !AnyBlocksPresent(piece);
++   }
++   void SetBlocksAbsent(unsigned piece) {
++      piece_info[piece].set_blocks_absent();
++   }
++   void SetBlockPresent(unsigned piece,unsigned block) {
++      piece_info[piece].set_block_present(block,BlocksInPiece(piece));
++   }
 +
- Http::Connection::Connection(int s,const char *c)
-    : closure(c), sock(s)
+    void RebuildPiecesNeeded();
+    Timer pieces_needed_rebuild_timer;
+    xarray<unsigned> pieces_needed;
+@@ -296,6 +404,7 @@ public:
+    static void ClassInit();
+    Torrent(const char *mf,const char *cwd,const char *output_dir);
++   ~Torrent();
+    int Do();
+    int Done() const;
+@@ -315,7 +424,7 @@ public:
+    static void SHA1(const xstring& str,xstring& buf);
+    void ValidatePiece(unsigned p);
+    unsigned PieceLength(unsigned p) const { return p==total_pieces-1 ? last_piece_length : piece_length; }
+-   unsigned BlocksInPiece(unsigned p) const { return (PieceLength(p)+BLOCK_SIZE-1)/BLOCK_SIZE; }
++   unsigned BlocksInPiece(unsigned p) const { return p==total_pieces-1 ? blocks_in_last_piece : blocks_in_piece; }
+    const TaskRefArray<TorrentPeer>& GetPeers() const { return peers; }
+    void AddPeer(TorrentPeer *);
+@@ -772,7 +881,7 @@ public:
+ class TorrentBlackList
  {
-@@ -103,7 +105,7 @@ void Http::Init()
-    array_send=0;
-    chunked=false;
--   chunk_size=-1;
-+   chunk_size=CHUNK_SIZE_UNKNOWN;
-    chunk_pos=0;
-    chunked_trailer=false;
-@@ -190,7 +192,7 @@ void Http::ResetRequestData()
-    keep_alive_max=-1;
-    array_send=fileset_for_info?fileset_for_info->curr_index():0;
-    chunked=false;
--   chunk_size=-1;
-+   chunk_size=CHUNK_SIZE_UNKNOWN;
-    chunk_pos=0;
-    chunked_trailer=false;
-    propfind=0;
-@@ -688,7 +690,7 @@ void Http::SendRequest(const char *connection,const char *f)
-    keep_alive=false;
-    chunked=false;
--   chunk_size=-1;
-+   chunk_size=CHUNK_SIZE_UNKNOWN;
-    chunk_pos=0;
-    chunked_trailer=false;
-    inflate=0;
-@@ -864,7 +866,7 @@ void Http::HandleHeaderLine(const char *name,const char *value)
-       if(!strcasecmp(value,"chunked"));
-       {
-        chunked=true;
--       chunk_size=-1; // to indicate "before first chunk"
-+       chunk_size=CHUNK_SIZE_UNKNOWN;   // expecting first chunk
-        chunk_pos=0;
-        chunked_trailer=false;
-       }
-@@ -1337,6 +1339,15 @@ int Http::Do()
-              }
-              if(mode==ARRAY_INFO)
-              {
-+                if((status_code==400 || status_code==501)
-+                && !xstrcmp(last_method,"PROPFIND"))
-+                {
-+                   ResMgr::Set("http:use-propfind",hostname,"no");
-+                   use_propfind_now=false;
-+                   try_time=0;
-+                   Disconnect();
-+                   return MOVED;
-+                }
-                 // we'll have to receive next header
-                 status.set(0);
-                 status_code=0;
-@@ -1566,8 +1577,7 @@ int Http::Do()
-                 if(!xstrcmp(last_method,"MKCOL"))
-                    ResMgr::Set("http:use-mkcol",hostname,"no");
-              }
--             if((mode==CHANGE_DIR || mode==ARRAY_INFO)
--             && !xstrcmp(last_method,"PROPFIND"))
-+             if(mode==CHANGE_DIR && !xstrcmp(last_method,"PROPFIND"))
-              {
-                 use_propfind_now=false;
-                 try_time=0;
-@@ -1776,7 +1786,7 @@ get_again:
-       if(chunked_trailer && state==RECEIVING_HEADER)
-        return DO_AGAIN;
-       const char *nl;
--      if(chunk_size==-1) // expecting first/next chunk
-+      if(chunk_size==CHUNK_SIZE_UNKNOWN) // expecting first/next chunk
-       {
-        nl=(const char*)memchr(buf1,'\n',size1);
-        if(nl==0)  // not yet
-@@ -1794,6 +1804,7 @@ get_again:
-        }
-        conn->recv_buf->Skip(nl-buf1+1);
-        chunk_pos=0;
-+       LogNote(9,"next chunk size: %ld",chunk_size);
-        goto get_again;
-       }
-       if(chunk_size==0) // eof
-@@ -1815,7 +1826,7 @@ get_again:
-           return FATAL;
-        }
-        conn->recv_buf->Skip(2);
--       chunk_size=-1;
-+       chunk_size=CHUNK_SIZE_UNKNOWN;
-        goto get_again;
-       }
-       // ok, now we may get portion of data
-diff --git a/src/OutputJob.cc b/src/OutputJob.cc
-index fc09524..ebaa090 100644
---- a/src/OutputJob.cc
-+++ b/src/OutputJob.cc
-@@ -228,7 +228,7 @@ OutputJob::OutputJob(const char *path, const char *a0, FileAccess *fa0)
-    Init(a0);
+-   xmap<Timer*> bl;
++   xmap_p<Timer> bl;
+    void check_expire();
+ public:
+    bool Listed(const sockaddr_u &a);
+diff --git a/src/resource.cc b/src/resource.cc
+index 4aa071a..e4707e7 100644
+--- a/src/resource.cc
++++ b/src/resource.cc
+@@ -57,13 +57,39 @@ static const char *FtpProxyValidate(xstring_c *p)
+    return 0;
  }
  
--OutputJob::~OutputJob()
-+void OutputJob::PrepareToDie()
++static const char *SetValidate(xstring_c& s,const char *const *set,const char *name)
++{
++   const char *const *scan;
++   for(scan=set; *scan; scan++)
++      if(s.eq(*scan))
++       return 0;
++
++   xstring &j=xstring::get_tmp();
++   if(name)
++      j.setf(_("%s must be one of: "),name);
++   else
++      j.set(_("must be one of: "));
++   bool had_empty=false;
++   for(scan=set; *scan; scan++) {
++      if(!**scan) {
++       had_empty=true;
++       continue;
++      }
++      if(scan>set)
++       j.append(", ");
++      j.append(*scan);
++   }
++   if(had_empty)
++      j.append(_(", or empty"));
++   return j;
++}
++
+ static const char *FtpProxyAuthTypeValidate(xstring_c *s)
  {
-    Bg();
-    AcceptSig(SIGTERM);
-@@ -236,6 +236,10 @@ OutputJob::~OutputJob()
-    Delete(input);
-    if(input != output)
-       Delete(output);
-+   input=0;
-+   output=0;
+-   if(s->ne("user") && s->ne("joined") && s->ne("joined-acct")
+-   && s->ne("open") && s->ne("proxy-user@host"))
+-      // for translator: `user', `joined', `joined-acct', `open' are literals.
+-      return _("ftp:proxy-auth-type must be one of: user, joined, joined-acct, open, proxy-user@host");
+-   return 0;
++   static const char *const valid_set[]={
++      "user", "joined", "joined-acct", "open", "proxy-user@host", 0
++   };
++   return SetValidate(*s,valid_set,"ftp:proxy-auth-type");
+ }
+ static const char *HttpProxyValidate(xstring_c *p)
+@@ -130,6 +156,14 @@ const char *OrderValidate(xstring_c *s)
+    return 0;
+ }
++static const char *SortByValidate(xstring_c *s)
++{
++   static const char * const valid_set[]={
++      "name", "name-desc", "size", "size-desc", "date", "date-desc", 0
++   };
++   return SetValidate(*s,valid_set,"mirror:order-by");
++}
 +
-+   super::PrepareToDie();
+ #if USE_SSL
+ static
+ const char *AuthArgValidate(xstring_c *s)
+@@ -137,30 +171,21 @@ const char *AuthArgValidate(xstring_c *s)
+    for(char *i=s->get_non_const(); *i; i++)
+       *i=to_ascii_upper(*i);
+-   if(strcmp(*s,"SSL")
+-   && strcmp(*s,"TLS")
+-   && strcmp(*s,"TLS-P")
+-   && strcmp(*s,"TLS-C"))
+-      return _("ftp:ssl-auth must be one of: SSL, TLS, TLS-P, TLS-C");
+-
+-   return 0;
++   const char *const valid_set[]={
++      "SSL", "TLS", "TLS-P", "TLS-C", 0
++   };
++   return SetValidate(*s,valid_set,"ftp:ssl-auth");
  }
+ static
+ const char *ProtValidate(xstring_c *s)
+ {
+-   if(!**s)
+-      return 0;
+-
+    for(char *i=s->get_non_const(); *i; i++)
+       *i=to_ascii_upper(*i);
  
- /* This is called to ask us "permission" to display a status line. */
-diff --git a/src/OutputJob.h b/src/OutputJob.h
-index f925b01..99e3292 100644
---- a/src/OutputJob.h
-+++ b/src/OutputJob.h
-@@ -71,7 +71,7 @@ class OutputJob : public Job
- public:
-    OutputJob(FDStream *output, const char *a0);
-    OutputJob(const char *path, const char *a0, FA *fa=0);
--   ~OutputJob();
-+   void PrepareToDie();
-    /* Set the main filter: */
-    void SetFilter(const char *f) { filter.set(f); }
-diff --git a/src/commands.cc b/src/commands.cc
-index 6c24185..d70a7bc 100644
---- a/src/commands.cc
-+++ b/src/commands.cc
-@@ -2850,13 +2850,15 @@ CMD(find)
-    static struct option find_options[]=
-    {
-       {"maxdepth",required_argument,0,'d'},
-+      {"ls",no_argument,0,'l'},
-       {0,0,0,0}
-    };
-    int opt;
-    int maxdepth = -1;
-+   bool long_listing=false;
-    const char *op=args->a0();
--   while((opt=args->getopt_long("+d:",find_options))!=EOF)
-+   while((opt=args->getopt_long("+d:l",find_options))!=EOF)
-    {
-       switch(opt)
-       {
-@@ -2868,6 +2870,9 @@ CMD(find)
-        }
-        maxdepth = atoi(optarg);
-        break;
-+      case 'l':
-+       long_listing=true;
-+       break;
-       case '?':
-        eprintf(_("Usage: %s [-d #] dir\n"),op);
-        return 0;
-@@ -2878,6 +2883,7 @@ CMD(find)
-       args->Append(".");
-    FinderJob_List *j=new class FinderJob_List(session->Clone(),args.borrow(),output.borrow());
-    j->set_maxdepth(maxdepth);
-+   j->DoLongListing(long_listing);
-    return j;
+-   if(strcmp(*s,"P")
+-   && strcmp(*s,"C")
+-   && strcmp(*s,"S")
+-   && strcmp(*s,"E"))
+-      return _("must be one of: C, S, E, P, or empty");
+-
+-   return 0;
++   const char *const valid_set[]={
++      "C", "S", "E", "P", "", 0
++   };
++   return SetValidate(*s,valid_set,"ftps:initial-prot");
  }
+ #endif
+@@ -300,6 +325,7 @@ static ResType lftp_vars[] = {
+    {"net:connection-limit",    "0",     ResMgr::UNumberValidate,0},
+    {"net:connection-takeover",         "yes",   ResMgr::BoolValidate,0},
++   {"mirror:sort-by",          "name",  SortByValidate,ResMgr::NoClosure},
+    {"mirror:order",            "*.sfv *.sig *.md5* *.sum * */", 0,ResMgr::NoClosure},
+    {"mirror:parallel-directories", "yes", ResMgr::BoolValidate,ResMgr::NoClosure},
+    {"mirror:parallel-transfer-count", "1",ResMgr::UNumberValidate,ResMgr::NoClosure},
+diff --git a/src/xmap.cc b/src/xmap.cc
+index 4bc9a86..2276bce 100644
+--- a/src/xmap.cc
++++ b/src/xmap.cc
+@@ -136,6 +136,7 @@ void _xmap::_remove(entry **ep)
+    if(!ep || !*ep)
+       return;
+    entry *e=*ep;
++   e->key.unset();
+    *ep=e->next;
+    xfree(e);
+    entry_count--;
+diff --git a/src/xstring.h b/src/xstring.h
+index 264baf6..4a284ee 100644
+--- a/src/xstring.h
++++ b/src/xstring.h
+@@ -222,7 +222,8 @@ public:
+    static xstring& cat(const char *first,...) ATTRIBUTE_SENTINEL;
+    static xstring& join(const char *sep,int n,...);
  
-diff --git a/src/ftpclass.cc b/src/ftpclass.cc
-index 72cc04e..09d39ad 100644
---- a/src/ftpclass.cc
-+++ b/src/ftpclass.cc
-@@ -2743,6 +2743,7 @@ int Ftp::ReceiveOneLine()
-       if(nl==resp+resp_size-1 && now-conn->control_recv->EventTime()>5)
-       {
-        LogError(1,"server bug: single <NL>");
-+       nl=find_char(resp,resp_size,'\n');
-        line_len=nl-resp;
-        skip_len=nl-resp+1;
-        break;
+-   void truncate(size_t n=0);
++   void truncate() { set_length(0); }
++   void truncate(size_t n);
+    void truncate_at(char c);
+    /* set_length can be used to extend the string, e.g. after modification
+       with get_space+get_non_const. */
This page took 0.103025 seconds and 4 git commands to generate.