#include <config.h>

#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

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

#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

#include <string.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/stat.h>
#include <math.h>
#include <errno.h>
#include <ctype.h>

#ifdef DMALLOC
#include <dmalloc.h>
#endif

#include "suck_config.h"
#include "both.h"
#include "suck.h"
#include "suckutils.h"
#include "dedupe.h"
#include "phrases.h"
#include "killfile.h"
#include "timer.h"
#include "active.h"
#include "batch.h"
#include "xover.h"
#include "db.h"

#ifdef MYSIGNAL
#include <signal.h>
#endif

#ifdef CHECK_HISTORY
#include "chkhistory.h"
#endif

/* function prototypes */
int get_articles(PMaster);
int get_one_article(PMaster, int, long);
int do_supplemental(PMaster);
int restart_yn(PMaster);
int scan_args(PMaster, int, char *[]);
void do_cleanup(PMaster);
int do_authenticate(PMaster);
void load_phrases(PMaster);
void free_phrases(void);
int parse_args(PMaster, int, char *[]);
int get_group_number(PMaster, char *);
int do_supplemental(PMaster);
int do_sup_bynr(PMaster, char *);
int do_nodownload(PMaster);
#ifdef MYSIGNAL
RETSIGTYPE sighandler(int);
void pause_signal(int, PMaster);
enum {PAUSE_SETUP, PAUSE_DO};

/*------------------------------------------*/
int GotSignal = FALSE;
/* the only static variable allowed, it's   */
/* the only graceful way to handle a signal */
/*------------------------------------------*/
#endif

/* set up for phrases */
char **both_phrases=default_both_phrases;
char **suck_phrases=default_suck_phrases;
char **timer_phrases=default_timer_phrases;
char **chkh_phrases=default_chkh_phrases;
char **dedupe_phrases=default_dedupe_phrases;
char **killf_reasons=default_killf_reasons;
char **killf_phrases=default_killf_phrases;
char **killp_phrases=default_killp_phrases;
char **sucku_phrases=default_sucku_phrases;
char **active_phrases=default_active_phrases;
char **batch_phrases=default_batch_phrases;
char **xover_phrases=default_xover_phrases;
char **xover_reasons=default_xover_reasons;


enum { STATUS_STDOUT, STATUS_STDERR };
enum { RESTART_YES, RESTART_NO, RESTART_ERROR };

enum {
	ARG_NO_MATCH, ARG_ALWAYS_BATCH, ARG_BATCH_INN, ARG_BATCH_RNEWS, ARG_BATCH_LMOVE, \
	ARG_BATCH_INNFEED, ARG_BATCH_POST, ARG_CLEANUP, ARG_DIR_TEMP, ARG_DIR_DATA, ARG_DIR_MSGS, \
	ARG_DEF_ERRLOG, ARG_HOST, ARG_NO_POSTFIX, ARG_LANGUAGE, ARG_MULTIFILE,  ARG_POSTFIX, \
	ARG_QUIET, ARG_RNEWSSIZE, ARG_DEF_STATLOG, ARG_WAIT_SIG, ARG_ACTIVE, ARG_RECONNECT, \
	ARG_DEBUG, ARG_ERRLOG, ARG_HISTORY, ARG_KILLFILE, ARG_KLOG_NONE, ARG_KLOG_SHORT,  \
	ARG_KLOG_LONG, ARG_MODEREADER, ARG_PORTNR, ARG_PASSWD, ARG_RESCAN, ARG_STATLOG, \
	ARG_USERID, ARG_VERSION, ARG_WAIT, ARG_LOCALHOST, ARG_TIMEOUT, ARG_NRMODE, ARG_AUTOAUTH, \
	ARG_NODEDUPE, ARG_NO_CHK_MSGID, ARG_READACTIVE, ARG_PREBATCH, ARG_SKIP_ON_RESTART, \
	ARG_KLOG_NAME, ARG_USEGUI, ARG_XOVER, ARG_CONN_DEDUPE, ARG_POST_FILTER, ARG_CONN_ACTIVE, \
	ARG_HIST_FILE, ARG_HEADER_ONLY, ARG_ACTIVE_LASTREAD, ARG_USEXOVER, ARG_RESETCOUNTER, \
	ARG_LOW_READ, ARG_SHOW_GROUP, ARG_USE_SSL, ARG_LOCAL_SSL, ARG_BATCH_POST_NR, \
	ARG_PASSWD_ENV,
};

typedef struct Arglist{
	const char *sarg;
	const char *larg;
	int nr_params;
	int flag;
	int errmsg;		/* this is an index into suck_phrases */
} Args, *Pargs;

const Args arglist[] = {
	{"a",  "always_batch", 0, ARG_ALWAYS_BATCH, -1},
	{"bi", "batch_inn",    1, ARG_BATCH_INN, 40},
	{"br", "batch_rnews",  1, ARG_BATCH_RNEWS, 40},
	{"bl", "batch_lmove",  1, ARG_BATCH_LMOVE, 40},
	{"bf", "batch_innfeed",1, ARG_BATCH_INNFEED, 40},
	{"bp", "batch_post",   0, ARG_BATCH_POST, -1},
	{"bP", "batch_post_nr", 1, ARG_BATCH_POST_NR, 72},
	{"c",  "cleanup",      0, ARG_CLEANUP, -1},
	{"dt", "dir_temp",    1, ARG_DIR_TEMP, 37},
	{"dd", "dir_data",    1, ARG_DIR_DATA, 37},
	{"dm", "dir_msgs",    1, ARG_DIR_MSGS, 37},
	{"e",  "def_error_log", 0, ARG_DEF_ERRLOG, -1},
	{"f",  "reconnect_dedupe", 0, ARG_CONN_DEDUPE, -1},
	{"g",  "header_only", 0, ARG_HEADER_ONLY, -1},
	{"h",  "host", 1, ARG_HOST, 51},
	{"hl", "localhost", 1, ARG_LOCALHOST, 50},
	{"i",  "default_activeread", 1, ARG_ACTIVE_LASTREAD, 65},
	{"k",  "kill_no_postfix", 0, ARG_NO_POSTFIX, -1},
	{"l",  "language_file", 1, ARG_LANGUAGE, 47},
	{"lr", "low_read", 0, ARG_LOW_READ, -1},
	{"m",  "multifile", 0, ARG_MULTIFILE, -1},
	{"n",  "number_mode", 0, ARG_NRMODE, -1},
	{"p",  "postfix", 1, ARG_POSTFIX, 36},
	{"q",  "quiet", 0, ARG_QUIET, -1},
	{"r",  "rnews_size", 1, ARG_RNEWSSIZE, 35},
	{"rc", "reset_counter", 0, ARG_RESETCOUNTER, -1},
	{"s",  "def_status_log", 0, ARG_DEF_STATLOG, -1},
	{"sg", "show_group", 0, ARG_SHOW_GROUP, -1},
#ifdef HAVE_LIBSSL
	{"ssl","use_ssl", 0, ARG_USE_SSL, -1},
#endif
	{"u",  "auto_authorization", 0, ARG_AUTOAUTH, -1},
	{"w",  "wait_signal", 2, ARG_WAIT_SIG, 46},
	{"x",  "no_chk_msgid", 0, ARG_NO_CHK_MSGID, -1},
	{"y",  "post_filter", 1, ARG_POST_FILTER, 62},
	{"z",  "no_dedupe", 0, ARG_NODEDUPE, -1},
	{"A",  "active", 0, ARG_ACTIVE, -1},
	{"AL", "read_active", 1, ARG_READACTIVE, 56},
	{"B",  "pre-batch", 0, ARG_PREBATCH, -1},
	{"C",  "reconnect", 1, ARG_RECONNECT, 49},
	{"D",  "debug", 0, ARG_DEBUG, -1},
	{"E",  "error_log", 1, ARG_ERRLOG, 41},
	{"F",  "reconnect_active", 0, ARG_CONN_ACTIVE, -1},
	{"G",  "use_gui", 0, ARG_USEGUI, -1},
	{"H",  "no_history", 0, ARG_HISTORY, -1},
	{"HF", "history_file", 1, ARG_HIST_FILE, 64},
	{"K",  "killfile", 0, ARG_KILLFILE, -1},
	{"L",  "kill_log_none", 0, ARG_KLOG_NONE, -1},
	{"LF", "kill_log_name", 1, ARG_KLOG_NAME, 61},
	{"LS", "kill_log_short", 0, ARG_KLOG_SHORT, -1},
	{"LL", "kill_log_long",  0, ARG_KLOG_LONG, -1},
	{"M",  "mode_reader", 0, ARG_MODEREADER, -1},
	{"N",  "portnr", 1, ARG_PORTNR, 45},
	{"O",  "skip_on_restart", 0, ARG_SKIP_ON_RESTART, -1},
	{"P",  "password", 1, ARG_PASSWD, 44},
        {"Q",  "password_env", 0, ARG_PASSWD_ENV, -1},
	{"R",  "no_rescan", 0, ARG_RESCAN, -1},
	{"S",  "status_log", 1, ARG_STATLOG, 42},
#ifdef HAVE_LIBSSL
	{"SSL" "local_use_ssl", 0, ARG_LOCAL_SSL, -1},
#endif
#ifdef TIMEOUT
	{"T",  "timeout", 1, ARG_TIMEOUT, 52},
#endif
	{"U",  "userid",  1, ARG_USERID,  43},
	{"V",  "version", 0, ARG_VERSION, -1},
	{"W",  "wait", 2, ARG_WAIT, 46},
	{"X",  "no_xover", 0, ARG_XOVER, -1},
	{"Z",  "use_xover", 0, ARG_USEXOVER, -1},

};

#define MAX_ARG_PARAMS 2	/* max nr of params with any arg */
#define NR_ARGS (sizeof(arglist)/sizeof(arglist[0]))

