#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <netdb.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

#ifdef PERL_EMBED
#include <EXTERN.h>
#include <perl.h>
#ifdef OLD_PERL
#ifndef ERRSV
# define ERRSV (GvSV(errgv))  /* needed for perl 5.004 and earlier */
#endif
#ifndef PL_na
# define PL_na (na)
#endif
#endif /* OLD_PERL */u
#endif

#ifdef HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# ifdef HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
#endif

#include "suck_config.h"
#include "both.h"
#include "phrases.h"

struct nntp_auth {
	char *userid;
	char *passwd;
	int autoauth;
	int use_env;
};

typedef struct {
	char *batch;
	char *prefix;
	char *host;
	char *filter_args[RPOST_MAXARGS];
	int filter_argc;
	int filter_infilenr; /* nr of arg that we replace with infile */
	char *filter_outfile; /* pointer to name of outfile */
	int deleteyn;
	int do_modereader;
	int debug;
	struct nntp_auth auth;
	FILE *status_fptr;
	unsigned short int portnr;
	const char *phrases;
	char *rnews_file;
	char *rnews_path;
	int sockfd;
	int show_name;
	int ignore_readonly;
	int do_ssl;
	void *ssl_struct;
#ifdef PERL_EMBED
	PerlInterpreter *perl_int;
#endif
} Args, *Pargs;

/* function declarations */
int do_article(Pargs, FILE *);
int do_batch(Pargs);
int do_rnews(Pargs);
char *do_filter(Pargs, char *);
int send_command(Pargs, const char *, char **, int);
int do_authenticate(Pargs);
int scan_args(Pargs, int, char *[]);
int filter_post_article(Pargs, char *);
void log_fail(char *, char *);
void load_phrases(Pargs);
void free_phrases(void);
int parse_filter_args(Pargs myargs, int, char **);
#ifdef PERL_EMBED
void parse_perl(Pargs, char *);
void perl_done(Pargs);
char *filter_perl(Pargs, char *);
#endif


#define RNEWS_START  "#! rnews"   /* string that denotes the begin of a new message in rnews file */
#define RNEWS_START_LEN 8
#define TEMP_ARTICLE "tmp-article" /* name for temp article in rnews_path */

/* stuff needed for language phrases */
	/* set up defaults */	
char **rpost_phrases = default_rpost_phrases;
char **both_phrases = default_both_phrases;

