1 /*
2 prot_nntp.* - nntp protocol handler
3 Copyright (C) 1999-2004 Matthew Mueller <donut AT dakotacom.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include "prot_nntp.h"
24 #include <list>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28
29 #include "misc.h"
30 #include "path.h"
31 #include "termstuff.h"
32 #include "strreps.h"
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <time.h>
37 #include <unistd.h>
38 #include "log.h"
39 #include "file.h"
40 #include "nget.h"
41 #include "status.h"
42 #include "strtoker.h"
43 #include "par.h"
44 #include "decode.h"
45
46 extern SockPool sockpool;
47
48
printCommEx(const baseEx & e,const c_server::ptr & s,int redone,const nget_options & options)49 static void printCommEx(const baseEx &e, const c_server::ptr &s, int redone, const nget_options &options) {
50 if (e.isfatal())
51 print_ex_with_message(e, "fatal error, won't try %s again", s->alias.c_str());
52 else if (redone+1 < options.maxretry)
53 print_ex_with_message(e, "error, will try %s again", s->alias.c_str());
54 else //if this is the last retry, don't say that we will try it again.
55 print_ex_with_message(e, "error, retries exhausted, won't try %s again", s->alias.c_str());
56 }
57
putline(int echo,const char * str,...)58 int c_prot_nntp::putline(int echo,const char * str,...){
59 va_list ap;
60 va_start(ap,str);
61 int i=connection->doputline(echo,str,ap);
62 va_end(ap);
63 return i;
64 }
stdputline(int echo,const char * str,...)65 int c_prot_nntp::stdputline(int echo,const char * str,...){
66 va_list ap;
67 int i;
68 va_start(ap,str);
69 connection->doputline(echo,str,ap);
70 va_end(ap);
71 i=getreply(echo);
72 if (i==450 || i==480) {
73 nntp_auth();
74 va_start(ap,str);
75 connection->doputline(echo,str,ap);
76 va_end(ap);
77 i=getreply(echo);
78 }
79 return i;
80 }
81
chkreply(int reply) const82 int c_prot_nntp::chkreply(int reply) const {
83 // int i=getreply(echo);
84 if (reply==400) {
85 // 400 response is used on connection for "Service temporarily unavailable" or during a session if the server has to terminate the connection for some reason.
86 connection->close(1);
87 throw ProtocolExError(Ex_INIT,"server says byebye: %s", cbuf);
88 }
89 if (reply/100!=2)
90 throw ProtocolExFatal(Ex_INIT,"bad reply %i: %s",reply,cbuf);
91 return reply;
92 }
93
chkreply_setok(int reply)94 int c_prot_nntp::chkreply_setok(int reply){
95 //only set the server_ok flag if the command had a "normal" error status (like group not found, article expired, etc). If the command sequence completes successfully, then the server_ok will be set before releasing the ConnectionHolder.
96 if (reply/100==4 && reply!=400)
97 connection->server_ok = true;
98 return chkreply(reply);
99 }
100
getline(int echo)101 int c_prot_nntp::getline(int echo){
102 int r = connection->getline(echo);
103 cbuf = connection->sock.rbufp();
104 return r;
105 }
106
getreply(int echo)107 int c_prot_nntp::getreply(int echo){
108 int code;
109 if ((code=getline(echo))>=0)
110 code=atoi(cbuf);
111 return code;
112 }
113
114
115 class Progress {
116 public:
117 time_t lasttime, starttime, curt;
needupdate(void)118 bool needupdate(void) {
119 time(&curt);
120 return !quiet && curt>lasttime;
121 };
Progress(void)122 Progress(void) {
123 lasttime = 0;
124 starttime = time(NULL);
125 };
126 };
127
128
129 class DumbProgress : public Progress {
130 public:
print_retrieving(const char * what,ulong done,ulong bytes)131 void print_retrieving(const char *what, ulong done, ulong bytes){
132 time(&lasttime);
133 time_t dtime=lasttime-starttime;
134 long Bps=(dtime>0)?bytes/dtime:0;
135 if (!quiet) clear_line_and_return();
136 printf("Retrieving %s: %lu %liB/s %s",what,done,Bps,durationstr(dtime).c_str());
137
138 fflush(stdout);//@@@@
139 }
140 };
141
nntp_dogetgrouplist(void)142 void c_prot_nntp::nntp_dogetgrouplist(void){
143 ulong bytes=0, done=0;
144 DumbProgress progress;
145 while (1) {
146 if (progress.needupdate())
147 progress.print_retrieving("group list", done, bytes);
148 bytes+=getline(debug>=DEBUG_ALL);
149 if (cbuf[0]=='.' && cbuf[1]=='\0')break;
150 char * p = strpbrk(cbuf, " \t");
151 if (p)
152 *p = '\0';
153 //####do something with last/first/postingallowed info?
154
155 glist->addgroup(connection->server->serverid, cbuf);
156 done++;
157 }
158 if(quiet<2){
159 progress.print_retrieving("group list", done, bytes);
160 printf("\n");
161 }
162 }
nntp_dogrouplist(void)163 void c_prot_nntp::nntp_dogrouplist(void){
164 c_nntp_grouplist_server_info::ptr servinfo = glist->getserverinfo(connection->server->serverid);
165 string newdate;
166 int r=stdputline(quiet<2,"DATE");
167 if (r==111)
168 newdate = cbuf+4;
169 else {
170 char tbuf[40];
171 time_t t = time(NULL);
172 tconv(tbuf, 40, &t, "%Y%m%d%H%M%S", 0);
173 printf("bad DATE reply '%s', using local date %s\n", cbuf, tbuf);
174 newdate = tbuf;
175 }
176
177 if (!servinfo->date.empty()) {
178 int dsepn = servinfo->date.size()-6;
179 chkreply(stdputline(quiet<2,"NEWGROUPS %s %s GMT",servinfo->date.substr(0,dsepn).c_str(), servinfo->date.substr(dsepn,6).c_str()));
180 }
181 if (servinfo->date.empty()) {
182 chkreply(stdputline(quiet<2,"LIST"));
183 }
184
185 nntp_dogetgrouplist();
186 servinfo->date = newdate;
187 }
188
nntp_dogrouplist(const char * wildmat)189 void c_prot_nntp::nntp_dogrouplist(const char *wildmat){
190 chkreply(stdputline(quiet<2,"LIST ACTIVE %s", wildmat));
191 nntp_dogetgrouplist();
192 }
193
nntp_dogroupdescriptions(const char * wildmat)194 void c_prot_nntp::nntp_dogroupdescriptions(const char *wildmat){
195 ulong bytes=0, done=0;
196 int r;
197 if (wildmat)
198 r=stdputline(quiet<2,"LIST NEWSGROUPS %s", wildmat);
199 else
200 r=stdputline(quiet<2,"LIST NEWSGROUPS");
201 if (r/100!=2) {
202 // if server doesn't support LIST NEWSGROUPS, just ignore the error.
203 return;
204 }
205 DumbProgress progress;
206 while (1) {
207 if (progress.needupdate())
208 progress.print_retrieving("group descriptions", done, bytes);
209
210 bytes+=getline(debug>=DEBUG_ALL);
211 if (cbuf[0]=='.' && cbuf[1]=='\0')break;
212 char * desc = strpbrk(cbuf, " \t");
213 if (desc) {
214 *desc = '\0';
215 desc++;
216 desc += strspn(desc, " \t");
217 }
218
219 glist->addgroupdesc(connection->server->serverid, cbuf, desc ? desc : "");
220 done++;
221 }
222 if(quiet<2){
223 progress.print_retrieving("group descriptions", done, bytes);
224 printf("\n");
225 }
226 }
227
nntp_grouplist(int update,const nget_options & options)228 void c_prot_nntp::nntp_grouplist(int update, const nget_options &options){
229 if (!glist)
230 glist = new c_nntp_grouplist(ngcachehome+"newsgroups");
231 if (update) {
232 c_server::ptr s;
233 list<c_server::ptr> doservers;
234 c_server_priority_grouping *priogroup;
235 if (!(priogroup=nconfig.getpriogrouping("_grouplist")))
236 priogroup=nconfig.getpriogrouping("default");
237 nntp_simple_prioritize(priogroup, doservers);
238
239 int redone=0, succeeded=0, attempted=doservers.size();
240 while (!doservers.empty() && redone<options.maxretry) {
241 if (redone){
242 printf("nntp_grouplist: trying again. %i\n",redone);
243 if (options.retrydelay)
244 sleep(options.retrydelay);
245 }
246 list<c_server::ptr>::iterator dsi = doservers.begin();
247 list<c_server::ptr>::iterator ds_erase_i;
248 while (dsi != doservers.end()){
249 s=(*dsi);
250 assert(s);
251 PDEBUG(DEBUG_MED,"nntp_grouplist: serv(%s) %f>=%f",s->alias.c_str(),priogroup->getserverpriority(s),priogroup->defglevel);
252 try {
253 ConnectionHolder holder(&sockpool, &connection, s);
254 nntp_doopen();
255 nntp_dogrouplist();
256 nntp_dogroupdescriptions();//####make this a seperate option?
257 succeeded++;
258 connection->server_ok=true;
259 } catch (baseCommEx &e) {
260 printCommEx(e, s, redone, options);
261 if (!e.isfatal()) {
262 ++dsi;
263 continue;//don't remove server from list
264 }
265 }
266 ds_erase_i = dsi;
267 ++dsi;
268 doservers.erase(ds_erase_i);
269 }
270 redone++;
271 }
272 if (succeeded) {
273 set_grouplist_warn_status(attempted - succeeded);
274 set_grouplist_ok_status();
275 }else {
276 set_grouplist_error_status();
277 printf("no servers queried successfully\n");
278 }
279 }
280 }
281
nntp_xgrouplist(const t_xpat_list & patinfos,const nget_options & options)282 void c_prot_nntp::nntp_xgrouplist(const t_xpat_list &patinfos, const nget_options &options){
283 glist = new c_nntp_grouplist();
284
285 c_server::ptr s;
286 list<c_server::ptr> doservers;
287 c_server_priority_grouping *priogroup;
288 if (!(priogroup=nconfig.getpriogrouping("_grouplist")))
289 priogroup=nconfig.getpriogrouping("default");
290 nntp_simple_prioritize(priogroup, doservers);
291
292 int redone=0, succeeded=0, attempted=doservers.size();
293 while (!doservers.empty() && redone<options.maxretry) {
294 if (redone){
295 printf("nntp_xgrouplist: trying again. %i\n",redone);
296 if (options.retrydelay)
297 sleep(options.retrydelay);
298 }
299 list<c_server::ptr>::iterator dsi = doservers.begin();
300 list<c_server::ptr>::iterator ds_erase_i;
301 while (dsi != doservers.end()){
302 s=(*dsi);
303 assert(s);
304 PDEBUG(DEBUG_MED,"nntp_xgrouplist: serv(%s) %f>=%f",s->alias.c_str(),priogroup->getserverpriority(s),priogroup->defglevel);
305 try {
306 ConnectionHolder holder(&sockpool, &connection, s);
307 nntp_doopen();
308 for (t_xpat_list::const_iterator i = patinfos.begin(); i != patinfos.end(); ++i) {
309 nntp_dogrouplist((*i)->wildmat.c_str());
310 nntp_dogroupdescriptions((*i)->wildmat.c_str());//####make this a seperate option? //######handle (skip?) servers that ignore wildmat option to LIST NEWSGROUPS somehow? .. not that its really possible to tell before hand whether the server will ignore it.
311 }
312 succeeded++;
313 connection->server_ok=true;
314 } catch (baseCommEx &e) {
315 printCommEx(e, s, redone, options);
316 if (!e.isfatal()) {
317 ++dsi;
318 continue;//don't remove server from list
319 }
320 }
321 ds_erase_i = dsi;
322 ++dsi;
323 doservers.erase(ds_erase_i);
324 }
325 redone++;
326 }
327 if (succeeded) {
328 set_grouplist_warn_status(attempted - succeeded);
329 set_grouplist_ok_status();
330 }else {
331 set_grouplist_error_status();
332 printf("no servers queried successfully\n");
333 }
334 }
335
nntp_grouplist_search(const t_grouplist_getinfo_list & getinfos,const nget_options & options)336 void c_prot_nntp::nntp_grouplist_search(const t_grouplist_getinfo_list &getinfos, const nget_options &options){
337 if (glist) {
338 glist->printinfos(getinfos);
339 //####should we glist=NULL; here?
340 } else {
341 nntp_grouplist_printinfos(getinfos);
342 }
343 }
344
nntp_grouplist_search(const t_grouplist_getinfo_list & getinfos,const t_xpat_list & patinfos,const nget_options & options)345 void c_prot_nntp::nntp_grouplist_search(const t_grouplist_getinfo_list &getinfos, const t_xpat_list &patinfos, const nget_options &options){
346 nntp_xgrouplist(patinfos, options);
347 glist->printinfos(getinfos);
348 //####should we glist=NULL; here?
349 }
350
351
352 class XoverProgress : public Progress {
353 public:
print_retrieving_headers(ulong low,ulong high,ulong done,ulong realtotal,ulong total,ulong bytes,int doneranges,int streamed,int totalranges)354 void print_retrieving_headers(ulong low,ulong high,ulong done,ulong realtotal,ulong total,ulong bytes,int doneranges,int streamed,int totalranges){
355 time(&lasttime);
356 time_t dtime=lasttime-starttime;
357 long Bps=(dtime>0)?bytes/dtime:0;
358 long Bph=(done>0)?bytes/done:3;//if no headers have been retrieved yet, set the bytes per header to 3 just to get some sort of timeleft display. (3=strlen(".\r\n"))
359 if (!quiet) clear_line_and_return();
360 printf("Retrieving headers %lu-%lu : %lu/%lu/%lu %3li%% %liB/s %s",low,high,done,realtotal,total,(realtotal!=0)?((done+(total-realtotal))*100/total):0,Bps,durationstr(realtotal==done?dtime:(Bps>0)?((realtotal-done)*Bph)/(Bps):0).c_str());
361 if (totalranges>1)
362 printf(" (%i/%i/%i)",doneranges,doneranges+streamed,totalranges);
363 fflush(stdout);//@@@@
364 };
365 };
366 /*
367 2019 Re: question Katya Moon <MoonAngel@shadowrealm.com> Wed, 17 Nov 1999 09:42:53 -0600 <xMwyOI+pJ=mVLqMociXeHexHGW92@4ax.com> <3830CF31.D41E1511@tampabay.rr.com> 1145 9 Xref: rQdQ alt.chocobo:2019
368 2020 Re: well then! Katya Moon <MoonAngel@shadowrealm.com> Wed, 17 Nov 1999 09:44:37 -0600 <Fs0yOEVKaIamwKGBgE=82Fk21OcM@4ax.com> <3831B98A.72815A01@tampabay.rr.com> 1209 10 Xref: rQdQ alt.chocobo:2020
369 2021 free me from this hideous thing! Selah <sslanka@tampabay.rr.com> Wed, 17 Nov 1999 20:17:35 GMT <38330E93.C8AC2671@tampabay.rr.com> 1142 8 Xref: rQdQ alt.chocobo:2021
370 .
371 0=articlenum
372 1=subject
373 2=author
374 3=date
375 4=message id
376 5=references (aka in-reply-to) (used for threading)
377 6=bytes
378 7=lines
379 ... following are optional (and possibly different):
380 8=crossreferences
381
382 The sequence of fields must be in this order:
383 subject, author, date, message-id, references, byte count, and line count.
384 Other optional fields may follow line count. These fields are specified by
385 examining the response to the LIST OVERVIEW.FMT command. Where no data
386 exists, a null field must be provided
387 */
doxover(const c_group_info::ptr & group,c_nrange * r)388 void c_prot_nntp::doxover(const c_group_info::ptr &group, c_nrange *r){
389 if (r->empty())
390 return;
391 XoverProgress progress;
392 ulong lowest=r->rlist.begin()->second, highest=r->rlist.rbegin()->first;
393 ulong bytes=0, realnum=0, realtotal=r->get_total(), last;
394 ulong total=realtotal;
395 assert(connection);
396
397 t_rlist::iterator r_ri;
398 t_rlist::iterator w_ri=r->rlist.begin();
399 int streamed = 0, totalranges = r->num_ranges(), doneranges = 0;
400 for (r_ri=r->rlist.begin();r_ri!=r->rlist.end();++r_ri, ++doneranges){
401 if (progress.needupdate())
402 progress.print_retrieving_headers(lowest,highest,realnum,realtotal,total,bytes,doneranges,streamed,totalranges);
403 char *p;
404 char *t[10];
405 int i;
406 while (w_ri!=r->rlist.end() && streamed<=connection->server->maxstreaming) {
407 ulong low=(*w_ri).second, high=(*w_ri).first;
408 if (low==high)
409 putline(debug>=DEBUG_MED,"XOVER %lu",low);//save a few bytes
410 else
411 putline(debug>=DEBUG_MED,"XOVER %lu-%lu",low,high);
412 ++w_ri;
413 ++streamed;
414 }
415 ulong low=(*r_ri).second, high=(*r_ri).first;
416 last = low;
417 chkreply_setok(getreply(debug>=DEBUG_MED));
418 bytes+=strlen(cbuf)+2;//#### ugly.
419 --streamed;
420 unsigned long an=0;
421 c_nntp_header nh;
422 nh.group = group;
423 char * tp;
424 do {
425 bytes+=getline(debug>=DEBUG_ALL);
426 if (cbuf[0]=='.')break;
427 p=cbuf;
428 for(i=0;i<10;i++){
429 if((t[i]=goodstrtok(&p,'\t'))==NULL){
430 break;
431 }
432 // fields 0, 6, 7 must all be numeric
433 // otherwise restart
434 if (i==0 || i==6 || i==7){
435 tp=t[i];
436 while (*tp){
437 if (!isdigit(*tp) && !isspace(*tp))
438 break;
439 tp++;
440 }
441 // did the test finish, and/or was it a blank field?
442 if (*tp && tp!=t[i]){
443 // no - get out and read the next line
444 // Is this how we want to handle it? SMW
445 printf("error retrieving xover (%i non-numeric)\n",i);
446 break;
447 }
448 }
449 }
450 if (i<=1 && streamed) {
451 if (atoul(cbuf)==224){//some servers (DNEWS) will drop data while doing xover streaming, but seeminly only the ends of the requests. Then you get a 224 data follows message for the next range without getting the rest of the current range or the "." line. Retrying starting from the current range would be better, but this is easier ;)
452 connection->server->maxstreaming=0;
453 connection->close(1);
454 set_xover_warn_status();
455 throw ProtocolExError(Ex_INIT, "XOVER streaming error: recieved start of next range without end of current. Setting maxstreaming to 0. (You may wish to decrease or disable maxstreaming for this server in your ngetrc.)");
456 }
457 }
458 if (i>7){
459 // c=new c_nntp_cache_item(atol(t[0]), decode_textdate(t[3]), atol(t[6]), atol(t[7]),t[1],t[2]);
460 //gcache->additem(c);
461 an=atoul(t[0]);
462 nh.set(t[1],t[2],an,decode_textdate(t[3]),atoul(t[6]),atoul(t[7]),t[4],t[5]);
463 nh.serverid=connection->server->serverid;
464 //gcache->additem(an, decode_textdate(t[3]), atol(t[6]), atol(t[7]),t[1],t[2]);
465 gcache->additem(&nh);
466 //delete nh;
467 realnum++;
468 if (an<low || an>high || an<last) {
469 printf("weird: articlenum %lu not in range %lu-%lu (last=%lu)\n",an,low,high,last);
470 set_xover_warn_status();
471 } else {
472 #ifndef NDEBUG
473 ulong ort=realtotal;
474 #endif
475 realtotal -= an - last;
476 assert(ort>=realtotal);
477 last = an + 1;
478 }
479 if (progress.needupdate())
480 progress.print_retrieving_headers(lowest,highest,realnum,realtotal,total,bytes,doneranges,streamed,totalranges);
481 }else{
482 set_xover_warn_status();
483 printf("error retrieving xover (%i<=7): ",i);
484 for (int j=0;j<=i;j++)
485 if (t[j])
486 printf("%i:%s ",j,t[j]);
487 if (p)
488 printf("*:%s",p);
489 printf("\n");
490 }
491 }while(1);
492 #ifndef NDEBUG
493 ulong ort=realtotal;
494 #endif
495 realtotal -= high - last + 1;
496 assert(ort>=realtotal);
497 }
498 if(quiet<2 /*&& an*/){
499 progress.print_retrieving_headers(lowest,highest,realnum,realtotal,total,bytes,doneranges,streamed,totalranges);
500 printf("\n");
501 }
502 }
doxover(const c_group_info::ptr & group,ulong low,ulong high)503 void c_prot_nntp::doxover(const c_group_info::ptr &group, ulong low, ulong high){
504 c_nrange r;
505 r.insert(low, high);
506 doxover(group, &r);
507 }
508
509 class ListgroupProgress : public Progress {
510 public:
print_retrieving_article_list(ulong low,ulong high,ulong done,ulong total,ulong bytes,bool finished=false)511 void print_retrieving_article_list(ulong low, ulong high, ulong done, ulong total, ulong bytes, bool finished=false){
512 time(&lasttime);
513 time_t dtime=lasttime-starttime;
514 long Bps=(dtime>0)?bytes/dtime:0;
515 long Bph=(done>0)?bytes/done:3;//if no headers have been retrieved yet, set the bytes per header to 3 just to get some sort of timeleft display. (3=strlen(".\r\n"))
516 if (!quiet) clear_line_and_return();
517 printf("Retrieving article list %lu-%lu : %lu/%lu %3li%% %liB/s %s",low,high,done,total,(finished || total==0)?100:done*100/total,Bps,durationstr(finished?dtime:(Bps>0)?((total-done)*Bph)/(Bps):0).c_str());
518
519 fflush(stdout);//@@@@
520 }
521 };
dolistgroup(c_nrange & existing,ulong lowest,ulong highest,ulong total)522 void c_prot_nntp::dolistgroup(c_nrange &existing, ulong lowest, ulong highest, ulong total) {
523 ListgroupProgress progress;
524 chkreply(stdputline(debug>=DEBUG_MED,"LISTGROUP"));
525 ulong bytes=0, count=0, an;
526 char *eptr;
527 do {
528 if (progress.needupdate())
529 progress.print_retrieving_article_list(lowest,highest,count,total,bytes);
530 bytes+=getline(debug>=DEBUG_ALL);
531 if (cbuf[0]=='.')break;
532 an = strtoul(cbuf, &eptr, 10);
533 if (*cbuf=='\0' || *eptr!='\0') {
534 printf("error retrieving article number\n");
535 continue;
536 }
537 existing.insert(an);
538 count++;
539 }while (1);
540 if(quiet<2){
541 progress.print_retrieving_article_list(lowest,highest,count,total,bytes,true);
542 printf("\n");
543 }
544 }
545
nntp_dogroup(const c_group_info::ptr & group,ulong & num,ulong & low,ulong & high)546 void c_prot_nntp::nntp_dogroup(const c_group_info::ptr &group, ulong &num, ulong &low, ulong &high) {
547 assert(connection);
548 connection->curgroup=NULL; //unset curgroup, in case the group we asked for does not exist, some servers will keep us in the old group, whereas others will put us into the no group selected state.
549 int reply = stdputline(quiet<2,"GROUP %s",group->group.c_str());
550 if (reply/100==4) // if group doesn't exist, set ok flag. Otherwise let XOVER/ARTICLE reply set it. (If we always set it here, failure of xover/article would never result in penalization. But if we only set it after xover/article, then a host could be incorrectly penalized just because it didn't have the group (eg, if maxconnections=1 so it closed connection before any other commands could succeed and setok))
551 chkreply_setok(reply);
552 else
553 chkreply(reply);
554 connection->curgroup=group;
555
556 char *p;
557 p=strchr(cbuf,' ')+1;
558 num=atoul(p);
559 p=strchr(p,' ')+1;
560 low=atoul(p);
561 p=strchr(p,' ')+1;
562 high=atoul(p);
563 //printf("%i, %i, %i\n",num,low,high);
564 }
565
nntp_dogroup(const c_group_info::ptr & group,bool getheaders,int forcefullxover)566 void c_prot_nntp::nntp_dogroup(const c_group_info::ptr &group, bool getheaders, int forcefullxover){
567 ulong num,low,high;
568 if (connection->curgroup!=group || getheaders){
569 nntp_dogroup(group, num,low,high);
570 }
571
572 if (getheaders){
573 assert(gcache);
574 const int fullxover = forcefullxover==-1 ? connection->server->fullxover : forcefullxover;
575
576 c_nntp_server_info* servinfo=gcache->getserverinfo(connection->server->serverid);
577 assert(servinfo);
578 //shouldn't do fullxover2 on first update of group or if cached headers are ALL older than available headers
579 if (servinfo->high!=0 && servinfo->high>=low && fullxover==2){
580 c_nrange existing;
581 dolistgroup(existing, low, high, num);
582
583 c_nrange nonexistant;
584 nonexistant.invert(existing);
585 gcache->flush(servinfo,nonexistant,midinfo);
586 servinfo->low=existing.empty()?low:existing.low();//####this ok?
587
588 c_nrange r(existing);
589 gcache->getxrange(servinfo,&r);//remove the article numbers we already have, leaving only the ones we still need to get.
590
591 doxover(group, &r);
592 }else{
593 if (low>servinfo->low)
594 gcache->flushlow(servinfo,low,midinfo);
595 if (fullxover){
596 c_nrange r;
597 gcache->getxrange(servinfo,low,high,&r);
598 doxover(group, &r);
599 }else{
600 c_nrange r;
601
602 if (servinfo->high==0)
603 r.insert(low, high);
604 else {
605 if (servinfo->high<high)
606 r.insert(servinfo->high+1, high);
607 if (servinfo->low>low)
608 r.insert(low, servinfo->low-1);
609 }
610 doxover(group, &r);
611 }
612 }
613 }
614 };
615
nntp_simple_prioritize(c_server_priority_grouping * priogroup,list<c_server::ptr> & doservers)616 void c_prot_nntp::nntp_simple_prioritize(c_server_priority_grouping *priogroup, list<c_server::ptr> &doservers){
617 if (force_host){
618 doservers.push_front(force_host);
619 } else {
620 t_server_list::iterator sli = nconfig.serv.begin();
621 for (;sli!=nconfig.serv.end();++sli){
622 c_server::ptr &s=(*sli).second;
623 assert(s);
624 if (priogroup->getserverpriority(s) >= priogroup->defglevel) {
625 if (sockpool.is_connected(s)) //put current connected hosts at start of list
626 doservers.push_front(s);
627 else
628 doservers.push_back(s);
629 }
630 }
631 }
632 }
633
634
doxpat(c_nrange & r,c_xpat::ptr xpat,ulong total,ulong lowest,ulong highest)635 void c_prot_nntp::doxpat(c_nrange &r, c_xpat::ptr xpat, ulong total, ulong lowest, ulong highest) {
636 assert(gcache);
637 assert(connection);
638
639 chkreply_setok(stdputline(debug>=DEBUG_MED,"XPAT %s %lu-%lu %s", xpat->field.c_str(), lowest, highest, xpat->wildmat.c_str()));
640
641 ListgroupProgress progress;
642 ulong bytes=0, realnum=0;
643 ulong an;
644 char *eptr;
645
646 do {
647 if (progress.needupdate())
648 progress.print_retrieving_article_list(lowest,highest,realnum,total,bytes,false);
649 bytes+=getline(debug>=DEBUG_ALL);
650 if (cbuf[0]=='.')break;
651 an = strtoul(cbuf, &eptr, 10);
652 if (*cbuf=='\0' || !(*eptr==' ' || *eptr=='\t')) {
653 printf("error retrieving article number\n");
654 continue;
655 }
656 r.insert(an);
657 realnum++;
658 if (progress.needupdate())
659 progress.print_retrieving_article_list(lowest,highest,realnum,total,bytes,false);
660
661 }while (1);
662 if(quiet<2 /*&& an*/){
663 progress.print_retrieving_article_list(lowest,highest,realnum,total,bytes,true);
664 printf("\n");
665 }
666 }
667
nntp_xgroup(const c_group_info::ptr & group,const t_xpat_list & patinfos,const nget_options & options)668 void c_prot_nntp::nntp_xgroup(const c_group_info::ptr &group, const t_xpat_list &patinfos, const nget_options &options) {
669 c_server::ptr s;
670 list<c_server::ptr> doservers;
671 nntp_simple_prioritize(group->priogrouping, doservers);
672
673 int redone=0, succeeded=0, attempted=doservers.size();
674 while (!doservers.empty() && redone<options.maxretry) {
675 if (redone){
676 printf("nntp_xgroup: trying again. %i\n",redone);
677 if (options.retrydelay)
678 sleep(options.retrydelay);
679 }
680 list<c_server::ptr>::iterator dsi = doservers.begin();
681 list<c_server::ptr>::iterator ds_erase_i;
682 while (dsi != doservers.end()){
683 s=(*dsi);
684 assert(s);
685 PDEBUG(DEBUG_MED,"nntp_xgroup: serv(%s) %f>=%f",s->alias.c_str(),group->priogrouping->getserverpriority(s),group->priogrouping->defglevel);
686 try {
687 ConnectionHolder holder(&sockpool, &connection, s);
688 nntp_doopen();
689 ulong num,low,high;
690 nntp_dogroup(group, num,low,high);
691 c_nrange r;
692 for (t_xpat_list::const_iterator i = patinfos.begin(); i != patinfos.end(); ++i)
693 doxpat(r, *i, num, low, high);
694 doxover(group, &r);
695 succeeded++;
696 connection->server_ok=true;
697 } catch (baseCommEx &e) {
698 printCommEx(e, s, redone, options);
699 if (!e.isfatal()) {
700 ++dsi;
701 continue;//don't remove server from list
702 }
703 }
704 ds_erase_i = dsi;
705 ++dsi;
706 doservers.erase(ds_erase_i);
707 }
708 redone++;
709 }
710 if (succeeded) {
711 set_group_warn_status(attempted - succeeded);
712 set_group_ok_status();
713 }else {
714 set_group_error_status();
715 printf("no servers queried successfully\n");
716 }
717
718 gcache_ismultiserver = gcache->ismultiserver();
719 }
720
nntp_group(const c_group_info::ptr & ngroup,bool getheaders,const nget_options & options)721 void c_prot_nntp::nntp_group(const c_group_info::ptr &ngroup, bool getheaders, const nget_options &options){
722 if (group == ngroup && gcache && !getheaders)
723 return; // group is already selected, don't waste time reloading it
724
725 assert(ngroup);
726 group=ngroup;
727 // if (gcache) delete gcache;
728 cleanupcache();
729
730 midinfo=new meta_mid_info(ngcachehome, ngroup);
731 //gcache=new c_nntp_cache(nghome,group->group + ",cache");
732 gcache=new c_nntp_cache(ngcachehome, group, midinfo);
733 if (getheaders){
734 c_server::ptr s;
735 list<c_server::ptr> doservers;
736 nntp_simple_prioritize(group->priogrouping, doservers);
737
738 int redone=0, succeeded=0, attempted=doservers.size();
739 while (!doservers.empty() && redone<options.maxretry) {
740 if (redone){
741 printf("nntp_group: trying again. %i\n",redone);
742 if (options.retrydelay)
743 sleep(options.retrydelay);
744 }
745 list<c_server::ptr>::iterator dsi = doservers.begin();
746 list<c_server::ptr>::iterator ds_erase_i;
747 while (dsi != doservers.end()){
748 s=(*dsi);
749 assert(s);
750 PDEBUG(DEBUG_MED,"nntp_group: serv(%s) %f>=%f",s->alias.c_str(),group->priogrouping->getserverpriority(s),group->priogrouping->defglevel);
751 try {
752 ConnectionHolder holder(&sockpool, &connection, s);
753 nntp_doopen();
754 nntp_dogroup(ngroup, getheaders, options.fullxover);
755 succeeded++;
756 connection->server_ok=true;
757 } catch (baseCommEx &e) {
758 printCommEx(e, s, redone, options);
759 if (!e.isfatal()) {
760 ++dsi;
761 continue;//don't remove server from list
762 }
763 }
764 ds_erase_i = dsi;
765 ++dsi;
766 doservers.erase(ds_erase_i);
767 }
768 redone++;
769 }
770 if (succeeded) {
771 set_group_warn_status(attempted - succeeded);
772 set_group_ok_status();
773 }else {
774 set_group_error_status();
775 printf("no servers queried successfully\n");
776 }
777 }
778
779 gcache_ismultiserver = gcache->ismultiserver();
780 }
781
print_retrieving_articles(time_t curtime,quinfo * tot)782 inline void arinfo::print_retrieving_articles(time_t curtime, quinfo*tot){
783 dtime=curtime-starttime;
784 Bps=(dtime>0)?bytesdone/dtime:0;
785 if (!quiet) clear_line_and_return();
786 if (tot && tot->doarticle_show_multi!=NO_SHOW_MULTI)
787 printf("%s ",server_name);
788 printf("%lu (%i/%i): %li/%liL %li/%liB %3li%% %liB/s %s",
789 anum,partnum,partreq,linesdone,linestot,bytesdone,bytestot,
790 (linestot!=0)?(linesdone*100/linestot):0,Bps,
791 durationstr((linesdone>=linestot)?dtime:((Bps>0)?(bytestot-bytesdone)/(Bps):-1)).c_str());
792 if (tot)
793 printf(" %li/%li %s",tot->filesdone,tot->filestot,
794 //(tot->bytesdone>=tot->bytestot)?curtime-tot->starttime:((Bps>0)?(tot->bytestot-tot->bytesdone)/(Bps):-1));
795 // (linesdone>=linestot)?curtime-tot->starttime:((Bps>0)?(tot->bytestot-tot->bytesdone)/(Bps):-1));
796 durationstr((linesdone>=linestot)?curtime-tot->starttime:((Bps>0)?(tot->bytesleft-bytesdone)/(Bps):-1)).c_str());
797 fflush(stdout);
798 }
799
800 //inline void c_prot_nntp::nntp_print_retrieving_articles(long nnn, long tot,long done,long btot,long bbb,unsigned long obtot,unsigned long obdone,long ototf,long odonef,time_t tstarttime){
801 // time_t dtime=lasttime-starttime;
802 // long Bps=(dtime>0)?bbb/dtime:0;
803 // printf("\rRetrieving article %li: %li/%liL %li/%liB %3li%% %liB/s %lis %li/%li %lis",nnn,done,tot,bbb,btot,(tot!=0)?(done*100/tot):0,Bps,(done>=tot)?dtime:((Bps>0)?(btot-bbb)/(Bps):-1),odonef,ototf,(obdone>=obtot)?lasttime-tstarttime:((Bps>0)?(botot-obdone)/(Bps):-1));
804 // fflush(stdout);//@@@@
805 //}
806
nntp_doarticle_prioritize(c_nntp_part * part,t_nntp_server_articles_prioritized & sap,bool docurservmult)807 int c_prot_nntp::nntp_doarticle_prioritize(c_nntp_part *part,t_nntp_server_articles_prioritized &sap,bool docurservmult){
808 t_nntp_server_articles::iterator sai;
809 c_nntp_server_article *sa=NULL;
810 float prio;
811 for (sai = part->articles.begin(); sai != part->articles.end(); ++sai){
812 sa=(*sai);
813 assert(sa);
814 for (t_server_list_range servers = nconfig.getservers(sa->serverid); servers.first!=servers.second; ++servers.first) {
815 const c_server::ptr &s = servers.first->second;
816 if (force_host && s!=force_host)
817 continue;
818 prio=sa->group->priogrouping->getserverpriority(s);
819 if (docurservmult){
820 if (sockpool.is_connected(s))
821 prio*=nconfig.curservmult;
822 }
823 PDEBUG(DEBUG_MED,"prioritizing server %s(%lu) article %lu prio %f",s->alias.c_str(),sa->serverid,sa->articlenum,prio);
824 sap.insert(t_nntp_server_articles_prioritized::value_type(prio,t_real_server_article(sa,s)));
825 }
826 }
827
828 if (docurservmult && !sap.empty()) {
829 int connected=0, nonconnected=0;
830 t_nntp_server_articles_prioritized::iterator i;
831 pair<t_nntp_server_articles_prioritized::iterator,t_nntp_server_articles_prioritized::iterator> firstrange = sap.equal_range(sap.rend()->first);
832 for (i=firstrange.first; i!=firstrange.second; ++i)
833 if (sockpool.is_connected(i->second.second))
834 ++connected;
835 else
836 ++nonconnected;
837
838 if (connected && nonconnected) { //if both connected and nonconnected servers have the (same) highest priority, reprioritize the connected ones a bit higher to avoid excessive reconnecting.
839 t_nntp_server_articles_prioritized::iterator ci;
840 for (i=firstrange.first; i!=firstrange.second;){
841 ci = i;
842 ++i;
843 if (sockpool.is_connected(ci->second.second)) {
844 prio=(*ci).first;
845 sa=(*ci).second.first;
846 c_server::ptr s = (*ci).second.second;
847 sap.erase(ci);
848 prio*=1.001;
849 sap.insert(t_nntp_server_articles_prioritized::value_type(prio,t_real_server_article(sa,s)));
850 PDEBUG(DEBUG_MED,"server %s(%lu) article %lu reprioritized %f",s->alias.c_str(),sa->serverid,sa->articlenum,prio);
851 }
852 }
853 }
854
855 }
856 return 0;
857 }
858
859
nntp_dowritelite_article(c_file & fw,c_nntp_part * part,char * fn)860 int c_prot_nntp::nntp_dowritelite_article(c_file &fw,c_nntp_part *part,char *fn){
861 fw.putf("0\n%s\n%s\n",group?group->group.c_str():"",fn);
862
863 c_server::ptr whost;
864 c_nntp_server_article *sa=NULL;
865 t_nntp_server_articles_prioritized sap;
866 t_nntp_server_articles_prioritized::iterator sapi;
867 nntp_doarticle_prioritize(part,sap,false);
868 fw.putf("%lu\n",(ulong)sap.size());
869 for (sapi = sap.begin(); sapi != sap.end(); ++sapi){
870 sa=(*sapi).second.first;
871 whost=(*sapi).second.second;
872 fw.putf("%s\t%s\t%s\n",whost->addr.c_str(),whost->user.c_str(),whost->pass.c_str());
873 if (group)
874 fw.putf("%lu\n",sa->articlenum);
875 else
876 fw.putf("%s\n",part->messageid.c_str());
877 fw.putf("%lu\n%lu\n",sa->bytes,sa->lines);
878 }
879 return 0;
880 }
881
nntp_dogetarticle(arinfo * ari,quinfo * toti,list<string> & buf)882 void c_prot_nntp::nntp_dogetarticle(arinfo*ari,quinfo*toti,list<string> &buf){
883 int header=1;
884 time_t curt,lastt=0;
885 char *lp;
886 time(&ari->starttime);
887 curt=starttime;
888 long glr;
889 do {
890 glr=getline(debug>=DEBUG_ALL);
891 if (cbuf[0]=='.'){
892 if(cbuf[1]==0)
893 break;
894 lp=cbuf+1;
895 }else
896 lp=cbuf;
897 ari->bytesdone+=glr;
898 ari->linesdone++;
899 if (header && lp[0]==0){
900 // printf("\ntoasted header statssssssss\n");
901 header=0;
902 ari->linesdone=0;
903 time(&ari->starttime);//bytes=0;
904 }
905 time(&curt);
906 if (!quiet && curt>lastt){
907 // nntp_print_retrieving_articles(num,ltotal,lines,btotal,bytes);
908 ari->print_retrieving_articles(curt,toti);
909 lastt=curt;
910 }
911 buf.push_back(lp);
912 }while(1);
913 if (quiet<2){
914 //nntp_print_retrieving_articles(num,ltotal,lines,btotal,bytes);
915 ari->print_retrieving_articles(curt,toti);
916 printf("\n");
917 }
918
919 //some servers report # of bytes a bit off of what we expect.
920 if (!((ari->bytesdone <= ari->bytestot+3 && ari->bytesdone >= ari->bytestot-3) ||
921 //some servers also report # of bytes counted with LF endings, then send with CRLF
922 (ari->bytesdone <= ari->bytestot+3+ari->linesdone || ari->bytesdone >= ari->bytestot-3+ari->linesdone)) ||
923 ari->linesdone!=ari->linestot){
924 printf("doarticle %lu: %lu!=%lu || %lu!=%lu\n",ari->anum,ari->bytesdone,ari->bytestot,ari->linesdone,ari->linestot);
925 }
926 c_server::ptr host = connection->server;
927 if (!(ari->linesdone>=ari->linestot+host->lineleniencelow && ari->linesdone<=ari->linestot+host->lineleniencehigh)){
928 printf("unequal line count %lu should equal %lu",ari->linesdone,ari->linestot);
929 if (host->lineleniencelow||host->lineleniencehigh){
930 if (host->lineleniencelow==-host->lineleniencehigh)
931 printf("(+/- %i)",host->lineleniencehigh);
932 else
933 printf("(%+i/%+i)",host->lineleniencelow,host->lineleniencehigh);
934 }
935 printf("\n");
936 if (nconfig.unequal_line_error)
937 throw TransportExFatal(Ex_INIT, "unequal line count and unequal_line_error is true");
938 set_unequal_line_count_warn_status();
939 }
940 }
941
nntp_doarticle(c_nntp_part * part,arinfo * ari,quinfo * toti,char * fn,const nget_options & options)942 int c_prot_nntp::nntp_doarticle(c_nntp_part *part,arinfo*ari,quinfo*toti,char *fn, const nget_options &options){
943 c_nntp_server_article *sa=NULL;
944 t_nntp_server_articles_prioritized sap;
945 t_nntp_server_articles_prioritized::iterator sapi;
946 t_nntp_server_articles_prioritized::iterator sap_erase_i;
947 nntp_doarticle_prioritize(part,sap,true);
948 int redone=0, attempted=sap.size();
949 while (!sap.empty() && redone<options.maxretry) {
950 if (redone){
951 printf("nntp_doarticle: trying again. %i\n",redone);
952 if (options.retrydelay)
953 sleep(options.retrydelay);
954 }
955 for (sapi = sap.begin(); sapi != sap.end();){
956 sa=(*sapi).second.first;
957 const c_server::ptr &s = (*sapi).second.second;
958 assert(sa);
959 ari->partnum=part->partnum;
960 ari->anum=sa->articlenum;
961 ari->bytestot=sa->bytes;
962 ari->linestot=sa->lines;
963 ari->linesdone=0;
964 ari->bytesdone=0;
965 PDEBUG(DEBUG_MED,"trying server %s(%lu) article %lu",s->alias.c_str(),sa->serverid,sa->articlenum);
966 list<string> buf;//use a list of strings instead of char *. Easier and it cleans up after itself too.
967 try {
968 ConnectionHolder holder(&sockpool, &connection, s);
969 nntp_doopen();
970 if (toti->doarticle_show_multi==SHOW_MULTI_SHORT)
971 ari->server_name=connection->server->shortname.c_str();
972 else if (toti->doarticle_show_multi==SHOW_MULTI_LONG)
973 ari->server_name=connection->server->alias.c_str();
974 nntp_dogroup(sa->group, false);
975 chkreply_setok(stdputline(debug>=DEBUG_MED,"ARTICLE %lu",sa->articlenum));
976 nntp_dogetarticle(ari,toti,buf);
977 connection->server_ok=true;
978 } catch (baseCommEx &e) {
979 printCommEx(e, s, redone, options);
980 if (e.isfatal()) {
981 sap_erase_i = sapi;
982 ++sapi;
983 sap.erase(sap_erase_i);
984 }else{
985 ++sapi;
986 }
987 continue;
988 }
989 c_file_fd f(fn, O_CREAT|O_WRONLY|O_TRUNC, PRIVMODE);
990 list<string>::iterator curb;
991 try {
992 for(curb = buf.begin();curb!=buf.end();++curb){
993 f.putf("%s\n",(*curb).c_str());
994 }
995 f.close();
996 }catch(FileEx &e){
997 //if the drive is full or other error occurs, then the temp file will be cutoff and useless, so delete it.
998 if (unlink(fn))
999 perror("unlink:");
1000 throw;
1001 }
1002 set_retrieve_warn_status(attempted - sap.size());
1003 return 0; //article successfully retrieved, return.
1004 }
1005 redone++;
1006 }
1007 printf("couldn't get %s from anywhere\n",part->messageid.c_str());
1008 set_retrieve_error_status();
1009 return -1;
1010 }
1011
print_nntp_file_info(c_nntp_file::ptr f,t_show_multiserver show_multi)1012 void print_nntp_file_info(c_nntp_file::ptr f, t_show_multiserver show_multi) {
1013 char tconvbuf[TCONV_DEF_BUF_LEN];
1014 c_nntp_part *p=(*f->parts.begin());
1015 tconv(tconvbuf,TCONV_DEF_BUF_LEN,&p->date);
1016 if (f->iscomplete())
1017 printf("%i",f->have);
1018 else
1019 printf("%i/%i",f->have,f->req);
1020 if (show_multi!=NO_SHOW_MULTI){
1021 t_server_have_map have_map;
1022 f->get_server_have_map(have_map);
1023 if (show_multi==SHOW_MULTI_SHORT)
1024 printf(" ");
1025
1026 for (t_server_have_map::iterator i=have_map.begin(); i!=have_map.end(); ++i){
1027 for (t_server_list_range servers = nconfig.getservers(i->first); servers.first!=servers.second; ++servers.first) {
1028 c_server::ptr s=servers.first->second;
1029 if (show_multi==SHOW_MULTI_LONG){
1030 printf(" %s", s->alias.c_str());
1031 if (i->second<f->have)
1032 printf(":%i", i->second);
1033 }
1034 else{
1035 if (i->second<f->have){
1036 for (string::size_type j=0; j<s->shortname.size(); j++)
1037 printf("%c", toupper(s->shortname[j]));
1038 }
1039 else
1040 printf("%s", s->shortname.c_str());
1041 }
1042 }
1043 }
1044 }
1045 printf("\t%lil\t%s\t%s\t%s\t%s\n",f->lines(),tconvbuf,f->subject.c_str(),f->author.c_str(),p->messageid.c_str());
1046 }
1047
nntp_retrieve(const vector<c_group_info::ptr> & rgroups,const t_nntp_getinfo_list & getinfos,const t_xpat_list & patinfos,const nget_options & options)1048 void c_prot_nntp::nntp_retrieve(const vector<c_group_info::ptr> &rgroups, const t_nntp_getinfo_list &getinfos, const t_xpat_list &patinfos, const nget_options &options) {
1049 c_nntp_files_u filec;
1050 ParHandler parhandler;
1051 if (rgroups.size()!=1) {
1052 cleanupcache();
1053 group = NULL;
1054 midinfo=new meta_mid_info(ngcachehome, rgroups);
1055 } else {
1056 if (rgroups.front() != group) {
1057 cleanupcache();
1058 group = rgroups.front();
1059 }
1060 if (!midinfo) {
1061 midinfo=new meta_mid_info(ngcachehome, group);
1062 }
1063 }
1064 gcache=new c_nntp_cache();
1065
1066 for (vector<c_group_info::ptr>::const_iterator gi=rgroups.begin(); gi!=rgroups.end(); gi++)
1067 nntp_xgroup(*gi, patinfos, options);
1068
1069 gcache->getfiles(&filec, &parhandler, midinfo, getinfos);
1070
1071 gcache=NULL;
1072
1073 nntp_doretrieve(filec, parhandler, options);
1074 }
1075
nntp_retrieve(const vector<c_group_info::ptr> & rgroups,const t_nntp_getinfo_list & getinfos,const nget_options & options)1076 void c_prot_nntp::nntp_retrieve(const vector<c_group_info::ptr> &rgroups, const t_nntp_getinfo_list &getinfos, const nget_options &options){
1077 c_nntp_files_u filec;
1078 ParHandler parhandler;
1079 if (rgroups.size()!=1) {
1080 cleanupcache();
1081 group = NULL;
1082 midinfo=new meta_mid_info(ngcachehome, rgroups);
1083 nntp_cache_getfiles(&filec, &parhandler, &gcache_ismultiserver, ngcachehome, rgroups, midinfo, getinfos);
1084 } else {
1085 assert(rgroups.front());
1086 if (gcache) {
1087 assert(group == rgroups.front());
1088 gcache->getfiles(&filec, &parhandler, midinfo, getinfos);
1089 //attempt to free up some mem since all the data we need is now in filec, we don't need the whole cache. Unfortunatly due to STL's memory allocators this doesn't really return the memory to the OS, but at least its available for any further STL allocations while retrieving.
1090 gcache=NULL;
1091 } else {
1092 if (rgroups.front() != group) {
1093 cleanupcache();
1094 group = rgroups.front();
1095 }
1096 if (!midinfo) {
1097 midinfo=new meta_mid_info(ngcachehome, rgroups);
1098 }
1099
1100 nntp_cache_getfiles(&filec, &parhandler, &gcache_ismultiserver, ngcachehome, group, midinfo, getinfos);
1101 }
1102 }
1103 nntp_doretrieve(filec, parhandler, options);
1104 }
1105
1106
nntp_doretrieve(c_nntp_files_u & filec,ParHandler & parhandler,const nget_options & options)1107 void c_prot_nntp::nntp_doretrieve(c_nntp_files_u &filec, ParHandler &parhandler, const nget_options &options) {
1108 int optionflags = options.gflags;
1109 if (!(optionflags&GETFILES_AUTOPAR_DISABLING_FLAGS)) {
1110 parhandler.get_initial_pars(filec);
1111 }
1112
1113 if (filec.files.empty())
1114 return;
1115
1116 t_nntp_files_u::iterator curf;
1117 //c_nntp_file *f;
1118 c_nntp_file::ptr f;
1119 c_nntp_file_retr::ptr fr;
1120
1121 if (optionflags & GETFILES_UNMARK) {
1122 ulong nbytes=0;
1123 unsigned int nfiles=0;
1124 for(curf = filec.files.begin();curf!=filec.files.end();++curf){
1125 fr=(*curf).second;
1126 f=fr->file;
1127 if (optionflags & GETFILES_TESTMODE) {
1128 if (midinfo->check(f->bamid())){
1129 print_nntp_file_info(f,options.test_multi);
1130 nbytes+=f->bytes();
1131 nfiles++;
1132 }
1133 } else
1134 midinfo->remove(f->bamid());
1135 }
1136 if (optionflags & GETFILES_TESTMODE)
1137 printf("Would unmark %lu bytes in %u files\n",nbytes,nfiles);
1138 } else if (optionflags & GETFILES_TESTMODE){
1139 for(curf = filec.files.begin();curf!=filec.files.end();++curf){
1140 fr=(*curf).second;
1141 print_nntp_file_info(fr->file,options.test_multi);
1142 }
1143 if (optionflags & GETFILES_MARK)
1144 printf("Would mark ");
1145 printf("%"PRIuFAST64" bytes in %lu files\n",filec.bytes,(ulong)filec.files.size());
1146 } else if (optionflags & GETFILES_MARK) {
1147 for(curf = filec.files.begin();curf!=filec.files.end();++curf){
1148 fr=(*curf).second;
1149 f=fr->file;
1150 midinfo->insert(f);
1151 }
1152 } else {
1153 quinfo qtotinfo;
1154 arinfo ainfo;
1155 time(&qtotinfo.starttime);
1156 qtotinfo.filesdone=0;
1157 // qtotinfo.linestot=filec.lines;
1158 qtotinfo.filestot=filec.files.size();
1159 qtotinfo.bytesleft=filec.bytes;
1160 qtotinfo.doarticle_show_multi=gcache_ismultiserver?SHOW_MULTI_SHORT:NO_SHOW_MULTI;
1161 c_nntp_part *p;
1162 // s_part_u *bp;
1163 c_nntp_file_parts::iterator curp;
1164 char *fn;
1165 if (!options.writelite.empty())
1166 optionflags |= GETFILES_NODECODE;
1167 curf=filec.files.end();
1168 while (1){
1169 if (curf!=filec.files.end()){
1170 // delete (*lastf).second;//new cache implementation uses pointers to the same data
1171 filec.files.erase(curf);
1172 qtotinfo.filesdone++;
1173 filec.bytes=qtotinfo.bytesleft;//update bytes in case we have an exception and need to restart.
1174
1175 if (!(optionflags&GETFILES_AUTOPAR_DISABLING_FLAGS)) {
1176 //check if this was the last file to be downloaded to its path, and if so do autoparhandling
1177 int path_files_left=0;
1178 for (t_nntp_files_u::iterator dfi = filec.files.begin(); dfi!=filec.files.end(); ++dfi){
1179 const c_nntp_file_retr::ptr &dfr = (*dfi).second;
1180 if (dfr->path == fr->path)//fr will still be set to the just erased file_retr, here
1181 path_files_left++;
1182 }
1183 if (path_files_left==0) {
1184 long old_files_size = filec.files.size();
1185 parhandler.maybe_get_pxxs(fr->path, filec);
1186 //update status line information for any new pars that have been added
1187 qtotinfo.bytesleft = filec.bytes;
1188 qtotinfo.filestot += filec.files.size() - old_files_size;
1189 }
1190 }
1191 }
1192 if (filec.files.empty())
1193 break;
1194 curf = filec.files.begin();
1195 fr=(*curf).second;
1196 f=fr->file;
1197 printf("Retrieving: ");
1198 print_nntp_file_info(f, options.retr_show_multi);
1199 // bp=f->parts.begin()->second;
1200 int dlerr=0;
1201 Decoder decoder;
1202 for(curp = f->parts.begin();curp!=f->parts.end();++curp){
1203 //asprintf(&fn,"%s/%s-%s-%li-%li-%li",nghome.c_str(),host.c_str(),group.c_str(),fgnum,part,num);
1204 p=(*curp);
1205 if (dlerr){
1206 qtotinfo.bytesleft-=p->bytes();
1207 continue;
1208 }
1209 {
1210 const char *usepath;
1211 if (!options.writelite.empty())
1212 usepath="";
1213 else usepath=fr->temppath.c_str();
1214 if (optionflags & GETFILES_TEMPSHORTNAMES)
1215 asprintf(&fn,"%s%lx.%03i",usepath,f->getfileid(),p->partnum);
1216 else
1217 asprintf(&fn,"%sngettemp-%lx.%03i",usepath,f->getfileid(),p->partnum);
1218 }
1219 if (!fexists(fn)){
1220 ainfo.partreq = f->req;
1221 // ainfo.anum=p->articlenum;//set in doarticle now.
1222 // ainfo.linesdone=0;
1223 // ainfo.bytesdone=0;
1224 // ainfo.linestot=p->lines;
1225 // ainfo.bytestot=p->bytes;
1226 if (!options.writelite.empty()){
1227 c_file_fd fw(options.writelite.c_str(), O_WRONLY|O_CREAT|O_APPEND, PRIVMODE);
1228 nntp_dowritelite_article(fw,p,fn);
1229 fw.close();
1230 free(fn);
1231 qtotinfo.bytesleft-=p->bytes();
1232 // uustatus.derr=-1;//skip this file..
1233 continue;
1234 }
1235 if ((optionflags & GETFILES_NOCONNECT) || nntp_doarticle(p,&ainfo,&qtotinfo,fn,options)){
1236 free(fn);
1237 fn=NULL;
1238 if (!(optionflags & GETFILES_GETINCOMPLETE)) {
1239 qtotinfo.bytesleft-=p->bytes();
1240 dlerr=-1;//skip this file..
1241 continue;
1242 }
1243 }
1244 }else{
1245 // qtotinfo.bytestot-=p->bytes;
1246 if (quiet<2) printf("already have article %s (%s)\n",p->messageid.c_str(),fn);
1247 }
1248 qtotinfo.bytesleft-=p->bytes();
1249 if (fn)
1250 decoder.addpart(p->partnum,fn); //decoder will free fn when done.
1251 }
1252
1253 if (dlerr)
1254 printf("download error occured, keeping temp files.\n");
1255 else if (optionflags&GETFILES_NODECODE) {
1256 set_total_ok_status();
1257 if (quiet<2)
1258 printf("not decoding, keeping temp files.\n");
1259 }
1260 else {
1261 dupe_file_checker flist;
1262 if (!decoder.decode(options, fr, flist)) {
1263 midinfo->insert(f);
1264 if (!flist.empty()) {
1265 //check all remaining files against what we just decoded, and remove any dupes.
1266 t_nntp_files_u::iterator dfi = curf; ++dfi; //skip the current one
1267 t_nntp_files_u::iterator del_fi;
1268 c_nntp_file_retr::ptr dfr;
1269 while(dfi!=filec.files.end()){
1270 dfr = (*dfi).second;
1271 //only check files that are being downloaded to the same path
1272 if (dfr->dupecheck && dfr->path == fr->path) {
1273 c_nntp_file::ptr df = dfr->file;
1274 if (flist.checkhavefile(df->subject.c_str(), df->bamid(), df->bytes())) {
1275 set_skipped_ok_status();
1276 del_fi=dfi;
1277 ++dfi;
1278 filec.files.erase(del_fi);
1279 qtotinfo.filestot--;
1280 qtotinfo.bytesleft-=df->bytes();
1281 filec.bytes=qtotinfo.bytesleft;//update bytes in case we have an exception and need to restart.
1282 continue;
1283 }
1284 }
1285 ++dfi;
1286 }
1287 }
1288 }
1289 }
1290 }
1291 }
1292 //delete filec;filec=NULL;
1293 cleanupcache();
1294 }
nntp_auth(void)1295 void c_prot_nntp::nntp_auth(void){
1296 nntp_doauth(connection->server->user.c_str(),connection->server->pass.c_str());
1297 }
nntp_doauth(const char * user,const char * pass)1298 void c_prot_nntp::nntp_doauth(const char *user, const char *pass){
1299 int i;
1300
1301 if(!user || !*user){
1302 throw TransportExFatal(Ex_INIT,"nntp_doauth: no authorization info known");
1303 }
1304 putline(quiet<2,"AUTHINFO USER %s",user);
1305 i=getreply(quiet<2);
1306 if (i==350 || i==381){
1307 if(!pass || !*pass){
1308 throw TransportExFatal(Ex_INIT,"nntp_doauth: no password known");
1309 }
1310 if (quiet<2)
1311 printf("%s << AUTHINFO PASS *\n", connection->server->shortname.c_str());
1312 putline(0,"AUTHINFO PASS %s",pass);
1313 i=getreply(quiet<2);
1314 }
1315 chkreply(i);
1316 }
1317
nntp_open(c_server::ptr h)1318 void c_prot_nntp::nntp_open(c_server::ptr h){
1319 if (h)
1320 force_host=h;
1321 else
1322 force_host=NULL;
1323 }
1324
nntp_doopen(void)1325 void c_prot_nntp::nntp_doopen(void){
1326 assert(connection);
1327 if (connection->freshconnect){
1328 chkreply(getreply(quiet<2));
1329 putline(debug>=DEBUG_MED,"MODE READER");
1330 getline(debug>=DEBUG_MED);
1331 connection->freshconnect=false;
1332 }
1333 }
1334
cleanupcache(void)1335 void c_prot_nntp::cleanupcache(void){
1336 // if(gcache){gcache->dec_rcount();/*delete gcache;*/gcache=NULL;}
1337 gcache=NULL;//ref counted.
1338 if (midinfo){
1339 meta_mid_info *mi = midinfo; //store midinfo in temp pointer and NULL out real pointer, to prevent a second deletion attempt if the destructor aborts and the atexit calls the cleanup again.
1340 midinfo=NULL;
1341 delete mi;
1342 }
1343 }
cleanup(void)1344 void c_prot_nntp::cleanup(void){
1345 cleanupcache();
1346 }
1347
initready(void)1348 void c_prot_nntp::initready(void){
1349 // midinfo=new c_mid_info((nghome + ".midinfo"));
1350 }
c_prot_nntp(void)1351 c_prot_nntp::c_prot_nntp(void){
1352 // cbuf=new char[4096];
1353 // cbuf_size=4096;
1354 gcache=NULL;
1355 // ch=-1;
1356 connection=NULL;
1357 midinfo=NULL;
1358 force_host=NULL;
1359 }
~c_prot_nntp(void)1360 c_prot_nntp::~c_prot_nntp(void){
1361 // printf("nntp destructing\n");
1362 // if (midinfo)delete midinfo;
1363 cleanup();
1364 }
1365