/* * slurp - a passive nntp news client * * Copyright (C) 1992/93/94/95 Stephen Hebditch . * 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. * * 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. * 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. 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 #include #include #include #include #include #include #include #include #ifndef NOUNISTD #include #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 */