enum { RETVAL_ERROR = -1, RETVAL_OK, RETVAL_ARTICLE_PROB, RETVAL_NOAUTH, RETVAL_UNEXPECTEDANS};
/*------------------------------------------------*/
int main(int argc, char *argv[], char *env[]) {
	char *inbuf;
	int response, retval, loop, fargc, i;
	struct hostent *hi;
	struct stat sbuf;
	char **args, **fargs;
	Args myargs;

	/* initialize everything */
	retval = RETVAL_OK;
	fargc = 0;
	fargs = NULL;

	myargs.batch = NULL;
	myargs.prefix =NULL;
	myargs.status_fptr = stdout;
	myargs.deleteyn = FALSE;
	myargs.auth.userid = NULL;
	myargs.auth.passwd = NULL;
	myargs.auth.autoauth = FALSE;
	myargs.auth.use_env = FALSE;
	myargs.do_modereader = FALSE;
	myargs.portnr = DEFAULT_NNRP_PORT;
	myargs.host = getenv("NNTPSERVER");		/* the default */
	myargs.phrases = NULL;
	myargs.debug = FALSE;
	myargs.rnews_file = NULL;
	myargs.rnews_path = NULL;
	myargs.filter_argc = 0;
	myargs.filter_infilenr = -1;
	myargs.filter_outfile = NULL;
	myargs.show_name = FALSE;
	myargs.ignore_readonly = FALSE;
	myargs.do_ssl = FALSE;
	myargs.ssl_struct = NULL;
#ifdef PERL_EMBED
	myargs.perl_int = NULL;
#endif

	/* have to do this next so if set on cmd line, overrides this */
#ifdef N_PHRASES		/* in case someone nukes def */
	if(stat(N_PHRASES, &sbuf) == 0 && S_ISREG(sbuf.st_mode)) {
		/* we have a regular phrases file make it the default */
		myargs.phrases = N_PHRASES;
	}
	
#endif

	/* allow no args, only the hostname, or hostname as first arg */
	/* also have to do the file argument checking */
	switch(argc) {
	  case 1:
		break;
	  case 2:
		/* the fargs == NULL test so only process first file name */
		if(argv[1][0] == FILE_CHAR) {
			if((fargs = build_args(&argv[1][1], &fargc)) != NULL) {
				retval = scan_args(&myargs, fargc, fargs);
			}
		}   
		else {
			myargs.host = argv[1];
		}
		break;
	  default:
		for(loop=1;loop<argc && fargs == NULL;loop++) {
			if(argv[loop][0] == FILE_CHAR) {
				if((fargs = build_args(&argv[loop][1], &fargc)) != NULL) {
					retval = scan_args(&myargs, fargc, fargs);
				}
			}   
		}
		/* this is here so anything at command line overrides file */
		if(argv[1][0] != '-' && argv[1][0] != FILE_CHAR) {
			myargs.host = 	argv[1];
			argc-= 2;
			args = &(argv[2]);
		}
		else {
			args = &(argv[1]);
			argc--;
		}
		retval = scan_args(&myargs, argc, args);	
		break;
	}
	load_phrases(&myargs);	/* this has to be here so rest prints okay */

	if(retval == RETVAL_ERROR) {
		error_log(ERRLOG_REPORT, rpost_phrases[0], argv[0], NULL);
		error_log(ERRLOG_REPORT, rpost_phrases[1], NULL);
		error_log(ERRLOG_REPORT, rpost_phrases[2], NULL);
	}
	else {
		if(myargs.debug == TRUE) {
			do_debug("Rpost version %s\n",SUCK_VERSION);
			do_debug("myargs.batch = %s\n", null_str(myargs.batch));
			do_debug("myargs.prefix = %s\n", null_str(myargs.prefix));
			do_debug("myargs.filter_argc = %d\n", myargs.filter_argc);
			for(loop=0;loop<myargs.filter_argc;loop++) {
				do_debug("myargs.filter_args[%d] = %s\n", loop, null_str(myargs.filter_args[loop]));
			}
			do_debug("myargs.filter_infilenr = %d\n", myargs.filter_infilenr);
			do_debug("myargs.filter_outfile = %s\n", null_str(myargs.filter_outfile));
			do_debug("myargs.status_fptr = %s\n", (myargs.status_fptr == stdout) ? "stdout" : "not stdout");
			do_debug("myargs.deleteyn = %s\n", true_str(myargs.deleteyn));
			do_debug("myargs.do_modereader = %s\n", true_str(myargs.do_modereader));
			do_debug("myargs.portnr = %d\n", myargs.portnr);
			do_debug("myargs.host = %s\n", null_str(myargs.host));
			do_debug("myargs.phrases = %s\n", null_str(myargs.phrases));
			do_debug("myargs.auth.autoauth = %s\n", true_str(myargs.auth.autoauth));
			do_debug("myargs.auth.use_env = %s\n", true_str(myargs.auth.use_env));
			do_debug("myargs.rnews_file = %s\n", null_str(myargs.rnews_file));
			do_debug("myargs.rnews_path = %s\n", null_str(myargs.rnews_path));
			do_debug("myargs.show_name = %s\n", true_str(myargs.show_name));
			do_debug("myargs.ignore_readonly = %s\n", true_str(myargs.ignore_readonly));
			do_debug("myargs.do_ssl = %s\n", true_str(myargs.do_ssl));
#ifdef TIMEOUT
			do_debug("TimeOut = %d\n", TimeOut);
#endif
			do_debug("myargs.debug = TRUE\n");
		}

		

		/* we processed args okay */
		if(myargs.host == NULL) {
			error_log(ERRLOG_REPORT, rpost_phrases[20], NULL);
			retval = RETVAL_ERROR;
		}
		else {
			myargs.sockfd = connect_to_nntphost( myargs.host, &hi, myargs.status_fptr, myargs.portnr, myargs.do_ssl, &myargs.ssl_struct);
			if(myargs.sockfd < 0) {
				retval = RETVAL_ERROR;
			}
			else {
				/* Get the announcement line */
				if((i = sgetline(myargs.sockfd, &inbuf, myargs.do_ssl, myargs.ssl_struct)) < 0) {
					retval = RETVAL_ERROR;
				}
				else {
					fputs(inbuf, myargs.status_fptr );
					number(inbuf, &response);
					if(response == 480) {
						retval = do_authenticate(&myargs);
					}
				}
				if(retval == RETVAL_OK && myargs.do_modereader == TRUE) {
					retval = send_command(&myargs, "mode reader\r\n", &inbuf, 0);
					if( retval  == RETVAL_OK) {				
						/* Again the announcement */
						fputs(inbuf, myargs.status_fptr );
						number(inbuf, &response);
					}
				}
				if(response == 201 && myargs.ignore_readonly == FALSE) {
					/*	if((response != 200) && (response != 480)) {*/
					error_log(ERRLOG_REPORT, rpost_phrases[3], NULL);
					retval = RETVAL_ERROR;
				}
				else if(myargs.auth.autoauth == TRUE) {
					if(myargs.auth.passwd == NULL || myargs.auth.userid == NULL) {
						error_log(ERRLOG_REPORT, rpost_phrases[32], NULL);
						retval = RETVAL_ERROR;
					}
					else {
						retval = do_authenticate(&myargs);
					}
				}
				
			}
		}
		if(retval == RETVAL_OK) {
			if(myargs.rnews_file != NULL) {
				/* rnews mode */
				retval = do_rnews(&myargs);
			}
			else if(myargs.batch != NULL) {
				/* batch mode */
				retval = do_batch(&myargs);
			}
			else {
				/* do one article from stdin */
				retval = do_article(&myargs, stdin);
			}
	
			print_phrases(myargs.status_fptr, rpost_phrases[4], hi->h_name, NULL);
			if(myargs.debug == TRUE) {
				do_debug("Sending quit");
			}
			send_command(&myargs, "quit\r\n", NULL, 0);
			disconnect_from_nntphost(myargs.sockfd, myargs.do_ssl, &myargs.ssl_struct);
		}
	}
#ifdef PERL_EMBED
	if(myargs.perl_int != NULL) {
		perl_done(&myargs);
	}
#endif		
	if(myargs.status_fptr != NULL && myargs.status_fptr != stdout) {
		fclose(myargs.status_fptr);
	}
	free_phrases();	/* do this last so everything is displayed correctly */
	exit(retval);
}
/*----------------------------------------------------------------------------------*/
int do_article(Pargs myargs, FILE *fptr) {

	int len, response, retval, longline, dupeyn, i;
	char buf[MAXLINLEN+4], *inbuf, *ptr = NULL;
	/* the +4 in NL, CR, if have to double . in first pos */
	retval = RETVAL_OK;
	longline = FALSE;

	/* Initiate post mode don't use IHAVE since I'd have to scan the message for the msgid, */
	/* then rewind and reread the file The problem lies in if I'm using STDIN for the message, */
	/* I can't rewind!!!!, so no IHAVE */
	response = send_command(myargs, "POST\r\n", &inbuf, 340);
	fputs(inbuf, myargs->status_fptr);
	if(response!=RETVAL_OK) {
		error_log(ERRLOG_REPORT, rpost_phrases[5], NULL);
		retval = RETVAL_ERROR;
	}
	else {
		while(fgets(buf, MAXLINLEN, fptr) != NULL) {
			len=strlen(buf);
			if(longline == FALSE) {
				/* only check this if we are at the beginning of a line */
				if(buf[0]=='.')  {
				/* Yup, this has to be doubled */
					memmove(buf+1,buf,++len);
					buf[0]='.';
   				}
			}
			if(buf[len-1] == '\n') {
				/* only do this if we have an EOL in buffer */
				if(buf[len-2] != '\r') {
					/* only add the \r\n if it's not already there. */
					strncpy(&buf[len-1],"\r\n",3);
				}
				longline = FALSE;
			}
			else {
				longline = TRUE;
			}
   			sputline(myargs->sockfd, buf, myargs->do_ssl, myargs->ssl_struct);
			if(myargs->debug == TRUE) {
				do_debug("ARTICLE: %s", buf);
			}
		}
		/* if the last line didn't have a nl on it, we need to */
		/* put one so that the eom is recognized */
		if(longline == TRUE) {
			sputline(myargs->sockfd, "\r\n", myargs->do_ssl, myargs->ssl_struct);
		}
		sputline(myargs->sockfd, ".\r\n", myargs->do_ssl, myargs->ssl_struct);
		if(myargs->debug == TRUE) {
			do_debug("ARTICLE END\n");
		}

		if((i = sgetline(myargs->sockfd, &inbuf, myargs->do_ssl, myargs->ssl_struct)) < 0) {
			retval = RETVAL_ERROR;
		}
		else {
			fputs(inbuf, myargs->status_fptr);
			if(myargs->debug == TRUE) {
				do_debug("RESPONSE: %s", inbuf);
			}
			ptr = number(inbuf, &response);
		}
	 	if(retval == RETVAL_OK && response!=240) {
			dupeyn = FALSE;
			if(response == 441) {
				number(ptr, &response);
			}
			if(response == 435) { 
				dupeyn = TRUE;	
			}
			else {
				/* M$ server sends "441 (615) Article Rejected -- Duplicate Message ID" handle that */
				/* can't just call nr, cause of parens */
				/* ptr should be at ( after 441 */
				if( *ptr == '(' ) {
					number(++ptr, &response);
					if(response == 615) {
						dupeyn = TRUE;
					}		
				}
				else if (strstr(inbuf, RPOST_DUPE_STR) != NULL || strstr(inbuf, RPOST_DUPE_STR2)) {
					dupeyn = TRUE;
				}
			}
			if(dupeyn == TRUE) {
				print_phrases(myargs->status_fptr, rpost_phrases[6], NULL);
			}
			else {
				error_log(ERRLOG_REPORT, rpost_phrases[7], inbuf, NULL);
				retval = RETVAL_ARTICLE_PROB;
			}
		}
	}
	return retval;
}
/*----------------------------------------------------------------*/
int send_command(Pargs myargs, const char *cmd, char **ret_response, int good_response) {
	/* this is needed so can do user authorization */

	int len, retval = RETVAL_OK, nr;
	char *resp;

	if(myargs->debug == TRUE) {
		do_debug("sending command: %s", cmd);
	}
	sputline(myargs->sockfd, cmd, myargs->do_ssl, myargs->ssl_struct);
	len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);	
	if( len < 0) {	
		retval = RETVAL_ERROR;		  	
	}					
	else {
	  if(myargs->debug == TRUE) {
	    do_debug("got answer: %s", resp);
	  }

	  number(resp, &nr);
	  if(nr == 480 ) { 
		  /* we must do authorization */
		  retval = do_authenticate(myargs);
		  if(retval == RETVAL_OK) {
			  /* resend command */
			  sputline(myargs->sockfd, cmd, myargs->do_ssl, myargs->ssl_struct);
			  if(myargs->debug == TRUE) {
				  do_debug("sending command: %s", cmd);
			  }
			  len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);
			  if( len < 0) {	
				  retval = RETVAL_ERROR;		  	
			  }
			  else {
				  number(resp,&nr);
				  if(myargs->debug == TRUE) {
					  do_debug("got answer: %s", resp);
				  }
			  }
		  }
	  }
	  if (good_response != 0 && nr != good_response) {
	    error_log(ERRLOG_REPORT, rpost_phrases[18],cmd,resp,NULL);
	    retval = RETVAL_UNEXPECTEDANS;
	  }
	}
	if(ret_response != NULL) {
		*ret_response = resp;
	}
	return retval;
}
/*----------------------------------*/
/* authenticate when receive a 480 */
/*----------------------------------*/
int do_authenticate(Pargs myargs) {
	 int len, nr, retval = RETVAL_OK;
	 char *resp, buf[MAXLINLEN];

	 /* we must do authorization */
	 if((myargs->auth).userid == NULL || (myargs->auth).passwd == NULL) {
		 error_log(ERRLOG_REPORT, rpost_phrases[43], NULL);
		 retval = RETVAL_NOAUTH;
		 
	 }
	 else {
		 print_phrases(myargs->status_fptr, rpost_phrases[41], NULL);
	 
		 sprintf(buf, "AUTHINFO USER %s\r\n", (myargs->auth).userid);
		 if(myargs->debug == TRUE) {
			 do_debug("sending command: %s", buf);
		 }
		 sputline(myargs->sockfd, buf, myargs->do_ssl, myargs->ssl_struct);
		 len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);
		 if( len < 0) {	
			 retval = RETVAL_ERROR;		  	
		 }					
		 else {
			 if(myargs->debug == TRUE) {
				 do_debug("got answer: %s", resp);
			 }

			 number(resp, &nr);
			 if(nr == 480) {
				 /* this is because of the pipelining code */
				 /* we get the second need auth */
				 /* just ignore it and get the next line */
				 /* which should be the 381 we need */
				 if((len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct)) < 0) {
					 retval = RETVAL_ERROR;
				 }
				 else {
					 number(resp, &nr);
				 } 
			 }
			 if(retval == RETVAL_OK) {
				 if(nr != 381) {
					 error_log(ERRLOG_REPORT, rpost_phrases[16], resp, NULL);
					 retval = RETVAL_NOAUTH;
				 }
				 else {
					 sprintf(buf, "AUTHINFO PASS %s\r\n", (myargs->auth).passwd);
					 sputline(myargs->sockfd, buf, myargs->do_ssl, myargs->ssl_struct);
					 if(myargs->debug == TRUE) {
						 do_debug("sending command: %s", buf);
					 }
					 len = sgetline(myargs->sockfd, &resp, myargs->do_ssl, myargs->ssl_struct);
					 if(len < 0) {	
						 retval = RETVAL_ERROR;		  	
					 }					
					 else {
						 if(myargs->debug == TRUE) {
							 do_debug("got answer: %s", resp);
						 }
						 number(resp, &nr);
						 switch(nr) {
						 case 281: /* bingo */
							 retval = RETVAL_OK;
							 print_phrases(myargs->status_fptr, rpost_phrases[42], NULL);
							 break;
						 case 502: /* permission denied */
							 retval = RETVAL_NOAUTH;
							 error_log(ERRLOG_REPORT, rpost_phrases[17], NULL);
							 break;
						 default: /* wacko error */
							 error_log(ERRLOG_REPORT, rpost_phrases[16], resp, NULL);
							 retval = RETVAL_NOAUTH;
							 break;
						 }
					 }
				 }
			 }
		 }
		 
	 }
	 return retval;
}
/*--------------------------------------------------------------------------------------*/
int scan_args(Pargs myargs, int argc, char *argv[]) {

	int loop, retval = RETVAL_OK;
	
	for(loop=0;loop<argc && retval == RETVAL_OK;loop++) {
		if(myargs->debug == TRUE) {
			do_debug("Checking arg #%d-%d: '%s'\n", loop, argc, argv[loop]);
		}
		/* check for valid args format */
		if(argv[loop][0] != '-' && argv[loop][0] != FILE_CHAR) {
			retval = RETVAL_ERROR;
		}
		if(argv[loop][0] == '-' && argv[loop][2] != '\0') {
			retval = RETVAL_ERROR;
		}

		if(retval != RETVAL_OK) {
			error_log(ERRLOG_REPORT, rpost_phrases[19], argv[loop], NULL);
		}
		else if(argv[loop][0] != FILE_CHAR) {
			switch(argv[loop][1]) {
			  case 'h': 	/* hostname */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[20], NULL);
					retval = RETVAL_ERROR;
				}
				else { 
					myargs->host = argv[++loop];
				}
				break;
				
			  case 'b':	/* batch file Mode, next arg = batch file */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[21], NULL);
					retval = RETVAL_ERROR;
				}
				else { 
					myargs->batch = argv[++loop];
				}
				break;
			  case 'p':	/* specify directory prefix for files in batch file */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[22], NULL);
					retval = RETVAL_ERROR;
				}
				else {
					myargs->prefix = argv[++loop];
				}
				break;
			  case 'f':	/* filter prg, rest of cmd line is args */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[23], NULL);
					retval = RETVAL_ERROR;
				}
				else {
					/* set up filter args send it first arg to filter and arg count */
					retval = parse_filter_args(myargs, argc - (loop+1), &(argv[loop+1]));
					loop = argc;		/* terminate main loop */
				}
				break;