/*------------------------------------------------------------------*/
int main(int argc, char *argv[]) {

	struct stat sbuf;
	Master master;
	PList temp;
	PGroups ptemp;
	POverview pov;
	char *inbuf, **args, **fargs = NULL;
	int nr, resp, loop, fargc, retval = RETVAL_OK;

#ifdef LOCKFILE
	const char *lockfile = NULL;
#endif
#ifdef MYSIGNAL
#ifdef HAVE_SIGACTION
	struct sigaction sigs;
#endif
#endif

	/* initialize master structure */
	master.head = master.curr = NULL;
	master.nritems = master.nrgot = 0;
	master.MultiFile = FALSE;
	master.msgs = stdout;
	master.sockfd = -1;
	master.status_file = FALSE;	/* are status messages going to a file */
	master.status_file_name = NULL;
	master.do_killfile = TRUE;
	master.do_chkhistory = TRUE;
	master.do_modereader = FALSE;
	master.always_batch = FALSE;
	master.rnews_size = 0L;
	master.batch = BATCH_FALSE;
	master.batchfile = NULL;
	master.cleanup = FALSE;
	master.portnr = DEFAULT_NNRP_PORT;
	master.host = getenv("NNTPSERVER");		/* the default */
	master.pause_time = -1;
	master.pause_nrmsgs = -1;
	master.sig_pause_time = -1;
	master.sig_pause_nrmsgs = -1;
	master.killfile_log = KILL_LOG_LONG;	/* do we log killed messages */
	master.phrases = NULL;
	master.errlog = NULL;
	master.debug = FALSE;
	master.rescan = TRUE;		/* do we do rescan on a restart */
	master.quiet = FALSE;		/* do we display BPS and msg count */
	master.killp = NULL;		/* pointer to killfile structure */
	master.kill_ignore_postfix = FALSE;
	master.reconnect_nr = 0;	/* how many x msgs do we disconnect and reconnect 0 = never */
	master.innfeed = NULL;
	master.do_active = FALSE; /* do we scan the local active list  */
	master.localhost = NULL;
	master.groups = NULL;
	master.nrmode = FALSE;    /* use nrs or msgids to get article */
	master.auto_auth = FALSE; /* do we do auto authorization */
	master.passwd = NULL;
	master.userid = NULL;
	master.no_dedupe = FALSE;
	master.chk_msgid = TRUE; /* do we check MsgID for trailing > */
	master.activefile = NULL;
	master.prebatch = FALSE; /* do we try to batch any left over articles b4 we start? */
	master.grpnr = -1;	/* what group number are we currently on */
	master.skip_on_restart = FALSE;
	master.kill_log_name = N_KILLLOG;
	master.use_gui = FALSE;
	master.do_xover = TRUE;
	master.conn_dedupe = FALSE;
	master.post_filter= NULL;
	master.conn_active = FALSE;
	master.history_file = HISTORY_FILE;
	master.header_only = FALSE;
	master.db = -1;
	master.active_lastread = ACTIVE_DEFAULT_LASTREAD;
	master.use_xover = FALSE;
	master.xoverview = NULL;
	master.resetcounter = FALSE;
	master.low_read = FALSE;
	master.show_group = FALSE;
	master.do_ssl = FALSE;
	master.ssl_struct = NULL;
	master.local_ssl = FALSE;
	master.local_ssl_struct = NULL;
	master.batch_post_nr = 0;
	master.passwd_env = FALSE;

	/* 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 */
		master.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(&master, fargc, fargs);
			}
		}
		else if(argv[1][0] == '-') {
			/* in case of suck -V */
			retval = scan_args(&master, 1, &(argv[1]));
		}
		else{
			master.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(&master, fargc, fargs);
				}
			}
		}
		/* this is here so anything at command line overrides file */
		if(argv[1][0] != '-' && argv[1][0] != FILE_CHAR) {
			master.host = 	argv[1];
			argc-= 2;
			args = &(argv[2]);
		}
		else {
			args = &(argv[1]);
			argc--;
		}
		retval = scan_args(&master, argc, args);
		break;
	}
	/* print out status stuff */
	if(master.debug == TRUE) {
		do_debug("Suck version %s\n",SUCK_VERSION);
		do_debug("master.MultiFile = %d\n", master.MultiFile);
		do_debug("master.status_file = %d\n",master.status_file);
		do_debug("master.status_file_name = %s\n", null_str(master.status_file_name));
		do_debug("master.do_killfile = %s\n", true_str(master.do_killfile));
		do_debug("master.do_chkhistory = %s\n", true_str(master.do_chkhistory));
		do_debug("master.do_modereader = %s\n", true_str(master.do_modereader));
		do_debug("master.always_batch = %s\n", true_str(master.always_batch));
		do_debug("master.rnews_size = %ld\n", master.rnews_size);
		do_debug("master.batch = %d\n", master.batch);
		do_debug("master.batchfile = %s\n", null_str(master.batchfile));
		do_debug("master.cleanup = %s\n", true_str(master.cleanup));
		do_debug("master.host = %s\n", null_str(master.host));
		do_debug("master.portnr = %u\n", master.portnr);
		do_debug("master.pause_time = %d\n", master.pause_time);
		do_debug("master.pause_nrmsgs = %d\n", master.pause_nrmsgs);
		do_debug("master.sig_pause_time = %d\n", master.sig_pause_time);
		do_debug("master.sig_pause_nrmsgs = %d\n", master.sig_pause_nrmsgs);
		do_debug("master.killfile_log = %d\n", master.killfile_log);
		do_debug("master.phrases = %s\n", null_str(master.phrases));
		do_debug("master.errlog = %s\n", null_str(master.errlog));
		do_debug("master.rescan = %s\n", true_str(master.rescan == TRUE));
		do_debug("master.quiet = %s\n", true_str(master.quiet));
		do_debug("master.kill_ignore_postfix = %s\n", true_str(master.kill_ignore_postfix));
		do_debug("master.reconnect_nr=%d\n", master.reconnect_nr);
		do_debug("master.do_active = %s\n", true_str(master.do_active));
		do_debug("master.localhost = %s\n", null_str(master.localhost));
		do_debug("master.nrmode = %s\n", true_str(master.nrmode));
		do_debug("master.auto_auth = %s\n", true_str(master.auto_auth));
		do_debug("master.no_dedupe = %s\n", true_str(master.no_dedupe));
		do_debug("master.chk_msgid = %s\n", true_str(master.chk_msgid));
		do_debug("master.activefile = %s\n", null_str(master.activefile));
		do_debug("master.prebatch = %s\n", true_str(master.prebatch));
		do_debug("master.skip_on_restart = %s\n", true_str(master.skip_on_restart));
		do_debug("master.kill_log_name = %s\n", null_str(master.kill_log_name));
		do_debug("master.use_gui = %s\n", true_str(master.use_gui));
		do_debug("master.do_xover = %s\n", true_str(master.do_xover));
		do_debug("master.conn_dedupe = %s\n", true_str(master.conn_dedupe));
		do_debug("master.post_filter = %s\n", null_str(master.post_filter));
		do_debug("master.conn_active = %s\n", true_str(master.conn_active));
		do_debug("master.history_file = %s\n", null_str(master.history_file));
		do_debug("master.header_only = %s\n", true_str(master.header_only));
		do_debug("master.active_lastread = %d\n", master.active_lastread);
		do_debug("master.use_xover = %s\n", true_str(master.use_xover));
		do_debug("master.do_ssl = %s\n", true_str(master.do_ssl));
		do_debug("master.local_ssl = %s\n", true_str(master.local_ssl));
		do_debug("master.batch_post_nr =  %d\n",master.batch_post_nr);
		do_debug("master.passwd_env = %s\n", true_str(master.passwd_env));
#ifdef TIMEOUT
		do_debug("TimeOut = %d\n", TimeOut);
#endif
		do_debug("master.debug = TRUE\n");
	}
        /* now do any final args checks needed */
	/* check to see if we have enough info to scan the localhost active file */
	if((master.do_active == TRUE || master.batch == BATCH_LIHAVE) && master.localhost == NULL) {
		retval = RETVAL_ERROR;
		error_log(ERRLOG_REPORT, suck_phrases[6], NULL);
	}
	else if(master.host == NULL) {
		retval = RETVAL_ERROR;
		error_log(ERRLOG_REPORT, suck_phrases[74], NULL);
	}


	/* okay now the main stuff */
	if(retval == RETVAL_OK) {
		if(master.status_file == FALSE) {
			/* if not in multifile mode, all status msgs MUST go to stderr to not mess up articles */
			/* this has to go before lockfile code, so lockfile prints msg to correct place. */
			master.msgs = ( master.MultiFile == FALSE) ? stderr : stdout ;
		}
#ifdef LOCKFILE
		/* this has to be here since we use full_path() to get path for lock file */
		/* and it isn't set up until we process the args above. */
		if(do_lockfile(&master) != RETVAL_OK) {
			exit(-1);
		}
#endif

#ifdef MYSIGNAL
		/* set up signal handlers */
#ifdef HAVE_SIGACTION
		sigemptyset(&(sigs.sa_mask));
		sigs.sa_handler = sighandler;
		sigs.sa_flags = 0;
		if(sigaction(MYSIGNAL, &sigs, NULL) == -1
			     || sigaction(MYSIGNAL2, &sigs, NULL) == -1
			     || sigaction(PAUSESIGNAL, &sigs, NULL) == -1) {
			MyPerror(suck_phrases[67]);
		}
		else {
			signal_block(MYSIGNAL_SETUP);	/* set up sgetline() to block signal */
			pause_signal(PAUSE_SETUP, &master);	/* set up routine for pause swap if signal */
		}
#else
		/* the old-fashioned way */
		signal(MYSIGNAL, sighandler);
		signal(MYSIGNAL2, sighandler);
		signal(PAUSESIGNAL, sighander);
		pause_signal(PAUSE_SETUP, &master);
#endif
#endif
		load_phrases(&master);	/* this has to be here so rest prints okay */

		/* set up status log, if none specified or unable to open status log */
		/* then  use stdout or stderr */

		if(master.status_file_name != NULL) {
			/* okay attempt to open it up */
			if((master.msgs = fopen(master.status_file_name, "a")) == NULL) {
				MyPerror(suck_phrases[0]);
				master.msgs = stdout;	/* reset to default */
			}
			else {
				master.status_file = TRUE;
			}
		}
#ifdef HAVE_SETVBUF
		setvbuf(master.msgs, NULL, _IOLBF, 0);	/* set to line buffering */
#endif
		/* do we batch up any lingering articles ? */
		if(master.prebatch == TRUE) {
			switch(master.batch) {
			case BATCH_FALSE:
				error_log(ERRLOG_REPORT, suck_phrases[58], NULL);
				break;
			case BATCH_INNXMIT:
				do_innbatch(&master);
				break;
			case BATCH_RNEWS:
				do_rnewsbatch(&master);
				break;
			case BATCH_LMOVE:
				do_lmovebatch(&master);
				break;
			case BATCH_LIHAVE:
				do_localpost(&master);
				break;
			}
		}

		/* now parse the killfile */
#ifdef KILLFILE
		if(master.do_killfile == TRUE) {
			master.killp = parse_killfile(KILL_KILLFILE, master.killfile_log, master.debug, master.kill_ignore_postfix);
		}
#endif
		/* now parse the xover killfile */
		if(master.do_xover == TRUE) {
			master.xoverp = parse_killfile(KILL_XOVER, master.killfile_log, master.debug, master.kill_ignore_postfix);
		}

		print_phrases(master.msgs, suck_phrases[1], master.host, NULL);
		if(do_connect(&master, CONNECT_FIRST) != RETVAL_OK) {
			retval = RETVAL_ERROR;
		}
		else {
			/* if we have XOVER killfiles, we need to get the format of em before any processing */
			if(master.xoverp != NULL || master.use_xover == TRUE) {
				get_xoverview(&master);
				killprg_sendoverview(&master);
			}

			/* first, check for restart articles, */
			/* then scan for any new articles */
			if((loop = restart_yn(&master)) == RESTART_ERROR) {
				retval = RETVAL_ERROR;
			}
			else if(loop == RESTART_NO || master.rescan == TRUE) {
				/* we don't do scan if we had restart and option = FALSE */
				/* do we scan the local active file? */
				if(master.do_active == TRUE || master.activefile != NULL) {
					if(get_message_index_active(&master) < RETVAL_OK) {
						retval = RETVAL_ERROR;
					}
				}
				else if(get_message_index(&master) < RETVAL_OK) {
					retval = RETVAL_ERROR;
				}
				if(retval == RETVAL_OK) {
					retval = do_supplemental(&master);
				}
				if(retval == RETVAL_OK) {
                                        retval = do_nodownload(&master);
                                }
				if(retval == RETVAL_OK && master.head != NULL && master.nritems > 0) {
					/* if we don't have any messages, we don't need to do this */
					if(master.no_dedupe == FALSE) {
						dedupe_list(&master);
					}
#ifdef CHECK_HISTORY
					if(master.do_chkhistory == TRUE) {
						chkhistory(&master);
					}
#endif
					print_phrases(master.msgs,suck_phrases[20], str_int(master.nritems), NULL);
				}
			}
			if(retval == RETVAL_OK) {
				if(master.nritems == 0) {
 					print_phrases(master.msgs,suck_phrases[3], NULL);
					retval = RETVAL_NOARTICLES;
				}
				else {
					/* write out restart db */
					retval = db_write(&master);
				/* reconnect after dedupe, in case of time out due to long dedupe time*/
					if(retval == RETVAL_OK && master.conn_dedupe == TRUE) {
						retval = do_connect(&master, CONNECT_AGAIN);
					}
					if(retval == RETVAL_OK) {
						retval = get_articles(&master);
					}
				}

			}
			if(retval == RETVAL_OK) { /* if we got disconnected above, don't do this */
				/* send quit, and get reply */
				sputline(master.sockfd,"quit\r\n", master.do_ssl, master.ssl_struct);
				if(master.debug == TRUE) {
					do_debug("Sending command: quit\n");
				}
				do {
					resp = sgetline(master.sockfd, &inbuf, master.do_ssl, master.ssl_struct);
					if(resp>0) {
						if(master.debug == TRUE) {
							do_debug("Quitting GOT: %s", inbuf);
						}
						number(inbuf, &nr);
					}

				}while(nr != 205 && resp > 0);
			}
		}
		if(master.sockfd >= 0) {
			disconnect_from_nntphost(master.sockfd, master.do_ssl, &master.ssl_struct);
			print_phrases(master.msgs,suck_phrases[4], master.host, NULL);
		}
		if(master.debug == TRUE) {
			do_debug("retval=%d (RETVAL_OK=%d), m.nrgot=%d, m.batch=%d\n", retval, RETVAL_OK, master.nrgot, master.batch);
		}
		if((retval == RETVAL_OK || master.always_batch == TRUE) && master.nrgot > 0 && master.header_only == FALSE) {
			switch(master.batch) {
			  case BATCH_INNXMIT:
				  do_post_filter(&master);
				  do_innbatch(&master);
				  break;
			  case BATCH_RNEWS:
				  do_post_filter(&master);
				  do_rnewsbatch(&master);
				  break;
			  case BATCH_LMOVE:
				  do_post_filter(&master);
				  do_lmovebatch(&master);
				  break;
			  case BATCH_LIHAVE:
				  do_post_filter(&master);
				  do_localpost(&master);
				  break;
			  default:
				  break;
			}
		}
		if((retval == RETVAL_NOARTICLES || retval == RETVAL_OK) && master.cleanup == TRUE) {
			print_phrases(master.msgs, suck_phrases[7], NULL);
			do_cleanup(&master);
		}

		/* close out status log */
		if(master.msgs != NULL && master.msgs != stdout && master.msgs != stderr) {
			fclose(master.msgs);
		}
		if(master.head != NULL) {
			/* clean up memory */
			master.curr = master.head;
			while(master.curr != NULL) {
				temp = (master.curr)->next;
				free_one_node(master.curr);
				master.curr = temp;
			}
		}
		/* free up group list */
		while (master.groups != NULL) {
			ptemp=(master.groups)->next;
			free(master.groups);
			master.groups = ptemp;
		}
		/* free up overview.fmt list */
		while (master.xoverview != NULL) {
			pov = (master.xoverview)->next;
			if((master.xoverview)->header != NULL) {
				free((master.xoverview)->header);
			}
			free(master.xoverview);
			master.xoverview = pov;
		}

#ifdef KILLFILE
		free_killfile(master.killp);
#endif
		free_killfile(master.xoverp);

		if(fargs != NULL) {
			free_args(fargc, fargs);
		}
#ifdef LOCKFILE
		lockfile = full_path(FP_GET, FP_TMPDIR, N_LOCKFILE);
		if(lockfile != NULL) {
			unlink(lockfile);
		}
#endif
	}
	free_phrases();	/* do this last so everything is displayed correctly */
	exit(retval);
}
/*------------------------------------------------------------*/
int do_connect(PMaster master, int which_time) {

	char *inbuf;
	int nr, resp, retval = RETVAL_OK;
	FILE *fp;


	if(which_time != CONNECT_FIRST) {
		/* close down previous connection */
		sputline(master->sockfd, "quit\r\n", master->do_ssl, master->ssl_struct);
		do {
			resp = sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct);
			if(resp>0) {
				if(master->debug == TRUE) {
					do_debug("Reconnect GOT: %s", inbuf);
				}
				number(inbuf, &nr);
			}

		}while(nr != 205 && resp > 0);
		disconnect_from_nntphost(master->sockfd, master->do_ssl, &master->ssl_struct);

		/* now reset everything */
		if(master->curr != NULL) {
			(master->curr)->sentcmd = FALSE;
		}
		master->grpnr = -1;
	}

	if(master->debug == TRUE) {
		do_debug("Connecting to %s on port %d\n", master->host, master->portnr);
	}
	fp = (which_time == CONNECT_FIRST) ? master->msgs : NULL;

	master->sockfd = connect_to_nntphost( master->host, NULL, 0, fp, master->portnr, master->do_ssl, &master->ssl_struct);

	if(master->sockfd < 0 ) {
		retval = RETVAL_ERROR;
	}
	else if(sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct) < 0) {
	 	/* Get the announcement line */
		retval = RETVAL_ERROR;
	}
	else {
		if(master->debug == TRUE) {
			do_debug("Got: %s", inbuf);
		}
		if(which_time == CONNECT_FIRST) {
			fprintf(master->msgs ,"%s", inbuf );
		}
		/* check to see if we have to do authorization now */
		number(inbuf, &resp);
		if(resp == 480 ) {
			retval = do_authenticate(master);
		}
		if(retval == RETVAL_OK && master->do_modereader == TRUE) {
			retval = send_command(master, "mode reader\r\n", &inbuf, 0);
			if(retval == RETVAL_OK) {
				/* Again the announcement */
				if(which_time == CONNECT_FIRST) {
					fprintf(master->msgs ,"%s",inbuf);
				}
			}
		}
		if(master->auto_auth == TRUE) {
			if(master->passwd == NULL || master->userid == NULL) {
				error_log(ERRLOG_REPORT, suck_phrases[55], NULL);
				retval = RETVAL_ERROR;
			}
			else {

				/* auto authorize */
				retval = do_authenticate(master);
			}
		}
	}
	return retval;
}

