/* * articles - handle retrieval and batching of articles * * Copyright (C) 1992/93/94 Stephen Hebditch . * All rights reserved. TQM Communications, BCM Box 225, London, WC1N 3XX. * * See README for more information and disclaimers * * This file provides a set of routines to retrieve articles from the * remote NNTP server and add to a batch of articles being piped to * the local news system via rnews. * * $Id: articles.c,v 1.9 1995/01/10 12:38:07 root Exp $ * * $Log: articles.c,v $ * Revision 1.9 1995/01/10 12:38:07 root * Moved includes from slurp.h. * Run-time setting of directory or program to write batch to. * Added progress reporting option. * Modified to use new message id cache method rather than binary tree. * Added configurable pipelining. * General tidy-ups. * * Revision 1.7 1993/06/14 15:20:52 root * Removed some unnecessary initialisation. * * Revision 1.6 1993/04/22 18:24:21 root * Treat ERR_ACCESS result code when retrieving an article the same * as if the article was missing. * * Revision 1.5 1993/03/01 17:36:51 root * Removed stray text after an endif. * * Revision 1.4 1993/02/14 14:44:25 root * Fixed problem with newline being added to article line buffer which * could already be at its maximum length. * Support for writing out batch files as well as piping batches to * rnews. * If BATCHSIZE is zero then keep pipe open to rnews indefinitely. * If error occurs then submit batch and dump unretrieved ids. * * Revision 1.3 1992/12/15 * Minor tidy-ups, plus fixed flushing of tfp every time after it had * been opened once. * * Revision 1.1 1992/12/04 * Print line before it is sent to server when debugging is on. * * Revision 1.0 1992/11/27 * Adapted from nntpxfer-e code. * Pipe batches to rnews instead of creating in in.coming directory, * so can be used with INN. */ #include "conf.h" /* POSIX headers */ #undef _POSIX_SOURCE #include #include #include #include #include #include #include #include #ifndef NOUNISTD #include #endif /* Local headers */ #include "nntp.h" #include "slurp.h" #include "syslog.h" /* File-scope variables */ static char artbuf [COPYSIZE]; /* temp storage for article in memory */ static char *endart = artbuf; /* points to just past end of article */ static int incore = TRUE; /* article in memory, not temp file */ static FILE *tfp; /* temporary file descriptor */ static FILE *batchfp = NULL; /* file descriptor for batch */ static size_t batchsize = 0; /* size of current batch */ static char batchname [PATH_MAX]; /* name of current batch */ /* * new_batch - Determines if there is enough room for the batch on the * disk containing the news spool directories. If there is, then a pipe * is opened to rnews and the batch variable initialised with the * details. */ static void new_batch (void) { /* Make sure there is enough room for batch */ #ifdef MINFREE if (!space (MINFREE)) { log_msg ("new_batch: not enough space for incoming batch"); write_hostfile (); exit (5); } #endif if (pipe_flag) { /* Open a pipe to rnews for the batch */ if ((batchfp = popen (output, "w")) == NULL) log_sys ("new_batch: can't open pipe to %s", output); } else { /* Open a file in incoming news directory with temporary name */ (void) strcpy (batchname, BATCHNAME); (void) mktemp (batchname); if ((batchfp = fopen (batchname, "w")) == NULL) log_sys ("new_batch: can't open file %s", batchname); } } /* * enqueue_batch - Submit the batch to the new system by closing the * currently open pipe to rnews or renaming the temporary file in the * incoming news directory so it can be seen by the news system. */ void enqueue_batch (void) { char permname [PATH_MAX]; time_t now; /* Return if there is no currently open batch */ if (batchfp == NULL) return; if (pipe_flag) { /* Close the pipe to rnews */ if (pclose (batchfp)) log_sys ("enqueue_batch: can't close pipe to %s", output); } else { /* Close the temporary file */ if (fclose (batchfp)) log_sys ("enqueue_batch: can't close %s", batchname); /* Rename it so it can be seen by news */ for (;;) { (void) sprintf (permname, "%ld.t", (long) time (&now)); if (link (batchname, permname) == 0) break; if (errno != EEXIST) log_sys ("enqueue_batch: error linking %s to %s", batchname, permname); (void) sleep (2); } if (unlink (batchname)) log_sys ("enqueue_batch: error unlinking %s", batchname); } /* Reset the batch descriptor for a new batch */ batchfp = NULL; batchsize = 0; } /* * read_article - Read an article into artbuf or, if too large, into a * temporary file from the currently open NNTP server socket, reading up * to the end-of-article marker, a '.' on a single line. If it is stored * in memory, then incore will be TRUE, otherwise it will be FALSE and the * temporary file name will be stored in tempfile. */ static void read_article (void) { char *realline; char line [NNTP_STRLEN]; int lines = 0; int len; incore = TRUE; endart = artbuf; /* Read in the article */ for (;;) { get_server (line, sizeof (line)); /* Dot on its own means article end reached */ if (strcmp (line, ".") == 0) break; /* remove hidden dot if present */ realline = (line [0] == '.' ? line + 1 : line); /* Article is currently stored in memory */ if (incore) { /* If no room in artbuf, open tempfile and copy article there */ len = strlen (realline); if ((endart + len + 2 ) > (artbuf + sizeof (artbuf))) { if ((tfp = tmpfile ()) == NULL) log_sys ("read_article: can't create temporary article file"); (void) fwrite (artbuf, 1, endart - artbuf, tfp); if (ferror (tfp)) log_sys ("read_article: can't write to temporary article file"); (void) fputs (realline, tfp); (void) putc ('\n', tfp); if (ferror (tfp)) log_sys ("read_article: can't write to temporary article file"); incore = FALSE; } else { /* If fits, append realline to artbuf at endart */ (void) strcpy (endart, realline); endart += len; *endart++ = '\n'; *endart = '\0'; } } /* Already writing article to temp file */ else { (void) fputs (realline, tfp); (void) putc ('\n', tfp); if (ferror (tfp)) log_sys ("read_article: can't read from temporary article file"); } lines++; } /* Article successfully read in */ if (debug_flag) (void) fprintf (stderr, "-> %d lines\n", lines); } /* batch_article - Append "#! rnews " and the article from artbuf * or temporary file to the batch file. */ static void batch_article (void) { size_t bytes; size_t size; /* Find article size */ if (incore) size = endart - artbuf; else size = ftell (tfp); totalsize += size; batchsize += size; /* Print the article header */ (void) fprintf (batchfp, "#! rnews %ld %s\n", (long) size, hostname); /* Copy the article to the batch file */ if (incore) { (void) fwrite (artbuf, 1, size, batchfp); if (ferror (batchfp)) log_sys ("batch_article: can't write to batch"); } else { rewind (tfp); while ((bytes = fread (artbuf, 1, sizeof (artbuf), tfp)) > 0) { (void) fwrite (artbuf, 1, bytes, batchfp); if (ferror (batchfp)) log_sys ("batch_article: can't write to batch"); } (void) fclose (tfp); } } /* * fetch_article - Retrieve an article from the currently open NNTP * server socket which has already been requested. The article is written * to the end of the current batch. If there is not already a batch * then a new pipe to rnews for the batch will be opened. If the current * batch is too large or has too many articles then the pipe will be * closed so that the batch may be submitted to the news system. */ static void fetch_article (void) { /* Open a new batch if required */ if (batchfp == NULL) new_batch (); /* Read in article */ read_article (); /* Add it to the batch */ batch_article (); /* Submit batch if ready */ if ((batchsize > BATCHSIZEMAX) && (BATCHSIZEMAX != 0)) enqueue_batch (); } /* * get_article - process status line from server and get actual article * if appropriate. */ static void get_article (IDNODE *idnode) { char status [NNTP_STRLEN]; /* Read status line from server */ get_server (status, sizeof (status)); if (debug_flag) (void) fprintf (stderr, "-> %s\n", status); switch (atoi (status)) { /* If missing, then add to missing list */ case ERR_NOART: case ERR_ACCESS: misart++; break; /* If present, then fetch and add to batch */ case OK_ARTICLE: fetch_article (); xfrart++; #ifdef KEEPMISSINGARTS /* Note that this article has been retrieved and filed */ idnode -> used = TRUE; #endif /* KEEPMISSINGARTS */ break; /* Otherwise must be a protocol error */ default: log_msg ("get_article: NNTP protocol error: got '%s'", status); enqueue_batch (); write_hostfile (); exit (4); } #ifndef KEEPMISSINGARTS /* Note that this article has been retrieved and filed */ idnode -> used = TRUE; #endif /* KEEPMISSINGARTS */ /* Print progress if required */ if (progress_flag) { if ((xfrart % 10) == 0) { (void) fprintf (stdout, "%d/%d transferred\r", xfrart, waiting); (void) fflush (stdout); } } } /* * request_article - Request an article with specified id from the server. */ static void request_article (const char *msgid) { char request [NNTP_STRLEN]; (void) sprintf (request, "ARTICLE %s", msgid); if (debug_flag) (void) fprintf (stderr, "<- %s\n", request); put_server (request); } /* * get_articles - Get the articles from the server whose message ids * are stored in the cache. */ void get_articles (void) { IDNODE *idnode [SPEEDUP + 1]; int i, j; idnode [0] = first_id (); for (i = 0; i < (waiting + SPEEDUP); i++) { j = i % SPEEDUP; if ((i >= SPEEDUP) && (idnode [j] != NULL)) get_article (idnode [j]); if (i < waiting) { if (i != 0) idnode [j] = next_id (); if (check_id (idnode [j] -> msgid)) request_article (idnode [j] -> msgid); else { idnode [j] -> used = TRUE; idnode [j] = NULL; } } } if (progress_flag) { (void) fprintf (stdout, "%d/%d transferred\n", xfrart, waiting); (void) fflush (stdout); } } /* END-OF-FILE */