#ifdef PERL_EMBED
			  case 'F': /* filter using embedded perl */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[33], NULL);
				}
				else {
					/* parse the perl filter */
					parse_perl(myargs, argv[++loop]);
				}
				break;
#endif	  
			  case 'e':	/* use default error log path */
				error_log(ERRLOG_SET_FILE, ERROR_LOG,NULL);
				break;
			  case 'E':	/* error log path */
				if(loop+1 == argc) { 
					error_log(ERRLOG_REPORT, "%s\n",rpost_phrases[24],NULL);
					retval = RETVAL_ERROR;
				}
				else {
					error_log(ERRLOG_SET_FILE, argv[++loop], NULL);
				}
				break;
		  	  case 's':	/* use default status log path */
				if((myargs->status_fptr = fopen(STATUS_LOG, "a")) == NULL) {
					MyPerror(rpost_phrases[25]);
					retval = RETVAL_ERROR;
				}
				break;
			  case 'S':	/* status log path */
				if(loop+1 == argc) { 
					error_log(ERRLOG_REPORT, rpost_phrases[26],NULL);
					retval = RETVAL_ERROR;
				}	
				else if((myargs->status_fptr = fopen(argv[++loop], "a")) == NULL) {
					MyPerror(rpost_phrases[25]);
					retval = RETVAL_ERROR;
				}
				break;
			  case 'd':	/* delete batch file on success */
				myargs->deleteyn = TRUE;
				break;
			  case 'u':	/* do auto-authenticate */
				myargs->auth.autoauth = TRUE;
				break;
			  case 'U': 	/* next arg is userid for authorization */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[27],NULL);
					retval = RETVAL_ERROR;
				}
				else { 
					myargs->auth.userid = argv[++loop];
				}
				break;
			  case 'P': 	/* next arg is password for authorization */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[28],NULL);
					retval = RETVAL_ERROR;
				}
				else { 
					myargs->auth.passwd = argv[++loop];
				}
				break;
			  case 'Q':     /* get userid/passwd from env */
				myargs->auth.use_env = TRUE;
				myargs->auth.passwd = getenv("NNTP_PASS");
				myargs->auth.userid = getenv("NNTP_USER");
				break;
			  case 'M':	/* do mode reader command */
				myargs->do_modereader = TRUE;
				break;
			  case 'N':	/* override port number */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[29],NULL);
					retval = RETVAL_ERROR;
				}
				else {
					myargs->portnr = atoi(argv[++loop]);
				}
				break;
			  case 'l': 	/* language  phrase file */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[31],NULL);
					retval = RETVAL_ERROR;
				}
				else {
					myargs->phrases = argv[++loop];
				}
				break;
			  case 'D':	/* debug */
				myargs->debug = TRUE;
				/* so error_log goes to debug too */
				error_log(ERRLOG_SET_DEBUG, NULL, NULL);
				break;
			case 'r':      /* do rnews next args  base name then path for the rnews files */
				if(loop+2 >= argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[38], NULL);
					retval = RETVAL_ERROR;
				}
				else {
					myargs->rnews_file = argv[++loop];
					myargs->rnews_path = argv[++loop];
				}
				break;
			case 'n':  /* show the file name as we're uploading it */
				myargs->show_name = TRUE;
				break;
			case 'i':  /* ignore read_only opening announcement */
				myargs->ignore_readonly = TRUE;
				break;