/*--------------------------------------------------------------------*/
int get_message_index(PMaster master) {

	long lastread;
	int nrread, retval, maxread;

	char buf[MAXLINLEN], group[512];
	FILE *ifp,*tmpfp;

	retval = RETVAL_OK;
	ifp = tmpfp = NULL;

	TimerFunc(TIMER_START, 0, NULL);

	if((ifp = fopen(full_path(FP_GET, FP_DATADIR, N_OLDRC), "r" )) == NULL) {
		MyPerror(full_path(FP_GET, FP_DATADIR, N_OLDRC));
		retval = RETVAL_ERROR;
	}
	else if((tmpfp = fopen(full_path(FP_GET, FP_TMPDIR, N_NEWRC), "w" )) == NULL) {
		MyPerror(full_path(FP_GET, FP_TMPDIR, N_NEWRC));
		retval = RETVAL_ERROR;
	}
	while(retval == RETVAL_OK && fgets(buf, MAXLINLEN-1, ifp) != NULL) {
		if(buf[0] == SUCKNEWSRC_COMMENT_CHAR) {
			/* skip this group */
			fputs(buf, tmpfp);
			print_phrases(master->msgs, suck_phrases[8], buf, NULL);
		}
		else {
			maxread = -1; /* just in case */
			nrread = sscanf(buf, "%s %ld %d\n", group, &lastread, &maxread);
			if ( nrread < 2 || nrread > 3) {
				error_log(ERRLOG_REPORT, suck_phrases[9], buf, NULL);
				fputs(buf, tmpfp);	/* rewrite the line */
			}
			else if(maxread == 0) {
				/* just rewrite the line */
				fputs(buf, tmpfp);
			}
			else {
				retval = do_one_group(master, buf, group, tmpfp, lastread, maxread);
			}
		}
	}

	TimerFunc(TIMER_TIMEONLY, 0,master->msgs);

	if(retval == RETVAL_OK) {
		print_phrases(master->msgs, suck_phrases[16], str_int(master->nritems), NULL);
	}
	else if(ifp != NULL) {
		/* this is in case we had to abort the above while loop (due to loss of pipe to server) and */
		/* we hadn't finished writing out the suck.newrc, this finishes it up. */
		do {
			fputs(buf, tmpfp);
		}
		while(fgets(buf, MAXLINLEN-1, ifp) != NULL);
	}
	if(tmpfp != NULL) {
		fclose(tmpfp);
	}
	if(ifp != NULL) {
		fclose(ifp);
	}
	return retval;
}
/*-----------------------------------------------------------------------------------------------------*/
int do_one_group(PMaster master, char *buf, char *group, FILE *newrc, long lastread, int maxread) {

	char *sp, *inbuf, cmd[MAXLINLEN];
	long count,low,high;
	int response,retval,i;

	retval = RETVAL_OK;


	sprintf(cmd,"group %s\r\n",group);
	if(send_command(master,cmd,&inbuf,0) != RETVAL_OK) {
		retval = RETVAL_ERROR;
	}
	else {
		sp = number(inbuf, &response);
		if(response != 211) {
			fputs(buf, newrc);	/* rewrite line AS IS in newrc */
				/* handle group not found */
			switch(response) {
			case 411:
				error_log(ERRLOG_REPORT, suck_phrases[11], group, NULL);
				break;
			case 500:
				error_log(ERRLOG_REPORT, suck_phrases[48], NULL);
				break;
			default:
				error_log(ERRLOG_REPORT, suck_phrases[12],group,str_int(response),NULL);
				retval = RETVAL_ERROR; /* bomb out on wacko errors */
				break;
			}
		}
		else {
			sp = get_long(sp, &count);
  			sp = get_long(sp, &low);
			sp = get_long(sp, &high);
  			fprintf(newrc, "%s %ld", group, high);

			if(maxread > 0) {
				fprintf(newrc, " %d", maxread);
			}
			fputs("\n", newrc);

  			/* add a sanity check in case remote host changes its numbering scheme */
			/* the > 0 is needed, since if a nnrp site has no article it will reset */
			/* the count to 0.  So not an error */
  			if(lastread > high && high > 0) {
				if(master->resetcounter == TRUE) {
					/* reset lastread to low, so we pick up all articles in the group */
					lastread = low;
					print_phrases(master->msgs,suck_phrases[71],group,str_int(high),str_int(low),NULL);
				}
				else {
					print_phrases(master->msgs,suck_phrases[13],group,str_int(high),NULL);
				}
  			}

			if(lastread < 0 ) {
				/* if a neg number, get the last X nr of messages, handy for starting */
				/* a new group off ,say -100 will get the last 100 messages */
				lastread += high;	/* this works since lastread is neg */
				if(lastread < 0) {
					lastread = 0;	/* just to be on the safeside */
				}
			}

			/* this has to be >= 0 since if there are no article on server high = 0 */
			/* so if we write out 0, we must be able to handle zero as valid lastread */
			/* the count > 0 so if no messages available we don't even try */
			/* or if low > high no messages either */
			if (low <= high && count > 0 && lastread < high && lastread >= 0) {
				/* add group name to list of groups */

   				if(lastread < low) {
					lastread = low - 1;
				}
				/* now set the max nr of messages to read */
				if(maxread > 0 && high-maxread > lastread) {
					if(master->low_read == TRUE) {
						/* instead of limiting from the high-water mark (the latest articles) */
						/* limit from the low-water mark (the oldest articles) */
						high = (lastread+1) + maxread;
					}
					else {
						lastread = high-maxread;
					}
					print_phrases(master->msgs, suck_phrases[14], group, str_int(maxread), NULL);
				}
				print_phrases(master->msgs, suck_phrases[15], group, str_long(high-lastread), str_long(lastread+1), str_long(high), NULL);
				if(master->xoverp != NULL) {
					/* do this via the xover killfile command */
					retval = do_group_xover(master, group, lastread+1, high);
				}
				/* do we use xover or xhdr to get our article list */
				if(master->use_xover == TRUE) {
					retval = get_xover(master, group, lastread+1, high);
				}
				else if((master->xoverp == NULL) || (retval == RETVAL_NOXOVER)) {
					/* do this the normal way */
					sprintf(cmd, "xhdr Message-ID %ld-%ld\r\n", lastread+1,high);
					i = send_command(master,cmd,&inbuf,221);
					if(i != RETVAL_OK) {
						retval = RETVAL_ERROR;
					}
					else {
						do {
							if(sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct) < 0) {
								retval = RETVAL_ERROR;
							}
							else if (*inbuf != '.' ) {
								retval = allocnode(master, inbuf, MANDATORY_OPTIONAL, group, 0L);
							}
						} while (retval == RETVAL_OK && *inbuf != '.' && *(inbuf+1) != '\n');
					} /* end if response */
				} /* end if xover */
  			} /* end if lastread */
		} /* end response */
	} /* end else */

	if(retval == RETVAL_ERROR) {
		error_log(ERRLOG_REPORT, suck_phrases[59], NULL);
	}
	return retval;
}
/*-----------------------------------------------------------*/
int get_articles(PMaster master) {

	int retval, logcount, i, grpnr, grplen;
	long loop, downloaded;
	PGroups grps;
	const char *grpname;
	const char *empty = "";

#ifdef HAVE_GETTIMEOFDAY
	double bps;
#endif

#ifdef KILLFILE
	int ( *get_message)(PMaster, int, long); /* which function will we use get_one_article or get_one_article_kill) */

	grpnr = -1;
	grps = NULL;
	grpname = empty;
	grplen = 0;
		/* do we use killfiles? */
	get_message = (master->killp == NULL) ? get_one_article : get_one_article_kill;
#endif
	retval = RETVAL_OK;
	downloaded = loop = 0; /* used to track how many downloaded, for reconnect option */

	/* figure out how many digits wide the articleCount is for display purposes */
	/* this used to be log10()+1, but that meant I needed the math library */
	for(logcount=1, i=master->nritems; i > 9 ; logcount++) {
		i /= 10;
	}

	if(master->MultiFile == TRUE && checkdir(full_path(FP_GET, FP_MSGDIR, NULL)) == FALSE) {
		retval = RETVAL_ERROR;
	}
	else {
		retval = db_open(master);
		if(retval == RETVAL_OK) {
			master->curr = master->head;

			if(master->batch == BATCH_INNFEED) {
				/* open up batch file for appending to */
				if((master->innfeed = fopen(master->batchfile, "a")) == NULL) {
					MyPerror(master->batchfile);
				}
			}
			else if(master->batch == BATCH_LIHAVE) {
				if((master->innfeed = fopen(full_path(FP_GET, FP_TMPDIR, master->batchfile), "a")) == NULL) {
					MyPerror(full_path(FP_GET, FP_TMPDIR, master->batchfile));
					retval = RETVAL_ERROR;
				}
			}

			TimerFunc(TIMER_START, 0, NULL);

#ifdef MYSIGNAL
			while(retval == RETVAL_OK && master->curr != NULL && GotSignal == FALSE) {
#else
			while(retval == RETVAL_OK && master->curr != NULL) {
#endif
				if(master->debug == TRUE) {
					do_debug("Article nr = %s mandatory = %c\n", (master->curr)->msgnr, (master->curr)->mandatory);
				}
				loop++;
				if((master->curr)->downloaded == FALSE) {
					/* we haven't yet downloaded this article */
					downloaded++;

				        /* to be polite to the server, lets allow for a pause every once in a while */
					if(master->pause_time > 0 && master->pause_nrmsgs > 0) {
						if((downloaded > 0) && (downloaded % master->pause_nrmsgs == 0)) {
							sleep(master->pause_time);
						}
					}
					if(master->status_file == FALSE && master->quiet == FALSE) {
						/* if we are going to a file, we don't want all of these articles printed */
						/* or if quiet flag is set */
						/* this stuff doesn't go thru print_phrases so I can keep spacing right */
						/* and I only print numbers and the BPS */
#ifdef HAVE_GETTIMEOFDAY
						bps = TimerFunc(TIMER_GET_BPS, 0, master->msgs);
#endif
						if(master->use_gui == TRUE) {
							/* this stuff is formatted by the GUI so we put it out in a format it likes */
#ifndef HAVE_GETTIMEOFDAY
							fprintf(master->msgs, "---%ld+++\n",master->nritems - loop);
#else
							fprintf(master->msgs, "---%ld+++%f\n", master->nritems - loop, bps);
#endif /* HAVE_GETTIMEOFDAY */
						}
						else {
							if(master->show_group == TRUE) {
							/* add the group name (if available) to the line printed */
								if((master->curr)->groupnr >= 0) {
									if((master->curr)->groupnr != grpnr) {
										grpname = empty;
										grpnr = (master->curr)->groupnr;
										/* new group name find it */
										grps = master->groups;
										while(grps != NULL && grps->nr != grpnr) {
											grps = grps->next;
										}
										if(grps != NULL) {
											grpname = grps->group;
											/* calculate max len for format of printf*/
											/* which erases the previous group name */
											if(strlen(grpname) > grplen) {
												grplen = strlen(grpname);
											}
										}
									}
								}
								else {
									grpnr = -1;
									grpname = empty;
								}
							}
#ifndef HAVE_GETTIMEOFDAY
							fprintf(master->msgs, "%5ld %-*s\r",master->nritems  - loop, grplen, grpname);
#else
							fprintf(master->msgs, "%5ld %9.1f %s %-*s\r",master->nritems-loop,bps,suck_phrases[5], grplen, grpname);
#endif /* HAVE_GETTIMEOFDAY */
						}
						fflush(master->msgs);	/* so message gets printed now */
					}
#ifdef KILLFILE
				        /* get one article */
					retval = (*get_message)(master, logcount, loop);
#else
					retval = get_one_article(master, logcount, loop);
#endif
					if(retval == RETVAL_OK ) {
						retval = db_mark_dled(master, master->curr);
					}
				}

				master->curr = (master->curr)->next; 	/* get next article */

				/* to be NOT polite to the server reconnect every X msgs to combat the INND */
				/* LIKE_PULLERS=DONT.  This is last in loop, so if problem can abort gracefully */
				if((master->reconnect_nr > 0) && (downloaded > 0) && (downloaded % master->reconnect_nr == 0)) {
					retval =  do_connect(master, CONNECT_AGAIN);
					if(master->curr != NULL) {
						(master->curr)->sentcmd = FALSE;	/* if we sent command force resend */
					}
				}
				// do a local post every x number of articles
				if(master->batch == BATCH_LIHAVE && master->batch_post_nr > 0 && master->nrgot >=  master->batch_post_nr) {
					fclose(master->innfeed);
					master->innfeed = NULL;
					do_post_filter(master);
					retval = do_localpost(master);
					if(retval == RETVAL_OK) {
						master->nrgot = 0; // so if no more articles left we don't try to post in main routine
						if((master->innfeed = fopen(full_path(FP_GET, FP_TMPDIR, master->batchfile), "a")) == NULL) {
							MyPerror(full_path(FP_GET, FP_TMPDIR, master->batchfile));
							retval = RETVAL_ERROR;
						}
					}
				}

		 	} /* end while */
			db_close(master);
			if(retval == RETVAL_OK && master->nritems == loop) {
				db_delete(master);
			}

			if(retval == RETVAL_OK) {
				TimerFunc(TIMER_TOTALS, 0, master->msgs);
			}
			if(master->innfeed != NULL) {
				fclose(master->innfeed);
			}
		}
	}
	return retval;
}
/*-----------------------------------------------*/
/* add items from supplemental list to link list */
/* ----------------------------------------------*/
int do_supplemental(PMaster master) {

	int retval, oldkept;
	FILE *fp;
	char linein[MAXLINLEN+1];

	retval = RETVAL_OK;
	oldkept = master->nritems;

	if((fp = fopen(full_path(FP_GET, FP_DATADIR, N_SUPPLEMENTAL), "r")) != NULL) {
		print_phrases(master->msgs, suck_phrases[17], NULL);
		while(retval == RETVAL_OK && fgets(linein, MAXLINLEN, fp) != NULL) {
			if(linein[0] == '!') {
				retval = do_sup_bynr(master, linein);
			}
			else if(linein[0] == '<') {
				retval = allocnode(master, linein, MANDATORY_YES, NULL, 0L);
			}
			else {
				error_log(ERRLOG_REPORT, suck_phrases[18], linein, NULL);
			}
		}
		print_phrases(master->msgs, suck_phrases[19], str_int(master->nritems-oldkept), \
			str_int(master->nritems), NULL);
		fclose(fp);
	}

	return retval;
}
/*------------------------------------------------------------------------------------------*/
 int do_sup_bynr(PMaster master, char *linein) {
	 int retval = RETVAL_OK;
	 /* this routine takes in a line formated !group_name article_nr */
	 /* gets it's Msg-ID (via XHDR), then adds it to our list of articles */
	 /* as a mandatory article to download */

	 char grpname[MAX_GRP_LEN], cmd[MAXLINLEN], *resp;
	 int i, done;
	 long nrlow, nrhigh;

	 if(master->debug == TRUE) {
		 do_debug("supplemental adding %s", linein);
	 }
	 i = sscanf(linein, "!%s %ld-%ld", grpname, &nrlow, &nrhigh);
	 if(i < 2) {
		 error_log(ERRLOG_REPORT, suck_phrases[18], linein, NULL);
	 }
	 else {
		 sprintf(cmd,"group %s\r\n",grpname);
		 if(send_command(master,cmd,NULL,211) == RETVAL_OK) {
			 if(i == 2) {
				 sprintf(cmd,"xhdr Message-ID %ld\r\n",nrlow);
			 }
			 else {
				 sprintf(cmd,"xhdr Message-ID %ld-%ld\r\n",nrlow,nrhigh);
			 }
			 if(send_command(master,cmd,NULL,221) == RETVAL_OK) {
				 /* now have to get message-id and the . */
				 done = FALSE;
				 while( done == FALSE) {
					 if(sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct) < 0) {
						 retval = RETVAL_ERROR;
						 done = TRUE;
					 }
					 else {
						 if(master->debug == TRUE) {
							 do_debug("Got %s", resp);
						 }
						 if(*resp == '.') {
							 done = TRUE;
						 }
						 else {
							 retval = allocnode(master, resp, MANDATORY_YES, grpname, 0L);
						 }
					 }
				 }
			 }
		 }
	 }

	 return retval;

 }
 /*---------------------------------------------------------------------*/
 int do_nodownload(PMaster master) {
	 int i, retval = RETVAL_OK;
	 FILE *fp;
	 PList item, prev;
	 char linein[MAXLINLEN+1];
	 long nrlines = 0, nrnuked = 0;

	 if((fp = fopen(full_path(FP_GET, FP_DATADIR, N_NODOWNLOAD), "r")) != NULL) {
		print_phrases(master->msgs, suck_phrases[68], NULL);
		while(retval == RETVAL_OK && fgets(linein, MAXLINLEN, fp) != NULL) {
			nrlines++;
			i = strlen(linein) - 1; /* so it points at last char */
			/* strip off nl */
			if(linein[i] == '\n') {
				linein[i] = '\0';
				i--;
			}
			/* strip off extra spaces on the end */
			while(isspace(linein[i])){
				linein[i] = '\0';
				i--;
			}
			item = master->head;
			prev = NULL;
			/* find start of msgid */
			if(linein[0] != '<') {
				error_log(ERRLOG_REPORT, suck_phrases[69], linein, NULL);
			}
			else {
				if(master->debug == TRUE) {
					do_debug("Checking Nodownload - %s\n", linein);
				}

				while((item != NULL) && (cmp_msgid(linein, item->msgnr) == FALSE)) {
					prev = item;
					item = item->next;
				}
				if(item != NULL) {
					/* found a match, remove from the list */
					if(master->debug == TRUE) {
						do_debug("Matched, nuking from list\n");
					}
					nrnuked++;
					master->nritems--;
					if(item == master->head) {
						/* change top of list */
						master->head = (master->head)->next;
					}
					else {
                                                 /* its not the end of the list */
						prev->next = item->next;
						free_one_node(item);
					}
				}
			}
		}
		fclose(fp);
		print_phrases(master->msgs, suck_phrases[70], str_long(nrlines), str_long(nrnuked), str_long(master->nritems), NULL);
	}
	return retval;
 }
