1 #include <config.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #ifdef HAVE_UNISTD_H
7 #include <unistd.h>
8 #endif
9
10 #ifdef HAVE_SYS_WAIT_H
11 #include <sys/wait.h>
12 #endif
13
14 #ifdef HAVE_DIRENT_H
15 # include <dirent.h>
16 #else
17 # ifdef HAVE_SYS_NDIR_H
18 # include <sys/ndir.h>
19 # endif
20 # ifdef HAVE_SYS_DIR_H
21 # include <sys/dir.h>
22 # endif
23 #endif
24
25 #ifdef HAVE_LIMITS_H
26 #include <limits.h>
27 #endif
28
29 #include <string.h>
30 #include <sys/types.h>
31 #include <netdb.h>
32 #include <sys/stat.h>
33 #include <math.h>
34 #include <errno.h>
35 #include <ctype.h>
36
37 #ifdef DMALLOC
38 #include <dmalloc.h>
39 #endif
40
41 #include "suck_config.h"
42 #include "both.h"
43 #include "suck.h"
44 #include "suckutils.h"
45 #include "dedupe.h"
46 #include "phrases.h"
47 #include "killfile.h"
48 #include "timer.h"
49 #include "active.h"
50 #include "batch.h"
51 #include "xover.h"
52 #include "db.h"
53
54 #ifdef MYSIGNAL
55 #include <signal.h>
56 #endif
57
58 #ifdef CHECK_HISTORY
59 #include "chkhistory.h"
60 #endif
61
62 /* function prototypes */
63 int get_articles(PMaster);
64 int get_one_article(PMaster, int, long);
65 int do_supplemental(PMaster);
66 int restart_yn(PMaster);
67 int scan_args(PMaster, int, char *[]);
68 void do_cleanup(PMaster);
69 int do_authenticate(PMaster);
70 void load_phrases(PMaster);
71 void free_phrases(void);
72 int parse_args(PMaster, int, char *[]);
73 int get_group_number(PMaster, char *);
74 int do_supplemental(PMaster);
75 int do_sup_bynr(PMaster, char *);
76 int do_nodownload(PMaster);
77 #ifdef MYSIGNAL
78 RETSIGTYPE sighandler(int);
79 void pause_signal(int, PMaster);
80 enum {PAUSE_SETUP, PAUSE_DO};
81
82 /*------------------------------------------*/
83 int GotSignal = FALSE;
84 /* the only static variable allowed, it's */
85 /* the only graceful way to handle a signal */
86 /*------------------------------------------*/
87 #endif
88
89 /* set up for phrases */
90 char **both_phrases=default_both_phrases;
91 char **suck_phrases=default_suck_phrases;
92 char **timer_phrases=default_timer_phrases;
93 char **chkh_phrases=default_chkh_phrases;
94 char **dedupe_phrases=default_dedupe_phrases;
95 char **killf_reasons=default_killf_reasons;
96 char **killf_phrases=default_killf_phrases;
97 char **killp_phrases=default_killp_phrases;
98 char **sucku_phrases=default_sucku_phrases;
99 char **active_phrases=default_active_phrases;
100 char **batch_phrases=default_batch_phrases;
101 char **xover_phrases=default_xover_phrases;
102 char **xover_reasons=default_xover_reasons;
103
104
105 enum { STATUS_STDOUT, STATUS_STDERR };
106 enum { RESTART_YES, RESTART_NO, RESTART_ERROR };
107
108 enum {
109 ARG_NO_MATCH, ARG_ALWAYS_BATCH, ARG_BATCH_INN, ARG_BATCH_RNEWS, ARG_BATCH_LMOVE, \
110 ARG_BATCH_INNFEED, ARG_BATCH_POST, ARG_CLEANUP, ARG_DIR_TEMP, ARG_DIR_DATA, ARG_DIR_MSGS, \
111 ARG_DEF_ERRLOG, ARG_HOST, ARG_NO_POSTFIX, ARG_LANGUAGE, ARG_MULTIFILE, ARG_POSTFIX, \
112 ARG_QUIET, ARG_RNEWSSIZE, ARG_DEF_STATLOG, ARG_WAIT_SIG, ARG_ACTIVE, ARG_RECONNECT, \
113 ARG_DEBUG, ARG_ERRLOG, ARG_HISTORY, ARG_KILLFILE, ARG_KLOG_NONE, ARG_KLOG_SHORT, \
114 ARG_KLOG_LONG, ARG_MODEREADER, ARG_PORTNR, ARG_PASSWD, ARG_RESCAN, ARG_STATLOG, \
115 ARG_USERID, ARG_VERSION, ARG_WAIT, ARG_LOCALHOST, ARG_TIMEOUT, ARG_NRMODE, ARG_AUTOAUTH, \
116 ARG_NODEDUPE, ARG_NO_CHK_MSGID, ARG_READACTIVE, ARG_PREBATCH, ARG_SKIP_ON_RESTART, \
117 ARG_KLOG_NAME, ARG_USEGUI, ARG_XOVER, ARG_CONN_DEDUPE, ARG_POST_FILTER, ARG_CONN_ACTIVE, \
118 ARG_HIST_FILE, ARG_HEADER_ONLY, ARG_ACTIVE_LASTREAD, ARG_USEXOVER, ARG_RESETCOUNTER, \
119 ARG_LOW_READ, ARG_SHOW_GROUP, ARG_USE_SSL, ARG_LOCAL_SSL, ARG_BATCH_POST_NR, \
120 ARG_PASSWD_ENV,
121 };
122
123 typedef struct Arglist{
124 const char *sarg;
125 const char *larg;
126 int nr_params;
127 int flag;
128 int errmsg; /* this is an index into suck_phrases */
129 } Args, *Pargs;
130
131 const Args arglist[] = {
132 {"a", "always_batch", 0, ARG_ALWAYS_BATCH, -1},
133 {"bi", "batch_inn", 1, ARG_BATCH_INN, 40},
134 {"br", "batch_rnews", 1, ARG_BATCH_RNEWS, 40},
135 {"bl", "batch_lmove", 1, ARG_BATCH_LMOVE, 40},
136 {"bf", "batch_innfeed",1, ARG_BATCH_INNFEED, 40},
137 {"bp", "batch_post", 0, ARG_BATCH_POST, -1},
138 {"bP", "batch_post_nr", 1, ARG_BATCH_POST_NR, 72},
139 {"c", "cleanup", 0, ARG_CLEANUP, -1},
140 {"dt", "dir_temp", 1, ARG_DIR_TEMP, 37},
141 {"dd", "dir_data", 1, ARG_DIR_DATA, 37},
142 {"dm", "dir_msgs", 1, ARG_DIR_MSGS, 37},
143 {"e", "def_error_log", 0, ARG_DEF_ERRLOG, -1},
144 {"f", "reconnect_dedupe", 0, ARG_CONN_DEDUPE, -1},
145 {"g", "header_only", 0, ARG_HEADER_ONLY, -1},
146 {"h", "host", 1, ARG_HOST, 51},
147 {"hl", "localhost", 1, ARG_LOCALHOST, 50},
148 {"i", "default_activeread", 1, ARG_ACTIVE_LASTREAD, 65},
149 {"k", "kill_no_postfix", 0, ARG_NO_POSTFIX, -1},
150 {"l", "language_file", 1, ARG_LANGUAGE, 47},
151 {"lr", "low_read", 0, ARG_LOW_READ, -1},
152 {"m", "multifile", 0, ARG_MULTIFILE, -1},
153 {"n", "number_mode", 0, ARG_NRMODE, -1},
154 {"p", "postfix", 1, ARG_POSTFIX, 36},
155 {"q", "quiet", 0, ARG_QUIET, -1},
156 {"r", "rnews_size", 1, ARG_RNEWSSIZE, 35},
157 {"rc", "reset_counter", 0, ARG_RESETCOUNTER, -1},
158 {"s", "def_status_log", 0, ARG_DEF_STATLOG, -1},
159 {"sg", "show_group", 0, ARG_SHOW_GROUP, -1},
160 #ifdef HAVE_LIBSSL
161 {"ssl","use_ssl", 0, ARG_USE_SSL, -1},
162 #endif
163 {"u", "auto_authorization", 0, ARG_AUTOAUTH, -1},
164 {"w", "wait_signal", 2, ARG_WAIT_SIG, 46},
165 {"x", "no_chk_msgid", 0, ARG_NO_CHK_MSGID, -1},
166 {"y", "post_filter", 1, ARG_POST_FILTER, 62},
167 {"z", "no_dedupe", 0, ARG_NODEDUPE, -1},
168 {"A", "active", 0, ARG_ACTIVE, -1},
169 {"AL", "read_active", 1, ARG_READACTIVE, 56},
170 {"B", "pre-batch", 0, ARG_PREBATCH, -1},
171 {"C", "reconnect", 1, ARG_RECONNECT, 49},
172 {"D", "debug", 0, ARG_DEBUG, -1},
173 {"E", "error_log", 1, ARG_ERRLOG, 41},
174 {"F", "reconnect_active", 0, ARG_CONN_ACTIVE, -1},
175 {"G", "use_gui", 0, ARG_USEGUI, -1},
176 {"H", "no_history", 0, ARG_HISTORY, -1},
177 {"HF", "history_file", 1, ARG_HIST_FILE, 64},
178 {"K", "killfile", 0, ARG_KILLFILE, -1},
179 {"L", "kill_log_none", 0, ARG_KLOG_NONE, -1},
180 {"LF", "kill_log_name", 1, ARG_KLOG_NAME, 61},
181 {"LS", "kill_log_short", 0, ARG_KLOG_SHORT, -1},
182 {"LL", "kill_log_long", 0, ARG_KLOG_LONG, -1},
183 {"M", "mode_reader", 0, ARG_MODEREADER, -1},
184 {"N", "portnr", 1, ARG_PORTNR, 45},
185 {"O", "skip_on_restart", 0, ARG_SKIP_ON_RESTART, -1},
186 {"P", "password", 1, ARG_PASSWD, 44},
187 {"Q", "password_env", 0, ARG_PASSWD_ENV, -1},
188 {"R", "no_rescan", 0, ARG_RESCAN, -1},
189 {"S", "status_log", 1, ARG_STATLOG, 42},
190 #ifdef HAVE_LIBSSL
191 {"SSL" "local_use_ssl", 0, ARG_LOCAL_SSL, -1},
192 #endif
193 #ifdef TIMEOUT
194 {"T", "timeout", 1, ARG_TIMEOUT, 52},
195 #endif
196 {"U", "userid", 1, ARG_USERID, 43},
197 {"V", "version", 0, ARG_VERSION, -1},
198 {"W", "wait", 2, ARG_WAIT, 46},
199 {"X", "no_xover", 0, ARG_XOVER, -1},
200 {"Z", "use_xover", 0, ARG_USEXOVER, -1},
201
202 };
203
204 #define MAX_ARG_PARAMS 2 /* max nr of params with any arg */
205 #define NR_ARGS (sizeof(arglist)/sizeof(arglist[0]))
206
207 /*------------------------------------------------------------------*/
main(int argc,char * argv[])208 int main(int argc, char *argv[]) {
209
210 struct stat sbuf;
211 Master master;
212 PList temp;
213 PGroups ptemp;
214 POverview pov;
215 char *inbuf, **args, **fargs = NULL;
216 int nr, resp, loop, fargc, retval = RETVAL_OK;
217
218 #ifdef LOCKFILE
219 const char *lockfile = NULL;
220 #endif
221 #ifdef MYSIGNAL
222 #ifdef HAVE_SIGACTION
223 struct sigaction sigs;
224 #endif
225 #endif
226
227 /* initialize master structure */
228 master.head = master.curr = NULL;
229 master.nritems = master.nrgot = 0;
230 master.MultiFile = FALSE;
231 master.msgs = stdout;
232 master.sockfd = -1;
233 master.status_file = FALSE; /* are status messages going to a file */
234 master.status_file_name = NULL;
235 master.do_killfile = TRUE;
236 master.do_chkhistory = TRUE;
237 master.do_modereader = FALSE;
238 master.always_batch = FALSE;
239 master.rnews_size = 0L;
240 master.batch = BATCH_FALSE;
241 master.batchfile = NULL;
242 master.cleanup = FALSE;
243 master.portnr = DEFAULT_NNRP_PORT;
244 master.host = getenv("NNTPSERVER"); /* the default */
245 master.pause_time = -1;
246 master.pause_nrmsgs = -1;
247 master.sig_pause_time = -1;
248 master.sig_pause_nrmsgs = -1;
249 master.killfile_log = KILL_LOG_LONG; /* do we log killed messages */
250 master.phrases = NULL;
251 master.errlog = NULL;
252 master.debug = FALSE;
253 master.rescan = TRUE; /* do we do rescan on a restart */
254 master.quiet = FALSE; /* do we display BPS and msg count */
255 master.killp = NULL; /* pointer to killfile structure */
256 master.kill_ignore_postfix = FALSE;
257 master.reconnect_nr = 0; /* how many x msgs do we disconnect and reconnect 0 = never */
258 master.innfeed = NULL;
259 master.do_active = FALSE; /* do we scan the local active list */
260 master.localhost = NULL;
261 master.groups = NULL;
262 master.nrmode = FALSE; /* use nrs or msgids to get article */
263 master.auto_auth = FALSE; /* do we do auto authorization */
264 master.passwd = NULL;
265 master.userid = NULL;
266 master.no_dedupe = FALSE;
267 master.chk_msgid = TRUE; /* do we check MsgID for trailing > */
268 master.activefile = NULL;
269 master.prebatch = FALSE; /* do we try to batch any left over articles b4 we start? */
270 master.grpnr = -1; /* what group number are we currently on */
271 master.skip_on_restart = FALSE;
272 master.kill_log_name = N_KILLLOG;
273 master.use_gui = FALSE;
274 master.do_xover = TRUE;
275 master.conn_dedupe = FALSE;
276 master.post_filter= NULL;
277 master.conn_active = FALSE;
278 master.history_file = HISTORY_FILE;
279 master.header_only = FALSE;
280 master.db = -1;
281 master.active_lastread = ACTIVE_DEFAULT_LASTREAD;
282 master.use_xover = FALSE;
283 master.xoverview = NULL;
284 master.resetcounter = FALSE;
285 master.low_read = FALSE;
286 master.show_group = FALSE;
287 master.do_ssl = FALSE;
288 master.ssl_struct = NULL;
289 master.local_ssl = FALSE;
290 master.local_ssl_struct = NULL;
291 master.batch_post_nr = 0;
292 master.passwd_env = FALSE;
293
294 /* have to do this next so if set on cmd line, overrides this */
295
296 #ifdef N_PHRASES /* in case someone nukes def */
297 if(stat(N_PHRASES, &sbuf) == 0 && S_ISREG(sbuf.st_mode)) {
298 /* we have a regular phrases file make it the default */
299 master.phrases = N_PHRASES;
300 }
301
302 #endif
303
304 /* allow no args, only the hostname, or hostname as first arg */
305 /* also have to do the file argument checking */
306 switch(argc) {
307 case 1:
308 break;
309 case 2:
310 /* the fargs == NULL test so only process first file name */
311 if(argv[1][0] == FILE_CHAR) {
312 if((fargs = build_args(&argv[1][1], &fargc)) != NULL) {
313 retval = scan_args(&master, fargc, fargs);
314 }
315 }
316 else if(argv[1][0] == '-') {
317 /* in case of suck -V */
318 retval = scan_args(&master, 1, &(argv[1]));
319 }
320 else{
321 master.host = argv[1];
322 }
323 break;
324 default:
325 for(loop=1;loop<argc && fargs == NULL;loop++) {
326 if(argv[loop][0] == FILE_CHAR) {
327 if((fargs = build_args(&argv[loop][1], &fargc)) != NULL) {
328 retval = scan_args(&master, fargc, fargs);
329 }
330 }
331 }
332 /* this is here so anything at command line overrides file */
333 if(argv[1][0] != '-' && argv[1][0] != FILE_CHAR) {
334 master.host = argv[1];
335 argc-= 2;
336 args = &(argv[2]);
337 }
338 else {
339 args = &(argv[1]);
340 argc--;
341 }
342 retval = scan_args(&master, argc, args);
343 break;
344 }
345 /* print out status stuff */
346 if(master.debug == TRUE) {
347 do_debug("Suck version %s\n",SUCK_VERSION);
348 do_debug("master.MultiFile = %d\n", master.MultiFile);
349 do_debug("master.status_file = %d\n",master.status_file);
350 do_debug("master.status_file_name = %s\n", null_str(master.status_file_name));
351 do_debug("master.do_killfile = %s\n", true_str(master.do_killfile));
352 do_debug("master.do_chkhistory = %s\n", true_str(master.do_chkhistory));
353 do_debug("master.do_modereader = %s\n", true_str(master.do_modereader));
354 do_debug("master.always_batch = %s\n", true_str(master.always_batch));
355 do_debug("master.rnews_size = %ld\n", master.rnews_size);
356 do_debug("master.batch = %d\n", master.batch);
357 do_debug("master.batchfile = %s\n", null_str(master.batchfile));
358 do_debug("master.cleanup = %s\n", true_str(master.cleanup));
359 do_debug("master.host = %s\n", null_str(master.host));
360 do_debug("master.portnr = %u\n", master.portnr);
361 do_debug("master.pause_time = %d\n", master.pause_time);
362 do_debug("master.pause_nrmsgs = %d\n", master.pause_nrmsgs);
363 do_debug("master.sig_pause_time = %d\n", master.sig_pause_time);
364 do_debug("master.sig_pause_nrmsgs = %d\n", master.sig_pause_nrmsgs);
365 do_debug("master.killfile_log = %d\n", master.killfile_log);
366 do_debug("master.phrases = %s\n", null_str(master.phrases));
367 do_debug("master.errlog = %s\n", null_str(master.errlog));
368 do_debug("master.rescan = %s\n", true_str(master.rescan == TRUE));
369 do_debug("master.quiet = %s\n", true_str(master.quiet));
370 do_debug("master.kill_ignore_postfix = %s\n", true_str(master.kill_ignore_postfix));
371 do_debug("master.reconnect_nr=%d\n", master.reconnect_nr);
372 do_debug("master.do_active = %s\n", true_str(master.do_active));
373 do_debug("master.localhost = %s\n", null_str(master.localhost));
374 do_debug("master.nrmode = %s\n", true_str(master.nrmode));
375 do_debug("master.auto_auth = %s\n", true_str(master.auto_auth));
376 do_debug("master.no_dedupe = %s\n", true_str(master.no_dedupe));
377 do_debug("master.chk_msgid = %s\n", true_str(master.chk_msgid));
378 do_debug("master.activefile = %s\n", null_str(master.activefile));
379 do_debug("master.prebatch = %s\n", true_str(master.prebatch));
380 do_debug("master.skip_on_restart = %s\n", true_str(master.skip_on_restart));
381 do_debug("master.kill_log_name = %s\n", null_str(master.kill_log_name));
382 do_debug("master.use_gui = %s\n", true_str(master.use_gui));
383 do_debug("master.do_xover = %s\n", true_str(master.do_xover));
384 do_debug("master.conn_dedupe = %s\n", true_str(master.conn_dedupe));
385 do_debug("master.post_filter = %s\n", null_str(master.post_filter));
386 do_debug("master.conn_active = %s\n", true_str(master.conn_active));
387 do_debug("master.history_file = %s\n", null_str(master.history_file));
388 do_debug("master.header_only = %s\n", true_str(master.header_only));
389 do_debug("master.active_lastread = %d\n", master.active_lastread);
390 do_debug("master.use_xover = %s\n", true_str(master.use_xover));
391 do_debug("master.do_ssl = %s\n", true_str(master.do_ssl));
392 do_debug("master.local_ssl = %s\n", true_str(master.local_ssl));
393 do_debug("master.batch_post_nr = %d\n",master.batch_post_nr);
394 do_debug("master.passwd_env = %s\n", true_str(master.passwd_env));
395 #ifdef TIMEOUT
396 do_debug("TimeOut = %d\n", TimeOut);
397 #endif
398 do_debug("master.debug = TRUE\n");
399 }
400 /* now do any final args checks needed */
401 /* check to see if we have enough info to scan the localhost active file */
402 if((master.do_active == TRUE || master.batch == BATCH_LIHAVE) && master.localhost == NULL) {
403 retval = RETVAL_ERROR;
404 error_log(ERRLOG_REPORT, suck_phrases[6], NULL);
405 }
406 else if(master.host == NULL) {
407 retval = RETVAL_ERROR;
408 error_log(ERRLOG_REPORT, suck_phrases[74], NULL);
409 }
410
411
412 /* okay now the main stuff */
413 if(retval == RETVAL_OK) {
414 if(master.status_file == FALSE) {
415 /* if not in multifile mode, all status msgs MUST go to stderr to not mess up articles */
416 /* this has to go before lockfile code, so lockfile prints msg to correct place. */
417 master.msgs = ( master.MultiFile == FALSE) ? stderr : stdout ;
418 }
419 #ifdef LOCKFILE
420 /* this has to be here since we use full_path() to get path for lock file */
421 /* and it isn't set up until we process the args above. */
422 if(do_lockfile(&master) != RETVAL_OK) {
423 exit(-1);
424 }
425 #endif
426
427 #ifdef MYSIGNAL
428 /* set up signal handlers */
429 #ifdef HAVE_SIGACTION
430 sigemptyset(&(sigs.sa_mask));
431 sigs.sa_handler = sighandler;
432 sigs.sa_flags = 0;
433 if(sigaction(MYSIGNAL, &sigs, NULL) == -1
434 || sigaction(MYSIGNAL2, &sigs, NULL) == -1
435 || sigaction(PAUSESIGNAL, &sigs, NULL) == -1) {
436 MyPerror(suck_phrases[67]);
437 }
438 else {
439 signal_block(MYSIGNAL_SETUP); /* set up sgetline() to block signal */
440 pause_signal(PAUSE_SETUP, &master); /* set up routine for pause swap if signal */
441 }
442 #else
443 /* the old-fashioned way */
444 signal(MYSIGNAL, sighandler);
445 signal(MYSIGNAL2, sighandler);
446 signal(PAUSESIGNAL, sighander);
447 pause_signal(PAUSE_SETUP, &master);
448 #endif
449 #endif
450 load_phrases(&master); /* this has to be here so rest prints okay */
451
452 /* set up status log, if none specified or unable to open status log */
453 /* then use stdout or stderr */
454
455 if(master.status_file_name != NULL) {
456 /* okay attempt to open it up */
457 if((master.msgs = fopen(master.status_file_name, "a")) == NULL) {
458 MyPerror(suck_phrases[0]);
459 master.msgs = stdout; /* reset to default */
460 }
461 else {
462 master.status_file = TRUE;
463 }
464 }
465 #ifdef HAVE_SETVBUF
466 setvbuf(master.msgs, NULL, _IOLBF, 0); /* set to line buffering */
467 #endif
468 /* do we batch up any lingering articles ? */
469 if(master.prebatch == TRUE) {
470 switch(master.batch) {
471 case BATCH_FALSE:
472 error_log(ERRLOG_REPORT, suck_phrases[58], NULL);
473 break;
474 case BATCH_INNXMIT:
475 do_innbatch(&master);
476 break;
477 case BATCH_RNEWS:
478 do_rnewsbatch(&master);
479 break;
480 case BATCH_LMOVE:
481 do_lmovebatch(&master);
482 break;
483 case BATCH_LIHAVE:
484 do_localpost(&master);
485 break;
486 }
487 }
488
489 /* now parse the killfile */
490 #ifdef KILLFILE
491 if(master.do_killfile == TRUE) {
492 master.killp = parse_killfile(KILL_KILLFILE, master.killfile_log, master.debug, master.kill_ignore_postfix);
493 }
494 #endif
495 /* now parse the xover killfile */
496 if(master.do_xover == TRUE) {
497 master.xoverp = parse_killfile(KILL_XOVER, master.killfile_log, master.debug, master.kill_ignore_postfix);
498 }
499
500 print_phrases(master.msgs, suck_phrases[1], master.host, NULL);
501 if(do_connect(&master, CONNECT_FIRST) != RETVAL_OK) {
502 retval = RETVAL_ERROR;
503 }
504 else {
505 /* if we have XOVER killfiles, we need to get the format of em before any processing */
506 if(master.xoverp != NULL || master.use_xover == TRUE) {
507 get_xoverview(&master);
508 killprg_sendoverview(&master);
509 }
510
511 /* first, check for restart articles, */
512 /* then scan for any new articles */
513 if((loop = restart_yn(&master)) == RESTART_ERROR) {
514 retval = RETVAL_ERROR;
515 }
516 else if(loop == RESTART_NO || master.rescan == TRUE) {
517 /* we don't do scan if we had restart and option = FALSE */
518 /* do we scan the local active file? */
519 if(master.do_active == TRUE || master.activefile != NULL) {
520 if(get_message_index_active(&master) < RETVAL_OK) {
521 retval = RETVAL_ERROR;
522 }
523 }
524 else if(get_message_index(&master) < RETVAL_OK) {
525 retval = RETVAL_ERROR;
526 }
527 if(retval == RETVAL_OK) {
528 retval = do_supplemental(&master);
529 }
530 if(retval == RETVAL_OK) {
531 retval = do_nodownload(&master);
532 }
533 if(retval == RETVAL_OK && master.head != NULL && master.nritems > 0) {
534 /* if we don't have any messages, we don't need to do this */
535 if(master.no_dedupe == FALSE) {
536 dedupe_list(&master);
537 }
538 #ifdef CHECK_HISTORY
539 if(master.do_chkhistory == TRUE) {
540 chkhistory(&master);
541 }
542 #endif
543 print_phrases(master.msgs,suck_phrases[20], str_int(master.nritems), NULL);
544 }
545 }
546 if(retval == RETVAL_OK) {
547 if(master.nritems == 0) {
548 print_phrases(master.msgs,suck_phrases[3], NULL);
549 retval = RETVAL_NOARTICLES;
550 }
551 else {
552 /* write out restart db */
553 retval = db_write(&master);
554 /* reconnect after dedupe, in case of time out due to long dedupe time*/
555 if(retval == RETVAL_OK && master.conn_dedupe == TRUE) {
556 retval = do_connect(&master, CONNECT_AGAIN);
557 }
558 if(retval == RETVAL_OK) {
559 retval = get_articles(&master);
560 }
561 }
562
563 }
564 if(retval == RETVAL_OK) { /* if we got disconnected above, don't do this */
565 /* send quit, and get reply */
566 sputline(master.sockfd,"quit\r\n", master.do_ssl, master.ssl_struct);
567 if(master.debug == TRUE) {
568 do_debug("Sending command: quit\n");
569 }
570 do {
571 resp = sgetline(master.sockfd, &inbuf, master.do_ssl, master.ssl_struct);
572 if(resp>0) {
573 if(master.debug == TRUE) {
574 do_debug("Quitting GOT: %s", inbuf);
575 }
576 number(inbuf, &nr);
577 }
578
579 }while(nr != 205 && resp > 0);
580 }
581 }
582 if(master.sockfd >= 0) {
583 disconnect_from_nntphost(master.sockfd, master.do_ssl, &master.ssl_struct);
584 print_phrases(master.msgs,suck_phrases[4], master.host, NULL);
585 }
586 if(master.debug == TRUE) {
587 do_debug("retval=%d (RETVAL_OK=%d), m.nrgot=%d, m.batch=%d\n", retval, RETVAL_OK, master.nrgot, master.batch);
588 }
589 if((retval == RETVAL_OK || master.always_batch == TRUE) && master.nrgot > 0 && master.header_only == FALSE) {
590 switch(master.batch) {
591 case BATCH_INNXMIT:
592 do_post_filter(&master);
593 do_innbatch(&master);
594 break;
595 case BATCH_RNEWS:
596 do_post_filter(&master);
597 do_rnewsbatch(&master);
598 break;
599 case BATCH_LMOVE:
600 do_post_filter(&master);
601 do_lmovebatch(&master);
602 break;
603 case BATCH_LIHAVE:
604 do_post_filter(&master);
605 do_localpost(&master);
606 break;
607 default:
608 break;
609 }
610 }
611 if((retval == RETVAL_NOARTICLES || retval == RETVAL_OK) && master.cleanup == TRUE) {
612 print_phrases(master.msgs, suck_phrases[7], NULL);
613 do_cleanup(&master);
614 }
615
616 /* close out status log */
617 if(master.msgs != NULL && master.msgs != stdout && master.msgs != stderr) {
618 fclose(master.msgs);
619 }
620 if(master.head != NULL) {
621 /* clean up memory */
622 master.curr = master.head;
623 while(master.curr != NULL) {
624 temp = (master.curr)->next;
625 free_one_node(master.curr);
626 master.curr = temp;
627 }
628 }
629 /* free up group list */
630 while (master.groups != NULL) {
631 ptemp=(master.groups)->next;
632 free(master.groups);
633 master.groups = ptemp;
634 }
635 /* free up overview.fmt list */
636 while (master.xoverview != NULL) {
637 pov = (master.xoverview)->next;
638 if((master.xoverview)->header != NULL) {
639 free((master.xoverview)->header);
640 }
641 free(master.xoverview);
642 master.xoverview = pov;
643 }
644
645 #ifdef KILLFILE
646 free_killfile(master.killp);
647 #endif
648 free_killfile(master.xoverp);
649
650 if(fargs != NULL) {
651 free_args(fargc, fargs);
652 }
653 #ifdef LOCKFILE
654 lockfile = full_path(FP_GET, FP_TMPDIR, N_LOCKFILE);
655 if(lockfile != NULL) {
656 unlink(lockfile);
657 }
658 #endif
659 }
660 free_phrases(); /* do this last so everything is displayed correctly */
661 exit(retval);
662 }
663 /*------------------------------------------------------------*/
do_connect(PMaster master,int which_time)664 int do_connect(PMaster master, int which_time) {
665
666 char *inbuf;
667 int nr, resp, retval = RETVAL_OK;
668 FILE *fp;
669
670
671 if(which_time != CONNECT_FIRST) {
672 /* close down previous connection */
673 sputline(master->sockfd, "quit\r\n", master->do_ssl, master->ssl_struct);
674 do {
675 resp = sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct);
676 if(resp>0) {
677 if(master->debug == TRUE) {
678 do_debug("Reconnect GOT: %s", inbuf);
679 }
680 number(inbuf, &nr);
681 }
682
683 }while(nr != 205 && resp > 0);
684 disconnect_from_nntphost(master->sockfd, master->do_ssl, &master->ssl_struct);
685
686 /* now reset everything */
687 if(master->curr != NULL) {
688 (master->curr)->sentcmd = FALSE;
689 }
690 master->grpnr = -1;
691 }
692
693 if(master->debug == TRUE) {
694 do_debug("Connecting to %s on port %d\n", master->host, master->portnr);
695 }
696 fp = (which_time == CONNECT_FIRST) ? master->msgs : NULL;
697
698 master->sockfd = connect_to_nntphost( master->host, NULL, 0, fp, master->portnr, master->do_ssl, &master->ssl_struct);
699
700 if(master->sockfd < 0 ) {
701 retval = RETVAL_ERROR;
702 }
703 else if(sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct) < 0) {
704 /* Get the announcement line */
705 retval = RETVAL_ERROR;
706 }
707 else {
708 if(master->debug == TRUE) {
709 do_debug("Got: %s", inbuf);
710 }
711 if(which_time == CONNECT_FIRST) {
712 fprintf(master->msgs ,"%s", inbuf );
713 }
714 /* check to see if we have to do authorization now */
715 number(inbuf, &resp);
716 if(resp == 480 ) {
717 retval = do_authenticate(master);
718 }
719 if(retval == RETVAL_OK && master->do_modereader == TRUE) {
720 retval = send_command(master, "mode reader\r\n", &inbuf, 0);
721 if(retval == RETVAL_OK) {
722 /* Again the announcement */
723 if(which_time == CONNECT_FIRST) {
724 fprintf(master->msgs ,"%s",inbuf);
725 }
726 }
727 }
728 if(master->auto_auth == TRUE) {
729 if(master->passwd == NULL || master->userid == NULL) {
730 error_log(ERRLOG_REPORT, suck_phrases[55], NULL);
731 retval = RETVAL_ERROR;
732 }
733 else {
734
735 /* auto authorize */
736 retval = do_authenticate(master);
737 }
738 }
739 }
740 return retval;
741 }
742
743 /*--------------------------------------------------------------------*/
get_message_index(PMaster master)744 int get_message_index(PMaster master) {
745
746 long lastread;
747 int nrread, retval, maxread;
748
749 char buf[MAXLINLEN], group[512];
750 FILE *ifp,*tmpfp;
751
752 retval = RETVAL_OK;
753 ifp = tmpfp = NULL;
754
755 TimerFunc(TIMER_START, 0, NULL);
756
757 if((ifp = fopen(full_path(FP_GET, FP_DATADIR, N_OLDRC), "r" )) == NULL) {
758 MyPerror(full_path(FP_GET, FP_DATADIR, N_OLDRC));
759 retval = RETVAL_ERROR;
760 }
761 else if((tmpfp = fopen(full_path(FP_GET, FP_TMPDIR, N_NEWRC), "w" )) == NULL) {
762 MyPerror(full_path(FP_GET, FP_TMPDIR, N_NEWRC));
763 retval = RETVAL_ERROR;
764 }
765 while(retval == RETVAL_OK && fgets(buf, MAXLINLEN-1, ifp) != NULL) {
766 if(buf[0] == SUCKNEWSRC_COMMENT_CHAR) {
767 /* skip this group */
768 fputs(buf, tmpfp);
769 print_phrases(master->msgs, suck_phrases[8], buf, NULL);
770 }
771 else {
772 maxread = -1; /* just in case */
773 nrread = sscanf(buf, "%s %ld %d\n", group, &lastread, &maxread);
774 if ( nrread < 2 || nrread > 3) {
775 error_log(ERRLOG_REPORT, suck_phrases[9], buf, NULL);
776 fputs(buf, tmpfp); /* rewrite the line */
777 }
778 else if(maxread == 0) {
779 /* just rewrite the line */
780 fputs(buf, tmpfp);
781 }
782 else {
783 retval = do_one_group(master, buf, group, tmpfp, lastread, maxread);
784 }
785 }
786 }
787
788 TimerFunc(TIMER_TIMEONLY, 0,master->msgs);
789
790 if(retval == RETVAL_OK) {
791 print_phrases(master->msgs, suck_phrases[16], str_int(master->nritems), NULL);
792 }
793 else if(ifp != NULL) {
794 /* this is in case we had to abort the above while loop (due to loss of pipe to server) and */
795 /* we hadn't finished writing out the suck.newrc, this finishes it up. */
796 do {
797 fputs(buf, tmpfp);
798 }
799 while(fgets(buf, MAXLINLEN-1, ifp) != NULL);
800 }
801 if(tmpfp != NULL) {
802 fclose(tmpfp);
803 }
804 if(ifp != NULL) {
805 fclose(ifp);
806 }
807 return retval;
808 }
809 /*-----------------------------------------------------------------------------------------------------*/
do_one_group(PMaster master,char * buf,char * group,FILE * newrc,long lastread,int maxread)810 int do_one_group(PMaster master, char *buf, char *group, FILE *newrc, long lastread, int maxread) {
811
812 char *sp, *inbuf, cmd[MAXLINLEN];
813 long count,low,high;
814 int response,retval,i;
815
816 retval = RETVAL_OK;
817
818
819 sprintf(cmd,"group %s\r\n",group);
820 if(send_command(master,cmd,&inbuf,0) != RETVAL_OK) {
821 retval = RETVAL_ERROR;
822 }
823 else {
824 sp = number(inbuf, &response);
825 if(response != 211) {
826 fputs(buf, newrc); /* rewrite line AS IS in newrc */
827 /* handle group not found */
828 switch(response) {
829 case 411:
830 error_log(ERRLOG_REPORT, suck_phrases[11], group, NULL);
831 break;
832 case 500:
833 error_log(ERRLOG_REPORT, suck_phrases[48], NULL);
834 break;
835 default:
836 error_log(ERRLOG_REPORT, suck_phrases[12],group,str_int(response),NULL);
837 retval = RETVAL_ERROR; /* bomb out on wacko errors */
838 break;
839 }
840 }
841 else {
842 sp = get_long(sp, &count);
843 sp = get_long(sp, &low);
844 sp = get_long(sp, &high);
845 fprintf(newrc, "%s %ld", group, high);
846
847 if(maxread > 0) {
848 fprintf(newrc, " %d", maxread);
849 }
850 fputs("\n", newrc);
851
852 /* add a sanity check in case remote host changes its numbering scheme */
853 /* the > 0 is needed, since if a nnrp site has no article it will reset */
854 /* the count to 0. So not an error */
855 if(lastread > high && high > 0) {
856 if(master->resetcounter == TRUE) {
857 /* reset lastread to low, so we pick up all articles in the group */
858 lastread = low;
859 print_phrases(master->msgs,suck_phrases[71],group,str_int(high),str_int(low),NULL);
860 }
861 else {
862 print_phrases(master->msgs,suck_phrases[13],group,str_int(high),NULL);
863 }
864 }
865
866 if(lastread < 0 ) {
867 /* if a neg number, get the last X nr of messages, handy for starting */
868 /* a new group off ,say -100 will get the last 100 messages */
869 lastread += high; /* this works since lastread is neg */
870 if(lastread < 0) {
871 lastread = 0; /* just to be on the safeside */
872 }
873 }
874
875 /* this has to be >= 0 since if there are no article on server high = 0 */
876 /* so if we write out 0, we must be able to handle zero as valid lastread */
877 /* the count > 0 so if no messages available we don't even try */
878 /* or if low > high no messages either */
879 if (low <= high && count > 0 && lastread < high && lastread >= 0) {
880 /* add group name to list of groups */
881
882 if(lastread < low) {
883 lastread = low - 1;
884 }
885 /* now set the max nr of messages to read */
886 if(maxread > 0 && high-maxread > lastread) {
887 if(master->low_read == TRUE) {
888 /* instead of limiting from the high-water mark (the latest articles) */
889 /* limit from the low-water mark (the oldest articles) */
890 high = (lastread+1) + maxread;
891 }
892 else {
893 lastread = high-maxread;
894 }
895 print_phrases(master->msgs, suck_phrases[14], group, str_int(maxread), NULL);
896 }
897 print_phrases(master->msgs, suck_phrases[15], group, str_long(high-lastread), str_long(lastread+1), str_long(high), NULL);
898 if(master->xoverp != NULL) {
899 /* do this via the xover killfile command */
900 retval = do_group_xover(master, group, lastread+1, high);
901 }
902 /* do we use xover or xhdr to get our article list */
903 if(master->use_xover == TRUE) {
904 retval = get_xover(master, group, lastread+1, high);
905 }
906 else if((master->xoverp == NULL) || (retval == RETVAL_NOXOVER)) {
907 /* do this the normal way */
908 sprintf(cmd, "xhdr Message-ID %ld-%ld\r\n", lastread+1,high);
909 i = send_command(master,cmd,&inbuf,221);
910 if(i != RETVAL_OK) {
911 retval = RETVAL_ERROR;
912 }
913 else {
914 do {
915 if(sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct) < 0) {
916 retval = RETVAL_ERROR;
917 }
918 else if (*inbuf != '.' ) {
919 retval = allocnode(master, inbuf, MANDATORY_OPTIONAL, group, 0L);
920 }
921 } while (retval == RETVAL_OK && *inbuf != '.' && *(inbuf+1) != '\n');
922 } /* end if response */
923 } /* end if xover */
924 } /* end if lastread */
925 } /* end response */
926 } /* end else */
927
928 if(retval == RETVAL_ERROR) {
929 error_log(ERRLOG_REPORT, suck_phrases[59], NULL);
930 }
931 return retval;
932 }
933 /*-----------------------------------------------------------*/
get_articles(PMaster master)934 int get_articles(PMaster master) {
935
936 int retval, logcount, i, grpnr, grplen;
937 long loop, downloaded;
938 PGroups grps;
939 const char *grpname;
940 const char *empty = "";
941
942 #ifdef HAVE_GETTIMEOFDAY
943 double bps;
944 #endif
945
946 #ifdef KILLFILE
947 int ( *get_message)(PMaster, int, long); /* which function will we use get_one_article or get_one_article_kill) */
948
949 grpnr = -1;
950 grps = NULL;
951 grpname = empty;
952 grplen = 0;
953 /* do we use killfiles? */
954 get_message = (master->killp == NULL) ? get_one_article : get_one_article_kill;
955 #endif
956 retval = RETVAL_OK;
957 downloaded = loop = 0; /* used to track how many downloaded, for reconnect option */
958
959 /* figure out how many digits wide the articleCount is for display purposes */
960 /* this used to be log10()+1, but that meant I needed the math library */
961 for(logcount=1, i=master->nritems; i > 9 ; logcount++) {
962 i /= 10;
963 }
964
965 if(master->MultiFile == TRUE && checkdir(full_path(FP_GET, FP_MSGDIR, NULL)) == FALSE) {
966 retval = RETVAL_ERROR;
967 }
968 else {
969 retval = db_open(master);
970 if(retval == RETVAL_OK) {
971 master->curr = master->head;
972
973 if(master->batch == BATCH_INNFEED) {
974 /* open up batch file for appending to */
975 if((master->innfeed = fopen(master->batchfile, "a")) == NULL) {
976 MyPerror(master->batchfile);
977 }
978 }
979 else if(master->batch == BATCH_LIHAVE) {
980 if((master->innfeed = fopen(full_path(FP_GET, FP_TMPDIR, master->batchfile), "a")) == NULL) {
981 MyPerror(full_path(FP_GET, FP_TMPDIR, master->batchfile));
982 retval = RETVAL_ERROR;
983 }
984 }
985
986 TimerFunc(TIMER_START, 0, NULL);
987
988 #ifdef MYSIGNAL
989 while(retval == RETVAL_OK && master->curr != NULL && GotSignal == FALSE) {
990 #else
991 while(retval == RETVAL_OK && master->curr != NULL) {
992 #endif
993 if(master->debug == TRUE) {
994 do_debug("Article nr = %s mandatory = %c\n", (master->curr)->msgnr, (master->curr)->mandatory);
995 }
996 loop++;
997 if((master->curr)->downloaded == FALSE) {
998 /* we haven't yet downloaded this article */
999 downloaded++;
1000
1001 /* to be polite to the server, lets allow for a pause every once in a while */
1002 if(master->pause_time > 0 && master->pause_nrmsgs > 0) {
1003 if((downloaded > 0) && (downloaded % master->pause_nrmsgs == 0)) {
1004 sleep(master->pause_time);
1005 }
1006 }
1007 if(master->status_file == FALSE && master->quiet == FALSE) {
1008 /* if we are going to a file, we don't want all of these articles printed */
1009 /* or if quiet flag is set */
1010 /* this stuff doesn't go thru print_phrases so I can keep spacing right */
1011 /* and I only print numbers and the BPS */
1012 #ifdef HAVE_GETTIMEOFDAY
1013 bps = TimerFunc(TIMER_GET_BPS, 0, master->msgs);
1014 #endif
1015 if(master->use_gui == TRUE) {
1016 /* this stuff is formatted by the GUI so we put it out in a format it likes */
1017 #ifndef HAVE_GETTIMEOFDAY
1018 fprintf(master->msgs, "---%ld+++\n",master->nritems - loop);
1019 #else
1020 fprintf(master->msgs, "---%ld+++%f\n", master->nritems - loop, bps);
1021 #endif /* HAVE_GETTIMEOFDAY */
1022 }
1023 else {
1024 if(master->show_group == TRUE) {
1025 /* add the group name (if available) to the line printed */
1026 if((master->curr)->groupnr >= 0) {
1027 if((master->curr)->groupnr != grpnr) {
1028 grpname = empty;
1029 grpnr = (master->curr)->groupnr;
1030 /* new group name find it */
1031 grps = master->groups;
1032 while(grps != NULL && grps->nr != grpnr) {
1033 grps = grps->next;
1034 }
1035 if(grps != NULL) {
1036 grpname = grps->group;
1037 /* calculate max len for format of printf*/
1038 /* which erases the previous group name */
1039 if(strlen(grpname) > grplen) {
1040 grplen = strlen(grpname);
1041 }
1042 }
1043 }
1044 }
1045 else {
1046 grpnr = -1;
1047 grpname = empty;
1048 }
1049 }
1050 #ifndef HAVE_GETTIMEOFDAY
1051 fprintf(master->msgs, "%5ld %-*s\r",master->nritems - loop, grplen, grpname);
1052 #else
1053 fprintf(master->msgs, "%5ld %9.1f %s %-*s\r",master->nritems-loop,bps,suck_phrases[5], grplen, grpname);
1054 #endif /* HAVE_GETTIMEOFDAY */
1055 }
1056 fflush(master->msgs); /* so message gets printed now */
1057 }
1058 #ifdef KILLFILE
1059 /* get one article */
1060 retval = (*get_message)(master, logcount, loop);
1061 #else
1062 retval = get_one_article(master, logcount, loop);
1063 #endif
1064 if(retval == RETVAL_OK ) {
1065 retval = db_mark_dled(master, master->curr);
1066 }
1067 }
1068
1069 master->curr = (master->curr)->next; /* get next article */
1070
1071 /* to be NOT polite to the server reconnect every X msgs to combat the INND */
1072 /* LIKE_PULLERS=DONT. This is last in loop, so if problem can abort gracefully */
1073 if((master->reconnect_nr > 0) && (downloaded > 0) && (downloaded % master->reconnect_nr == 0)) {
1074 retval = do_connect(master, CONNECT_AGAIN);
1075 if(master->curr != NULL) {
1076 (master->curr)->sentcmd = FALSE; /* if we sent command force resend */
1077 }
1078 }
1079 // do a local post every x number of articles
1080 if(master->batch == BATCH_LIHAVE && master->batch_post_nr > 0 && master->nrgot >= master->batch_post_nr) {
1081 fclose(master->innfeed);
1082 master->innfeed = NULL;
1083 do_post_filter(master);
1084 retval = do_localpost(master);
1085 if(retval == RETVAL_OK) {
1086 master->nrgot = 0; // so if no more articles left we don't try to post in main routine
1087 if((master->innfeed = fopen(full_path(FP_GET, FP_TMPDIR, master->batchfile), "a")) == NULL) {
1088 MyPerror(full_path(FP_GET, FP_TMPDIR, master->batchfile));
1089 retval = RETVAL_ERROR;
1090 }
1091 }
1092 }
1093
1094 } /* end while */
1095 db_close(master);
1096 if(retval == RETVAL_OK && master->nritems == loop) {
1097 db_delete(master);
1098 }
1099
1100 if(retval == RETVAL_OK) {
1101 TimerFunc(TIMER_TOTALS, 0, master->msgs);
1102 }
1103 if(master->innfeed != NULL) {
1104 fclose(master->innfeed);
1105 }
1106 }
1107 }
1108 return retval;
1109 }
1110 /*-----------------------------------------------*/
1111 /* add items from supplemental list to link list */
1112 /* ----------------------------------------------*/
1113 int do_supplemental(PMaster master) {
1114
1115 int retval, oldkept;
1116 FILE *fp;
1117 char linein[MAXLINLEN+1];
1118
1119 retval = RETVAL_OK;
1120 oldkept = master->nritems;
1121
1122 if((fp = fopen(full_path(FP_GET, FP_DATADIR, N_SUPPLEMENTAL), "r")) != NULL) {
1123 print_phrases(master->msgs, suck_phrases[17], NULL);
1124 while(retval == RETVAL_OK && fgets(linein, MAXLINLEN, fp) != NULL) {
1125 if(linein[0] == '!') {
1126 retval = do_sup_bynr(master, linein);
1127 }
1128 else if(linein[0] == '<') {
1129 retval = allocnode(master, linein, MANDATORY_YES, NULL, 0L);
1130 }
1131 else {
1132 error_log(ERRLOG_REPORT, suck_phrases[18], linein, NULL);
1133 }
1134 }
1135 print_phrases(master->msgs, suck_phrases[19], str_int(master->nritems-oldkept), \
1136 str_int(master->nritems), NULL);
1137 fclose(fp);
1138 }
1139
1140 return retval;
1141 }
1142 /*------------------------------------------------------------------------------------------*/
1143 int do_sup_bynr(PMaster master, char *linein) {
1144 int retval = RETVAL_OK;
1145 /* this routine takes in a line formated !group_name article_nr */
1146 /* gets it's Msg-ID (via XHDR), then adds it to our list of articles */
1147 /* as a mandatory article to download */
1148
1149 char grpname[MAX_GRP_LEN], cmd[MAXLINLEN], *resp;
1150 int i, done;
1151 long nrlow, nrhigh;
1152
1153 if(master->debug == TRUE) {
1154 do_debug("supplemental adding %s", linein);
1155 }
1156 i = sscanf(linein, "!%s %ld-%ld", grpname, &nrlow, &nrhigh);
1157 if(i < 2) {
1158 error_log(ERRLOG_REPORT, suck_phrases[18], linein, NULL);
1159 }
1160 else {
1161 sprintf(cmd,"group %s\r\n",grpname);
1162 if(send_command(master,cmd,NULL,211) == RETVAL_OK) {
1163 if(i == 2) {
1164 sprintf(cmd,"xhdr Message-ID %ld\r\n",nrlow);
1165 }
1166 else {
1167 sprintf(cmd,"xhdr Message-ID %ld-%ld\r\n",nrlow,nrhigh);
1168 }
1169 if(send_command(master,cmd,NULL,221) == RETVAL_OK) {
1170 /* now have to get message-id and the . */
1171 done = FALSE;
1172 while( done == FALSE) {
1173 if(sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct) < 0) {
1174 retval = RETVAL_ERROR;
1175 done = TRUE;
1176 }
1177 else {
1178 if(master->debug == TRUE) {
1179 do_debug("Got %s", resp);
1180 }
1181 if(*resp == '.') {
1182 done = TRUE;
1183 }
1184 else {
1185 retval = allocnode(master, resp, MANDATORY_YES, grpname, 0L);
1186 }
1187 }
1188 }
1189 }
1190 }
1191 }
1192
1193 return retval;
1194
1195 }
1196 /*---------------------------------------------------------------------*/
1197 int do_nodownload(PMaster master) {
1198 int i, retval = RETVAL_OK;
1199 FILE *fp;
1200 PList item, prev;
1201 char linein[MAXLINLEN+1];
1202 long nrlines = 0, nrnuked = 0;
1203
1204 if((fp = fopen(full_path(FP_GET, FP_DATADIR, N_NODOWNLOAD), "r")) != NULL) {
1205 print_phrases(master->msgs, suck_phrases[68], NULL);
1206 while(retval == RETVAL_OK && fgets(linein, MAXLINLEN, fp) != NULL) {
1207 nrlines++;
1208 i = strlen(linein) - 1; /* so it points at last char */
1209 /* strip off nl */
1210 if(linein[i] == '\n') {
1211 linein[i] = '\0';
1212 i--;
1213 }
1214 /* strip off extra spaces on the end */
1215 while(isspace(linein[i])){
1216 linein[i] = '\0';
1217 i--;
1218 }
1219 item = master->head;
1220 prev = NULL;
1221 /* find start of msgid */
1222 if(linein[0] != '<') {
1223 error_log(ERRLOG_REPORT, suck_phrases[69], linein, NULL);
1224 }
1225 else {
1226 if(master->debug == TRUE) {
1227 do_debug("Checking Nodownload - %s\n", linein);
1228 }
1229
1230 while((item != NULL) && (cmp_msgid(linein, item->msgnr) == FALSE)) {
1231 prev = item;
1232 item = item->next;
1233 }
1234 if(item != NULL) {
1235 /* found a match, remove from the list */
1236 if(master->debug == TRUE) {
1237 do_debug("Matched, nuking from list\n");
1238 }
1239 nrnuked++;
1240 master->nritems--;
1241 if(item == master->head) {
1242 /* change top of list */
1243 master->head = (master->head)->next;
1244 }
1245 else {
1246 /* its not the end of the list */
1247 prev->next = item->next;
1248 free_one_node(item);
1249 }
1250 }
1251 }
1252 }
1253 fclose(fp);
1254 print_phrases(master->msgs, suck_phrases[70], str_long(nrlines), str_long(nrnuked), str_long(master->nritems), NULL);
1255 }
1256 return retval;
1257 }
1258 /*----------------------------------------------------------------------*/
1259 int get_group_number(PMaster master, char *grpname) {
1260
1261 PGroups grps, gptr;
1262 int groupnr = 0;
1263
1264 /* first, find out if it doesn't exist already */
1265 gptr = master->groups;
1266 while(gptr != NULL && groupnr == 0) {
1267 if(strcmp(gptr->group, grpname) == 0) {
1268 /* bingo */
1269 groupnr = gptr->nr;
1270 }
1271 else {
1272 gptr = gptr->next;
1273 }
1274 }
1275
1276 if(groupnr == 0) {
1277 /* add group to group list and get group nr */
1278 if((grps = malloc(sizeof(Groups))) == NULL) {
1279 /* out of memory */
1280 error_log(ERRLOG_REPORT, suck_phrases[22], NULL);
1281 }
1282 else {
1283 grps->next = NULL;
1284 strcpy(grps->group, grpname);
1285 /* now add to list and count groupnr */
1286 if(master->groups == NULL) {
1287 grps->nr = 1;
1288 master->groups = grps;
1289 }
1290 else {
1291 gptr = master->groups;
1292 while(gptr->next != NULL) {
1293 gptr = gptr->next;
1294 }
1295 gptr->next = grps;
1296 grps->nr = gptr->nr + 1;
1297 }
1298 groupnr = grps->nr;
1299
1300 if(master->debug == TRUE) {
1301 do_debug("Adding to group list: %d %s\n", grps->nr, grps->group);
1302 }
1303 }
1304 }
1305 return groupnr;
1306
1307 }
1308 /*-----------------------------------------------------------------------*/
1309 int allocnode(PMaster master, char *linein, int mandatory, char *group, long msgnr_in) {
1310 /* if msgnr_in is not filled in (0), then parse the msgnr off the linein */
1311
1312 /* if allocate memory here, must free in free_one_node */
1313
1314 PList ptr = NULL;
1315 char *end_ptr, *st_ptr;
1316 static PList curr = NULL; /* keep track of current end of list */
1317 int groupnr = 0, retval = RETVAL_OK;
1318 long msgnr = 0;
1319
1320 static int warned = FALSE;
1321
1322 /* get the article nr */
1323 if(group == NULL) {
1324 /* we're called by do_supplemental */
1325 st_ptr = linein;
1326 }
1327 else {
1328 if (msgnr_in > 0) {
1329 /* we're prob called by xover code */
1330 st_ptr = linein;
1331 msgnr = msgnr_in;
1332 }
1333 else {
1334 st_ptr = get_long(linein,&msgnr);
1335 if(msgnr == 0 && warned == FALSE) {
1336 warned = TRUE;
1337 error_log(ERRLOG_REPORT, suck_phrases[53], NULL);
1338 }
1339 }
1340
1341 if(msgnr > 0 && group != NULL) {
1342 groupnr = get_group_number(master, group);
1343 }
1344 }
1345
1346 if(retval == RETVAL_OK) {
1347 /* find the message id */
1348 while(*st_ptr != '<' && *st_ptr != '\0') {
1349 st_ptr++;
1350 }
1351 end_ptr = st_ptr;
1352 while(*end_ptr != '>' && *end_ptr != '\0') {
1353 end_ptr++;
1354 }
1355 if((*st_ptr != '<') || ( master->chk_msgid == TRUE && *end_ptr != '>')) {
1356 error_log(ERRLOG_REPORT, suck_phrases[21], linein, NULL);
1357 }
1358 else {
1359 *(end_ptr+1) = '\0'; /* ensure null termination */
1360
1361 if((ptr = malloc(sizeof(List))) == NULL) {
1362 error_log(ERRLOG_REPORT, suck_phrases[22], NULL);
1363 retval = RETVAL_ERROR;
1364 }
1365 else if(strlen(st_ptr) >= MAX_MSGID_LEN) {
1366 error_log(ERRLOG_REPORT, suck_phrases[63], group, msgnr, NULL);
1367 free(ptr);
1368 }
1369 else {
1370 strcpy(ptr->msgnr, st_ptr);
1371 ptr->next = NULL;
1372 ptr->mandatory = (char) mandatory;
1373 ptr->sentcmd = FALSE; /* have we sent command to remote */
1374 ptr->nr = msgnr;
1375 ptr->groupnr = groupnr;
1376 ptr->downloaded = FALSE;
1377 ptr->dbnr = 0L;
1378 ptr->delete = FALSE;
1379
1380 if(master->debug == TRUE) {
1381 do_debug("MSGID %s NR %d GRP %d MANDATORY %c added\n",ptr->msgnr, ptr->nr, ptr->groupnr, ptr->mandatory);
1382 }
1383
1384 /* now put on list */
1385 if( curr == NULL) {
1386 if(master->head == NULL) {
1387 /* first node */
1388 master->head = curr = ptr;
1389 }
1390 else {
1391 /* came in with a restart, find end of list */
1392 curr = master->head;
1393 while(curr->next != NULL) {
1394 curr = curr->next;
1395 }
1396
1397 /* now add node on */
1398 curr->next = ptr;
1399 curr = ptr;
1400 }
1401 }
1402 else {
1403 curr->next = ptr;
1404 curr = ptr;
1405 }
1406 master->nritems++;
1407 }
1408 }
1409 }
1410
1411 return retval;
1412 }
1413 /*------------------------------------------------------------------------*/
1414 void free_one_node(PList node) {
1415
1416 free(node);
1417 }
1418 /*----------------------------------------------------------------------------------*/
1419 const char *build_command(PMaster master, const char *cmdstart, PList article) {
1420 static char cmd[MAXLINLEN+1];
1421 static int warned = FALSE;
1422 char *resp;
1423 char grpcmd[MAXLINLEN+1];
1424
1425 PGroups grps;
1426
1427 /* build command to get article/head/body */
1428 /* if nrmode is on send group cmd if needed */
1429 if(master->nrmode == TRUE) {
1430 if(article->nr == 0) {
1431 master->nrmode = FALSE;
1432 }
1433 else if(master->grpnr != article->groupnr) {
1434 grps = master->groups;
1435 while(grps != NULL && grps->nr != article->groupnr) {
1436 grps = grps->next;
1437 }
1438 if(grps != NULL) {
1439 /* okay send group command */
1440 snprintf(grpcmd, MAXLINLEN, "GROUP %s\r\n", grps->group);
1441 if(send_command(master, grpcmd, &resp, 211) != RETVAL_OK) {
1442 master->nrmode = FALSE; /* can't chg groups turn it off */
1443 }
1444 else {
1445 /* so don't redo group command on next one */
1446 master->grpnr = grps->nr;
1447 }
1448 }
1449 }
1450 if(master->nrmode == TRUE) {
1451 /* everything hunky dory, we've switched groups, and got a good nr */
1452 snprintf(cmd, MAXLINLEN, "%s %ld\r\n", cmdstart, article->nr);
1453 }
1454 else if(warned == FALSE) {
1455 /* tell of the switch to nonnr mode */
1456 warned = TRUE;
1457 error_log(ERRLOG_REPORT, suck_phrases[54], NULL);
1458 }
1459 }
1460 /* the if is in case we changed it above */
1461 if(master->nrmode == FALSE) {
1462 snprintf(cmd, MAXLINLEN, "%s %s\r\n", cmdstart, article->msgnr);
1463 }
1464 return cmd;
1465 }
1466 /*----------------------------------------------------------------------------------*/
1467 int get_one_article(PMaster master, int logcount, long itemon) {
1468
1469 int nr, len, retval = RETVAL_OK;
1470 char buf[MAXLINLEN+1];
1471 const char *cmd = "", *tname = NULL;
1472 char fname[PATH_MAX+1];
1473
1474 char *resp;
1475 PList plist;
1476
1477 FILE *fptr = stdout; /* the default */
1478
1479 fname[0] = '\0'; /* just in case */
1480
1481 /* first send command to get article if not already sent */
1482 if((master->curr)->sentcmd == FALSE) {
1483 cmd = (master->header_only == FALSE)
1484 ? build_command(master, "article", master->curr)
1485 : build_command(master, "head", master->curr);
1486 if(master->debug == TRUE) {
1487 do_debug("Sending command: \"%s\"",cmd);
1488 }
1489 sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
1490 (master->curr)->sentcmd = TRUE;
1491 }
1492 plist = (master->curr)->next;
1493 if(plist != NULL && master->batch_post_nr == 0) {
1494 /* if batch_post_nr > 0 we're doing periodic batch posts */
1495 /* which confuses the remote server so we can't use pipelining */
1496 if(master->nrmode == FALSE || plist->groupnr == (master->curr)->groupnr) {
1497 /* can't use pipelining if nrmode on, cause of GROUP command I'll have to send*/
1498 if(plist->sentcmd == FALSE) {
1499 /* send next command */
1500 cmd = (master->header_only == FALSE)
1501 ? build_command(master, "article", plist)
1502 : build_command(master, "head", plist);
1503 if(master->debug == TRUE) {
1504 do_debug("Sending command: \"%s\"",cmd);
1505 }
1506 sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
1507 plist->sentcmd = TRUE;
1508 }
1509 }
1510 }
1511
1512 /* now while the remote is finding the article lets set up */
1513
1514 if(master->MultiFile == TRUE) {
1515 /* open file */
1516 /* file name will be ####-#### ex 001-166 (nron,total) */
1517 sprintf(buf,"%0*ld-%d", logcount, itemon ,master->nritems);
1518 /* the strcpy to avoid wiping out fname in second call to full_path */
1519 strcpy(fname, full_path(FP_GET, FP_MSGDIR, buf));
1520 strcat(buf, N_TMP_EXTENSION); /* add tmp extension */
1521 tname = full_path(FP_GET, FP_TMPDIR, buf); /* temp file name */
1522
1523 if(master->debug == TRUE) {
1524 do_debug("File name = \"%s\" temp = \"%s\"", fname, tname);
1525 }
1526 if((fptr = fopen(tname, "w")) == NULL) {
1527 MyPerror(tname);
1528 retval = RETVAL_ERROR;
1529 }
1530 }
1531
1532 /* okay hopefully by this time the remote end is ready */
1533 if((len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct)) < 0) {
1534 retval = RETVAL_ERROR;
1535 }
1536 else {
1537 if(master->debug == TRUE) {
1538 do_debug("got answer: %s", resp);
1539 }
1540
1541 TimerFunc(TIMER_ADDBYTES, len, NULL);
1542
1543 number(resp, &nr);
1544 if(nr == 480) {
1545 /* got to authorize, negate send-ahead for next msg */
1546 if(plist != NULL) {
1547 plist->sentcmd = FALSE; /* cause its gonna error out in do_auth() */
1548 }
1549
1550 if(do_authenticate(master) == RETVAL_OK) {
1551 /* resend command for current article */
1552 cmd = build_command(master, "article", master->curr);
1553 if(master->debug == TRUE) {
1554 do_debug("Sending command: \"%s\"",cmd);
1555 }
1556 sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
1557 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
1558 if(master->debug == TRUE) {
1559 do_debug("got answer: %s", resp);
1560 }
1561 TimerFunc(TIMER_ADDBYTES, len, NULL);
1562 number(resp, &nr);
1563 }
1564 else {
1565 retval = RETVAL_ERROR;
1566 }
1567 }
1568 /* 221 = header 220 = article */
1569 if(nr == 220 || nr == 221 ) {
1570 /* get the article */
1571 if((retval = get_a_chunk(master, fptr)) == RETVAL_OK) {
1572 master->nrgot++;
1573 if (fptr == stdout) {
1574 /* gracefully end the message in stdout version */
1575 fputs(".\n", stdout);
1576 }
1577 }
1578 }
1579 else
1580 {
1581 /* don't return RETVAL_ERROR so we can get next article */
1582 error_log(ERRLOG_REPORT, suck_phrases[29], cmd ,resp, NULL);
1583 }
1584 }
1585 if(fptr != NULL && fptr != stdout) {
1586 fclose(fptr);
1587 }
1588
1589 if(master->MultiFile == TRUE) {
1590 if(retval == RETVAL_ERROR || (nr != 221 && nr != 220)) {
1591 unlink(tname);
1592 }
1593 /* now rename it to the permanent file name */
1594 else {
1595 move_file(tname, fname);
1596 if((master->batch == BATCH_INNFEED || master->batch == BATCH_LIHAVE) && master->innfeed != NULL) {
1597 /* write path name and msgid to file */
1598 fprintf(master->innfeed, "%s %s\n", fname, (master->curr)->msgnr);
1599 fflush(master->innfeed); /* so it gets written sooner */
1600 }
1601 }
1602 }
1603 return retval;
1604 }
1605 /*---------------------------------------------------------------------------*/
1606 int get_a_chunk(PMaster master, FILE *fptr) {
1607
1608 int done, partial, len, retval;
1609 char *inbuf;
1610 size_t nr;
1611
1612 retval = RETVAL_OK;
1613 done = FALSE;
1614 partial = FALSE;
1615 len = 0; /* just to get rid of a compiler warning */
1616 /* partial = was the previous line a complete line or not */
1617 /* this is needed to avoid a scenario where the line is MAXLINLEN+1 */
1618 /* long and the last character is a ., which would make us think */
1619 /* that we are at the end of the article when we actually aren't */
1620
1621 while(done == FALSE && (len = sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct)) >= 0) {
1622
1623 TimerFunc(TIMER_ADDBYTES, len, NULL);
1624
1625 if(inbuf[0] == '.' && partial == FALSE) {
1626 if(len == 2 && inbuf[1] == '\n') {
1627 done = TRUE;
1628 }
1629 else {
1630 /* handle double dots IAW RFC977 2.4.1*/
1631 inbuf++; /* move past first dot */
1632 len--;
1633 }
1634 }
1635 if(done == FALSE) {
1636 if(retval == RETVAL_OK) {
1637 nr = fwrite(inbuf, sizeof(inbuf[0]), len, fptr);
1638 fflush(fptr);
1639 if(nr != len) {
1640 retval = RETVAL_ERROR;
1641 error_log(ERRLOG_REPORT, suck_phrases[57], NULL);
1642 }
1643 }
1644 partial= (len==MAXLINLEN&&inbuf[len-1]!='\n') ? TRUE : FALSE;
1645 }
1646 }
1647 if(len < 0) {
1648 retval = RETVAL_ERROR;
1649 }
1650 return retval;
1651 }
1652 /*-----------------------------------------------------------------------------*/
1653 int restart_yn(PMaster master) {
1654 int done, retval = RESTART_NO;
1655 PList itemon;
1656 long itemnr;
1657 const char *fname;
1658 struct stat buf;
1659
1660 fname = full_path(FP_GET, FP_TMPDIR, N_DBFILE);
1661 if(stat(fname, &buf) == 0) {
1662 /* restart file exists */
1663 retval = db_read(master);
1664 if(retval == RETVAL_OK) {
1665 /* find out where we stopped */
1666 itemon = master->head;
1667 itemnr = 0L;
1668 done = FALSE;
1669 while(itemon != NULL && done == FALSE) {
1670 itemnr++;
1671 if(itemon->downloaded == FALSE) {
1672 /* so that we don't think we already sent command */
1673 itemon->sentcmd = FALSE;
1674 if(master->skip_on_restart == TRUE) {
1675 /* mark one more */
1676 print_phrases(master->msgs, suck_phrases[60], NULL);
1677 itemon->downloaded = TRUE;
1678 }
1679 else {
1680 itemnr--; /* don't count this one */
1681 }
1682 done = TRUE;
1683 }
1684 itemon = itemon->next;
1685 }
1686 }
1687 }
1688 return retval;
1689 }
1690 /*-----------------------------------------------------------------*/
1691 #ifdef MYSIGNAL
1692 RETSIGTYPE sighandler(int what) {
1693
1694 switch(what) {
1695 case PAUSESIGNAL:
1696 pause_signal(PAUSE_DO, NULL);
1697 /* if we don't do this, the next time called, we'll abort */
1698 /* signal(PAUSESIGNAL, sighandler); */
1699 /* signal_block(MYSIGNAL_SETUP); */ /* just to be on the safe side */
1700 break;
1701 case MYSIGNAL:
1702 case MYSIGNAL2:
1703 default:
1704 error_log(ERRLOG_REPORT, suck_phrases[24], NULL);
1705 GotSignal = TRUE;
1706 }
1707 }
1708 /*----------------------------------------------------------------*/
1709 void pause_signal(int action, PMaster master) {
1710
1711 int x, y;
1712 static PMaster psave = NULL;
1713
1714 switch(action) {
1715 case PAUSE_SETUP:
1716 psave = master;
1717 break;
1718 case PAUSE_DO:
1719 if(psave == NULL) {
1720 error_log(ERRLOG_REPORT, suck_phrases[25], NULL);
1721 }
1722 else {
1723 /* swap pause_time and pause_nrmsgs with the sig versions */
1724 x = psave->pause_time;
1725 y = psave->pause_nrmsgs;
1726 psave->pause_time = psave->sig_pause_time;
1727 psave->pause_nrmsgs = psave->sig_pause_nrmsgs;
1728 psave->sig_pause_time = x;
1729 psave->sig_pause_nrmsgs = y;
1730 print_phrases(psave->msgs, suck_phrases[26], NULL);
1731 }
1732 }
1733 }
1734 #endif
1735 /*----------------------------------------------------------------*/
1736 int send_command(PMaster master, const char *cmd, char **ret_response, int good_response) {
1737 /* this is needed so can do user authorization */
1738
1739 int len, retval = RETVAL_OK, nr;
1740 char *resp;
1741
1742 if(master->debug == TRUE) {
1743 do_debug("sending command: %s", cmd);
1744 }
1745 sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
1746 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
1747 if( len < 0) {
1748 retval = RETVAL_ERROR;
1749 }
1750 else {
1751 if(master->debug == TRUE) {
1752 do_debug("got answer: %s", resp);
1753 }
1754
1755 TimerFunc(TIMER_ADDBYTES, len, NULL);
1756
1757 number(resp, &nr);
1758 if(nr == 480 ) {
1759 /* we must do authorization */
1760 retval = do_authenticate(master);
1761 if(retval == RETVAL_OK) {
1762 /* resend command */
1763 sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
1764 if(master->debug == TRUE) {
1765 do_debug("sending command: %s", cmd);
1766 }
1767 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
1768 if( len < 0) {
1769 retval = RETVAL_ERROR;
1770 }
1771 else {
1772 number(resp,&nr);
1773 if(master->debug == TRUE) {
1774 do_debug("got answer: %s", resp);
1775 }
1776
1777 TimerFunc(TIMER_ADDBYTES, len, NULL);
1778
1779 }
1780 }
1781 }
1782 if (good_response != 0 && nr != good_response) {
1783 error_log(ERRLOG_REPORT, suck_phrases[29],cmd,resp,NULL);
1784 retval = RETVAL_UNEXPECTEDANS;
1785 }
1786 }
1787 if(ret_response != NULL) {
1788 *ret_response = resp;
1789 }
1790 return retval;
1791 }
1792 /*------------------------------------------------------------------------------*/
1793 /* 1. move N_OLDRC to N_OLD_OLDRC N_OLDRC might not exist */
1794 /* 2. move N_NEWRC to N_OLDRC */
1795 /* 3. rm N_SUPPLEMENTAL */
1796 /*------------------------------------------------------------------------------*/
1797 void do_cleanup(PMaster master) {
1798 const char *oldptr;
1799 char ptr[PATH_MAX+1];
1800 struct stat buf;
1801 int exist;
1802 int okay = TRUE;
1803
1804 if(master->debug == TRUE) {
1805 do_debug("checking for existance of suck.newrc\n");
1806 }
1807
1808 if(stat(full_path(FP_GET, FP_TMPDIR, N_NEWRC), &buf) == 0) {
1809 if(master->debug == TRUE) {
1810 do_debug("found suck.newrc\n");
1811 }
1812
1813 /* we do the above test, in case we were in a restart */
1814 /* with -R which would cause no suck.newrc to be created */
1815 /* since message_index() would be skipped */
1816 /* since no suck.newrc, don't move any of the sucknewrcs around */
1817 strcpy(ptr,full_path(FP_GET, FP_DATADIR, N_OLDRC));
1818 /* must strcpy since full path overwrites itself everytime */
1819 oldptr = full_path(FP_GET, FP_DATADIR, N_OLD_OLDRC);
1820
1821 /* does the sucknewsrc file exist ? */
1822 exist = TRUE;
1823 if(stat(ptr, &buf) != 0 && errno == ENOENT) {
1824 exist = FALSE;
1825 }
1826 if(master->debug == TRUE) {
1827 do_debug("sucknewsrc.old = %s sucknewsrc = %s exist = %s\n", oldptr, ptr, true_str(exist));
1828 }
1829 if(exist == TRUE && move_file(ptr, oldptr) != 0) {
1830 MyPerror(suck_phrases[30]);
1831 okay = FALSE;
1832 }
1833 else if(move_file(full_path(FP_GET, FP_TMPDIR, N_NEWRC), ptr) != 0) {
1834 MyPerror(suck_phrases[31]);
1835 okay = FALSE;
1836 }
1837 }
1838 else if( errno != ENOENT) {
1839 MyPerror(full_path(FP_GET, FP_DATADIR, N_NEWRC));
1840 okay = FALSE;
1841 }
1842 if(okay == TRUE && unlink(full_path(FP_GET, FP_DATADIR, N_SUPPLEMENTAL)) != 0 && errno != ENOENT) {
1843 /* ENOENT is not an error since this file may not always exist */
1844 MyPerror(suck_phrases[33]);
1845 }
1846 }
1847 /*--------------------------------------------------------------------------------------*/
1848 int scan_args(PMaster master, int argc, char *argv[]) {
1849
1850 int loop, i, whicharg, arg, retval = RETVAL_OK;
1851
1852 for(loop = 0 ; loop < argc && retval == RETVAL_OK ; loop++) {
1853 arg = ARG_NO_MATCH;
1854 whicharg = -1;
1855
1856 if(master->debug == TRUE) {
1857 do_debug("Checking arg #%d-%d: '%s'\n", loop, argc, argv[loop]);
1858 }
1859
1860 if(argv[loop][0] == FILE_CHAR) {
1861 /* totally skip these they are processed elsewhere */
1862 continue;
1863 }
1864
1865 if(argv[loop][0] == '-' && argv[loop][1] == '-') {
1866 /* check long args */
1867 for(i = 0 ; i < NR_ARGS && arg == ARG_NO_MATCH ; i++) {
1868 if(arglist[i].larg != NULL) {
1869 if(strcmp(&argv[loop][2], arglist[i].larg) == 0) {
1870 arg = arglist[i].flag;
1871 whicharg = i;
1872 }
1873 }
1874 }
1875 }
1876 else if (argv[loop][0] == '-') {
1877 /* check short args */
1878 for(i = 0 ; i < NR_ARGS ; i++) {
1879 if(arglist[i].sarg != NULL) {
1880 if(strcmp(&argv[loop][1], arglist[i].sarg) == 0) {
1881 arg = arglist[i].flag;
1882 whicharg = i;
1883 }
1884 }
1885 }
1886 }
1887 if(arg == ARG_NO_MATCH) {
1888 /* we ignore the file_char arg, it is processed elsewhere */
1889 retval = RETVAL_ERROR;
1890 error_log(ERRLOG_REPORT, suck_phrases[34], argv[loop], NULL);
1891 }
1892 else {
1893 /* need to check for valid nr of args and feed em down */
1894 if(( loop + arglist[whicharg].nr_params) < argc) {
1895 retval = parse_args(master, arg, &(argv[loop+1]));
1896 /* skip em so they don't get processed as args */
1897 loop += arglist[whicharg].nr_params;
1898 }
1899 else {
1900 i = (arglist[whicharg].errmsg < 0) ? 34 : arglist[whicharg].errmsg;
1901 error_log(ERRLOG_REPORT, suck_phrases[i], NULL);
1902 retval = RETVAL_ERROR;
1903 }
1904 }
1905 }
1906
1907 return retval;
1908 }
1909 /*---------------------------------------------------------------------------------------*/
1910 int parse_args(PMaster master, int arg, char *argv[]) {
1911
1912 int x, retval = RETVAL_OK;
1913
1914 switch(arg) {
1915 case ARG_ALWAYS_BATCH:
1916 /* if we have downloaded at least one article, then batch up */
1917 /* even on errors */
1918 master->always_batch = TRUE;
1919 break;
1920 /* batch file implies MultiFile mode */
1921 case ARG_BATCH_INN:
1922 master->batch = BATCH_INNXMIT;
1923 master->MultiFile = TRUE;
1924 master->batchfile = argv[0];
1925 break;
1926 case ARG_BATCH_RNEWS:
1927 master->batch = BATCH_RNEWS;
1928 master->MultiFile = TRUE;
1929 master->batchfile = argv[0];
1930 break;
1931 case ARG_BATCH_LMOVE:
1932 master->batch = BATCH_LMOVE;
1933 master->MultiFile = TRUE;
1934 master->batchfile = argv[0];
1935 break;
1936 case ARG_BATCH_INNFEED:
1937 master->batch = BATCH_INNFEED;
1938 master->MultiFile = TRUE;
1939 master->batchfile = argv[0];
1940 break;
1941 case ARG_BATCH_POST_NR:
1942 master->batch_post_nr = atoi(argv[0]);
1943 /* no break fall thru to the rest of batch_post*/
1944 case ARG_BATCH_POST:
1945 master->batch = BATCH_LIHAVE;
1946 master->MultiFile = TRUE;
1947 master->batchfile = N_POSTFILE;
1948 break;
1949 case ARG_CLEANUP: /* cleanup option */
1950 master->cleanup = TRUE;
1951 break;
1952 case ARG_DIR_TEMP:
1953 full_path(FP_SET, FP_TMPDIR, argv[0]);
1954 break;
1955 case ARG_DIR_DATA:
1956 full_path(FP_SET, FP_DATADIR, argv[0]);
1957 break;
1958 case ARG_DIR_MSGS:
1959 full_path(FP_SET, FP_MSGDIR, argv[0]);
1960 break;
1961 case ARG_DEF_ERRLOG: /* use default error log path */
1962 error_log(ERRLOG_SET_FILE, ERROR_LOG, NULL);
1963 master->errlog = ERROR_LOG;
1964 break;
1965 case ARG_HOST: /* host name */
1966 master->host = argv[0];
1967 break;
1968 case ARG_NO_POSTFIX: /* kill files don't use the postfix */
1969 master->kill_ignore_postfix = TRUE;
1970 break;
1971 case ARG_LANGUAGE: /* load language support */
1972 master->phrases = argv[0];
1973 break;
1974 case ARG_MULTIFILE:
1975 master->MultiFile = TRUE;
1976 break;
1977 case ARG_POSTFIX:
1978 full_path(FP_SET_POSTFIX, FP_NONE, argv[0]);
1979 break;
1980 case ARG_QUIET: /* don't display BPS and message count */
1981 master->quiet = TRUE;
1982 break;
1983 case ARG_RNEWSSIZE:
1984 master->rnews_size = atol(argv[0]);
1985 break;
1986 case ARG_DEF_STATLOG: /* use default status log name */
1987 master->status_file_name = STATUS_LOG;
1988 break;
1989 case ARG_WAIT_SIG: /* wait (sleep) x seconds between every y articles IF we get a signal */
1990 master->sig_pause_time = atoi(argv[0]);
1991 master->sig_pause_nrmsgs = atoi(argv[1]);
1992 break;
1993 case ARG_ACTIVE:
1994 master->do_active = TRUE;
1995 break;
1996 case ARG_RECONNECT: /* how often do we drop and reconnect to battle INNS's LIKE_PULLER=DONT */
1997 master->reconnect_nr = atoi(argv[0]);
1998 break;
1999 case ARG_DEBUG: /* debug */
2000 master->debug = TRUE;
2001 /* now set up our error log and MyPerror reporting so it goes to debug.suck */
2002 error_log(ERRLOG_SET_DEBUG, NULL, NULL);
2003 break;
2004 case ARG_ERRLOG: /* error log path */
2005 error_log(ERRLOG_SET_FILE, argv[0], NULL);
2006 master->errlog = argv[0];
2007 break;
2008 case ARG_HISTORY:
2009 master->do_chkhistory = FALSE;
2010 break;
2011 case ARG_KILLFILE:
2012 master->do_killfile = FALSE;
2013 break;
2014 case ARG_KLOG_NONE:
2015 master->killfile_log = KILL_LOG_NONE;
2016 break;
2017 case ARG_KLOG_SHORT:
2018 master->killfile_log = KILL_LOG_SHORT;
2019 break;
2020 case ARG_KLOG_LONG:
2021 master->killfile_log = KILL_LOG_LONG;
2022 break;
2023 case ARG_MODEREADER: /* mode reader */
2024 master->do_modereader = TRUE;
2025 break;
2026 case ARG_PORTNR: /* override default portnr */
2027 master->portnr = atoi(argv[0]);
2028 break;
2029 case ARG_PASSWD: /* passwd */
2030 master->passwd = argv[0];
2031 break;
2032 case ARG_RESCAN: /* don't rescan on restart */
2033 master->rescan = FALSE;
2034 break;
2035 case ARG_STATLOG: /* status log path */
2036 master->status_file_name = argv[0];
2037 break;
2038 case ARG_USERID: /* userid */
2039 master->userid = argv[0];
2040 break;
2041 case ARG_PASSWD_ENV: /* get userid/password from ENV */
2042 master->userid = getenv("NNTP_USER");
2043 master->passwd = getenv("NNTP_PASS");
2044 master->passwd_env = TRUE;
2045 break;
2046 case ARG_VERSION: /* show version number */
2047 error_log(ERRLOG_REPORT,"Suck version %v1%\n",SUCK_VERSION, NULL);
2048 retval = RETVAL_VERNR; /* so we don't do anything else */
2049 break;
2050 case ARG_WAIT: /* wait (sleep) x seconds between every y articles */
2051 master->pause_time = atoi(argv[0]);
2052 master->pause_nrmsgs = atoi(argv[1]);
2053 break;
2054 case ARG_LOCALHOST:
2055 master->localhost = argv[0];
2056 break;
2057 case ARG_NRMODE:
2058 master->nrmode = TRUE;
2059 break;
2060 case ARG_AUTOAUTH:
2061 master->auto_auth = TRUE;
2062 break;
2063 case ARG_NODEDUPE:
2064 master->no_dedupe = TRUE;
2065 break;
2066 case ARG_READACTIVE:
2067 master->activefile = argv[0];
2068 break;
2069 case ARG_PREBATCH:
2070 master->prebatch = TRUE;
2071 break;
2072 case ARG_SKIP_ON_RESTART:
2073 master->skip_on_restart = TRUE;
2074 break;
2075 case ARG_KLOG_NAME:
2076 master->kill_log_name = argv[0];
2077 break;
2078 case ARG_USEGUI:
2079 master->use_gui = TRUE;
2080 break;
2081 case ARG_XOVER:
2082 master->do_xover = FALSE;
2083 break;
2084 case ARG_CONN_DEDUPE:
2085 master->conn_dedupe = TRUE;
2086 break;
2087 case ARG_POST_FILTER:
2088 master->post_filter = argv[0];
2089 break;
2090 case ARG_CONN_ACTIVE:
2091 master->conn_active = TRUE;
2092 break;
2093 case ARG_HIST_FILE:
2094 master->history_file = argv[0];
2095 break;
2096 case ARG_HEADER_ONLY:
2097 master->header_only = TRUE;
2098 break;
2099 case ARG_ACTIVE_LASTREAD:
2100 x = atoi(argv[0]);
2101 if(x > 0) {
2102 error_log(ERRLOG_REPORT, suck_phrases[66], NULL);
2103 }
2104 else {
2105 master->active_lastread = atoi(argv[0]);
2106 }
2107 break;
2108 case ARG_USEXOVER:
2109 master->use_xover = TRUE;
2110 break;
2111 case ARG_RESETCOUNTER:
2112 master->resetcounter = TRUE;
2113 break;
2114 case ARG_LOW_READ:
2115 master->low_read = TRUE;
2116 break;
2117 case ARG_SHOW_GROUP:
2118 master->show_group = TRUE;
2119 break;
2120 #ifdef TIMEOUT
2121 case ARG_TIMEOUT:
2122 TimeOut = atoi(argv[0]);
2123 break;
2124 #endif
2125 #ifdef HAVE_LIBSSL
2126 case ARG_USE_SSL:
2127 master->do_ssl = TRUE;
2128 master->portnr = DEFAULT_SSL_PORT;
2129 break;
2130 case ARG_LOCAL_SSL:
2131 master->local_ssl = TRUE;
2132 break;
2133 #endif
2134
2135 }
2136
2137 return retval;
2138
2139 }
2140 /*----------------------------------*/
2141 /* authenticate when receive a 480 */
2142 /*----------------------------------*/
2143 int do_authenticate(PMaster master) {
2144 int len, nr, retval = RETVAL_OK;
2145 char *resp, buf[MAXLINLEN];
2146
2147
2148 if(master->userid == NULL || master->passwd == NULL) {
2149 error_log(ERRLOG_REPORT, suck_phrases[73], NULL);
2150 retval = RETVAL_NOAUTH;
2151 }
2152 else {
2153 /* we must do authorization */
2154 sprintf(buf, "AUTHINFO USER %s\r\n", master->userid);
2155 if(master->debug == TRUE) {
2156 do_debug("sending command: %s", buf);
2157 }
2158 sputline(master->sockfd, buf, master->do_ssl, master->ssl_struct);
2159 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
2160 if( len < 0) {
2161 retval = RETVAL_ERROR;
2162 }
2163 else {
2164 if(master->debug == TRUE) {
2165 do_debug("got answer: %s", resp);
2166 }
2167
2168 TimerFunc(TIMER_ADDBYTES, len, NULL);
2169
2170 number(resp, &nr);
2171 if(nr == 480) {
2172 /* this is because of the pipelining code */
2173 /* we get the second need auth */
2174 /* just ignore it and get the next line */
2175 /* which should be the 381 we need */
2176 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
2177 TimerFunc(TIMER_ADDBYTES, len, NULL);
2178
2179 number(resp, &nr);
2180 }
2181
2182 if(nr != 381) {
2183 error_log(ERRLOG_REPORT, suck_phrases[27], resp, NULL);
2184 retval = RETVAL_NOAUTH;
2185 }
2186 else {
2187 sprintf(buf, "AUTHINFO PASS %s\r\n", master->passwd);
2188 sputline(master->sockfd, buf, master->do_ssl, master->ssl_struct);
2189 if(master->debug == TRUE) {
2190 do_debug("sending command: %s", buf);
2191 }
2192 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
2193 if(len < 0) {
2194 retval = RETVAL_ERROR;
2195 }
2196 else {
2197 if(master->debug == TRUE) {
2198 do_debug("got answer: %s", resp);
2199 }
2200
2201 TimerFunc(TIMER_ADDBYTES, len, NULL);
2202
2203 number(resp, &nr);
2204 switch(nr) {
2205 case 281: /* bingo */
2206 retval = RETVAL_OK;
2207 break;
2208 case 502: /* permission denied */
2209 retval = RETVAL_NOAUTH;
2210 error_log(ERRLOG_REPORT, suck_phrases[28], NULL);
2211 break;
2212 default: /* wacko error */
2213 error_log(ERRLOG_REPORT, suck_phrases[27], resp, NULL);
2214 retval = RETVAL_NOAUTH;
2215 break;
2216 }
2217 }
2218 }
2219 }
2220 }
2221 return retval;
2222 }
2223 /*--------------------------------------------------------------------------------*/
2224 /* THE strings in this routine is the only one not in the arrays, since */
2225 /* we are in the middle of reading the arrays, and they may or may not be valid. */
2226 /*--------------------------------------------------------------------------------*/
2227 void load_phrases(PMaster master) {
2228
2229 int error=TRUE;
2230 FILE *fpi;
2231 char buf[MAXLINLEN];
2232
2233 if(master->phrases != NULL) {
2234
2235 if((fpi = fopen(master->phrases, "r")) == NULL) {
2236 MyPerror(master->phrases);
2237 }
2238 else {
2239 fgets(buf, MAXLINLEN, fpi);
2240 if(strncmp( buf, SUCK_VERSION, strlen(SUCK_VERSION)) != 0) {
2241 error_log(ERRLOG_REPORT, "Invalid Phrase File, wrong version\n", NULL);
2242 }
2243 else if((both_phrases = read_array(fpi, NR_BOTH_PHRASES, TRUE)) != NULL) {
2244 read_array(fpi, NR_RPOST_PHRASES, FALSE); /* skip these */
2245 read_array(fpi, NR_TEST_PHRASES, FALSE);
2246 if(( suck_phrases = read_array(fpi, NR_SUCK_PHRASES, TRUE)) != NULL &&
2247 ( timer_phrases= read_array(fpi, NR_TIMER_PHRASES,TRUE)) &&
2248 ( chkh_phrases= read_array(fpi, NR_CHKH_PHRASES,TRUE)) &&
2249 (dedupe_phrases= read_array(fpi, NR_DEDUPE_PHRASES,TRUE)) &&
2250 ( killf_reasons= read_array(fpi, NR_KILLF_REASONS,TRUE)) &&
2251 ( killf_phrases= read_array(fpi, NR_KILLF_PHRASES,TRUE)) &&
2252 ( sucku_phrases= read_array(fpi, NR_SUCKU_PHRASES,TRUE)) &&
2253 (read_array(fpi, NR_LMOVE_PHRASES, FALSE) == NULL) && /* skip this one */
2254 ( active_phrases=read_array(fpi, NR_ACTIVE_PHRASES,TRUE)) &&
2255 ( batch_phrases= read_array(fpi, NR_BATCH_PHRASES,TRUE)) &&
2256 ( xover_phrases= read_array(fpi, NR_XOVER_PHRASES,TRUE)) &&
2257 ( xover_reasons= read_array(fpi, NR_XOVER_REASONS,TRUE))) {
2258 error = FALSE;
2259 }
2260 }
2261
2262 }
2263 fclose(fpi);
2264 if(error == TRUE) {
2265 /* reset back to default */
2266 error_log(ERRLOG_REPORT, "Using default Language phrases\n", NULL);
2267 both_phrases = default_both_phrases;
2268 suck_phrases=default_suck_phrases;
2269 timer_phrases=default_timer_phrases;
2270 chkh_phrases=default_chkh_phrases;
2271 dedupe_phrases=default_dedupe_phrases;
2272 killf_reasons=default_killf_reasons;
2273 killf_phrases=default_killf_phrases;
2274 killp_phrases=default_killp_phrases;
2275 sucku_phrases=default_sucku_phrases;
2276 active_phrases=default_active_phrases;
2277 batch_phrases=default_batch_phrases;
2278 xover_phrases=default_xover_phrases;
2279 xover_reasons=default_xover_reasons;
2280 }
2281 }
2282 }
2283 /*--------------------------------------------------------------------------------*/
2284 void free_phrases(void) {
2285 /* free up the memory alloced in load_phrases() */
2286 if(both_phrases != default_both_phrases) {
2287 free_array(NR_BOTH_PHRASES, both_phrases);
2288 }
2289 if(suck_phrases != default_suck_phrases) {
2290 free_array(NR_SUCK_PHRASES, suck_phrases);
2291 }
2292 if(timer_phrases != default_timer_phrases) {
2293 free_array(NR_TIMER_PHRASES, timer_phrases);
2294 }
2295 if(chkh_phrases != default_chkh_phrases) {
2296 free_array(NR_CHKH_PHRASES, chkh_phrases);
2297 }
2298 if(dedupe_phrases != default_dedupe_phrases) {
2299 free_array(NR_DEDUPE_PHRASES, dedupe_phrases);
2300 }
2301 if(killf_reasons != default_killf_reasons) {
2302 free_array(NR_KILLF_REASONS, killf_reasons);
2303 }
2304 if(killf_phrases != default_killf_phrases) {
2305 free_array(NR_KILLF_PHRASES, killf_phrases);
2306 }
2307 if(killp_phrases != default_killp_phrases) {
2308 free_array(NR_KILLP_PHRASES, killp_phrases);
2309 }
2310 if(sucku_phrases != default_sucku_phrases) {
2311 free_array(NR_SUCKU_PHRASES, sucku_phrases);
2312 }
2313 if(active_phrases != default_active_phrases) {
2314 free_array(NR_ACTIVE_PHRASES, active_phrases);
2315 }
2316 if(batch_phrases != default_batch_phrases) {
2317 free_array(NR_BATCH_PHRASES, batch_phrases);
2318 }
2319 if(xover_phrases != default_xover_phrases) {
2320 free_array(NR_XOVER_PHRASES, xover_phrases);
2321 }
2322 if(xover_reasons != default_xover_reasons) {
2323 free_array(NR_XOVER_REASONS, xover_reasons);
2324 }
2325
2326
2327
2328 }
2329
2330
2331