#ifdef TIMEOUT
			case 'T': /* timeout */
				if(loop+1 == argc) {
					error_log(ERRLOG_REPORT, rpost_phrases[31], NULL);
					retval = RETVAL_ERROR;
				}
				else {
					TimeOut = atoi(argv[++loop]);
				}
				break;
#endif
#ifdef HAVE_LIBSSL
			case 'z': /* use SSL */
				myargs->do_ssl = TRUE;
				myargs->portnr = DEFAULT_SSL_PORT;
				break;
#endif
			  default:
				error_log(ERRLOG_REPORT, rpost_phrases[30], argv[loop],NULL);
				break;
			}
		}
	}
	return retval;
}

#ifdef PERL_EMBED
/*-----------------------------------------------------------------------------------------*/
void parse_perl(Pargs args, char *fname) {

	/* this code is copied from killprg.c killperl_setup() */
	/* which comes from PERLEMBED man page */
	
	char *ptr = NULL;
	char *prgs[] = { ptr, ptr };
	
	if ((args->perl_int = perl_alloc()) == NULL) {
		error_log(ERRLOG_REPORT, rpost_phrases[34], NULL);
	}
	else {
		if(args->debug == TRUE) {
			do_debug("Perl program name =%s\n", fname);
		}
		perl_construct(args->perl_int);
		prgs[1] = fname;
		if(perl_parse(args->perl_int, NULL, 2, prgs, NULL) == 0) {
			perl_run(args->perl_int);
		}
		else {
			error_log(ERRLOG_REPORT, rpost_phrases[35], fname, NULL);
			perl_done(args);
		}
	} 
}
/*---------------------------------------------------------------------------------------*/
void perl_done(Pargs args) {
	/* all done, free everything up */
	if(args->perl_int != NULL) {
		perl_destruct(args->perl_int);
		perl_free(args->perl_int);
		args->perl_int = NULL;
	}
}
/*--------------------------------------------------------------------------------------*/
char *filter_perl(Pargs myargs, char *file) {

	 int i;
	 char *args[2] ={ NULL, NULL};
	 char infilen[MAXLINLEN+1];
	 char *infile=NULL;
	 dSP; /* perl stack pointer */
	 SV *fname; /* used to get fname off of perl stack */
	 
				/* here's where we send it to perl and get back our filename */
				/* we need infile to become our file name to post*/
				/* this code comes from chk_msg_kill_perl() which uses */
				/* both perlcall and perlembed man pages */
				/* set up args */
	 args[0] = file;

	 if(myargs->debug == TRUE) {
		 do_debug("Calling %s with arg %s\n", PERL_RPOST_SUB, file);
	 }
				
	 ENTER;
	 SAVETMPS;
	 PUSHMARK(SP);

	 i = perl_call_argv(PERL_RPOST_SUB, G_SCALAR | G_EVAL | G_KEEPERR, args);
	 
	 SPAGAIN;

	 if(SvTRUE(ERRSV)) {
		 error_log(ERRLOG_REPORT, rpost_phrases[36], SvPV(ERRSV,PL_na));
		 POPs;
		 perl_done(myargs);
	 }
	 else if(i != 1) {
		 error_log(ERRLOG_REPORT, rpost_phrases[37], PERL_RPOST_SUB, NULL);
		 perl_done(myargs);
	 }
	 else {
		 fname = POPs;
		 infile = SvPV(fname, PL_na);
		 if(myargs->debug == TRUE) {
			 do_debug("got string =%s\n", null_str(infile));
		 }
                 /* have to do the following cause as soon as we do FREETMPS, the */
                 /* data that infile points to will disappear */
		 strcpy(infilen, infile);
		 infile = infilen; /* so we return the name of the file */
	 }
	 PUTBACK;
	 FREETMPS;
	 LEAVE;

	 return infile;
	 
}
#endif
/*---------------------------------------------------------------------------------------*/
int parse_filter_args(Pargs myargs, int argcount, char **args) {
	 
	 int argon, loop, i, retval = RETVAL_OK;
	 
	 /* build filter args */ 
	 i = strlen(RPOST_FILTER_OUT);

	 /* just in case */
	 myargs->filter_outfile = NULL;
	 myargs->filter_infilenr = -1;
	 /* build array of args to past to fork, execl */
	 /* if see RPOST_FILTER_IN as arg, substitute the infile name */
	 /* if see RPOST_FILTER_OUT as first part of arg, get outfile name */
	 for(argon = 0, loop = 0; loop < argcount; loop++) {

		 if(myargs->debug == TRUE) {
			 do_debug("loop=%d argon=%d arg=%s\n", loop, argon, args[loop]);
		 }
		 
		 if(strcmp(args[loop], RPOST_FILTER_IN) == 0) {
			 /* substitute infile name */
			 myargs->filter_args[argon] = NULL; /* just so debug.suck looks okay */
			 myargs->filter_infilenr = argon++;
		 }
		 else if(strncmp(args[loop], RPOST_FILTER_OUT, (unsigned int) i) == 0) {
			 /* arg should be RPOST_FILTER_OUT=filename */
			 if(args[loop][i] != '=') {
				 error_log(ERRLOG_REPORT, rpost_phrases[17], args[loop], NULL);
			 }
			 else {
				 myargs->filter_outfile = (char *)&(args[loop][i+1]);
			 }
		 }
		 else {
			 myargs->filter_args[argon++] = args[loop];
		 }
	 }
	 myargs->filter_argc = argon; /* the count of arguments */
	 myargs->filter_args[argon] = NULL;  /* just to be on the safe side */
	 if(myargs->filter_outfile == NULL) {
                                /* no outfile defined, use built-in default */
		 myargs->filter_outfile = tmpnam(NULL);
		 error_log(ERRLOG_REPORT, rpost_phrases[9], myargs->filter_outfile, NULL);
	 }
	 if(myargs->filter_infilenr < 0) {
		 error_log(ERRLOG_REPORT, rpost_phrases[10], NULL);
		 retval = RETVAL_ERROR;
	 }
	 return retval;
}
/*--------------------------------------------------------------------*/
char *do_filter(Pargs myargs, char *filename) {
	char *infile = NULL;
	int i;
	
	myargs->filter_args[myargs->filter_infilenr] = filename; /* so this points to the right name */
	
	if( myargs->debug == TRUE) {
		do_debug("ARGS:");
		for(i=0;i<myargs->filter_argc;i++) {
			do_debug(" %d=%s", i, myargs->filter_args[i]);
		}
		do_debug("\n");
	}
	switch((int) fork()) {
	  case 0:	/* in child, do execl */
		if(execvp(myargs->filter_args[0], myargs->filter_args) == -1) {
			MyPerror(rpost_phrases[14]);
			exit(-1); /* so we don't get two running */
		}
		break;
	  case -1:	/* should never get here */
		MyPerror(rpost_phrases[15]);
		break;
	  default:	/* in parent, wait for child to finish */
		wait(NULL);		/* no status check on finish */
		infile = myargs->filter_outfile;
	}					

	return infile;
}
/*----------------------------------------------------------------------------------*/
int filter_post_article(Pargs myargs, char *filename) {

	 char *infile = NULL; /* this is the filename that gets passed to do_article() */
	 int retval = RETVAL_OK;
	 FILE *fpi_msg;
	 
#ifdef PERL_EMBED
	 if(myargs->perl_int != NULL) {
		 infile  = filter_perl(myargs, filename);
	 }		
	 else
#endif
		 if(myargs->filter_argc >0) {
			 infile = do_filter(myargs, filename);
		 }
		 else {
				/* so we open up the right file */
			 infile = filename;
		 } 
	 
	 if(infile == NULL) {
		 error_log(ERRLOG_REPORT, rpost_phrases[11], NULL);
		 retval = RETVAL_ERROR;
	 }
	 else {
		 if(myargs->show_name == TRUE) {
			 /* show the file name */
			 print_phrases(myargs->status_fptr, rpost_phrases[40], infile, NULL);
		 }
		 
		 if((fpi_msg = fopen(infile, "r")) == NULL) {
			 /* skip to next article if infile don't exist */
			 error_log(ERRLOG_REPORT, rpost_phrases[12],NULL);
		 }
		 else {
			 retval = do_article(myargs, fpi_msg);
			 if(myargs->debug == TRUE && retval != RETVAL_OK) {
				 do_debug("do_article() returned %d\n", retval);
			 }
			 fclose(fpi_msg);
		 }
	 }	
	 return retval;
}
/*--------------------------------------------------------------------------------*/
int do_batch(Pargs myargs) {

	int i, x, nrdone, nrok, retval;
	FILE *fpi_batch;
	char buf[MAXLINLEN+1], file[MAXLINLEN+1];
	char *outfile;

	outfile = NULL;
	retval = RETVAL_OK;
	nrdone = nrok = 0;

	if((fpi_batch = fopen(myargs->batch, "r")) == NULL) {
		MyPerror(myargs->batch);
		retval = RETVAL_ERROR;
	}
	else {		
		while((retval != RETVAL_ERROR) && (fgets(buf, MAXLINLEN, fpi_batch) != NULL)) {
			/* build file name */
			/* if no prefix, or if this is a inn token (not a real file), just copy it */
			if(myargs->prefix == NULL || (file[0] == '@' && file[strlen(file)-1] == '@')) {
				strcpy(file, buf);
			}
			else {
				strcpy(file, myargs->prefix);
				if(file[strlen(file)-1] != '/') {
					strcat(file, "/");
				}
				strcat(file, buf);
			}
			/* strip off nl */
			i = strlen(file);
			if(file[i-1] == '\n') {
				file[i-1] = '\0';
			}
			/* some INNs put article number on line in addition to file name */
			/* so lets parse through string, if find a ' ' NULL terminate at */
			/* that point */
			for(x=0;x<i;x++) {
				if(file[x] == ' ') {
					file[x] = '\0';
					x = i;	/* to break loop */
				}
			}
			/* okay here have file name */
			if(myargs->debug == TRUE) {
				do_debug("Article Name: %s\n", file);
			}
			retval = filter_post_article(myargs, file);

			nrdone++;
			if(retval == RETVAL_OK) {
				nrok++;
			}
				/* log failed uploads (dupe article is not a failed upload ) */
			else if (retval == RETVAL_ARTICLE_PROB) {
				log_fail(myargs->batch, buf);
			}
		
		}
		if(retval == RETVAL_ERROR) {
			/* write out the rest of the batch file to the failed file */
			do {
				log_fail(myargs->batch, buf);
			} while (fgets(buf, MAXLINLEN, fpi_batch) != NULL);
		}	
	 	fclose(fpi_batch);

		if(retval != RETVAL_ERROR) {
			retval = (nrok == nrdone) ? RETVAL_OK : RETVAL_ARTICLE_PROB;
		}		
		if(myargs->deleteyn == TRUE && retval == RETVAL_OK) {
			unlink(myargs->batch);
			print_phrases(myargs->status_fptr, rpost_phrases[13], myargs->batch,NULL);
		}
	}

	return retval;
}
/*---------------------------------------------------------------*/
void log_fail(char *batch_file, char *article) {

	char file[MAXLINLEN+1];

	FILE *fptr;

	sprintf(file, "%s%s", batch_file, RPOST_FAIL_EXT);

	if((fptr = fopen(file, "a")) == NULL) {
		MyPerror(file);
	}
	else {
		fputs(article, fptr);
		fclose(fptr);
	}
}
/*------------------------------------------------------------------------------------------------*/
int do_rnews(Pargs myargs) {
	 /* handle rnews files */
	 int len, nrdone, nrok, retval = RETVAL_OK;
	 DIR *dptr;
	 FILE *f_rnews, *f_temp;
	 char rnews_path[PATH_MAX+1], temp_path[PATH_MAX+1], linein[MAXLINLEN];
	 struct dirent *dentry;

	 sprintf(temp_path, "%s/%s", myargs->rnews_path, TEMP_ARTICLE); /* only need to do this once */
	 
	 if(myargs->rnews_file == NULL || myargs->rnews_path == NULL) {
		 error_log(ERRLOG_REPORT, rpost_phrases[39], NULL);
		 retval = RETVAL_ERROR;
	 }
	 else if((dptr = opendir(myargs->rnews_path)) == NULL) {
		 error_log(ERRLOG_REPORT, rpost_phrases[39], NULL);
		 retval = RETVAL_ERROR;
	 }
	 else {
		 if(myargs->debug == TRUE) {
			 do_debug("Reading directory %s\n", myargs->rnews_path);
		 }
		 
		 len = strlen(myargs->rnews_file);
		 /* work our way thru the directory find our files that */
		 /* start with our rnews_file prefix */
		 while((dentry = readdir(dptr)) != NULL && retval != RETVAL_ERROR) {
			 if(strncmp(dentry->d_name, myargs->rnews_file, len) == 0) {
				 /* bingo, we've found one of the files*/
				 /* now work our way thru it and create our temp article */
				 /* which can be fed thru the filters and then to do_article() */
				 nrdone = nrok = 0; /* which article are we on */
				 sprintf(rnews_path, "%s/%s", myargs->rnews_path, dentry->d_name);
				 if(myargs->debug == TRUE) {
					 do_debug("Trying to read rnews file %s\n", rnews_path);
				 }
				 
				 if((f_rnews = fopen(rnews_path, "r")) == NULL) {
					 MyPerror(rnews_path);
					 retval = RETVAL_ERROR;
				 }
				 else {
					 f_temp = NULL;
					 /* now work our way thru the file */
					 while(retval != RETVAL_ERROR && fgets(linein, MAXLINLEN, f_rnews) != NULL) {
						 if(strncmp(linein, RNEWS_START, RNEWS_START_LEN) == 0) {
							 if(myargs->debug == TRUE) {
								 do_debug("Found rnews line %s", linein);
							 }	 
							 if(f_temp != NULL) {
								 /* close out the old file, and process it */
								 fclose(f_temp);
								 nrdone++;
								 retval = filter_post_article(myargs, temp_path);
								 if(retval == RETVAL_OK) {
									 nrok++;
								 }
							 }
							 if((f_temp = fopen(temp_path, "w")) == NULL) {
								 MyPerror(temp_path);
								 retval = RETVAL_ERROR;
							 }
						 }
						 else if(f_temp != NULL) {
							 /* write the line out to the file */
							 fputs(linein, f_temp);
						 } 
					 }
					 if(f_temp != NULL) {
						 /* close out our final article */
						 fclose(f_temp);
						 nrdone++;
						 retval = filter_post_article(myargs, temp_path);
						 if(retval == RETVAL_OK) {
							 nrok++;
						 }
					 }
					 fclose(f_rnews);
					 if(retval != RETVAL_ERROR) {
						 retval = (nrok == nrdone) ? RETVAL_OK : RETVAL_ARTICLE_PROB;
					 }		
					 if(myargs->deleteyn == TRUE && retval == RETVAL_OK) {
						 unlink(rnews_path);
						 print_phrases(myargs->status_fptr, rpost_phrases[13], rnews_path,NULL);
					 }
				 }
			 }
		 }
		 closedir(dptr);
	 }
	 
	 return retval;
} 