/*----------------------------------------------------------------------*/
 int get_group_number(PMaster master, char *grpname) {

	 PGroups grps, gptr;
	 int groupnr = 0;

         /* first, find out if it doesn't exist already */
	 gptr = master->groups;
	 while(gptr != NULL && groupnr == 0) {
		 if(strcmp(gptr->group, grpname) == 0) {
				/* bingo */
			 groupnr = gptr->nr;
		 }
		 else {
			 gptr = gptr->next;
		 }
	 }

	 if(groupnr == 0) {
		 /* add group to group list and get group nr */
		 if((grps = malloc(sizeof(Groups))) == NULL) {
				/* out of memory */
			 error_log(ERRLOG_REPORT, suck_phrases[22], NULL);
		 }
		 else {
			 grps->next = NULL;
			 strcpy(grps->group, grpname);
				/* now add to list and count groupnr */
			 if(master->groups == NULL) {
				 grps->nr = 1;
				 master->groups = grps;
			 }
			 else {
				 gptr = master->groups;
				 while(gptr->next != NULL) {
					 gptr = gptr->next;
				 }
				 gptr->next = grps;
				 grps->nr = gptr->nr + 1;
			 }
			 groupnr = grps->nr;

			 if(master->debug == TRUE) {
				 do_debug("Adding to group list: %d %s\n", grps->nr, grps->group);
			 }
		 }
	 }
	 return groupnr;

 }
