1 #include <config.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include <netdb.h>
9
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
13 #ifdef HAVE_SYS_WAIT_H
14 #include <sys/wait.h>
15 #endif
16 #ifdef HAVE_LIMITS_H
17 #include <limits.h>
18 #endif
19
20 #ifdef PERL_EMBED
21 #include <EXTERN.h>
22 #include <perl.h>
23 #ifdef OLD_PERL
24 #ifndef ERRSV
25 # define ERRSV (GvSV(errgv)) /* needed for perl 5.004 and earlier */
26 #endif
27 #ifndef PL_na
28 # define PL_na (na)
29 #endif
30 #endif /* OLD_PERL */
31 #endif
32
33 #ifdef HAVE_DIRENT_H
34 # include <dirent.h>
35 #else
36 # define dirent direct
37 # ifdef HAVE_SYS_NDIR_H
38 # include <sys/ndir.h>
39 # endif
40 # ifdef HAVE_SYS_DIR_H
41 # include <sys/dir.h>
42 # endif
43 #endif
44
45 #include "suck_config.h"
46 #include "both.h"
47 #include "phrases.h"
48
49 struct nntp_auth {
50 char *userid;
51 char *passwd;
52 int autoauth;
53 int use_env;
54 };
55
56 typedef struct {
57 char *batch;
58 char *prefix;
59 char *host;
60 char *filter_args[RPOST_MAXARGS];
61 int filter_argc;
62 int filter_infilenr; /* nr of arg that we replace with infile */
63 char *filter_outfile; /* pointer to name of outfile */
64 int deleteyn;
65 int do_modereader;
66 int debug;
67 struct nntp_auth auth;
68 FILE *status_fptr;
69 unsigned short int portnr;
70 const char *phrases;
71 char *rnews_file;
72 char *rnews_path;
73 int sockfd;
74 int show_name;
75 int ignore_readonly;
76 int do_ssl;
77 void *ssl_struct;
78 #ifdef PERL_EMBED
79 PerlInterpreter *perl_int;
80 #endif
81 } Args, *Pargs;
82
83 /* function declarations */
84 int do_article(Pargs, FILE *);
85 int do_batch(Pargs);
86 int do_rnews(Pargs);
87 char *do_filter(Pargs, char *);
88 int send_command(Pargs, const char *, char **, int);
89 int do_authenticate(Pargs);
90 int scan_args(Pargs, int, char *[]);
91 int filter_post_article(Pargs, char *);
92 void log_fail(char *, char *);
93 void load_phrases(Pargs);
94 void free_phrases(void);
95 int parse_filter_args(Pargs myargs, int, char **);
96 #ifdef PERL_EMBED
97 void parse_perl(Pargs, char *);
98 void perl_done(Pargs);
99 char *filter_perl(Pargs, char *);
100 #endif
101
102
103 #define RNEWS_START "#! rnews" /* string that denotes the begin of a new message in rnews file */
104 #define RNEWS_START_LEN 8
105 #define TEMP_ARTICLE "tmp-article" /* name for temp article in rnews_path */
106
107 /* stuff needed for language phrases */
108 /* set up defaults */
109 char **rpost_phrases = default_rpost_phrases;
110 char **both_phrases = default_both_phrases;
111
112 enum { RETVAL_ERROR = -1, RETVAL_OK, RETVAL_ARTICLE_PROB, RETVAL_NOAUTH, RETVAL_UNEXPECTEDANS};
113 /*------------------------------------------------*/
main(int argc,char * argv[],char * env[])114 int main(int argc, char *argv[], char *env[]) {
115 char *inbuf;
116 int response, retval, loop, fargc, i;
117 struct stat sbuf;
118 char **args, **fargs;
119 Args myargs;
120
121 /* initialize everything */
122 retval = RETVAL_OK;
123 fargc = 0;
124 fargs = NULL;
125
126 myargs.batch = NULL;
127 myargs.prefix =NULL;
128 myargs.status_fptr = stdout;
129 myargs.deleteyn = FALSE;
130 myargs.auth.userid = NULL;
131 myargs.auth.passwd = NULL;
132 myargs.auth.autoauth = FALSE;
133 myargs.auth.use_env = FALSE;
134 myargs.do_modereader = FALSE;
135 myargs.portnr = DEFAULT_NNRP_PORT;
136 myargs.host = getenv("NNTPSERVER"); /* the default */
137 myargs.phrases = NULL;
138 myargs.debug = FALSE;
139 myargs.rnews_file = NULL;
140 myargs.rnews_path = NULL;
141 myargs.filter_argc = 0;
142 myargs.filter_infilenr = -1;
143 myargs.filter_outfile = NULL;
144 myargs.show_name = FALSE;
145 myargs.ignore_readonly = FALSE;
146 myargs.do_ssl = FALSE;
147 myargs.ssl_struct = NULL;
148 #ifdef PERL_EMBED
149 myargs.perl_int = NULL;
150 #endif
151
152 /* have to do this next so if set on cmd line, overrides this */
153 #ifdef N_PHRASES /* in case someone nukes def */
154 if(stat(N_PHRASES, &sbuf) == 0 && S_ISREG(sbuf.st_mode)) {
155 /* we have a regular phrases file make it the default */
156 myargs.phrases = N_PHRASES;
157 }
158
159 #endif
160
161 /* allow no args, only the hostname, or hostname as first arg */
162 /* also have to do the file argument checking */
163 switch(argc) {
164 case 1:
165 break;
166 case 2:
167 /* the fargs == NULL test so only process first file name */
168 if(argv[1][0] == FILE_CHAR) {
169 if((fargs = build_args(&argv[1][1], &fargc)) != NULL) {
170 retval = scan_args(&myargs, fargc, fargs);
171 }
172 }
173 else {
174 myargs.host = argv[1];
175 }
176 break;
177 default:
178 for(loop=1;loop<argc && fargs == NULL;loop++) {
179 if(argv[loop][0] == FILE_CHAR) {
180 if((fargs = build_args(&argv[loop][1], &fargc)) != NULL) {
181 retval = scan_args(&myargs, fargc, fargs);
182 }
183 }
184 }
185 /* this is here so anything at command line overrides file */
186 if(argv[1][0] != '-' && argv[1][0] != FILE_CHAR) {
187 myargs.host = argv[1];
188 argc-= 2;
189 args = &(argv[2]);
190 }
191 else {
192 args = &(argv[1]);
193 argc--;
194 }
195 retval = scan_args(&myargs, argc, args);
196 break;
197 }
198 load_phrases(&myargs); /* this has to be here so rest prints okay */
199
200 if(retval == RETVAL_ERROR) {
201 error_log(ERRLOG_REPORT, rpost_phrases[0], argv[0], NULL);
202 error_log(ERRLOG_REPORT, rpost_phrases[1], NULL);
203 error_log(ERRLOG_REPORT, rpost_phrases[2], NULL);
204 }
205 else {
206 if(myargs.debug == TRUE) {
207 do_debug("Rpost version %s\n",SUCK_VERSION);
208 do_debug("myargs.batch = %s\n", null_str(myargs.batch));
209 do_debug("myargs.prefix = %s\n", null_str(myargs.prefix));
210 do_debug("myargs.filter_argc = %d\n", myargs.filter_argc);
211 for(loop=0;loop<myargs.filter_argc;loop++) {
212 do_debug("myargs.filter_args[%d] = %s\n", loop, null_str(myargs.filter_args[loop]));
213 }
214 do_debug("myargs.filter_infilenr = %d\n", myargs.filter_infilenr);
215 do_debug("myargs.filter_outfile = %s\n", null_str(myargs.filter_outfile));
216 do_debug("myargs.status_fptr = %s\n", (myargs.status_fptr == stdout) ? "stdout" : "not stdout");
217 do_debug("myargs.deleteyn = %s\n", true_str(myargs.deleteyn));
218 do_debug("myargs.do_modereader = %s\n", true_str(myargs.do_modereader));
219 do_debug("myargs.portnr = %d\n", myargs.portnr);
220 do_debug("myargs.host = %s\n", null_str(myargs.host));
221 do_debug("myargs.phrases = %s\n", null_str(myargs.phrases));
222 do_debug("myargs.auth.autoauth = %s\n", true_str(myargs.auth.autoauth));
223 do_debug("myargs.auth.use_env = %s\n", true_str(myargs.auth.use_env));
224 do_debug("myargs.rnews_file = %s\n", null_str(myargs.rnews_file));
225 do_debug("myargs.rnews_path = %s\n", null_str(myargs.rnews_path));
226 do_debug("myargs.show_name = %s\n", true_str(myargs.show_name));
227 do_debug("myargs.ignore_readonly = %s\n", true_str(myargs.ignore_readonly));
228 do_debug("myargs.do_ssl = %s\n", true_str(myargs.do_ssl));
229 #ifdef TIMEOUT
230 do_debug("TimeOut = %d\n", TimeOut);
231 #endif
232 do_debug("myargs.debug = TRUE\n");
233 }
234
235 char hostname[NI_MAXHOST];
236
237 /* we processed args okay */
238 if(myargs.host == NULL) {
239 error_log(ERRLOG_REPORT, rpost_phrases[20], NULL);
240 retval = RETVAL_ERROR;
241 }
242 else {
243 myargs.sockfd = connect_to_nntphost( myargs.host, hostname, sizeof hostname, myargs.status_fptr, myargs.portnr, myargs.do_ssl, &myargs.ssl_struct);
244 if(myargs.sockfd < 0) {
245 retval = RETVAL_ERROR;
246 }
247 else {
248 /* Get the announcement line */
249 if((i = sgetline(myargs.sockfd, &inbuf, myargs.do_ssl, myargs.ssl_struct)) < 0) {
250 retval = RETVAL_ERROR;
251 }
252 else {
253 fputs(inbuf, myargs.status_fptr );
254 number(inbuf, &response);
255 if(response == 480) {
256 retval = do_authenticate(&myargs);
257 }
258 }
259 if(retval == RETVAL_OK && myargs.do_modereader == TRUE) {
260 retval = send_command(&myargs, "mode reader\r\n", &inbuf, 0);
261 if( retval == RETVAL_OK) {
262 /* Again the announcement */
263 fputs(inbuf, myargs.status_fptr );
264 number(inbuf, &response);
265 }
266 }
267 if(response == 201 && myargs.ignore_readonly == FALSE) {
268 /* if((response != 200) && (response != 480)) {*/
269 error_log(ERRLOG_REPORT, rpost_phrases[3], NULL);
270 retval = RETVAL_ERROR;
271 }
272 else if(myargs.auth.autoauth == TRUE) {
273 if(myargs.auth.passwd == NULL || myargs.auth.userid == NULL) {
274 error_log(ERRLOG_REPORT, rpost_phrases[32], NULL);
275 retval = RETVAL_ERROR;
276 }
277 else {
278 retval = do_authenticate(&myargs);
279 }
280 }
281
282 }
283 }
284 if(retval == RETVAL_OK) {
285 if(myargs.rnews_file != NULL) {
286 /* rnews mode */
287 retval = do_rnews(&myargs);
288 }
289 else if(myargs.batch != NULL) {
290 /* batch mode */
291 retval = do_batch(&myargs);
292 }
293 else {
294 /* do one article from stdin */
295 retval = do_article(&myargs, stdin);
296 }
297
298 print_phrases(myargs.status_fptr, rpost_phrases[4], *hostname ? hostname : "the server", NULL);
299 if(myargs.debug == TRUE) {
300 do_debug("Sending quit");
301 }
302 send_command(&myargs, "quit\r\n", NULL, 0);
303 disconnect_from_nntphost(myargs.sockfd, myargs.do_ssl, &myargs.ssl_struct);
304 }
305 }
306 #ifdef PERL_EMBED
307 if(myargs.perl_int != NULL) {
308 perl_done(&myargs);
309 }
310 #endif
311 if(myargs.status_fptr != NULL && myargs.status_fptr != stdout) {
312 fclose(myargs.status_fptr);
313 }
314 free_phrases(); /* do this last so everything is displayed correctly */
315 exit(retval);
316 }
317 /*----------------------------------------------------------------------------------*/
do_article(Pargs myargs,FILE * fptr)318 int do_article(Pargs myargs, FILE *fptr) {
319
320 int len, response, retval, longline, dupeyn, i;
321 char buf[MAXLINLEN+4], *inbuf, *ptr = NULL;
322 /* the +4 in NL, CR, if have to double . in first pos */
323 retval = RETVAL_OK;
324 longline = FALSE;
325
326 /* Initiate post mode don't use IHAVE since I'd have to scan the message for the msgid, */
327 /* then rewind and reread the file The problem lies in if I'm using STDIN for the message, */
328 /* I can't rewind!!!!, so no IHAVE */
329 response = send_command(myargs, "POST\r\n", &inbuf, 340);
330 fputs(inbuf, myargs->status_fptr);
331 if(response!=RETVAL_OK) {
332 error_log(ERRLOG_REPORT, rpost_phrases[5], NULL);
333 retval = RETVAL_ERROR;
334 }
335 else {
336 while(fgets(buf, MAXLINLEN, fptr) != NULL) {
337 len=strlen(buf);
338 if(longline == FALSE) {
339 /* only check this if we are at the beginning of a line */
340 if(buf[0]=='.') {
341 /* Yup, this has to be doubled */
342 memmove(buf+1,buf,++len);
343 buf[0]='.';
344 }
345 }
346 if(buf[len-1] == '\n') {
347 /* only do this if we have an EOL in buffer */
348 if(buf[len-2] != '\r') {
349 /* only add the \r\n if it's not already there. */
350 strncpy(&buf[len-1],"\r\n",3);
351 }
352 longline = FALSE;
353 }
354 else {
355 longline = TRUE;
356 }
357 sputline(myargs->sockfd, buf, myargs->do_ssl, myargs->ssl_struct);
358 if(myargs->debug == TRUE) {
359 do_debug("ARTICLE: %s", buf);
360 }
361 }
362 /* if the last line didn't have a nl on it, we need to */
363 /* put one so that the eom is recognized */
364 if(longline == TRUE) {
365 sputline(myargs->sockfd, "\r\n", myargs->do_ssl, myargs->ssl_struct);
366 }
367 sputline(myargs->sockfd, ".\r\n", myargs->do_ssl, myargs->ssl_struct);
368 if(myargs->debug == TRUE) {
369 do_debug("ARTICLE END\n");
370 }
371
372 if((i = sgetline(myargs->sockfd, &inbuf, myargs->do_ssl, myargs->ssl_struct)) < 0) {
373 retval = RETVAL_ERROR;
374 }
375 else {
376 fputs(inbuf, myargs->status_fptr);
377 if(myargs->debug == TRUE) {
378 do_debug("RESPONSE: %s", inbuf);
379 }
380 ptr = number(inbuf, &response);
381 }
382 if(retval == RETVAL_OK && response!=240) {
383 dupeyn = FALSE;
384 if(response == 441) {
385 number(ptr, &response);
386 }
387 if(response == 435) {
388 dupeyn = TRUE;
389 }
390 else {
391 /* M$ server sends "441 (615) Article Rejected -- Duplicate Message ID" handle that */
392 /* can't just call nr, cause of parens */
393 /* ptr should be at ( after 441 */
394 if( *ptr == '(' ) {
395 number(++ptr, &response);
396 if(response == 615) {
397 dupeyn = TRUE;
398 }
399 }
400 else if (strstr(inbuf, RPOST_DUPE_STR) != NULL || strstr(inbuf, RPOST_DUPE_STR2)) {
401 dupeyn = TRUE;
402 }
403 }
404 if(dupeyn == TRUE) {
405 print_phrases(myargs->status_fptr, rpost_phrases[6], NULL);
406 }
407 else {
408 error_log(ERRLOG_REPORT, rpost_phrases[7], inbuf, NULL);
409 retval = RETVAL_ARTICLE_PROB;
410 }
411 }
412 }
413 return retval;
414 }
415 /*----------------------------------------------------------------*/
send_command(Pargs myargs,const char * cmd,char ** ret_response,int good_response)416 int send_command(Pargs myargs, const char *cmd, char **ret_response, int good_response) {
417 /* this is needed so can do user authorization */
418
419 int len, retval = RETVAL_OK, nr;
420 char *resp;
421
422 if(myargs->debug == TRUE) {
423 do_debug("sending command: %s", cmd);
424 }
425 sputline(myargs->sockfd, cmd, myargs->do_ssl, myargs->ssl_struct);
426 len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);
427 if( len < 0) {
428 retval = RETVAL_ERROR;
429 }
430 else {
431 if(myargs->debug == TRUE) {
432 do_debug("got answer: %s", resp);
433 }
434
435 number(resp, &nr);
436 if(nr == 480 ) {
437 /* we must do authorization */
438 retval = do_authenticate(myargs);
439 if(retval == RETVAL_OK) {
440 /* resend command */
441 sputline(myargs->sockfd, cmd, myargs->do_ssl, myargs->ssl_struct);
442 if(myargs->debug == TRUE) {
443 do_debug("sending command: %s", cmd);
444 }
445 len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);
446 if( len < 0) {
447 retval = RETVAL_ERROR;
448 }
449 else {
450 number(resp,&nr);
451 if(myargs->debug == TRUE) {
452 do_debug("got answer: %s", resp);
453 }
454 }
455 }
456 }
457 if (good_response != 0 && nr != good_response) {
458 error_log(ERRLOG_REPORT, rpost_phrases[18],cmd,resp,NULL);
459 retval = RETVAL_UNEXPECTEDANS;
460 }
461 }
462 if(ret_response != NULL) {
463 *ret_response = resp;
464 }
465 return retval;
466 }
467 /*----------------------------------*/
468 /* authenticate when receive a 480 */
469 /*----------------------------------*/
do_authenticate(Pargs myargs)470 int do_authenticate(Pargs myargs) {
471 int len, nr, retval = RETVAL_OK;
472 char *resp, buf[MAXLINLEN];
473
474 /* we must do authorization */
475 if((myargs->auth).userid == NULL || (myargs->auth).passwd == NULL) {
476 error_log(ERRLOG_REPORT, rpost_phrases[43], NULL);
477 retval = RETVAL_NOAUTH;
478
479 }
480 else {
481 print_phrases(myargs->status_fptr, rpost_phrases[41], NULL);
482
483 sprintf(buf, "AUTHINFO USER %s\r\n", (myargs->auth).userid);
484 if(myargs->debug == TRUE) {
485 do_debug("sending command: %s", buf);
486 }
487 sputline(myargs->sockfd, buf, myargs->do_ssl, myargs->ssl_struct);
488 len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);
489 if( len < 0) {
490 retval = RETVAL_ERROR;
491 }
492 else {
493 if(myargs->debug == TRUE) {
494 do_debug("got answer: %s", resp);
495 }
496
497 number(resp, &nr);
498 if(nr == 480) {
499 /* this is because of the pipelining code */
500 /* we get the second need auth */
501 /* just ignore it and get the next line */
502 /* which should be the 381 we need */
503 if((len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct)) < 0) {
504 retval = RETVAL_ERROR;
505 }
506 else {
507 number(resp, &nr);
508 }
509 }
510 if(retval == RETVAL_OK) {
511 if(nr != 381) {
512 error_log(ERRLOG_REPORT, rpost_phrases[16], resp, NULL);
513 retval = RETVAL_NOAUTH;
514 }
515 else {
516 sprintf(buf, "AUTHINFO PASS %s\r\n", (myargs->auth).passwd);
517 sputline(myargs->sockfd, buf, myargs->do_ssl, myargs->ssl_struct);
518 if(myargs->debug == TRUE) {
519 do_debug("sending command: %s", buf);
520 }
521 len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);
522 if(len < 0) {
523 retval = RETVAL_ERROR;
524 }
525 else {
526 if(myargs->debug == TRUE) {
527 do_debug("got answer: %s", resp);
528 }
529 number(resp, &nr);
530 switch(nr) {
531 case 281: /* bingo */
532 retval = RETVAL_OK;
533 print_phrases(myargs->status_fptr, rpost_phrases[42], NULL);
534 break;
535 case 502: /* permission denied */
536 retval = RETVAL_NOAUTH;
537 error_log(ERRLOG_REPORT, rpost_phrases[17], NULL);
538 break;
539 default: /* wacko error */
540 error_log(ERRLOG_REPORT, rpost_phrases[16], resp, NULL);
541 retval = RETVAL_NOAUTH;
542 break;
543 }
544 }
545 }
546 }
547 }
548
549 }
550 return retval;
551 }
552 /*--------------------------------------------------------------------------------------*/
scan_args(Pargs myargs,int argc,char * argv[])553 int scan_args(Pargs myargs, int argc, char *argv[]) {
554
555 int loop, retval = RETVAL_OK;
556
557 for(loop=0;loop<argc && retval == RETVAL_OK;loop++) {
558 if(myargs->debug == TRUE) {
559 do_debug("Checking arg #%d-%d: '%s'\n", loop, argc, argv[loop]);
560 }
561 /* check for valid args format */
562 if(argv[loop][0] != '-' && argv[loop][0] != FILE_CHAR) {
563 retval = RETVAL_ERROR;
564 }
565 if(argv[loop][0] == '-' && argv[loop][2] != '\0') {
566 retval = RETVAL_ERROR;
567 }
568
569 if(retval != RETVAL_OK) {
570 error_log(ERRLOG_REPORT, rpost_phrases[19], argv[loop], NULL);
571 }
572 else if(argv[loop][0] != FILE_CHAR) {
573 switch(argv[loop][1]) {
574 case 'h': /* hostname */
575 if(loop+1 == argc) {
576 error_log(ERRLOG_REPORT, rpost_phrases[20], NULL);
577 retval = RETVAL_ERROR;
578 }
579 else {
580 myargs->host = argv[++loop];
581 }
582 break;
583
584 case 'b': /* batch file Mode, next arg = batch file */
585 if(loop+1 == argc) {
586 error_log(ERRLOG_REPORT, rpost_phrases[21], NULL);
587 retval = RETVAL_ERROR;
588 }
589 else {
590 myargs->batch = argv[++loop];
591 }
592 break;
593 case 'p': /* specify directory prefix for files in batch file */
594 if(loop+1 == argc) {
595 error_log(ERRLOG_REPORT, rpost_phrases[22], NULL);
596 retval = RETVAL_ERROR;
597 }
598 else {
599 myargs->prefix = argv[++loop];
600 }
601 break;
602 case 'f': /* filter prg, rest of cmd line is args */
603 if(loop+1 == argc) {
604 error_log(ERRLOG_REPORT, rpost_phrases[23], NULL);
605 retval = RETVAL_ERROR;
606 }
607 else {
608 /* set up filter args send it first arg to filter and arg count */
609 retval = parse_filter_args(myargs, argc - (loop+1), &(argv[loop+1]));
610 loop = argc; /* terminate main loop */
611 }
612 break;
613 #ifdef PERL_EMBED
614 case 'F': /* filter using embedded perl */
615 if(loop+1 == argc) {
616 error_log(ERRLOG_REPORT, rpost_phrases[33], NULL);
617 }
618 else {
619 /* parse the perl filter */
620 parse_perl(myargs, argv[++loop]);
621 }
622 break;
623 #endif
624 case 'e': /* use default error log path */
625 error_log(ERRLOG_SET_FILE, ERROR_LOG,NULL);
626 break;
627 case 'E': /* error log path */
628 if(loop+1 == argc) {
629 error_log(ERRLOG_REPORT, "%s\n",rpost_phrases[24],NULL);
630 retval = RETVAL_ERROR;
631 }
632 else {
633 error_log(ERRLOG_SET_FILE, argv[++loop], NULL);
634 }
635 break;
636 case 's': /* use default status log path */
637 if((myargs->status_fptr = fopen(STATUS_LOG, "a")) == NULL) {
638 MyPerror(rpost_phrases[25]);
639 retval = RETVAL_ERROR;
640 }
641 break;
642 case 'S': /* status log path */
643 if(loop+1 == argc) {
644 error_log(ERRLOG_REPORT, rpost_phrases[26],NULL);
645 retval = RETVAL_ERROR;
646 }
647 else if((myargs->status_fptr = fopen(argv[++loop], "a")) == NULL) {
648 MyPerror(rpost_phrases[25]);
649 retval = RETVAL_ERROR;
650 }
651 break;
652 case 'd': /* delete batch file on success */
653 myargs->deleteyn = TRUE;
654 break;
655 case 'u': /* do auto-authenticate */
656 myargs->auth.autoauth = TRUE;
657 break;
658 case 'U': /* next arg is userid for authorization */
659 if(loop+1 == argc) {
660 error_log(ERRLOG_REPORT, rpost_phrases[27],NULL);
661 retval = RETVAL_ERROR;
662 }
663 else {
664 myargs->auth.userid = argv[++loop];
665 }
666 break;
667 case 'P': /* next arg is password for authorization */
668 if(loop+1 == argc) {
669 error_log(ERRLOG_REPORT, rpost_phrases[28],NULL);
670 retval = RETVAL_ERROR;
671 }
672 else {
673 myargs->auth.passwd = argv[++loop];
674 }
675 break;
676 case 'Q': /* get userid/passwd from env */
677 myargs->auth.use_env = TRUE;
678 myargs->auth.passwd = getenv("NNTP_PASS");
679 myargs->auth.userid = getenv("NNTP_USER");
680 break;
681 case 'M': /* do mode reader command */
682 myargs->do_modereader = TRUE;
683 break;
684 case 'N': /* override port number */
685 if(loop+1 == argc) {
686 error_log(ERRLOG_REPORT, rpost_phrases[29],NULL);
687 retval = RETVAL_ERROR;
688 }
689 else {
690 myargs->portnr = atoi(argv[++loop]);
691 }
692 break;
693 case 'l': /* language phrase file */
694 if(loop+1 == argc) {
695 error_log(ERRLOG_REPORT, rpost_phrases[31],NULL);
696 retval = RETVAL_ERROR;
697 }
698 else {
699 myargs->phrases = argv[++loop];
700 }
701 break;
702 case 'D': /* debug */
703 myargs->debug = TRUE;
704 /* so error_log goes to debug too */
705 error_log(ERRLOG_SET_DEBUG, NULL, NULL);
706 break;
707 case 'r': /* do rnews next args base name then path for the rnews files */
708 if(loop+2 >= argc) {
709 error_log(ERRLOG_REPORT, rpost_phrases[38], NULL);
710 retval = RETVAL_ERROR;
711 }
712 else {
713 myargs->rnews_file = argv[++loop];
714 myargs->rnews_path = argv[++loop];
715 }
716 break;
717 case 'n': /* show the file name as we're uploading it */
718 myargs->show_name = TRUE;
719 break;
720 case 'i': /* ignore read_only opening announcement */
721 myargs->ignore_readonly = TRUE;
722 break;
723 #ifdef TIMEOUT
724 case 'T': /* timeout */
725 if(loop+1 == argc) {
726 error_log(ERRLOG_REPORT, rpost_phrases[31], NULL);
727 retval = RETVAL_ERROR;
728 }
729 else {
730 TimeOut = atoi(argv[++loop]);
731 }
732 break;
733 #endif
734 #ifdef HAVE_LIBSSL
735 case 'z': /* use SSL */
736 myargs->do_ssl = TRUE;
737 myargs->portnr = DEFAULT_SSL_PORT;
738 break;
739 #endif
740 default:
741 error_log(ERRLOG_REPORT, rpost_phrases[30], argv[loop],NULL);
742 break;
743 }
744 }
745 }
746 return retval;
747 }
748
749 #ifdef PERL_EMBED
750 /*-----------------------------------------------------------------------------------------*/
parse_perl(Pargs args,char * fname)751 void parse_perl(Pargs args, char *fname) {
752
753 /* this code is copied from killprg.c killperl_setup() */
754 /* which comes from PERLEMBED man page */
755
756 char *ptr = NULL;
757 char *prgs[] = { ptr, ptr };
758
759 if ((args->perl_int = perl_alloc()) == NULL) {
760 error_log(ERRLOG_REPORT, rpost_phrases[34], NULL);
761 }
762 else {
763 if(args->debug == TRUE) {
764 do_debug("Perl program name =%s\n", fname);
765 }
766 perl_construct(args->perl_int);
767 prgs[1] = fname;
768 if(perl_parse(args->perl_int, NULL, 2, prgs, NULL) == 0) {
769 perl_run(args->perl_int);
770 }
771 else {
772 error_log(ERRLOG_REPORT, rpost_phrases[35], fname, NULL);
773 perl_done(args);
774 }
775 }
776 }
777 /*---------------------------------------------------------------------------------------*/
perl_done(Pargs args)778 void perl_done(Pargs args) {
779 /* all done, free everything up */
780 if(args->perl_int != NULL) {
781 perl_destruct(args->perl_int);
782 perl_free(args->perl_int);
783 args->perl_int = NULL;
784 }
785 }
786 /*--------------------------------------------------------------------------------------*/
filter_perl(Pargs myargs,char * file)787 char *filter_perl(Pargs myargs, char *file) {
788
789 int i;
790 char *args[2] ={ NULL, NULL};
791 char infilen[MAXLINLEN+1];
792 char *infile=NULL;
793 dSP; /* perl stack pointer */
794 SV *fname; /* used to get fname off of perl stack */
795
796 /* here's where we send it to perl and get back our filename */
797 /* we need infile to become our file name to post*/
798 /* this code comes from chk_msg_kill_perl() which uses */
799 /* both perlcall and perlembed man pages */
800 /* set up args */
801 args[0] = file;
802
803 if(myargs->debug == TRUE) {
804 do_debug("Calling %s with arg %s\n", PERL_RPOST_SUB, file);
805 }
806
807 ENTER;
808 SAVETMPS;
809 PUSHMARK(SP);
810
811 i = perl_call_argv(PERL_RPOST_SUB, G_SCALAR | G_EVAL | G_KEEPERR, args);
812
813 SPAGAIN;
814
815 if(SvTRUE(ERRSV)) {
816 error_log(ERRLOG_REPORT, rpost_phrases[36], SvPV(ERRSV,PL_na));
817 POPs;
818 perl_done(myargs);
819 }
820 else if(i != 1) {
821 error_log(ERRLOG_REPORT, rpost_phrases[37], PERL_RPOST_SUB, NULL);
822 perl_done(myargs);
823 }
824 else {
825 fname = POPs;
826 infile = SvPV(fname, PL_na);
827 if(myargs->debug == TRUE) {
828 do_debug("got string =%s\n", null_str(infile));
829 }
830 /* have to do the following cause as soon as we do FREETMPS, the */
831 /* data that infile points to will disappear */
832 strcpy(infilen, infile);
833 infile = infilen; /* so we return the name of the file */
834 }
835 PUTBACK;
836 FREETMPS;
837 LEAVE;
838
839 return infile;
840
841 }
842 #endif
843 /*---------------------------------------------------------------------------------------*/
parse_filter_args(Pargs myargs,int argcount,char ** args)844 int parse_filter_args(Pargs myargs, int argcount, char **args) {
845
846 int argon, loop, i, retval = RETVAL_OK;
847
848 /* build filter args */
849 i = strlen(RPOST_FILTER_OUT);
850
851 /* just in case */
852 myargs->filter_outfile = NULL;
853 myargs->filter_infilenr = -1;
854 /* build array of args to past to fork, execl */
855 /* if see RPOST_FILTER_IN as arg, substitute the infile name */
856 /* if see RPOST_FILTER_OUT as first part of arg, get outfile name */
857 for(argon = 0, loop = 0; loop < argcount; loop++) {
858
859 if(myargs->debug == TRUE) {
860 do_debug("loop=%d argon=%d arg=%s\n", loop, argon, args[loop]);
861 }
862
863 if(strcmp(args[loop], RPOST_FILTER_IN) == 0) {
864 /* substitute infile name */
865 myargs->filter_args[argon] = NULL; /* just so debug.suck looks okay */
866 myargs->filter_infilenr = argon++;
867 }
868 else if(strncmp(args[loop], RPOST_FILTER_OUT, (unsigned int) i) == 0) {
869 /* arg should be RPOST_FILTER_OUT=filename */
870 if(args[loop][i] != '=') {
871 error_log(ERRLOG_REPORT, rpost_phrases[17], args[loop], NULL);
872 }
873 else {
874 myargs->filter_outfile = (char *)&(args[loop][i+1]);
875 }
876 }
877 else {
878 myargs->filter_args[argon++] = args[loop];
879 }
880 }
881 myargs->filter_argc = argon; /* the count of arguments */
882 myargs->filter_args[argon] = NULL; /* just to be on the safe side */
883 if(myargs->filter_outfile == NULL) {
884 /* no outfile defined, use built-in default */
885 myargs->filter_outfile = tmpnam(NULL);
886 error_log(ERRLOG_REPORT, rpost_phrases[9], myargs->filter_outfile, NULL);
887 }
888 if(myargs->filter_infilenr < 0) {
889 error_log(ERRLOG_REPORT, rpost_phrases[10], NULL);
890 retval = RETVAL_ERROR;
891 }
892 return retval;
893 }
894 /*--------------------------------------------------------------------*/
do_filter(Pargs myargs,char * filename)895 char *do_filter(Pargs myargs, char *filename) {
896 char *infile = NULL;
897 int i;
898
899 myargs->filter_args[myargs->filter_infilenr] = filename; /* so this points to the right name */
900
901 if( myargs->debug == TRUE) {
902 do_debug("ARGS:");
903 for(i=0;i<myargs->filter_argc;i++) {
904 do_debug(" %d=%s", i, myargs->filter_args[i]);
905 }
906 do_debug("\n");
907 }
908 switch((int) fork()) {
909 case 0: /* in child, do execl */
910 if(execvp(myargs->filter_args[0], myargs->filter_args) == -1) {
911 MyPerror(rpost_phrases[14]);
912 exit(-1); /* so we don't get two running */
913 }
914 break;
915 case -1: /* should never get here */
916 MyPerror(rpost_phrases[15]);
917 break;
918 default: /* in parent, wait for child to finish */
919 wait(NULL); /* no status check on finish */
920 infile = myargs->filter_outfile;
921 }
922
923 return infile;
924 }
925 /*----------------------------------------------------------------------------------*/
filter_post_article(Pargs myargs,char * filename)926 int filter_post_article(Pargs myargs, char *filename) {
927
928 char *infile = NULL; /* this is the filename that gets passed to do_article() */
929 int retval = RETVAL_OK;
930 FILE *fpi_msg;
931
932 #ifdef PERL_EMBED
933 if(myargs->perl_int != NULL) {
934 infile = filter_perl(myargs, filename);
935 }
936 else
937 #endif
938 if(myargs->filter_argc >0) {
939 infile = do_filter(myargs, filename);
940 }
941 else {
942 /* so we open up the right file */
943 infile = filename;
944 }
945
946 if(infile == NULL) {
947 error_log(ERRLOG_REPORT, rpost_phrases[11], NULL);
948 retval = RETVAL_ERROR;
949 }
950 else {
951 if(myargs->show_name == TRUE) {
952 /* show the file name */
953 print_phrases(myargs->status_fptr, rpost_phrases[40], infile, NULL);
954 }
955
956 if((fpi_msg = fopen(infile, "r")) == NULL) {
957 /* skip to next article if infile don't exist */
958 error_log(ERRLOG_REPORT, rpost_phrases[12],NULL);
959 }
960 else {
961 retval = do_article(myargs, fpi_msg);
962 if(myargs->debug == TRUE && retval != RETVAL_OK) {
963 do_debug("do_article() returned %d\n", retval);
964 }
965 fclose(fpi_msg);
966 }
967 }
968 return retval;
969 }
970 /*--------------------------------------------------------------------------------*/
do_batch(Pargs myargs)971 int do_batch(Pargs myargs) {
972
973 int i, x, nrdone, nrok, retval;
974 FILE *fpi_batch;
975 char buf[MAXLINLEN+1], file[MAXLINLEN+1];
976 char *outfile;
977
978 outfile = NULL;
979 retval = RETVAL_OK;
980 nrdone = nrok = 0;
981
982 if((fpi_batch = fopen(myargs->batch, "r")) == NULL) {
983 MyPerror(myargs->batch);
984 retval = RETVAL_ERROR;
985 }
986 else {
987 while((retval != RETVAL_ERROR) && (fgets(buf, MAXLINLEN, fpi_batch) != NULL)) {
988 /* build file name */
989 /* if no prefix, or if this is a inn token (not a real file), just copy it */
990 if(myargs->prefix == NULL || (file[0] == '@' && file[strlen(file)-1] == '@')) {
991 strcpy(file, buf);
992 }
993 else {
994 strcpy(file, myargs->prefix);
995 if(file[strlen(file)-1] != '/') {
996 strcat(file, "/");
997 }
998 strcat(file, buf);
999 }
1000 /* strip off nl */
1001 i = strlen(file);
1002 if(file[i-1] == '\n') {
1003 file[i-1] = '\0';
1004 }
1005 /* some INNs put article number on line in addition to file name */
1006 /* so lets parse through string, if find a ' ' NULL terminate at */
1007 /* that point */
1008 for(x=0;x<i;x++) {
1009 if(file[x] == ' ') {
1010 file[x] = '\0';
1011 x = i; /* to break loop */
1012 }
1013 }
1014 /* okay here have file name */
1015 if(myargs->debug == TRUE) {
1016 do_debug("Article Name: %s\n", file);
1017 }
1018 retval = filter_post_article(myargs, file);
1019
1020 nrdone++;
1021 if(retval == RETVAL_OK) {
1022 nrok++;
1023 }
1024 /* log failed uploads (dupe article is not a failed upload ) */
1025 else if (retval == RETVAL_ARTICLE_PROB) {
1026 log_fail(myargs->batch, buf);
1027 }
1028
1029 }
1030 if(retval == RETVAL_ERROR) {
1031 /* write out the rest of the batch file to the failed file */
1032 do {
1033 log_fail(myargs->batch, buf);
1034 } while (fgets(buf, MAXLINLEN, fpi_batch) != NULL);
1035 }
1036 fclose(fpi_batch);
1037
1038 if(retval != RETVAL_ERROR) {
1039 retval = (nrok == nrdone) ? RETVAL_OK : RETVAL_ARTICLE_PROB;
1040 }
1041 if(myargs->deleteyn == TRUE && retval == RETVAL_OK) {
1042 unlink(myargs->batch);
1043 print_phrases(myargs->status_fptr, rpost_phrases[13], myargs->batch,NULL);
1044 }
1045 }
1046
1047 return retval;
1048 }
1049 /*---------------------------------------------------------------*/
log_fail(char * batch_file,char * article)1050 void log_fail(char *batch_file, char *article) {
1051
1052 char file[MAXLINLEN+1];
1053
1054 FILE *fptr;
1055
1056 sprintf(file, "%s%s", batch_file, RPOST_FAIL_EXT);
1057
1058 if((fptr = fopen(file, "a")) == NULL) {
1059 MyPerror(file);
1060 }
1061 else {
1062 fputs(article, fptr);
1063 fclose(fptr);
1064 }
1065 }
1066 /*------------------------------------------------------------------------------------------------*/
do_rnews(Pargs myargs)1067 int do_rnews(Pargs myargs) {
1068 /* handle rnews files */
1069 int len, nrdone, nrok, retval = RETVAL_OK;
1070 DIR *dptr;
1071 FILE *f_rnews, *f_temp;
1072 char rnews_path[PATH_MAX+1], temp_path[PATH_MAX+1], linein[MAXLINLEN];
1073 struct dirent *dentry;
1074
1075 sprintf(temp_path, "%s/%s", myargs->rnews_path, TEMP_ARTICLE); /* only need to do this once */
1076
1077 if(myargs->rnews_file == NULL || myargs->rnews_path == NULL) {
1078 error_log(ERRLOG_REPORT, rpost_phrases[39], NULL);
1079 retval = RETVAL_ERROR;
1080 }
1081 else if((dptr = opendir(myargs->rnews_path)) == NULL) {
1082 error_log(ERRLOG_REPORT, rpost_phrases[39], NULL);
1083 retval = RETVAL_ERROR;
1084 }
1085 else {
1086 if(myargs->debug == TRUE) {
1087 do_debug("Reading directory %s\n", myargs->rnews_path);
1088 }
1089
1090 len = strlen(myargs->rnews_file);
1091 /* work our way thru the directory find our files that */
1092 /* start with our rnews_file prefix */
1093 while((dentry = readdir(dptr)) != NULL && retval != RETVAL_ERROR) {
1094 if(strncmp(dentry->d_name, myargs->rnews_file, len) == 0) {
1095 /* bingo, we've found one of the files*/
1096 /* now work our way thru it and create our temp article */
1097 /* which can be fed thru the filters and then to do_article() */
1098 nrdone = nrok = 0; /* which article are we on */
1099 sprintf(rnews_path, "%s/%s", myargs->rnews_path, dentry->d_name);
1100 if(myargs->debug == TRUE) {
1101 do_debug("Trying to read rnews file %s\n", rnews_path);
1102 }
1103
1104 if((f_rnews = fopen(rnews_path, "r")) == NULL) {
1105 MyPerror(rnews_path);
1106 retval = RETVAL_ERROR;
1107 }
1108 else {
1109 f_temp = NULL;
1110 /* now work our way thru the file */
1111 while(retval != RETVAL_ERROR && fgets(linein, MAXLINLEN, f_rnews) != NULL) {
1112 if(strncmp(linein, RNEWS_START, RNEWS_START_LEN) == 0) {
1113 if(myargs->debug == TRUE) {
1114 do_debug("Found rnews line %s", linein);
1115 }
1116 if(f_temp != NULL) {
1117 /* close out the old file, and process it */
1118 fclose(f_temp);
1119 nrdone++;
1120 retval = filter_post_article(myargs, temp_path);
1121 if(retval == RETVAL_OK) {
1122 nrok++;
1123 }
1124 }
1125 if((f_temp = fopen(temp_path, "w")) == NULL) {
1126 MyPerror(temp_path);
1127 retval = RETVAL_ERROR;
1128 }
1129 }
1130 else if(f_temp != NULL) {
1131 /* write the line out to the file */
1132 fputs(linein, f_temp);
1133 }
1134 }
1135 if(f_temp != NULL) {
1136 /* close out our final article */
1137 fclose(f_temp);
1138 nrdone++;
1139 retval = filter_post_article(myargs, temp_path);
1140 if(retval == RETVAL_OK) {
1141 nrok++;
1142 }
1143 }
1144 fclose(f_rnews);
1145 if(retval != RETVAL_ERROR) {
1146 retval = (nrok == nrdone) ? RETVAL_OK : RETVAL_ARTICLE_PROB;
1147 }
1148 if(myargs->deleteyn == TRUE && retval == RETVAL_OK) {
1149 unlink(rnews_path);
1150 print_phrases(myargs->status_fptr, rpost_phrases[13], rnews_path,NULL);
1151 }
1152 }
1153 }
1154 }
1155 closedir(dptr);
1156 }
1157
1158 return retval;
1159 }
1160
1161 /*--------------------------------------------------------------------------------*/
1162 /* THE strings in this routine is the only one not in the arrays, since */
1163 /* we are in the middle of reading the arrays, and they may or may not be valid. */
1164 /*--------------------------------------------------------------------------------*/
load_phrases(Pargs myargs)1165 void load_phrases(Pargs myargs) {
1166
1167 int error=TRUE;
1168 FILE *fpi;
1169 char buf[MAXLINLEN];
1170
1171 if(myargs->phrases != NULL) {
1172
1173 if((fpi = fopen(myargs->phrases, "r")) == NULL) {
1174 MyPerror(myargs->phrases);
1175 }
1176 else {
1177 fgets(buf, MAXLINLEN, fpi);
1178 if(strncmp( buf, SUCK_VERSION, strlen(SUCK_VERSION)) != 0) {
1179 error_log(ERRLOG_REPORT, "Invalid Phrase File, wrong version\n", NULL);
1180 }
1181 else if((both_phrases = read_array(fpi, NR_BOTH_PHRASES, TRUE)) != NULL &&
1182 (rpost_phrases = read_array(fpi, NR_RPOST_PHRASES, TRUE)) != NULL) {
1183 error = FALSE;
1184 }
1185 }
1186 fclose(fpi);
1187 if(error == TRUE) {
1188 /* reset back to default */
1189 error_log(ERRLOG_REPORT, "Using default Language phrases\n",NULL);
1190 rpost_phrases = default_rpost_phrases;
1191 both_phrases = default_both_phrases;
1192 }
1193 }
1194 }
1195 /*--------------------------------------------------------------------------------*/
free_phrases(void)1196 void free_phrases(void) {
1197 /* free up the memory alloced in load_phrases() */
1198 if(rpost_phrases != default_rpost_phrases) {
1199 free_array(NR_RPOST_PHRASES, rpost_phrases);
1200 }
1201 if(both_phrases != default_both_phrases) {
1202 free_array(NR_BOTH_PHRASES, both_phrases);
1203 }
1204
1205 }
1206