/*--------------------------------------------------------------------------------*/
/* THE strings in this routine is the only one not in the arrays, since           */
/* we are in the middle of reading the arrays, and they may or may not be valid.  */
/*--------------------------------------------------------------------------------*/
void load_phrases(Pargs myargs) {

	int error=TRUE;
	FILE *fpi;
	char buf[MAXLINLEN];

	if(myargs->phrases != NULL) {
		
		if((fpi = fopen(myargs->phrases, "r")) == NULL) {
			MyPerror(myargs->phrases);
		}
		else {
			fgets(buf, MAXLINLEN, fpi);
			if(strncmp( buf, SUCK_VERSION, strlen(SUCK_VERSION)) != 0) {
				error_log(ERRLOG_REPORT, "Invalid Phrase File, wrong version\n", NULL);
			}
			else if((both_phrases = read_array(fpi, NR_BOTH_PHRASES, TRUE)) != NULL &&
				(rpost_phrases = read_array(fpi, NR_RPOST_PHRASES, TRUE)) != NULL) {
				error = FALSE;
			}
		}
		fclose(fpi);
		if(error == TRUE) {
			/* reset back to default */
			error_log(ERRLOG_REPORT, "Using default Language phrases\n",NULL);
			rpost_phrases = default_rpost_phrases;
			both_phrases = default_both_phrases;
		}
	}		
}
/*--------------------------------------------------------------------------------*/
void free_phrases(void) {
		/* free up the memory alloced in load_phrases() */
		if(rpost_phrases != default_rpost_phrases) {
			free_array(NR_RPOST_PHRASES, rpost_phrases);
		}
		if(both_phrases != default_both_phrases) {
			free_array(NR_BOTH_PHRASES, both_phrases);
		}
		
}


syntax highlighted by Code2HTML, v. 0.9.1