/*-----------------------------------------------------------------------*/
int allocnode(PMaster master, char *linein, int mandatory, char *group, long msgnr_in) {
	/* if msgnr_in is not filled in (0), then parse the msgnr off the linein */

	/* if allocate memory here, must free in free_one_node */

	PList ptr = NULL;
	char *end_ptr, *st_ptr;
	static PList curr = NULL;	/* keep track of current end of list */
	int groupnr = 0, retval = RETVAL_OK;
	long msgnr = 0;

	static int warned = FALSE;

	/* get the article nr */
	if(group == NULL) {
		/* we're called by do_supplemental */
		st_ptr = linein;
	}
	else {
		if (msgnr_in > 0) {
			/* we're prob called by xover code */
			st_ptr = linein;
			msgnr = msgnr_in;
		}
		else {
			st_ptr = get_long(linein,&msgnr);
			if(msgnr == 0 && warned == FALSE) {
				warned = TRUE;
				error_log(ERRLOG_REPORT, suck_phrases[53], NULL);
			}
		}

		if(msgnr > 0 && group != NULL) {
			groupnr = get_group_number(master, group);
		}
	}

	if(retval == RETVAL_OK) {
		/* find the message id */
		while(*st_ptr != '<' && *st_ptr != '\0') {
			st_ptr++;
		}
		end_ptr = st_ptr;
		while(*end_ptr != '>' && *end_ptr != '\0') {
			end_ptr++;
		}
		if((*st_ptr != '<') || ( master->chk_msgid == TRUE && *end_ptr != '>')) {
			error_log(ERRLOG_REPORT, suck_phrases[21], linein, NULL);
		}
		else {
			*(end_ptr+1) = '\0';	/* ensure null termination */

			if((ptr = malloc(sizeof(List))) == NULL) {
				error_log(ERRLOG_REPORT, suck_phrases[22], NULL);
				retval = RETVAL_ERROR;
			}
			else if(strlen(st_ptr) >= MAX_MSGID_LEN) {
				error_log(ERRLOG_REPORT, suck_phrases[63], group, msgnr, NULL);
				free(ptr);
			}
			else {
				strcpy(ptr->msgnr, st_ptr);
				ptr->next = NULL;
				ptr->mandatory = (char) mandatory;
				ptr->sentcmd = FALSE;		/* have we sent command to remote */
				ptr->nr = msgnr;
				ptr->groupnr = groupnr;
				ptr->downloaded = FALSE;
				ptr->dbnr = 0L;
				ptr->delete = FALSE;

				if(master->debug == TRUE) {
					do_debug("MSGID %s NR %d GRP %d MANDATORY %c added\n",ptr->msgnr, ptr->nr, ptr->groupnr, ptr->mandatory);
				}

				/* now put on list */
				if( curr == NULL) {
					if(master->head == NULL) {
				                /* first node */
						master->head = curr = ptr;
					}
					else {
						/* came in with a restart, find end of list */
						curr = master->head;
						while(curr->next != NULL) {
							curr = curr->next;
						}

						/* now add node on */
						curr->next = ptr;
						curr = ptr;
					}
				}
				else {
					curr->next = ptr;
					curr = ptr;
				}
				master->nritems++;
			}
		}
	}

	return retval;
}
/*------------------------------------------------------------------------*/
 void free_one_node(PList node) {

	free(node);
}
/*----------------------------------------------------------------------------------*/
 const char *build_command(PMaster master, const char *cmdstart, PList article) {
	 static char cmd[MAXLINLEN+1];
	 static int warned = FALSE;
	 char *resp;
	 char grpcmd[MAXLINLEN+1];

	 PGroups grps;

	 /* build command to get article/head/body */
	 /* if nrmode is on send group cmd if needed */
	 if(master->nrmode == TRUE) {
		 if(article->nr == 0) {
			 master->nrmode = FALSE;
		 }
		 else if(master->grpnr != article->groupnr) {
			 grps = master->groups;
			 while(grps != NULL && grps->nr != article->groupnr) {
				 grps = grps->next;
			 }
			 if(grps != NULL) {
				 /* okay send group command */
				 snprintf(grpcmd, MAXLINLEN, "GROUP %s\r\n", grps->group);
				 if(send_command(master, grpcmd, &resp, 211) != RETVAL_OK) {
					 master->nrmode = FALSE; /* can't chg groups turn it off */
				 }
				 else {
					 /* so don't redo group command on next one */
					 master->grpnr = grps->nr;
				 }
			 }
		 }
		 if(master->nrmode == TRUE) {
			 /* everything hunky dory, we've switched groups, and got a good nr */
			 snprintf(cmd, MAXLINLEN, "%s %ld\r\n", cmdstart, article->nr);
		 }
		 else if(warned == FALSE) {
			 /* tell of the switch to nonnr mode */
			 warned = TRUE;
			 error_log(ERRLOG_REPORT, suck_phrases[54], NULL);
		 }
	 }
	 /* the if is in case we changed it above */
	 if(master->nrmode == FALSE) {
		 snprintf(cmd, MAXLINLEN, "%s %s\r\n", cmdstart, article->msgnr);
	 }
	 return cmd;
 }
