]>
Commit | Line | Data |
---|---|---|
93dfd0ea AM |
1 | diff --git a/doc/lftp.1 b/doc/lftp.1 |
2 | index d5d408f..9222795 100644 | |
3 | --- a/doc/lftp.1 | |
4 | +++ b/doc/lftp.1 | |
5 | @@ -1748,10 +1748,15 @@ is applied. It is never used if mirror:exclude-regex is empty. | |
6 | .BR mirror:no-empty-dirs " (boolean)" | |
7 | when true, mirror doesn't create empty directories (like \-\-no\-empty\-dirs option). | |
8 | .TP | |
9 | +.BR mirror:sort-by " (string)" | |
10 | +specifies order of file transfers. Valid values are: name, name-desc, size, size-desc, | |
11 | +date, date-desc. When the value is name or name-desc, then mirror:order setting also | |
12 | +affects the order or transfers. | |
13 | +.TP | |
14 | .BR mirror:order " (list of patterns)" | |
15 | -specifies order of file transfers. E.g. setting this to "*.sfv *.sum" makes mirror to | |
16 | +specifies order of file transfers when sorting by name. E.g. setting this to "*.sfv *.sum" makes mirror to | |
17 | transfer files matching *.sfv first, then ones matching *.sum and then all other | |
18 | -files. To process directories after other files, add "*/" to end of pattern list. | |
19 | +files. To process directories after other files, add "*/" to the end of pattern list. | |
20 | .TP | |
21 | .BR mirror:parallel-directories " (boolean)" | |
22 | if true, mirror will start processing of several directories in parallel | |
23 | diff --git a/po/pl.po b/po/pl.po | |
24 | index d569428..89e8b11 100644 | |
25 | --- a/po/pl.po | |
26 | +++ b/po/pl.po | |
27 | @@ -7,10 +7,10 @@ | |
28 | # | |
29 | msgid "" | |
30 | msgstr "" | |
31 | -"Project-Id-Version: lftp 4.4.16\n" | |
32 | +"Project-Id-Version: lftp 4.5.3\n" | |
33 | "Report-Msgid-Bugs-To: lftp-bugs@lftp.yar.ru\n" | |
34 | "POT-Creation-Date: 2014-07-06 16:15+0400\n" | |
35 | -"PO-Revision-Date: 2014-05-18 06:54+0200\n" | |
36 | +"PO-Revision-Date: 2014-07-13 14:15+0200\n" | |
37 | "Last-Translator: Jakub Bogusz <qboosh@pld-linux.org>\n" | |
38 | "Language-Team: Polish <translation-team-pl@lists.sourceforge.net>\n" | |
39 | "Language: pl\n" | |
40 | @@ -1152,12 +1152,10 @@ msgstr "" | |
41 | "uczyni dopełnianie nazw plików nie rozpoznającym wielkości liter.\n" | |
42 | ||
43 | #: src/commands.cc:216 | |
44 | -#, fuzzy | |
45 | msgid "debug [OPTS] [<level>|off]" | |
46 | -msgstr "debug [<poziom>|off] [-o <plik>]" | |
47 | +msgstr "debug [OPCJE] [<poziom>|off]" | |
48 | ||
49 | #: src/commands.cc:217 | |
50 | -#, fuzzy | |
51 | msgid "" | |
52 | "Set debug level to given value or turn debug off completely.\n" | |
53 | " -o <file> redirect debug output to the file\n" | |
54 | @@ -1165,8 +1163,11 @@ msgid "" | |
55 | " -p show PID\n" | |
56 | " -t show timestamps\n" | |
57 | msgstr "" | |
58 | -"Ustaw poziom debugowania na daną wartość lub wyłącz zupełnie.\n" | |
59 | -" -o <plik> przekieruj wyjście debugowania do pliku.\n" | |
60 | +"Ustaw poziom diagnostyki na daną wartość lub wyłącz zupełnie.\n" | |
61 | +" -o <plik> przekieruj wyjście diagnostyki do pliku.\n" | |
62 | +" -c wypisuj kontekst komunikatu\n" | |
63 | +" -p wypisuj PID\n" | |
64 | +" -t wypisuj znaczniki czasu\n" | |
65 | ||
66 | #: src/commands.cc:222 | |
67 | msgid "du [options] <dirs>" | |
68 | @@ -1477,12 +1478,10 @@ msgstr "" | |
69 | "podano nazw obu katalogów, to będą użyte bieżące katalogi lokalny i zdalny.\n" | |
70 | ||
71 | #: src/commands.cc:339 | |
72 | -#, fuzzy | |
73 | msgid "mkdir [OPTS] <dirs>" | |
74 | -msgstr "mkdir [-p] <katalogi>" | |
75 | +msgstr "mkdir [OPCJE] <katalogi>" | |
76 | ||
77 | #: src/commands.cc:340 | |
78 | -#, fuzzy | |
79 | msgid "" | |
80 | "Make remote directories\n" | |
81 | " -p make all levels of path\n" | |
82 | @@ -1490,6 +1489,7 @@ msgid "" | |
83 | msgstr "" | |
84 | "Utwórz zdalne katalogi\n" | |
85 | " -p tworzy wszystkie poziomy ścieżki\n" | |
86 | +" -f ciche działanie, bez komunikatów\n" | |
87 | ||
88 | #: src/commands.cc:343 | |
89 | msgid "module name [args]" | |
90 | @@ -2378,9 +2378,9 @@ msgid "unsupported network protocol" | |
91 | msgstr "protokół nie jest obsługiwany" | |
92 | ||
93 | #: src/ftpclass.cc:2133 src/ftpclass.cc:2228 | |
94 | -#, fuzzy, c-format | |
95 | +#, c-format | |
96 | msgid "Data socket error (%s) - reconnecting" | |
97 | -msgstr "Błąd połączenia (%s) - powtórne łączenie" | |
98 | +msgstr "Błąd gniazda danych (%s) - powtórne łączenie" | |
99 | ||
100 | #: src/ftpclass.cc:2161 | |
101 | #, c-format | |
102 | @@ -2700,13 +2700,12 @@ msgid "peer unexpectedly closed connection after %s" | |
103 | msgstr "druga strona nieoczekiwanie zamknęła połączenie po %s" | |
104 | ||
105 | #: src/Torrent.cc:2903 src/Torrent.cc:3020 | |
106 | -#, fuzzy | |
107 | msgid "peer unexpectedly closed connection" | |
108 | -msgstr "druga strona nieoczekiwanie zamknęła połączenie po %s" | |
109 | +msgstr "druga strona nieoczekiwanie zamknęła połączenie" | |
110 | ||
111 | #: src/Torrent.cc:2905 src/Torrent.cc:2906 | |
112 | msgid "peer closed connection (before handshake)" | |
113 | -msgstr "druga strona zamknęła połączenie (przed jego ustanowieniem)" | |
114 | +msgstr "druga strona zamknęła połączenie (przed przywitaniem)" | |
115 | ||
116 | #: src/Torrent.cc:2909 src/Torrent.cc:3022 src/Torrent.cc:3023 | |
117 | msgid "invalid peer response format" | |
118 | @@ -2732,15 +2731,15 @@ msgstr "Przyjęto połączenie od [%s]:%d" | |
119 | #: src/Torrent.cc:3552 | |
120 | #, c-format | |
121 | msgid "peer sent unknown info_hash=%s in handshake" | |
122 | -msgstr "" | |
123 | +msgstr "druga strona wysłała nieznane info_hash=%s przy przywitaniu" | |
124 | ||
125 | #: src/Torrent.cc:3576 | |
126 | msgid "peer handshake timeout" | |
127 | -msgstr "upłynął limit czasu nawiązywania połączenia z drugą stroną" | |
128 | +msgstr "upłynął limit czasu przywitania z drugą stroną" | |
129 | ||
130 | #: src/Torrent.cc:3588 | |
131 | msgid "peer short handshake" | |
132 | -msgstr "nawiązanie połączenia z drugą stroną zostało ucięte" | |
133 | +msgstr "przywitanie z drugą stroną zostało ucięte" | |
134 | ||
135 | #: src/Torrent.cc:3590 | |
136 | msgid "peer closed just accepted connection" | |
137 | diff --git a/src/FileSet.cc b/src/FileSet.cc | |
138 | index 389578a..6380a68 100644 | |
139 | --- a/src/FileSet.cc | |
140 | +++ b/src/FileSet.cc | |
141 | @@ -232,6 +232,21 @@ void FileSet::Sort(sort_e newsort, bool casefold, bool reverse) | |
142 | } | |
143 | } | |
144 | ||
145 | +// reverse current sort order | |
146 | +void FileSet::ReverseSort() | |
147 | +{ | |
148 | + if(!sorted) { | |
149 | + Sort(BYNAME,false,true); | |
150 | + return; | |
151 | + } | |
152 | + int i=0; | |
153 | + int j=sorted.length()-1; | |
154 | + while(i<j) { | |
155 | + sorted[i]=replace_value(sorted[j],sorted[i]); | |
156 | + ++i,--j; | |
157 | + } | |
158 | +} | |
159 | + | |
160 | /* Remove the current sort, allowing new entries to be added. | |
161 | * (Nothing uses this ... */ | |
162 | void FileSet::Unsort() | |
163 | diff --git a/src/FileSet.h b/src/FileSet.h | |
164 | index 7c3ac2a..155536f 100644 | |
165 | --- a/src/FileSet.h | |
166 | +++ b/src/FileSet.h | |
167 | @@ -180,6 +180,7 @@ public: | |
168 | void Sort(sort_e newsort, bool casefold=false, bool reverse=false); | |
169 | void Unsort(); | |
170 | void SortByPatternList(const char *list_c); | |
171 | + void ReverseSort(); | |
172 | ||
173 | void Exclude(const char *prefix,const PatternSet *x); | |
174 | void ExcludeDots(); | |
175 | diff --git a/src/Fish.cc b/src/Fish.cc | |
176 | index d172a20..29ba4c6 100644 | |
177 | --- a/src/Fish.cc | |
178 | +++ b/src/Fish.cc | |
179 | @@ -98,6 +98,15 @@ int Fish::Do() | |
180 | timeout_timer.Reset(send_buf->EventTime()); | |
181 | if(recv_buf) | |
182 | timeout_timer.Reset(recv_buf->EventTime()); | |
183 | + if(pty_send_buf) | |
184 | + timeout_timer.Reset(pty_send_buf->EventTime()); | |
185 | + if(pty_recv_buf) | |
186 | + timeout_timer.Reset(pty_recv_buf->EventTime()); | |
187 | + | |
188 | + // check for timeout only if there should be connection activity. | |
189 | + if(state!=DISCONNECTED && (state!=CONNECTED || !RespQueueIsEmpty()) | |
190 | + && mode!=CLOSED && CheckTimeout()) | |
191 | + return MOVED; | |
192 | ||
193 | if((state==FILE_RECV || state==FILE_SEND) | |
194 | && rate_limit==0) | |
195 | @@ -275,14 +284,6 @@ int Fish::Do() | |
196 | case DONE: | |
197 | break; | |
198 | } | |
199 | - if(m==MOVED) | |
200 | - return MOVED; | |
201 | - if(send_buf) | |
202 | - timeout_timer.Reset(send_buf->EventTime()); | |
203 | - if(recv_buf) | |
204 | - timeout_timer.Reset(recv_buf->EventTime()); | |
205 | - if(CheckTimeout()) | |
206 | - return MOVED; | |
207 | return m; | |
208 | } | |
209 | ||
210 | diff --git a/src/MirrorJob.cc b/src/MirrorJob.cc | |
211 | index a19ff8e..eb148f1 100644 | |
212 | --- a/src/MirrorJob.cc | |
213 | +++ b/src/MirrorJob.cc | |
214 | @@ -589,10 +589,16 @@ void MirrorJob::InitSets(const FileSet *source,const FileSet *dest) | |
215 | if(!(flags&DELETE)) | |
216 | to_transfer->SubtractAny(to_rm_mismatched); | |
217 | ||
218 | - to_transfer->SortByPatternList(ResMgr::Query("mirror:order",0)); | |
219 | - to_transfer->CountBytes(&bytes_to_transfer); | |
220 | - if(parent_mirror) | |
221 | - parent_mirror->AddBytesToTransfer(bytes_to_transfer); | |
222 | + const char *sort_by=ResMgr::Query("mirror:sort-by",0); | |
223 | + bool desc=strstr(sort_by,"-desc"); | |
224 | + if(!strncmp(sort_by,"name",4)) | |
225 | + to_transfer->SortByPatternList(ResMgr::Query("mirror:order",0)); | |
226 | + else if(!strncmp(sort_by,"date",4)) | |
227 | + to_transfer->Sort(FileSet::BYDATE); | |
228 | + else if(!strncmp(sort_by,"size",4)) | |
229 | + to_transfer->Sort(FileSet::BYSIZE,false,true); | |
230 | + if(desc) | |
231 | + to_transfer->ReverseSort(); | |
232 | ||
233 | int dir_count=0; | |
234 | to_transfer->Count(&dir_count,NULL,NULL,NULL); | |
235 | @@ -851,10 +857,16 @@ int MirrorJob::Do() | |
236 | stats.dirs++; | |
237 | ||
238 | InitSets(source_set,target_set); | |
239 | + | |
240 | + to_transfer->CountBytes(&bytes_to_transfer); | |
241 | + if(parent_mirror) | |
242 | + parent_mirror->AddBytesToTransfer(bytes_to_transfer); | |
243 | + | |
244 | to_rm->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files); | |
245 | to_rm->rewind(); | |
246 | to_rm_mismatched->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files); | |
247 | to_rm_mismatched->rewind(); | |
248 | + | |
249 | set_state(TARGET_REMOVE_OLD_FIRST); | |
250 | goto TARGET_REMOVE_OLD_FIRST_label; | |
251 | ||
252 | diff --git a/src/PollVec.cc b/src/PollVec.cc | |
253 | index a18b517..e1d72fb 100644 | |
254 | --- a/src/PollVec.cc | |
255 | +++ b/src/PollVec.cc | |
256 | @@ -31,7 +31,7 @@ static inline bool operator<(const timeval& a,const timeval& b) | |
257 | void PollVec::AddTimeoutU(unsigned t) | |
258 | { | |
259 | struct timeval new_timeout={t/1000000,t%1000000}; | |
260 | - if(new_timeout<tv_timeout) | |
261 | + if(tv_timeout.tv_sec<0 || new_timeout<tv_timeout) | |
262 | SetTimeout(new_timeout); | |
263 | } | |
264 | ||
265 | diff --git a/src/ProcWait.cc b/src/ProcWait.cc | |
266 | index 0a065ad..d3dfec4 100644 | |
267 | --- a/src/ProcWait.cc | |
268 | +++ b/src/ProcWait.cc | |
269 | @@ -24,7 +24,14 @@ | |
270 | #include "ProcWait.h" | |
271 | #include "SignalHook.h" | |
272 | ||
273 | -ProcWait *ProcWait::chain=0; | |
274 | +xmap<ProcWait*> ProcWait::all_proc; | |
275 | + | |
276 | +const xstring& ProcWait::proc_key(pid_t p) | |
277 | +{ | |
278 | + static xstring tmp_key; | |
279 | + tmp_key.nset((const char*)&p,sizeof(p)); | |
280 | + return tmp_key; | |
281 | +} | |
282 | ||
283 | int ProcWait::Do() | |
284 | { | |
285 | @@ -100,27 +107,19 @@ int ProcWait::Kill(int sig) | |
286 | } | |
287 | ||
288 | ProcWait::ProcWait(pid_t p) | |
289 | + : pid(p) | |
290 | { | |
291 | auto_die=false; | |
292 | - pid=p; | |
293 | status=RUNNING; | |
294 | term_info=-1; | |
295 | saved_errno=0; | |
296 | ||
297 | - next=chain; | |
298 | - chain=this; | |
299 | + all_proc.add(proc_key(pid),this); | |
300 | } | |
301 | ||
302 | ProcWait::~ProcWait() | |
303 | { | |
304 | - for(ProcWait **scan=&chain; *scan; scan=&(*scan)->next) | |
305 | - { | |
306 | - if(*scan==this) | |
307 | - { | |
308 | - *scan=next; | |
309 | - return; | |
310 | - } | |
311 | - } | |
312 | + all_proc.remove(proc_key(pid)); | |
313 | } | |
314 | ||
315 | void ProcWait::SIGCHLD_handler(int sig) | |
316 | @@ -130,16 +129,9 @@ void ProcWait::SIGCHLD_handler(int sig) | |
317 | pid_t pp=waitpid(-1,&info,WUNTRACED|WNOHANG); | |
318 | if(pp==-1) | |
319 | return; | |
320 | - for(ProcWait *scan=chain; scan; scan=scan->next) | |
321 | - { | |
322 | - if(scan->pid==pp) | |
323 | - { | |
324 | - scan->handle_info(info); | |
325 | - return; | |
326 | - } | |
327 | - } | |
328 | - // no WaitProc for the pid. Probably the process died too fast, | |
329 | - // but next waitpid should take care of it. | |
330 | + ProcWait *w=all_proc.lookup(proc_key(pp)); | |
331 | + if(w && w->handle_info(info)) | |
332 | + w->Timeout(0); | |
333 | } | |
334 | ||
335 | void ProcWait::Signal(bool yes) | |
336 | @@ -156,6 +148,6 @@ void ProcWait::Signal(bool yes) | |
337 | void ProcWait::DeleteAll() | |
338 | { | |
339 | Signal(false); | |
340 | - for(ProcWait *scan=chain; scan; scan=scan->next) | |
341 | - Delete(scan); | |
342 | + for(ProcWait *w=all_proc.each_begin(); w; w=all_proc.each_next()) | |
343 | + Delete(w); | |
344 | } | |
345 | diff --git a/src/ProcWait.h b/src/ProcWait.h | |
346 | index d6f0bad..651bc21 100644 | |
347 | --- a/src/ProcWait.h | |
348 | +++ b/src/ProcWait.h | |
349 | @@ -23,6 +23,7 @@ | |
350 | #include <sys/types.h> | |
351 | #include <signal.h> | |
352 | #include "SMTask.h" | |
353 | +#include "xmap.h" | |
354 | ||
355 | class ProcWait : public SMTask | |
356 | { | |
357 | @@ -35,10 +36,10 @@ public: | |
358 | }; | |
359 | ||
360 | protected: | |
361 | - static ProcWait *chain; | |
362 | - ProcWait *next; | |
363 | + static xmap<ProcWait*> all_proc; | |
364 | + static const xstring& proc_key(pid_t p); // make key for xmap | |
365 | ||
366 | - pid_t pid; | |
367 | + const pid_t pid; | |
368 | State status; | |
369 | int term_info; | |
370 | int saved_errno; | |
371 | diff --git a/src/Ref.h b/src/Ref.h | |
372 | index 8731089..c96ee96 100644 | |
373 | --- a/src/Ref.h | |
374 | +++ b/src/Ref.h | |
375 | @@ -51,4 +51,22 @@ public: | |
376 | ||
377 | template<typename T> const Ref<T> Ref<T>::null; | |
378 | ||
379 | +template<typename T> class RefToArray : public Ref<T> | |
380 | +{ | |
381 | + RefToArray<T>(const RefToArray<T>&); // disable cloning | |
382 | + void operator=(const RefToArray<T>&); // and assignment | |
383 | + | |
384 | +public: | |
385 | + RefToArray<T>() {} | |
386 | + RefToArray<T>(T *p) : Ref<T>(p) {} | |
387 | + ~RefToArray<T>() { delete[] Ref<T>::ptr; Ref<T>::ptr=0; } | |
388 | + void operator=(T *p) { delete[] Ref<T>::ptr; Ref<T>::ptr=p; } | |
389 | + T& operator[](unsigned i) const { return Ref<T>::ptr[i]; } | |
390 | + | |
391 | + static const RefToArray<T> null; | |
392 | +}; | |
393 | + | |
394 | +template<typename T> const RefToArray<T> RefToArray<T>::null; | |
395 | + | |
396 | + | |
397 | #endif | |
398 | diff --git a/src/SFtp.cc b/src/SFtp.cc | |
399 | index 8e6d0d5..3225701 100644 | |
400 | --- a/src/SFtp.cc | |
401 | +++ b/src/SFtp.cc | |
402 | @@ -915,11 +915,7 @@ void SFtp::HandleExpect(Expect *e) | |
403 | for(int i=0; i<r->GetCount(); i++) | |
404 | { | |
405 | const NameAttrs *a=r->GetNameAttrs(i); | |
406 | - if(!file_set) | |
407 | - file_set=new FileSet; | |
408 | FileInfo *info=MakeFileInfo(a); | |
409 | - if(info) | |
410 | - file_set->Add(info); | |
411 | if(mode==LIST) | |
412 | { | |
413 | file_buf->Put(a->name); | |
414 | @@ -941,6 +937,12 @@ void SFtp::HandleExpect(Expect *e) | |
415 | file_buf->Put("\n"); | |
416 | } | |
417 | } | |
418 | + if(info) | |
419 | + { | |
420 | + if(!file_set) | |
421 | + file_set=new FileSet; | |
422 | + file_set->Add(info); | |
423 | + } | |
424 | } | |
425 | if(r->Eof()) | |
426 | goto eof; | |
427 | diff --git a/src/Torrent.cc b/src/Torrent.cc | |
428 | index ffe6433..270bbd6 100644 | |
429 | --- a/src/Torrent.cc | |
430 | +++ b/src/Torrent.cc | |
431 | @@ -278,6 +278,10 @@ Torrent::Torrent(const char *mf,const char *c,const char *od) | |
432 | dht_announce_timer.Stop(); | |
433 | } | |
434 | ||
435 | +Torrent::~Torrent() | |
436 | +{ | |
437 | +} | |
438 | + | |
439 | bool Torrent::TrackersDone() const | |
440 | { | |
441 | if(shutting_down && shutting_down_timer.Stopped()) | |
442 | @@ -318,6 +322,8 @@ void Torrent::PrepareToDie() | |
443 | RemoveTorrent(this); | |
444 | if(GetTorrentsCount()==0) { | |
445 | StopListener(); | |
446 | + StopDHT(); | |
447 | + StopListenerUDP(); | |
448 | fd_cache=0; | |
449 | black_list=0; | |
450 | } | |
451 | @@ -348,9 +354,7 @@ double Torrent::GetRatio() const | |
452 | ||
453 | void Torrent::SetDownloader(unsigned piece,unsigned block,const TorrentPeer *o,const TorrentPeer *n) | |
454 | { | |
455 | - const TorrentPeer*& downloader=piece_info[piece]->downloader[block]; | |
456 | - if(downloader==o) | |
457 | - downloader=n; | |
458 | + piece_info[piece].set_downloader(block,o,n,BlocksInPiece(piece)); | |
459 | } | |
460 | ||
461 | BeNode *Torrent::Lookup(xmap_p<BeNode>& dict,const char *name,BeNode::be_type_t type) | |
462 | @@ -421,25 +425,18 @@ void Torrent::ValidatePiece(unsigned p) | |
463 | complete_pieces--; | |
464 | my_bitfield->set_bit(p,0); | |
465 | } | |
466 | - piece_info[p]->block_map.clear(); | |
467 | + SetBlocksAbsent(p); | |
468 | } else { | |
469 | LogNote(11,"piece %u ok",p); | |
470 | if(!my_bitfield->get_bit(p)) { | |
471 | total_left-=PieceLength(p); | |
472 | complete_pieces++; | |
473 | my_bitfield->set_bit(p,1); | |
474 | + piece_info[p].free_block_map(); | |
475 | } | |
476 | } | |
477 | } | |
478 | ||
479 | -bool TorrentPiece::has_a_downloader() const | |
480 | -{ | |
481 | - for(int i=0; i<downloader.count(); i++) | |
482 | - if(downloader[i]) | |
483 | - return true; | |
484 | - return false; | |
485 | -} | |
486 | - | |
487 | template<typename T> | |
488 | static inline int cmp(T a,T b) | |
489 | { | |
490 | @@ -453,8 +450,8 @@ static inline int cmp(T a,T b) | |
491 | static Torrent *cmp_torrent; | |
492 | int Torrent::PiecesNeededCmp(const unsigned *a,const unsigned *b) | |
493 | { | |
494 | - int ra=cmp_torrent->piece_info[*a]->sources_count; | |
495 | - int rb=cmp_torrent->piece_info[*b]->sources_count; | |
496 | + int ra=cmp_torrent->piece_info[*a].get_sources_count(); | |
497 | + int rb=cmp_torrent->piece_info[*b].get_sources_count(); | |
498 | int c=cmp(ra,rb); | |
499 | if(c) return c; | |
500 | return cmp(*a,*b); | |
501 | @@ -721,7 +718,7 @@ void Torrent::SetMetadata(const xstring& md) | |
502 | } | |
503 | ||
504 | BeNode *b_piece_length=Lookup(info,"piece length",BeNode::BE_INT); | |
505 | - if(!b_piece_length) | |
506 | + if(!b_piece_length || b_piece_length->num<1024 || b_piece_length->num>INT_MAX/4) | |
507 | return; | |
508 | piece_length=b_piece_length->num; | |
509 | LogNote(4,"Piece length is %u",piece_length); | |
510 | @@ -744,7 +741,7 @@ void Torrent::SetMetadata(const xstring& md) | |
511 | if(!files) { | |
512 | single_file=true; | |
513 | BeNode *length=Lookup(info,"length",BeNode::BE_INT); | |
514 | - if(!length) | |
515 | + if(!length || length->num<0) | |
516 | return; | |
517 | total_length=length->num; | |
518 | } else { | |
519 | @@ -764,9 +761,12 @@ void Torrent::SetMetadata(const xstring& md) | |
520 | return; | |
521 | if(!Lookup(files->list[i]->dict,"path",BeNode::BE_LIST)) | |
522 | return; | |
523 | + if(f->num<0) | |
524 | + return; | |
525 | total_length+=f->num; | |
526 | } | |
527 | } | |
528 | + this->files=new TorrentFiles(files,this); | |
529 | LogNote(4,"Total length is %llu",total_length); | |
530 | total_left=total_length; | |
531 | ||
532 | @@ -800,16 +800,18 @@ void Torrent::SetMetadata(const xstring& md) | |
533 | SaveMetadata(); | |
534 | ||
535 | my_bitfield=new BitField(total_pieces); | |
536 | - for(unsigned p=0; p<total_pieces; p++) | |
537 | - piece_info.append(new TorrentPiece(BlocksInPiece(p))); | |
538 | + | |
539 | + blocks_in_piece=(piece_length+BLOCK_SIZE-1)/BLOCK_SIZE; | |
540 | + blocks_in_last_piece=(last_piece_length+BLOCK_SIZE-1)/BLOCK_SIZE; | |
541 | + | |
542 | + piece_info=new TorrentPiece[total_pieces](); | |
543 | ||
544 | if(!force_valid) { | |
545 | validate_index=0; | |
546 | validating=true; | |
547 | recv_rate.Reset(); | |
548 | } else { | |
549 | - for(unsigned i=0; i<total_pieces; i++) | |
550 | - my_bitfield->set_bit(i,1); | |
551 | + my_bitfield->set_range(0,total_pieces,1); | |
552 | complete_pieces=total_pieces; | |
553 | total_left=0; | |
554 | complete=true; | |
555 | @@ -927,7 +929,7 @@ void Torrent::CalcPiecesStats() | |
556 | for(unsigned i=0; i<total_pieces; i++) { | |
557 | if(my_bitfield->get_bit(i)) | |
558 | continue; | |
559 | - unsigned sc=piece_info[i]->sources_count; | |
560 | + unsigned sc=piece_info[i].get_sources_count(); | |
561 | if(min_piece_sources>sc) | |
562 | min_piece_sources=sc; | |
563 | if(sc==0) | |
564 | @@ -945,12 +947,13 @@ void Torrent::RebuildPiecesNeeded() | |
565 | bool enter_end_game=true; | |
566 | for(unsigned i=0; i<total_pieces; i++) { | |
567 | if(!my_bitfield->get_bit(i)) { | |
568 | - if(!piece_info[i]->has_a_downloader()) | |
569 | + if(!piece_info[i].has_a_downloader()) | |
570 | enter_end_game=false; | |
571 | - if(piece_info[i]->sources_count==0) | |
572 | + if(piece_info[i].has_no_sources()) | |
573 | continue; | |
574 | pieces_needed.append(i); | |
575 | } | |
576 | + piece_info[i].cleanup(); | |
577 | } | |
578 | if(!end_game && enter_end_game) { | |
579 | LogNote(1,"entering End Game mode"); | |
580 | @@ -1084,6 +1087,8 @@ int Torrent::Do() | |
581 | a.sa.sa_family=AF_INET6; | |
582 | if(inet_pton(AF_INET6,b_ip->str,&a.in6.sin6_addr)<=0) | |
583 | continue; | |
584 | + if(b_port->num<=0 || b_port->num>=0x10000) | |
585 | + continue; | |
586 | a.set_port(b_port->num); | |
587 | dht_ipv6->SendPing(a); | |
588 | } else | |
589 | @@ -1092,6 +1097,8 @@ int Torrent::Do() | |
590 | a.sa.sa_family=AF_INET; | |
591 | if(!inet_aton(b_ip->str,&a.in.sin_addr)) | |
592 | continue; | |
593 | + if(b_port->num<=0 || b_port->num>=0x10000) | |
594 | + continue; | |
595 | a.set_port(b_port->num); | |
596 | dht->SendPing(a); | |
597 | } | |
598 | @@ -1249,24 +1256,51 @@ const char *Torrent::MakePath(BeNode *p) const | |
599 | } | |
600 | const char *Torrent::FindFileByPosition(unsigned piece,unsigned begin,off_t *f_pos,off_t *f_tail) const | |
601 | { | |
602 | - const BeNode *files=info->lookup("files",BeNode::BE_LIST); | |
603 | off_t target_pos=(off_t)piece*piece_length+begin; | |
604 | + TorrentFile *file=files->FindByPosition(target_pos); | |
605 | + if(!file) | |
606 | + return 0; | |
607 | + | |
608 | + *f_pos=target_pos-file->pos; | |
609 | + *f_tail=file->length-*f_pos; | |
610 | + | |
611 | + return file->path; | |
612 | +} | |
613 | + | |
614 | +TorrentFiles::TorrentFiles(const BeNode *files,const Torrent *t) | |
615 | +{ | |
616 | if(!files) { | |
617 | - *f_pos=target_pos; | |
618 | - *f_tail=total_length-target_pos; | |
619 | - return name; | |
620 | + grow_space(1); | |
621 | + set_length(1); | |
622 | + file(0)->set(t->GetName(),0,t->TotalLength()); | |
623 | } else { | |
624 | + int count=files->list.length(); | |
625 | + grow_space(count); | |
626 | + set_length(count); | |
627 | off_t scan_pos=0; | |
628 | - for(int i=0; i<files->list.length(); i++) { | |
629 | - off_t file_length=files->list[i]->lookup_int("length"); | |
630 | - if(scan_pos<=target_pos && scan_pos+file_length>target_pos) { | |
631 | - *f_pos=target_pos-scan_pos; | |
632 | - *f_tail=file_length-*f_pos; | |
633 | - return MakePath(files->list[i]); | |
634 | - } | |
635 | + for(int i=0; i<count; i++) { | |
636 | + BeNode *node=files->list[i]; | |
637 | + off_t file_length=node->lookup_int("length"); | |
638 | + file(i)->set(t->MakePath(node),scan_pos,file_length); | |
639 | scan_pos+=file_length; | |
640 | } | |
641 | } | |
642 | + qsort(pos_cmp); | |
643 | +} | |
644 | +TorrentFile *TorrentFiles::FindByPosition(off_t pos) | |
645 | +{ | |
646 | + int i=0; | |
647 | + int j=length()-1; | |
648 | + while(i<=j) { | |
649 | + // invariant: the target element is in the range [i,j] | |
650 | + int m=(i+j)/2; | |
651 | + if(file(m)->contains_pos(pos)) | |
652 | + return file(m); | |
653 | + if(file(m)->pos>pos) | |
654 | + j=m-1; | |
655 | + else | |
656 | + i=m+1; | |
657 | + } | |
658 | return 0; | |
659 | } | |
660 | ||
661 | @@ -1485,9 +1519,9 @@ void Torrent::StoreBlock(unsigned piece,unsigned begin,unsigned len,const char * | |
662 | } | |
663 | ||
664 | while(bc-->0) { | |
665 | - piece_info[piece]->block_map.set_bit(b++,1); | |
666 | + SetBlockPresent(piece,b++); | |
667 | } | |
668 | - if(piece_info[piece]->block_map.has_all_set() && !my_bitfield->get_bit(piece)) { | |
669 | + if(AllBlocksPresent(piece) && !my_bitfield->get_bit(piece)) { | |
670 | ValidatePiece(piece); | |
671 | if(!my_bitfield->get_bit(piece)) { | |
672 | LogError(0,"new piece %u digest mismatch",piece); | |
673 | @@ -2021,12 +2055,12 @@ int TorrentPeer::SendDataRequests(unsigned p) | |
674 | unsigned blocks=parent->BlocksInPiece(p); | |
675 | unsigned bytes_allowed=BytesAllowed(RateLimit::GET); | |
676 | for(unsigned b=0; b<blocks; b++) { | |
677 | - if(parent->piece_info[p]->block_map.get_bit(b)) | |
678 | + if(parent->BlockPresent(p,b)) | |
679 | continue; | |
680 | - if(parent->piece_info[p]->downloader[b]) { | |
681 | + if(parent->piece_info[p].downloader_for(b)) { | |
682 | if(!parent->end_game) | |
683 | continue; | |
684 | - if(parent->piece_info[p]->downloader[b]==this) | |
685 | + if(parent->piece_info[p].downloader_for(b)==this) | |
686 | continue; | |
687 | if(FindRequest(p,b*Torrent::BLOCK_SIZE)>=0) | |
688 | continue; | |
689 | @@ -2118,8 +2152,7 @@ void TorrentPeer::SendDataRequests() | |
690 | if(parent->my_bitfield->get_bit(p)) | |
691 | continue; | |
692 | // add some randomness, so that different instances don't synchronize | |
693 | - if(!parent->piece_info[p]->block_map.has_any_set() | |
694 | - && random()/13%16==0) | |
695 | + if(parent->AllBlocksAbsent(p) && random()/13%16==0) | |
696 | continue; | |
697 | if(SendDataRequests(p)>0) | |
698 | return; | |
699 | @@ -2233,7 +2266,7 @@ unsigned TorrentPeer::GetLastPiece() const | |
700 | unsigned p=last_piece; | |
701 | // continue if have any blocks already | |
702 | if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p) | |
703 | - && parent->piece_info[p]->block_map.has_any_set() | |
704 | + && parent->AnyBlocksPresent(p) | |
705 | && peer_bitfield->get_bit(p)) | |
706 | return p; | |
707 | p=parent->last_piece; | |
708 | @@ -2302,11 +2335,11 @@ void TorrentPeer::SetPieceHaving(unsigned p,bool have) | |
709 | int diff = (have - peer_bitfield->get_bit(p)); | |
710 | if(!diff) | |
711 | return; | |
712 | - parent->piece_info[p]->sources_count+=diff; | |
713 | + parent->piece_info[p].add_sources_count(diff); | |
714 | peer_complete_pieces+=diff; | |
715 | peer_bitfield->set_bit(p,have); | |
716 | ||
717 | - if(parent->piece_info[p]->sources_count==0) | |
718 | + if(parent->piece_info[p].get_sources_count()==0) | |
719 | parent->SetPieceNotWanted(p); | |
720 | if(have && send_buf && !am_interested && !parent->my_bitfield->get_bit(p) | |
721 | && parent->NeedMoreUploaders()) { | |
722 | @@ -2641,7 +2674,7 @@ void TorrentPeer::HandleExtendedMessage(PacketExtended *pp) | |
723 | return; | |
724 | } | |
725 | BeNode *piece=pp->data->lookup("piece",BeNode::BE_INT); | |
726 | - if(!piece) { | |
727 | + if(!piece || piece->num<0 || piece->num>=INT_MAX/Torrent::BLOCK_SIZE) { | |
728 | SetError("ut_metadata piece bad or missing"); | |
729 | return; | |
730 | } | |
731 | @@ -2692,6 +2725,9 @@ void TorrentPeer::HandleExtendedMessage(PacketExtended *pp) | |
732 | } | |
733 | case UT_METADATA_REJECT: | |
734 | break; | |
735 | + default: | |
736 | + SetError("ut_metadata msg_type invalid value"); | |
737 | + return; | |
738 | } | |
739 | } else if(pp->code==MSG_EXT_PEX) { | |
740 | if(!pex.recv_timer.Stopped()) | |
741 | @@ -3322,14 +3358,16 @@ bool BitField::has_all_set(int from,int to) const { | |
742 | return false; | |
743 | return true; | |
744 | } | |
745 | - | |
746 | +void BitField::set_range(int from,int to,bool value) { | |
747 | + for(int i=from; i<to; i++) | |
748 | + set_bit(i,value); | |
749 | +} | |
750 | ||
751 | void TorrentBlackList::check_expire() | |
752 | { | |
753 | for(Timer *e=bl.each_begin(); e; e=bl.each_next()) { | |
754 | if(e->Stopped()) { | |
755 | Log::global->Format(4,"---- black-delisting peer %s\n",bl.each_key().get()); | |
756 | - delete e; | |
757 | bl.remove(bl.each_key()); | |
758 | } | |
759 | } | |
760 | diff --git a/src/Torrent.h b/src/Torrent.h | |
761 | index a9943c6..ef5d75c 100644 | |
762 | --- a/src/Torrent.h | |
763 | +++ b/src/Torrent.h | |
764 | @@ -53,20 +53,105 @@ public: | |
765 | int get_bit_length() const { return bit_length; } | |
766 | void set_bit_length(int b) { bit_length=b; set_length((b+7)/8); } | |
767 | void clear() { memset(buf,0,length()); } | |
768 | + void set_range(int from,int to,bool value); | |
769 | }; | |
770 | ||
771 | -struct TorrentPiece | |
772 | +class TorrentPiece | |
773 | { | |
774 | unsigned sources_count; // how many peers have the piece | |
775 | + unsigned downloader_count; // how many downloaders of the piece are there | |
776 | + RefToArray<const TorrentPeer*> downloader; // which peers download the blocks | |
777 | + Ref<BitField> block_map; // which blocks are present. | |
778 | ||
779 | - BitField block_map; // which blocks are present | |
780 | - xarray<const TorrentPeer*> downloader; // which peers download the blocks | |
781 | +public: | |
782 | + TorrentPiece() : sources_count(0), downloader_count(0) {} | |
783 | + ~TorrentPiece() {} | |
784 | + | |
785 | + unsigned get_sources_count() const { return sources_count; } | |
786 | + void add_sources_count(int diff) { sources_count+=diff; } | |
787 | + bool has_no_sources() const { return sources_count==0; } | |
788 | + | |
789 | + bool has_a_downloader() const { return downloader_count>0; } | |
790 | + void set_downloader(unsigned block,const TorrentPeer *o,const TorrentPeer *n,unsigned blk_count) { | |
791 | + if(!downloader) { | |
792 | + if(o || !n) | |
793 | + return; | |
794 | + downloader=new const TorrentPeer*[blk_count](); | |
795 | + } | |
796 | + const TorrentPeer*& d=downloader[block]; | |
797 | + if(d==o) { | |
798 | + d=n; | |
799 | + downloader_count+=(n!=0)-(o!=0); | |
800 | + } | |
801 | + } | |
802 | + void cleanup() { | |
803 | + if(downloader_count==0 && downloader) | |
804 | + downloader=0; | |
805 | + } | |
806 | + const TorrentPeer *downloader_for(unsigned block) { | |
807 | + return downloader ? downloader[block] : 0; | |
808 | + } | |
809 | ||
810 | - TorrentPiece(unsigned b) | |
811 | - : sources_count(0), block_map(b) | |
812 | - { downloader.allocate(b,0); } | |
813 | + void set_block_present(unsigned block,unsigned blk_count) { | |
814 | + if(!block_map) | |
815 | + block_map=new BitField(blk_count); | |
816 | + block_map->set_bit(block,1); | |
817 | + } | |
818 | + void set_blocks_absent() { | |
819 | + block_map=0; | |
820 | + } | |
821 | + void free_block_map() { | |
822 | + block_map=0; | |
823 | + } | |
824 | + bool block_present(unsigned block) const { | |
825 | + return block_map && block_map->get_bit(block); | |
826 | + } | |
827 | + bool all_blocks_present(unsigned blk_count) const { | |
828 | + return block_map && block_map->has_all_set(0,blk_count); | |
829 | + } | |
830 | + bool any_blocks_present() const { | |
831 | + return block_map; // it's allocated when setting any bit | |
832 | + } | |
833 | +}; | |
834 | + | |
835 | +struct TorrentFile | |
836 | +{ | |
837 | + char *path; | |
838 | + off_t pos; | |
839 | + off_t length; | |
840 | + void set(const char *n,off_t p,off_t l) { | |
841 | + path=xstrdup(n); | |
842 | + pos=p; | |
843 | + length=l; | |
844 | + } | |
845 | + void unset() { | |
846 | + xfree(path); path=0; | |
847 | + } | |
848 | + bool contains_pos(off_t p) const { | |
849 | + return p>=pos && p<pos+length; | |
850 | + } | |
851 | +}; | |
852 | ||
853 | - bool has_a_downloader() const; | |
854 | +class TorrentFiles : public xarray<TorrentFile> | |
855 | +{ | |
856 | + static int pos_cmp(const TorrentFile *a, const TorrentFile *b) { | |
857 | + if(a->pos < b->pos) | |
858 | + return -1; | |
859 | + if(a->pos > b->pos) | |
860 | + return 1; | |
861 | + // we want zero-sized files to placed before non-zero ones. | |
862 | + if(a->length != b->length) | |
863 | + return a->length < b->length ? -1 : 1; | |
864 | + return 0; | |
865 | + } | |
866 | +public: | |
867 | + TorrentFile *file(int i) { return get_non_const()+i; } | |
868 | + TorrentFiles(const BeNode *f_node,const Torrent *t); | |
869 | + ~TorrentFiles() { | |
870 | + for(int i=0; i<length(); i++) | |
871 | + file(i)->unset(); | |
872 | + } | |
873 | + TorrentFile *FindByPosition(off_t p); | |
874 | }; | |
875 | ||
876 | class TorrentListener : public SMTask, protected ProtoLog, protected Networker | |
877 | @@ -99,6 +184,7 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient | |
878 | friend class TorrentPeer; | |
879 | friend class TorrentDispatcher; | |
880 | friend class TorrentListener; | |
881 | + friend class TorrentFiles; | |
882 | friend class DHT; | |
883 | ||
884 | bool shutting_down; | |
885 | @@ -171,6 +257,7 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient | |
886 | xstring info_hash; | |
887 | const xstring *pieces; | |
888 | xstring name; | |
889 | + Ref<TorrentFiles> files; | |
890 | ||
891 | Ref<DirectedBuffer> recv_translate; | |
892 | Ref<DirectedBuffer> recv_translate_utf8; | |
893 | @@ -214,11 +301,32 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient | |
894 | BeNode *Lookup(Ref<BeNode>& d,const char *name,BeNode::be_type_t type) { return Lookup(d->dict,name,type); } | |
895 | ||
896 | TaskRefArray<TorrentPeer> peers; | |
897 | - RefArray<TorrentPiece> piece_info; | |
898 | static int PeersCompareActivity(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2); | |
899 | static int PeersCompareRecvRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2); | |
900 | static int PeersCompareSendRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2); | |
901 | ||
902 | + RefToArray<TorrentPiece> piece_info; | |
903 | + unsigned blocks_in_piece; | |
904 | + unsigned blocks_in_last_piece; | |
905 | + bool BlockPresent(unsigned piece,unsigned block) const { | |
906 | + return piece_info[piece].block_present(block); | |
907 | + } | |
908 | + bool AllBlocksPresent(unsigned piece) const { | |
909 | + return piece_info[piece].all_blocks_present(BlocksInPiece(piece)); | |
910 | + } | |
911 | + bool AnyBlocksPresent(unsigned piece) const { | |
912 | + return piece_info[piece].any_blocks_present(); | |
913 | + } | |
914 | + bool AllBlocksAbsent(unsigned piece) const { | |
915 | + return !AnyBlocksPresent(piece); | |
916 | + } | |
917 | + void SetBlocksAbsent(unsigned piece) { | |
918 | + piece_info[piece].set_blocks_absent(); | |
919 | + } | |
920 | + void SetBlockPresent(unsigned piece,unsigned block) { | |
921 | + piece_info[piece].set_block_present(block,BlocksInPiece(piece)); | |
922 | + } | |
923 | + | |
924 | void RebuildPiecesNeeded(); | |
925 | Timer pieces_needed_rebuild_timer; | |
926 | xarray<unsigned> pieces_needed; | |
927 | @@ -296,6 +404,7 @@ public: | |
928 | static void ClassInit(); | |
929 | ||
930 | Torrent(const char *mf,const char *cwd,const char *output_dir); | |
931 | + ~Torrent(); | |
932 | ||
933 | int Do(); | |
934 | int Done() const; | |
935 | @@ -315,7 +424,7 @@ public: | |
936 | static void SHA1(const xstring& str,xstring& buf); | |
937 | void ValidatePiece(unsigned p); | |
938 | unsigned PieceLength(unsigned p) const { return p==total_pieces-1 ? last_piece_length : piece_length; } | |
939 | - unsigned BlocksInPiece(unsigned p) const { return (PieceLength(p)+BLOCK_SIZE-1)/BLOCK_SIZE; } | |
940 | + unsigned BlocksInPiece(unsigned p) const { return p==total_pieces-1 ? blocks_in_last_piece : blocks_in_piece; } | |
941 | ||
942 | const TaskRefArray<TorrentPeer>& GetPeers() const { return peers; } | |
943 | void AddPeer(TorrentPeer *); | |
944 | @@ -772,7 +881,7 @@ public: | |
945 | ||
946 | class TorrentBlackList | |
947 | { | |
948 | - xmap<Timer*> bl; | |
949 | + xmap_p<Timer> bl; | |
950 | void check_expire(); | |
951 | public: | |
952 | bool Listed(const sockaddr_u &a); | |
953 | diff --git a/src/resource.cc b/src/resource.cc | |
954 | index 4aa071a..e4707e7 100644 | |
955 | --- a/src/resource.cc | |
956 | +++ b/src/resource.cc | |
957 | @@ -57,13 +57,39 @@ static const char *FtpProxyValidate(xstring_c *p) | |
958 | return 0; | |
959 | } | |
960 | ||
961 | +static const char *SetValidate(xstring_c& s,const char *const *set,const char *name) | |
962 | +{ | |
963 | + const char *const *scan; | |
964 | + for(scan=set; *scan; scan++) | |
965 | + if(s.eq(*scan)) | |
966 | + return 0; | |
967 | + | |
968 | + xstring &j=xstring::get_tmp(); | |
969 | + if(name) | |
970 | + j.setf(_("%s must be one of: "),name); | |
971 | + else | |
972 | + j.set(_("must be one of: ")); | |
973 | + bool had_empty=false; | |
974 | + for(scan=set; *scan; scan++) { | |
975 | + if(!**scan) { | |
976 | + had_empty=true; | |
977 | + continue; | |
978 | + } | |
979 | + if(scan>set) | |
980 | + j.append(", "); | |
981 | + j.append(*scan); | |
982 | + } | |
983 | + if(had_empty) | |
984 | + j.append(_(", or empty")); | |
985 | + return j; | |
986 | +} | |
987 | + | |
988 | static const char *FtpProxyAuthTypeValidate(xstring_c *s) | |
989 | { | |
990 | - if(s->ne("user") && s->ne("joined") && s->ne("joined-acct") | |
991 | - && s->ne("open") && s->ne("proxy-user@host")) | |
992 | - // for translator: `user', `joined', `joined-acct', `open' are literals. | |
993 | - return _("ftp:proxy-auth-type must be one of: user, joined, joined-acct, open, proxy-user@host"); | |
994 | - return 0; | |
995 | + static const char *const valid_set[]={ | |
996 | + "user", "joined", "joined-acct", "open", "proxy-user@host", 0 | |
997 | + }; | |
998 | + return SetValidate(*s,valid_set,"ftp:proxy-auth-type"); | |
999 | } | |
1000 | ||
1001 | static const char *HttpProxyValidate(xstring_c *p) | |
1002 | @@ -130,6 +156,14 @@ const char *OrderValidate(xstring_c *s) | |
1003 | return 0; | |
1004 | } | |
1005 | ||
1006 | +static const char *SortByValidate(xstring_c *s) | |
1007 | +{ | |
1008 | + static const char * const valid_set[]={ | |
1009 | + "name", "name-desc", "size", "size-desc", "date", "date-desc", 0 | |
1010 | + }; | |
1011 | + return SetValidate(*s,valid_set,"mirror:order-by"); | |
1012 | +} | |
1013 | + | |
1014 | #if USE_SSL | |
1015 | static | |
1016 | const char *AuthArgValidate(xstring_c *s) | |
1017 | @@ -137,30 +171,21 @@ const char *AuthArgValidate(xstring_c *s) | |
1018 | for(char *i=s->get_non_const(); *i; i++) | |
1019 | *i=to_ascii_upper(*i); | |
1020 | ||
1021 | - if(strcmp(*s,"SSL") | |
1022 | - && strcmp(*s,"TLS") | |
1023 | - && strcmp(*s,"TLS-P") | |
1024 | - && strcmp(*s,"TLS-C")) | |
1025 | - return _("ftp:ssl-auth must be one of: SSL, TLS, TLS-P, TLS-C"); | |
1026 | - | |
1027 | - return 0; | |
1028 | + const char *const valid_set[]={ | |
1029 | + "SSL", "TLS", "TLS-P", "TLS-C", 0 | |
1030 | + }; | |
1031 | + return SetValidate(*s,valid_set,"ftp:ssl-auth"); | |
1032 | } | |
1033 | static | |
1034 | const char *ProtValidate(xstring_c *s) | |
1035 | { | |
1036 | - if(!**s) | |
1037 | - return 0; | |
1038 | - | |
1039 | for(char *i=s->get_non_const(); *i; i++) | |
1040 | *i=to_ascii_upper(*i); | |
1041 | ||
1042 | - if(strcmp(*s,"P") | |
1043 | - && strcmp(*s,"C") | |
1044 | - && strcmp(*s,"S") | |
1045 | - && strcmp(*s,"E")) | |
1046 | - return _("must be one of: C, S, E, P, or empty"); | |
1047 | - | |
1048 | - return 0; | |
1049 | + const char *const valid_set[]={ | |
1050 | + "C", "S", "E", "P", "", 0 | |
1051 | + }; | |
1052 | + return SetValidate(*s,valid_set,"ftps:initial-prot"); | |
1053 | } | |
1054 | #endif | |
1055 | ||
1056 | @@ -300,6 +325,7 @@ static ResType lftp_vars[] = { | |
1057 | {"net:connection-limit", "0", ResMgr::UNumberValidate,0}, | |
1058 | {"net:connection-takeover", "yes", ResMgr::BoolValidate,0}, | |
1059 | ||
1060 | + {"mirror:sort-by", "name", SortByValidate,ResMgr::NoClosure}, | |
1061 | {"mirror:order", "*.sfv *.sig *.md5* *.sum * */", 0,ResMgr::NoClosure}, | |
1062 | {"mirror:parallel-directories", "yes", ResMgr::BoolValidate,ResMgr::NoClosure}, | |
1063 | {"mirror:parallel-transfer-count", "1",ResMgr::UNumberValidate,ResMgr::NoClosure}, | |
1064 | diff --git a/src/xmap.cc b/src/xmap.cc | |
1065 | index 4bc9a86..2276bce 100644 | |
1066 | --- a/src/xmap.cc | |
1067 | +++ b/src/xmap.cc | |
1068 | @@ -136,6 +136,7 @@ void _xmap::_remove(entry **ep) | |
1069 | if(!ep || !*ep) | |
1070 | return; | |
1071 | entry *e=*ep; | |
1072 | + e->key.unset(); | |
1073 | *ep=e->next; | |
1074 | xfree(e); | |
1075 | entry_count--; | |
1076 | diff --git a/src/xstring.h b/src/xstring.h | |
1077 | index 264baf6..4a284ee 100644 | |
1078 | --- a/src/xstring.h | |
1079 | +++ b/src/xstring.h | |
1080 | @@ -222,7 +222,8 @@ public: | |
1081 | static xstring& cat(const char *first,...) ATTRIBUTE_SENTINEL; | |
1082 | static xstring& join(const char *sep,int n,...); | |
1083 | ||
1084 | - void truncate(size_t n=0); | |
1085 | + void truncate() { set_length(0); } | |
1086 | + void truncate(size_t n); | |
1087 | void truncate_at(char c); | |
1088 | /* set_length can be used to extend the string, e.g. after modification | |
1089 | with get_space+get_non_const. */ |