/*
* slurp - a passive nntp news client
*
* Copyright (C) 1992/93/94/95 Stephen Hebditch <steveh@tqmcomms.co.uk>.
* All rights reserved. TQM Communications, BCM Box 225, London, WC1N 3XX.
*
* See README for more information and disclaimers
*
* This is the main routine for slurp together with the routines to
* handle the slurp.sys configuration file and command line arguments.
*
*
* $Id: slurp.c,v 1.10 1995/02/07 14:34:00 root Exp root $
*
* $Log: slurp.c,v $
* Revision 1.10 1995/02/07 14:34:00 root
* Don't write new time in hostfile if didn't do a NEWNEWS.
* Added copyright notice.
*
* Revision 1.9 1995/01/10 13:02:42 root
* Moved includes from slurp.h.
* Removed test_time() function.
* Added -c, -h, -n, -o, -p and -x options.
* Rewrote read_sys yet again, fixing a bug which caused it to be
* wrongly parsed when items were missing, adding support for new
* features and generally making it rather cleaner.
* Removed get_ntime() and set_ntime() functions, now renamed to
* read_hostfile() and write_hostfile() in hostfiles.c
* Moved do_authinfo() and do_mode_reader() to sockets.c.
* General tidying-up.
*
* Revision 1.8 1993/08/20 10:34:50 root
* Unlink backup time file before renaming or an error occurs
* with the rename under SVR3.
*
* Revision 1.7 1993/06/07 11:15:00 root
* Added support for users to supply a filename for the time file
* on the command line, for use with machines with short filenames.
* Rewrote read_sys (again!) for a much cleaner implementation.
* Fixed problem of time file being wrongly set if slurp was
* interrupted before the newnews phase had completed.
* If can't rename time file then don't abort, just write the new
* file.
*
* Revision 1.6 1993/04/22 18:22:20 root
* Added signal handler to catch SIGHUP, SIGINT, SIGQUIT and SIGTERM.
* If occur then report signal in syslog and possibly submit open
* batch to news and dump message ids of uncollected articles to
* slurp.<hostname>
*
* Revision 1.5 1993/03/01 17:51:33 root
* read_sys can cope with lines longer than BUFSIZ.
* report when attempting to load unretrieved message ids.
* Move sublist to a parameter after the hostname, separate by a slash.
* Changed some system error checking.
*
* Revision 1.4 1993/02/14 14:57:43 root
* Added support for simple authorisation protocol.
* Added support for INN's 'MODE READER' command.
* Re-arranged command line options.
* Rewrote read_sys and added flags and authorisation options to it.
* Rewrote get_ntime and set_ntime to use a filename of slurp.<hostname>
* instead of slurp.tim, solving lack of locking and allowing the file
* to contain a list of unretrieved message ids on the lines following
* the time.
* Don't care if slurp.<hostname> doesn't exist already.
* If RNEWS is not defined, then change to INDIR for writing out batch
* files.
*
* Revision 1.3 1992/12/15
* Open syslog *before* we start doing things that might write to it.
* Informational messages logged as LOG_INFO.
* Assorted minor tidy-ups.
*
* Revision 1.2 1992/12/07
* Corrected test for 4.2/4.3 BSD syslog open.
*
* Revision 1.1 1992/12/06
* Made no_time_flag global.
* Fixed null dereferencing of nn_distributions.
*
* Revision 1.0 1992/08/07
* Initial coding.
*
*/
#include "conf.h"
/* POSIX headers */
#define _POSIX_SOURCE 1
#include <sys/types.h>
#include <ctype.h>
#include <limits.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifndef NOUNISTD
#include <unistd.h>
#endif
/* Local headers */
#include "nntp.h"
#include "patchlevel.h"
#include "slurp.h"
#include "syslog.h"
/* Command line / configuration file flags */
int debug_flag = FALSE;
int no_history_flag = FALSE;
int no_id_read_flag = FALSE;
int no_id_write_flag = FALSE;
int no_time_write_flag = FALSE;
int pipe_flag = FALSE;
int progress_flag = FALSE;
static int authinfo_flag = FALSE;
static int local_time_flag = FALSE;
static int mode_reader_flag = FALSE;
static int no_collect_flag = FALSE;
static int no_newnews_flag = FALSE;
static int output_flag = FALSE;
/* Host information */
char *hostname = NULL;
static int portno = 0;
static char *ai_username = NULL;
static char *ai_password = NULL;
/* NEWNEWS information */
char *nn_newsgroups = NULL;
char *nn_time = NULL;
char *nn_distributions = NULL;
/* Misc options */
FILE *overflowfp = NULL;
char *output = NULL;
char hostfile [PATH_MAX];
static char *sublist = NULL;
/* Counters */
int waiting = 0;
int dupart = 0;
int misart = 0;
#ifdef SITEEXCLUDING
int exclart = 0;
#endif /* SITEEXCLUDING */
int xfrart = 0;
long totalsize = 0;
/* Misc variables */
char *pname;
time_t nexttime;
/*
* parse_args - Parse the command line arguments. Returns TRUE if there
* is an error, otherwise returns FALSE.
*/
static int
parse_args (int argc, char **argv)
{
char *extn;
char *pos;
extern char *optarg;
extern int optind;
int c;
while ((c = getopt (argc, argv, "a:g:o:p:t:cdhilmnrwx")) != EOF)
switch (c)
{
case 'a': /* Do an authinfo */
if ((pos = strchr (optarg, '/')) != NULL)
{
ai_username = optarg;
*pos++ = '\0';
ai_password = pos;
authinfo_flag++;
}
else
{
(void) fprintf (stderr, "No authinfo password supplied\n");
return (TRUE);
}
break;
case 'g': /* Newsgroups list */
if ((pos = strchr (optarg, '/')) != NULL)
{
*pos++ = '\0';
nn_distributions = pos;
}
else
nn_distributions = "";
nn_newsgroups = optarg;
break;
case 'o': /* Set output command or directory */
output = optarg;
output_flag++;
break;
case 'p': /* Set port */
portno = atoi (optarg);
break;
case 't': /* Start time */
nn_time = optarg;
if (!(isdigit (nn_time [0]) &&
isdigit (nn_time [1]) &&
isdigit (nn_time [2]) &&
isdigit (nn_time [3]) &&
isdigit (nn_time [4]) &&
isdigit (nn_time [5]) &&
isspace (nn_time [6]) &&
isdigit (nn_time [7]) &&
isdigit (nn_time [8]) &&
isdigit (nn_time [9]) &&
isdigit (nn_time [10]) &&
isdigit (nn_time [11]) &&
isdigit (nn_time [12])))
{
(void) fprintf (stderr, "Invalid time specification - should be 'YYMMDD HHMMSS'\n");
return (TRUE);
}
break;
case 'c': /* Don't collect articles */
no_collect_flag++;
break;
case 'd': /* Debugging on */
debug_flag++;
progress_flag = FALSE;
break;
case 'h': /* Don't do history lookups */
no_history_flag++;
break;
case 'i': /* Don't load unprocessed ids */
no_id_read_flag++;
break;
case 'l': /* Use local time */
local_time_flag++;
break;
case 'm': /* Don't write next time to hostfile */
no_time_write_flag++;
break;
case 'n': /* Don't do a NEWNEWS */
no_newnews_flag++;
no_time_write_flag++;
break;
case 'r': /* Do a 'MODE READER' */
mode_reader_flag++;
break;
case 'w': /* Don't write uncollected ids to hostfile */
no_id_write_flag++;
break;
case 'x': /* Print progress information */
progress_flag++;
debug_flag = FALSE;
break;
default:
return (TRUE);
}
/* Get server name and possible host file and sublist */
if (optind < argc)
{
hostname = argv [optind];
if ((extn = strchr (hostname, ':')) != NULL)
*extn++ = '\0';
if ((sublist = strchr (hostname, '/')) != NULL)
*sublist++ = '\0';
(void) strcpy (hostfile, HOSTFILE);
if (extn == NULL)
(void) strcat (hostfile, hostname);
else
(void) strcat (hostfile, extn);
if (sublist != NULL)
{
(void) strcat (hostfile, ".");
(void) strcat (hostfile, sublist);
}
}
else
{
(void) fprintf (stderr, "No server name supplied\n");
return (TRUE);
}
return (FALSE);
}
/*
* read_sys - Read in the appropriate entry from the slurp.sys file
* for the specified hostname. Stores the relevant newsgroups for that
* host in nn_newsgroups and the relevant distribution in nn_distributions.
*/
static void
read_sys (void)
{
FILE *sysfp;
char *flag;
char *p1;
char *p2;
char line [BUFSIZ];
char searchname [BUFSIZ];
int foundhost = FALSE;
int j;
int object = 0;
int more = FALSE;
/* Attempt to open the sys file */
if ((sysfp = fopen (SYSFILE, "r")) == NULL)
log_sys ("read_sys: error opening %s", SYSFILE);
/* Create search name */
(void) strcpy (searchname, hostname);
if (sublist != NULL)
{
(void) strcat (searchname, "/");
(void) strcat (searchname, sublist);
}
for (;;)
{
/* Read in a line */
(void) fgets (line, sizeof (line), sysfp);
/* If end of file then error unless we have a complete entry */
if (feof (sysfp))
{
if (foundhost)
break;
else
{
log_msg ("read_sys: no entry found for host %s in %s",
searchname, SYSFILE);
exit (2);
}
}
/* If a read error then report it */
if (ferror (sysfp))
log_sys ("read_sys: error reading %s", SYSFILE);
/* Zap anything after the comment character */
if ((p1 = strchr (line, '#')) != NULL)
*p1 = '\0';
/* If continuation character then indicate more to follow */
if ((p1 = strchr (line, '\\')) != NULL)
{
*p1 = '\0';
more = TRUE;
}
/* Strip leading spaces */
if (isspace (line [0]) != NULL)
{
/* Find first non-blank */
for (p1 = line; isspace (*p1); p1++);
/* Copy down the remainder of the line */
for (j = 0; p1 [j] != '\0'; j++)
line [j] = p1 [j];
}
/* If NL found, then there isn't any more of this entry */
if ((p1 = strchr (line, '\n')) != NULL)
{
*p1 = '\0';
more = FALSE;
}
else
more = TRUE;
/* Found a hostname */
if (object == 0)
{
if ((p1 = strchr (line, ':')) != NULL)
{
*p1++ = '\0';
if (strcmp (line, searchname) == 0)
foundhost = TRUE;
(void) strcpy (line, p1);
object = 1; /* Newsgroups next */
}
}
/* Currently adding newsgroups */
if (object == 1)
{
p1 = strchr (line, '/');
p2 = strchr (line, ':');
if ((p1 != NULL) && ((p2 == NULL) || (p1 < p2)))
{
*p1++ = '\0';
object = 2; /* Distributions next */
}
else if (p2 != NULL)
{
*p2++ = '\0';
object = 3; /* Flags next */
}
if (foundhost)
nn_newsgroups = stradd (nn_newsgroups, line);
if (object == 2)
(void) strcpy (line, p1);
else if (object == 3)
(void) strcpy (line, p2);
}
/* Currently adding distributions */
if (object == 2)
{
if ((p1 = strchr (line, ':')) != NULL)
{
*p1++ = '\0';
object = 3; /* Flags next */
}
if (foundhost)
nn_distributions = stradd (nn_distributions, line);
if (p1 != NULL)
(void) strcpy (line, p1);
}
/* Currently setting flags */
if (object == 3)
{
if ((p1 = strchr (line, ':')) != NULL)
{
*p1++ = '\0';
object = 4; /* authinfo user next */
}
if (foundhost)
{
for (flag = line; *flag != '\0'; flag++)
switch (*flag)
{
case 'i':
no_id_read_flag++;
break;
case 'l':
local_time_flag++;
break;
case 'r':
mode_reader_flag++;
break;
case 'w':
no_id_write_flag++;
break;
default:
break;
}
}
if (p1 != NULL)
(void) strcpy (line, p1);
}
/* Currently setting username */
if (object == 4)
{
p1 = strchr (line, '/');
p2 = strchr (line, ':');
if ((p1 != NULL) && ((p2 == NULL) || (p1 < p2)))
{
*p1++ = '\0';
object = 5; /* authinfo passwd next */
}
else if (p2 != NULL)
{
*p2++ = '\0';
object = 6; /* output next */
}
if ((foundhost) && (!authinfo_flag) && (*line != '\0'))
ai_username = stradd (ai_username, line);
if (object == 5)
(void) strcpy (line, p1);
else if (object == 6)
(void) strcpy (line, p2);
}
/* Currently setting password */
if (object == 5)
{
if ((p1 = strchr (line, ':')) != NULL)
{
*p1++ = '\0';
object = 6; /* output next */
}
if ((foundhost) && (!authinfo_flag) && (*line != '\0'))
ai_password = stradd (ai_password, line);
if (p1 != NULL)
(void) strcpy (line, p1);
}
/* Currently setting output */
if (object == 6)
{
if ((foundhost) && (!output_flag) && (*line != '\0'))
output = stradd (output, line);
}
/* If no more, then either got complete entry or not found yet */
if (!more)
{
if (foundhost)
break;
else
object = 0;
}
}
(void) fclose (sysfp);
if (nn_newsgroups == NULL)
{
log_msg ("read_sys: newsgroups missing for host %s in %s",
searchname, SYSFILE);
exit (2);
}
if (nn_distributions == NULL)
nn_distributions = "";
if ((ai_username == NULL) && (ai_password != NULL))
{
log_msg ("read_sys: authinfo username missing for host %s in %s",
searchname, SYSFILE);
exit (2);
}
if ((ai_username != NULL) && (ai_password == NULL))
{
log_msg ("read_sys: authinfo password missing for host %s in %s",
searchname, SYSFILE);
exit (2);
}
return;
}
/*
* interrupt - signal handler to report signal in log and possibly
* submit remaining batch to news and dump uncollected message ids.
*/
static void
interrupt (int signo)
{
log_msg ("interrupt: received signal %d", signo);
enqueue_batch ();
write_hostfile ();
exit (1);
}
/*
* set_signals - set up signal handler to catch appropriate signals.
*/
static void
set_signals (void)
{
if (signal (SIGHUP, interrupt) == SIG_ERR)
log_sys ("set_signals: can't catch SIGHUP");
if (signal (SIGINT, interrupt) == SIG_ERR)
log_sys ("set_signals: can't catch SIGINT");
if (signal (SIGQUIT, interrupt) == SIG_ERR)
log_sys ("set_signals: can't catch SIGQUIT");
if (signal (SIGTERM, interrupt) == SIG_ERR)
log_sys ("set_signals: can't catch SIGTERM");
}
/*
* MAIN PROCEDURE
*/
int
main (int argc, char **argv)
{
int ret;
time_t starttime, endtime;
/* Set the name of the program */
pname = (pname = (char *) strrchr (argv [0], '/')) ? pname + 1 : argv [0];
/* Parse the arguments */
if (parse_args (argc, argv))
{
(void) fprintf (stderr, "\nSlurp version %s. Copyright (C) 1992/93/94/95 TQM Communications.\n", PATCHLEVEL);
(void) fprintf (stderr, "All rights reserved. Slurp information: slurp-info@tqmcomms.co.uk.\n\n");
(void) fprintf (stderr, "Usage: %s [-g newsgroups/distribution] [-t time] [-a username/password]\n", pname);
(void) fprintf (stderr, " [-o output] [-p portno] [-c] [-d] [-h] [-i] [-l] [-m] [-n] [-r] [-w] [-x]\n");
(void) fprintf (stderr, " server[/sublist][:hostfile]\n");
exit (2);
}
/* Open syslog if required with appropriate BSD 4.2/4.3 call */
#ifdef SYSLOG
openlog (pname, LOG_PID, SYSLOG);
#endif
/* If groups not supplied in args, then get from slurp.sys file */
if (nn_newsgroups == NULL)
read_sys ();
/* If output not specified, then use default */
if (output == NULL)
output = OUTPUT;
/* Set variables for when output is a pipe to another program */
if (*output == '|')
{
output++;
pipe_flag++;
}
/* Open the history file */
if (open_history ())
log_sys ("can't open history file %s", HISTORY_FILE);
#ifdef SITEEXCLUDING
/* Get site names to exclude */
site_init();
#endif /* SITEEXCLUDING */
/* Load start time or ids from hostfile if appropriate */
read_hostfile ();
if (debug_flag)
{
(void) fprintf (stderr, "server: %s\n", hostname);
(void) fprintf (stderr, "time: %s\n", nn_time);
(void) fprintf (stderr, "newsgroups: '%s'\n", nn_newsgroups);
(void) fprintf (stderr, "distributions: '%s'\n", nn_distributions);
}
/* Change to the incoming batch directory */
if (!pipe_flag)
if (chdir (output))
log_sys ("can't change directory to %s", output);
/* Unless don't write flag set, get time for next NEWNEWS */
if (!no_time_write_flag)
{
if (local_time_flag)
nexttime = time ((time_t *) 0);
else
if ((nexttime = server_time (hostname)) == 0)
exit (3);
}
/* Set up the connection to the server */
switch (ret = server_init (hostname, portno))
{
case -1 :
exit (3);
case OK_CANPOST:
case OK_NOPOST:
break;
default:
log_msg ("can't talk to %s: got response code %d", hostname, ret);
exit (4);
}
/* If authinfo details supplied, then use 'em */
if (ai_username != NULL)
do_authinfo (ai_username, ai_password);
/* Switch INN to nnrpd instead of innd if needed */
if (mode_reader_flag)
do_mode_reader ();
/* Get a list of the new articles unless told not to */
if (!no_newnews_flag)
get_ids ();
/* Now get the actual articles unless told not to */
if (!no_collect_flag)
{
#ifdef SPEEDSTATS
starttime = time ((time_t *) 0);
#endif
if (waiting > 0)
{
set_signals ();
get_articles ();
}
#ifdef SPEEDSTATS
endtime = time ((time_t *) 0);
#endif
}
/* Time to say goodbye */
close_server ();
close_history ();
#ifdef SITEEXCLUDING
site_close();
#endif /* SITEEXCLUDING */
/* Submit the remaining batch, if present */
enqueue_batch ();
/* Update new time and / or uncollected ids */
write_hostfile ();
#ifdef SYSLOG
if (!debug_flag)
#ifdef SITEEXCLUDING
syslog (LOG_INFO, "Processed %d new, %d duplicate, %d missing, %d excluded articles",
xfrart, dupart, misart, exclart);
#else
syslog (LOG_INFO, "Processed %d new, %d duplicate, %d missing articles",
xfrart, dupart, misart);
#endif /* SITEEXCLUDING */
else
#endif
#ifdef SITEEXCLUDING
(void) fprintf (stderr, "Processed %d new, %d duplicate, %d missing, %d excluded articles\n",
xfrart, dupart, misart, exclart);
#else
(void) fprintf (stderr, "Processed %d new, %d duplicate, %d missing articles\n",
xfrart, dupart, misart);
#endif /* SITEEXCLUDING */
#ifdef SPEEDSTATS
#ifdef SYSLOG
if (!debug_flag)
syslog (LOG_INFO, "Average transfer speed %ld cps",
totalsize / (starttime == endtime ? 1 : endtime - starttime));
else
#endif
(void) fprintf (stderr, "Average transfer speed %ld cps\n",
totalsize / (starttime == endtime ? 1 : endtime - starttime));
#endif
exit (0);
}
/* END-OF-FILE */
syntax highlighted by Code2HTML, v. 0.9.1