/*----------------------------------------------------------------------------------*/
int get_one_article(PMaster master, int logcount, long itemon) {

	int nr, len, retval = RETVAL_OK;
	char buf[MAXLINLEN+1];
	const char *cmd = "", *tname = NULL;
	char fname[PATH_MAX+1];

	char *resp;
	PList plist;

	FILE *fptr = stdout;	/* the default */

	fname[0] = '\0';	/* just in case */

	/* first send command to get article if not already sent */
	if((master->curr)->sentcmd == FALSE) {
		cmd = (master->header_only == FALSE)
			? build_command(master, "article", master->curr)
			: build_command(master, "head", master->curr);
		if(master->debug == TRUE) {
			do_debug("Sending command: \"%s\"",cmd);
		}
		sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
		(master->curr)->sentcmd = TRUE;
	}
	plist = (master->curr)->next;
	if(plist != NULL && master->batch_post_nr == 0) {
		/* if batch_post_nr > 0 we're doing periodic batch posts */
		/* which confuses the remote server so we can't use pipelining */
		if(master->nrmode == FALSE || plist->groupnr == (master->curr)->groupnr) {
		/* can't use pipelining if nrmode on, cause of GROUP command I'll have to send*/
			if(plist->sentcmd == FALSE) {
				/* send next command */
				cmd = (master->header_only == FALSE)
					? build_command(master, "article", plist)
					: build_command(master, "head", plist);
				if(master->debug == TRUE) {
					do_debug("Sending command: \"%s\"",cmd);
				}
				sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
				plist->sentcmd = TRUE;
			}
		}
	}

	/* now while the remote is finding the article lets set up */

	if(master->MultiFile == TRUE) {
		/* open file */
		/* file name will be ####-#### ex 001-166 (nron,total) */
		sprintf(buf,"%0*ld-%d", logcount, itemon ,master->nritems);
		/* the strcpy to avoid wiping out fname in second call to full_path */
		strcpy(fname, full_path(FP_GET, FP_MSGDIR, buf));
		strcat(buf, N_TMP_EXTENSION);		/* add tmp extension */
		tname = full_path(FP_GET, FP_TMPDIR, buf);	/* temp file name */

		if(master->debug == TRUE) {
			do_debug("File name = \"%s\" temp = \"%s\"", fname, tname);
		}
		if((fptr = fopen(tname, "w")) == NULL) {
			MyPerror(tname);
			retval = RETVAL_ERROR;
		}
	}

	/* okay hopefully by this time the remote end is ready */
	if((len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct)) < 0) {
		retval = RETVAL_ERROR;
	}
	else {
		if(master->debug == TRUE) {
			do_debug("got answer: %s", resp);
		}

		TimerFunc(TIMER_ADDBYTES, len, NULL);

		number(resp, &nr);
		if(nr == 480) {
			/* got to authorize, negate send-ahead for next msg */
			if(plist != NULL) {
				plist->sentcmd = FALSE;	/* cause its gonna error out in do_auth() */
			}

			if(do_authenticate(master) == RETVAL_OK) {
				/* resend command for current article */
				cmd = build_command(master, "article", master->curr);
				if(master->debug == TRUE) {
					do_debug("Sending command: \"%s\"",cmd);
				}
				sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
				len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
				if(master->debug == TRUE) {
					do_debug("got answer: %s", resp);
				}
				TimerFunc(TIMER_ADDBYTES, len, NULL);
				number(resp, &nr);
			}
			else {
				retval = RETVAL_ERROR;
			}
		}
		/* 221 = header 220 = article */
		if(nr == 220 || nr == 221 ) {
			/* get the article */
			if((retval = get_a_chunk(master, fptr)) == RETVAL_OK) {
				master->nrgot++;
				if (fptr == stdout) {
					/* gracefully end the message in stdout version */
					fputs(".\n", stdout);
				}
			}
		}
		else
			{
			/* don't return RETVAL_ERROR so we can get next article */
			error_log(ERRLOG_REPORT, suck_phrases[29], cmd ,resp, NULL);
		}
	}
	if(fptr != NULL && fptr != stdout) {
		fclose(fptr);
	}

	if(master->MultiFile == TRUE) {
		if(retval == RETVAL_ERROR || (nr != 221 && nr != 220)) {
			unlink(tname);
		}
		/* now rename it to the permanent file name */
		else {
			move_file(tname, fname);
			if((master->batch == BATCH_INNFEED || master->batch == BATCH_LIHAVE) && master->innfeed != NULL) {
				/* write path name and msgid to file */
				fprintf(master->innfeed, "%s %s\n", fname, (master->curr)->msgnr);
				fflush(master->innfeed); /* so it gets written sooner */
			}
		}
	}
	return retval;
}
/*---------------------------------------------------------------------------*/
int get_a_chunk(PMaster master, FILE *fptr) {

	int done, partial, len, retval;
	char *inbuf;
	size_t nr;

	retval = RETVAL_OK;
	done = FALSE;
	partial = FALSE;
	len = 0; /* just to get rid of a compiler warning */
	/* partial = was the previous line a complete line or not */
	/* this is needed to avoid a scenario where the line is MAXLINLEN+1 */
	/* long and the last character is a ., which would make us think */
	/* that we are at the end of the article when we actually aren't */

	while(done == FALSE && (len = sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct)) >= 0) {

		TimerFunc(TIMER_ADDBYTES, len, NULL);

		if(inbuf[0] == '.' && partial == FALSE) {
			if(len == 2 && inbuf[1] == '\n') {
				done = TRUE;
			}
			else {
				/* handle double dots IAW RFC977 2.4.1*/
				inbuf++;	/* move past first dot */
				len--;
			}
		}
		if(done == FALSE) {
			if(retval == RETVAL_OK) {
				nr = fwrite(inbuf, sizeof(inbuf[0]), len, fptr);
				fflush(fptr);
				if(nr != len) {
					retval = RETVAL_ERROR;
					error_log(ERRLOG_REPORT, suck_phrases[57], NULL);
				}
			}
			partial= (len==MAXLINLEN&&inbuf[len-1]!='\n') ? TRUE : FALSE;
 		}
	}
	if(len < 0) {
		retval = RETVAL_ERROR;
	}
	return retval;
}
/*-----------------------------------------------------------------------------*/
int restart_yn(PMaster master) {
	int done, retval = RESTART_NO;
	PList itemon;
	long itemnr;
	const char *fname;
	struct stat buf;

	fname = full_path(FP_GET, FP_TMPDIR, N_DBFILE);
	if(stat(fname, &buf) == 0) {
		/* restart file exists */
		retval = db_read(master);
		if(retval == RETVAL_OK) {
			/* find out where we stopped */
			itemon = master->head;
			itemnr = 0L;
			done = FALSE;
			while(itemon != NULL && done == FALSE) {
				itemnr++;
				if(itemon->downloaded == FALSE) {
					/* so that we don't think we already sent command */
					itemon->sentcmd = FALSE;
					if(master->skip_on_restart == TRUE) {
						/* mark one more */
						print_phrases(master->msgs, suck_phrases[60], NULL);
						itemon->downloaded = TRUE;
					}
					else {
						itemnr--; /* don't count this one */
					}
					done = TRUE;
				}
				itemon = itemon->next;
			}
		}
	}
	return retval;
}
/*-----------------------------------------------------------------*/
#ifdef MYSIGNAL
RETSIGTYPE sighandler(int what) {

	switch(what) {
	  case PAUSESIGNAL:
		pause_signal(PAUSE_DO, NULL);
		/* if we don't do this, the next time called, we'll abort */
/*		signal(PAUSESIGNAL, sighandler); */
/*		signal_block(MYSIGNAL_SETUP); */	/* just to be on the safe side */
		break;
	  case MYSIGNAL:
	  case MYSIGNAL2:
	  default:
		error_log(ERRLOG_REPORT, suck_phrases[24], NULL);
		GotSignal = TRUE;
	}
}
/*----------------------------------------------------------------*/
void pause_signal(int action, PMaster master) {

	int x, y;
	static PMaster psave = NULL;

	switch(action) {
	  case PAUSE_SETUP:
		psave = master;
		break;
	  case PAUSE_DO:
		if(psave == NULL) {
			error_log(ERRLOG_REPORT, suck_phrases[25], NULL);
		}
		else {
			/* swap pause_time and pause_nrmsgs with the sig versions */
			x = psave->pause_time;
			y = psave->pause_nrmsgs;
			psave->pause_time = psave->sig_pause_time;
			psave->pause_nrmsgs = psave->sig_pause_nrmsgs;
			psave->sig_pause_time = x;
			psave->sig_pause_nrmsgs = y;
			print_phrases(psave->msgs, suck_phrases[26], NULL);
		}
	}
}
#endif
/*----------------------------------------------------------------*/
 int send_command(PMaster master, 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(master->debug == TRUE) {
		 do_debug("sending command: %s", cmd);
	 }
	 sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
	 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
	 if( len < 0) {
		 retval = RETVAL_ERROR;
	 }
	 else {
		 if(master->debug == TRUE) {
			 do_debug("got answer: %s", resp);
		 }

		 TimerFunc(TIMER_ADDBYTES, len, NULL);

		 number(resp, &nr);
		 if(nr == 480 ) {
			 /* we must do authorization */
			 retval = do_authenticate(master);
			 if(retval == RETVAL_OK) {
				 /* resend command */
				 sputline(master->sockfd, cmd, master->do_ssl, master->ssl_struct);
				 if(master->debug == TRUE) {
					 do_debug("sending command: %s", cmd);
				 }
				 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
				 if( len < 0) {
					 retval = RETVAL_ERROR;
				 }
				 else {
					 number(resp,&nr);
					 if(master->debug == TRUE) {
						 do_debug("got answer: %s", resp);
					 }

					 TimerFunc(TIMER_ADDBYTES, len, NULL);

				 }
			 }
		 }
		 if (good_response != 0 && nr != good_response) {
			 error_log(ERRLOG_REPORT, suck_phrases[29],cmd,resp,NULL);
			 retval = RETVAL_UNEXPECTEDANS;
		 }
	 }
	 if(ret_response != NULL) {
		 *ret_response = resp;
	 }
	 return retval;
 }
