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