/*------------------------------------------------------------------------------*/
/* 1. move N_OLDRC to N_OLD_OLDRC	N_OLDRC might not exist	       	        */
/* 2. move N_NEWRC to N_OLDRC							*/
/* 3. rm N_SUPPLEMENTAL								*/
/*------------------------------------------------------------------------------*/
void do_cleanup(PMaster master) {
	const char *oldptr;
	char ptr[PATH_MAX+1];
	struct stat buf;
	int exist;
	int okay = TRUE;

	if(master->debug == TRUE) {
		do_debug("checking for existance of suck.newrc\n");
	}

	if(stat(full_path(FP_GET, FP_TMPDIR, N_NEWRC), &buf) == 0) {
		if(master->debug == TRUE) {
			do_debug("found suck.newrc\n");
		}

		/* we do the above test, in case we were in a restart */
		/* with -R which would cause no suck.newrc to be created */
		/* since message_index() would be skipped */
		/* since no suck.newrc, don't move any of the sucknewrcs around */
		strcpy(ptr,full_path(FP_GET, FP_DATADIR, N_OLDRC));
		/* must strcpy since full path overwrites itself everytime */
		oldptr = full_path(FP_GET, FP_DATADIR, N_OLD_OLDRC);

		/* does the sucknewsrc file exist ? */
		exist = TRUE;
		if(stat(ptr, &buf) != 0 && errno == ENOENT) {
			exist = FALSE;
		}
		if(master->debug == TRUE) {
			do_debug("sucknewsrc.old = %s sucknewsrc = %s exist = %s\n", oldptr, ptr, true_str(exist));
		}
		if(exist == TRUE && move_file(ptr, oldptr) != 0) {
			MyPerror(suck_phrases[30]);
			okay = FALSE;
		}
		else if(move_file(full_path(FP_GET, FP_TMPDIR, N_NEWRC), ptr) != 0) {
			MyPerror(suck_phrases[31]);
			okay = FALSE;
		}
	}
	else if( errno != ENOENT) {
		MyPerror(full_path(FP_GET, FP_DATADIR, N_NEWRC));
		okay = FALSE;
	}
	if(okay == TRUE && unlink(full_path(FP_GET, FP_DATADIR, N_SUPPLEMENTAL)) != 0 && errno != ENOENT) {
		/* ENOENT is not an error since this file may not always exist */
		MyPerror(suck_phrases[33]);
	}
}
/*--------------------------------------------------------------------------------------*/
int scan_args(PMaster master, int argc, char *argv[]) {

	int loop, i, whicharg, arg, retval = RETVAL_OK;

	for(loop = 0 ; loop < argc && retval == RETVAL_OK ; loop++) {
		arg = ARG_NO_MATCH;
		whicharg = -1;

		if(master->debug == TRUE) {
			do_debug("Checking arg #%d-%d: '%s'\n", loop, argc, argv[loop]);
		}

		if(argv[loop][0] == FILE_CHAR) {
			/* totally skip these they are processed elsewhere */
			continue;
		}

		if(argv[loop][0] == '-' && argv[loop][1] == '-') {
			/* check long args */
			for(i = 0 ; i < NR_ARGS && arg == ARG_NO_MATCH ; i++) {
				if(arglist[i].larg != NULL) {
					if(strcmp(&argv[loop][2], arglist[i].larg) == 0) {
						arg = arglist[i].flag;
						whicharg = i;
					}
				}
			}
		}
		else if (argv[loop][0] == '-') {
			/* check short args */
			for(i = 0 ; i < NR_ARGS ; i++) {
				if(arglist[i].sarg != NULL) {
					if(strcmp(&argv[loop][1], arglist[i].sarg) == 0) {
						arg = arglist[i].flag;
						whicharg = i;
					}
				}
			}
		}
		if(arg == ARG_NO_MATCH) {
			/* we ignore the file_char arg, it is processed elsewhere */
			retval = RETVAL_ERROR;
			error_log(ERRLOG_REPORT, suck_phrases[34], argv[loop], NULL);
		}
		else {
			/* need to check for valid nr of args and feed em down */
			if(( loop + arglist[whicharg].nr_params) < argc) {
				retval = parse_args(master, arg, &(argv[loop+1]));
				/* skip em so they don't get processed as args */
				loop += arglist[whicharg].nr_params;
			}
			else {
				i = (arglist[whicharg].errmsg < 0) ? 34 : arglist[whicharg].errmsg;
				error_log(ERRLOG_REPORT, suck_phrases[i], NULL);
				retval = RETVAL_ERROR;
			}
		}
	}

	return retval;
}
/*---------------------------------------------------------------------------------------*/
int parse_args(PMaster master, int arg, char *argv[]) {

	int x, retval = RETVAL_OK;

	switch(arg) {
	case ARG_ALWAYS_BATCH:
		/* if we have downloaded at least one article, then batch up */
		/* even on errors */
		master->always_batch = TRUE;
		break;
/* batch file implies MultiFile mode */
	case  ARG_BATCH_INN:
		master->batch = BATCH_INNXMIT;
		master->MultiFile = TRUE;
		master->batchfile = argv[0];
		break;
	case ARG_BATCH_RNEWS:
		master->batch = BATCH_RNEWS;
		master->MultiFile = TRUE;
		master->batchfile = argv[0];
		break;
	case ARG_BATCH_LMOVE:
		master->batch = BATCH_LMOVE;
		master->MultiFile = TRUE;
		master->batchfile = argv[0];
		break;
	case ARG_BATCH_INNFEED:
		master->batch = BATCH_INNFEED;
		master->MultiFile = TRUE;
		master->batchfile = argv[0];
		break;
	case ARG_BATCH_POST_NR:
		master->batch_post_nr = atoi(argv[0]);
		/* no break fall thru to the rest of batch_post*/
	case ARG_BATCH_POST:
		master->batch = BATCH_LIHAVE;
		master->MultiFile = TRUE;
		master->batchfile = N_POSTFILE;
		break;
	case ARG_CLEANUP: 	/* cleanup option */
		master->cleanup = TRUE;
		break;
	case ARG_DIR_TEMP:
		full_path(FP_SET, FP_TMPDIR, argv[0]);
		break;
	case ARG_DIR_DATA:
		full_path(FP_SET, FP_DATADIR, argv[0]);
		break;
	case ARG_DIR_MSGS:
		full_path(FP_SET, FP_MSGDIR, argv[0]);
		break;
	case ARG_DEF_ERRLOG:	/* use default error log path */
		error_log(ERRLOG_SET_FILE, ERROR_LOG, NULL);
		master->errlog = ERROR_LOG;
		break;
	case ARG_HOST:	/* host name */
		master->host = argv[0];
		break;
	case ARG_NO_POSTFIX:	/* kill files don't use the postfix */
		master->kill_ignore_postfix = TRUE;
		break;
	case ARG_LANGUAGE: 	/* load language support */
		master->phrases = argv[0];
		break;
	case ARG_MULTIFILE:
		master->MultiFile = TRUE;
		break;
	case ARG_POSTFIX:
		full_path(FP_SET_POSTFIX, FP_NONE, argv[0]);
		break;
	case ARG_QUIET: 	/* don't display BPS and message count */
		master->quiet = TRUE;
		break;
	case ARG_RNEWSSIZE:
		master->rnews_size = atol(argv[0]);
		break;
	case ARG_DEF_STATLOG:	/* use default status log name */
		master->status_file_name = STATUS_LOG;
		break;
	case ARG_WAIT_SIG:	/* wait (sleep) x seconds between every y articles IF we get a signal */
		master->sig_pause_time = atoi(argv[0]);
		master->sig_pause_nrmsgs = atoi(argv[1]);
		break;
	case ARG_ACTIVE:
		master->do_active = TRUE;
		break;
	case ARG_RECONNECT:	/* how often do we drop and reconnect to battle INNS's LIKE_PULLER=DONT */
		master->reconnect_nr = atoi(argv[0]);
		break;
	case ARG_DEBUG:	/* debug */
		master->debug = TRUE;
		/* now set up our error log and MyPerror reporting so it goes to debug.suck */
		error_log(ERRLOG_SET_DEBUG, NULL, NULL);
		break;
	case ARG_ERRLOG:	/* error log path */
		error_log(ERRLOG_SET_FILE, argv[0], NULL);
		master->errlog = argv[0];
		break;
	case ARG_HISTORY:
		master->do_chkhistory = FALSE;
		break;
	case ARG_KILLFILE:
		master->do_killfile = FALSE;
		break;
	case ARG_KLOG_NONE:
		master->killfile_log = KILL_LOG_NONE;
		break;
	case ARG_KLOG_SHORT:
		master->killfile_log = KILL_LOG_SHORT;
		break;
	case ARG_KLOG_LONG:
		master->killfile_log = KILL_LOG_LONG;
		break;
	case ARG_MODEREADER: /* mode reader */
		master->do_modereader = TRUE;
		break;
	case ARG_PORTNR:	/* override default portnr */
		master->portnr = atoi(argv[0]);
		break;
	case ARG_PASSWD:	/* passwd */
		master->passwd = argv[0];
		break;
	case ARG_RESCAN:	/* don't rescan on restart */
		master->rescan = FALSE;
		break;
	case ARG_STATLOG:	/* status log path */
		master->status_file_name = argv[0];
		break;
	case ARG_USERID:	/* userid */
		master->userid = argv[0];
		break;
	case ARG_PASSWD_ENV:	/* get userid/password from ENV */
		master->userid = getenv("NNTP_USER");
		master->passwd = getenv("NNTP_PASS");
		master->passwd_env = TRUE;
		break;
	case ARG_VERSION: 	/* show version number */
		error_log(ERRLOG_REPORT,"Suck version %v1%\n",SUCK_VERSION, NULL);
		retval = RETVAL_VERNR;	/* so we don't do anything else */
		break;
	case ARG_WAIT:	/* wait (sleep) x seconds between every y articles */
		master->pause_time = atoi(argv[0]);
		master->pause_nrmsgs = atoi(argv[1]);
		break;
	case ARG_LOCALHOST:
		master->localhost = argv[0];
		break;
	case ARG_NRMODE:
		master->nrmode = TRUE;
		break;
	case ARG_AUTOAUTH:
		master->auto_auth = TRUE;
		break;
	case ARG_NODEDUPE:
		master->no_dedupe = TRUE;
		break;
	case ARG_READACTIVE:
		master->activefile = argv[0];
		break;
	case ARG_PREBATCH:
		master->prebatch = TRUE;
		break;
	case ARG_SKIP_ON_RESTART:
		master->skip_on_restart = TRUE;
		break;
	case ARG_KLOG_NAME:
		master->kill_log_name = argv[0];
		break;
	case ARG_USEGUI:
		master->use_gui = TRUE;
		break;
	case ARG_XOVER:
		master->do_xover = FALSE;
		break;
	case ARG_CONN_DEDUPE:
		master->conn_dedupe = TRUE;
		break;
	case ARG_POST_FILTER:
		master->post_filter = argv[0];
		break;
	case ARG_CONN_ACTIVE:
		master->conn_active = TRUE;
		break;
	case ARG_HIST_FILE:
		master->history_file = argv[0];
		break;
	case ARG_HEADER_ONLY:
		master->header_only = TRUE;
		break;
	case ARG_ACTIVE_LASTREAD:
		x = atoi(argv[0]);
		if(x > 0) {
			error_log(ERRLOG_REPORT, suck_phrases[66], NULL);
		}
		else {
			master->active_lastread = atoi(argv[0]);
		}
		break;
	case ARG_USEXOVER:
		master->use_xover = TRUE;
		break;
	case ARG_RESETCOUNTER:
		master->resetcounter = TRUE;
		break;
	case ARG_LOW_READ:
		master->low_read = TRUE;
		break;
	case ARG_SHOW_GROUP:
		master->show_group = TRUE;
		break;
#ifdef TIMEOUT
	case ARG_TIMEOUT:
		TimeOut = atoi(argv[0]);
		break;
#endif
#ifdef HAVE_LIBSSL
	case ARG_USE_SSL:
		master->do_ssl = TRUE;
		master->portnr = DEFAULT_SSL_PORT;
		break;
	case ARG_LOCAL_SSL:
		master->local_ssl = TRUE;
		break;
#endif

	}

	return retval;

}
/*----------------------------------*/
/* authenticate when receive a 480 */
/*----------------------------------*/
int do_authenticate(PMaster master) {
	 int len, nr, retval = RETVAL_OK;
	 char *resp, buf[MAXLINLEN];


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

			 TimerFunc(TIMER_ADDBYTES, len, NULL);

			 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 */
				 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
				 TimerFunc(TIMER_ADDBYTES, len, NULL);

				 number(resp, &nr);
			 }

			 if(nr != 381) {
				 error_log(ERRLOG_REPORT, suck_phrases[27], resp, NULL);
				 retval = RETVAL_NOAUTH;
			 }
			 else {
				 sprintf(buf, "AUTHINFO PASS %s\r\n", master->passwd);
				 sputline(master->sockfd, buf, master->do_ssl, master->ssl_struct);
				 if(master->debug == TRUE) {
					 do_debug("sending command: %s", buf);
				 }
				 len = sgetline(master->sockfd, &resp, master->do_ssl, master->ssl_struct);
				 if(len < 0) {
					 retval = RETVAL_ERROR;
				 }
				 else {
					 if(master->debug == TRUE) {
						 do_debug("got answer: %s", resp);
					 }

					 TimerFunc(TIMER_ADDBYTES, len, NULL);

					 number(resp, &nr);
					 switch(nr) {
					 case 281: /* bingo */
						 retval = RETVAL_OK;
						 break;
					 case 502: /* permission denied */
						 retval = RETVAL_NOAUTH;
						 error_log(ERRLOG_REPORT, suck_phrases[28], NULL);
						 break;
					 default: /* wacko error */
						 error_log(ERRLOG_REPORT, suck_phrases[27], resp, NULL);
						 retval = RETVAL_NOAUTH;
						 break;
					 }
				 }
			 }
		 }
	 }
	 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(PMaster master) {

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

	if(master->phrases != NULL) {

		if((fpi = fopen(master->phrases, "r")) == NULL) {
			MyPerror(master->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) {
				read_array(fpi, NR_RPOST_PHRASES, FALSE);	/* skip these */
				read_array(fpi, NR_TEST_PHRASES, FALSE);
				if(( suck_phrases = read_array(fpi, NR_SUCK_PHRASES, TRUE)) != NULL &&
				   ( timer_phrases= read_array(fpi, NR_TIMER_PHRASES,TRUE)) &&
				   (  chkh_phrases= read_array(fpi, NR_CHKH_PHRASES,TRUE)) &&
				   (dedupe_phrases= read_array(fpi, NR_DEDUPE_PHRASES,TRUE)) &&
				   ( killf_reasons= read_array(fpi, NR_KILLF_REASONS,TRUE)) &&
				   ( killf_phrases= read_array(fpi, NR_KILLF_PHRASES,TRUE)) &&
				   ( sucku_phrases= read_array(fpi, NR_SUCKU_PHRASES,TRUE)) &&
				   (read_array(fpi, NR_LMOVE_PHRASES, FALSE) == NULL) && /* skip this one */
				   ( active_phrases=read_array(fpi, NR_ACTIVE_PHRASES,TRUE)) &&
				   ( batch_phrases= read_array(fpi, NR_BATCH_PHRASES,TRUE)) &&
				   ( xover_phrases= read_array(fpi, NR_XOVER_PHRASES,TRUE)) &&
				   ( xover_reasons= read_array(fpi, NR_XOVER_REASONS,TRUE))) {
					error = FALSE;
				}
			}

		}
		fclose(fpi);
		if(error == TRUE) {
			/* reset back to default */
			error_log(ERRLOG_REPORT, "Using default Language phrases\n", NULL);
			both_phrases = default_both_phrases;
			suck_phrases=default_suck_phrases;
			timer_phrases=default_timer_phrases;
			chkh_phrases=default_chkh_phrases;
			dedupe_phrases=default_dedupe_phrases;
			killf_reasons=default_killf_reasons;
			killf_phrases=default_killf_phrases;
			killp_phrases=default_killp_phrases;
			sucku_phrases=default_sucku_phrases;
			active_phrases=default_active_phrases;
			batch_phrases=default_batch_phrases;
			xover_phrases=default_xover_phrases;
			xover_reasons=default_xover_reasons;
		}
	}
}
/*--------------------------------------------------------------------------------*/
void free_phrases(void) {
		/* free up the memory alloced in load_phrases() */
		if(both_phrases != default_both_phrases) {
			free_array(NR_BOTH_PHRASES, both_phrases);
		}
		if(suck_phrases != default_suck_phrases) {
			free_array(NR_SUCK_PHRASES, suck_phrases);
		}
		if(timer_phrases != default_timer_phrases) {
			free_array(NR_TIMER_PHRASES, timer_phrases);
		}
		if(chkh_phrases != default_chkh_phrases) {
			free_array(NR_CHKH_PHRASES, chkh_phrases);
		}
		if(dedupe_phrases != default_dedupe_phrases) {
			free_array(NR_DEDUPE_PHRASES, dedupe_phrases);
		}
		if(killf_reasons != default_killf_reasons) {
			free_array(NR_KILLF_REASONS, killf_reasons);
		}
		if(killf_phrases != default_killf_phrases) {
			free_array(NR_KILLF_PHRASES, killf_phrases);
		}
		if(killp_phrases != default_killp_phrases) {
			free_array(NR_KILLP_PHRASES, killp_phrases);
		}
		if(sucku_phrases != default_sucku_phrases) {
			free_array(NR_SUCKU_PHRASES, sucku_phrases);
		}
		if(active_phrases != default_active_phrases) {
			free_array(NR_ACTIVE_PHRASES, active_phrases);
		}
		if(batch_phrases != default_batch_phrases) {
			free_array(NR_BATCH_PHRASES, batch_phrases);
		}
		if(xover_phrases != default_xover_phrases) {
			free_array(NR_XOVER_PHRASES, xover_phrases);
		}
		if(xover_reasons != default_xover_reasons) {
			free_array(NR_XOVER_REASONS, xover_reasons);
		}



}


