+++ /dev/null
-/* $Id: archive.c 6138 2003-01-19 04:13:51Z rra $
-**
-** Read batchfiles on standard input and archive them.
-*/
-
-#include "config.h"
-#include "clibrary.h"
-#include <ctype.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <time.h>
-
-#ifdef TM_IN_SYS_TIME
-# include <sys/time.h>
-#endif
-
-#include "inn/innconf.h"
-#include "inn/messages.h"
-#include "inn/wire.h"
-#include "libinn.h"
-#include "paths.h"
-#include "storage.h"
-
-
-static char *Archive = NULL;
-static char *ERRLOG = NULL;
-
-/*
-** Return a YYYYMM string that represents the current year/month
-*/
-static char *
-DateString(void)
-{
- static char ds[10];
- time_t now;
- struct tm *x;
-
- time(&now);
- x = localtime(&now);
- snprintf(ds, sizeof(ds), "%d%d", x->tm_year + 1900, x->tm_mon + 1);
-
- return ds;
-}
-
-
-/*
-** Try to make one directory. Return false on error.
-*/
-static bool
-MakeDir(char *Name)
-{
- struct stat Sb;
-
- if (mkdir(Name, GROUPDIR_MODE) >= 0)
- return true;
-
- /* See if it failed because it already exists. */
- return stat(Name, &Sb) >= 0 && S_ISDIR(Sb.st_mode);
-}
-
-
-/*
-** Given an entry, comp/foo/bar/1123, create the directory and all
-** parent directories needed. Return false on error.
-*/
-static bool
-MakeArchiveDirectory(char *Name)
-{
- char *p;
- char *save;
- bool made;
-
- if ((save = strrchr(Name, '/')) != NULL)
- *save = '\0';
-
- /* Optimize common case -- parent almost always exists. */
- if (MakeDir(Name)) {
- if (save)
- *save = '/';
- return true;
- }
-
- /* Try to make each of comp and comp/foo in turn. */
- for (p = Name; *p; p++)
- if (*p == '/' && p != Name) {
- *p = '\0';
- made = MakeDir(Name);
- *p = '/';
- if (!made) {
- if (save)
- *save = '/';
- return false;
- }
- }
-
- made = MakeDir(Name);
- if (save)
- *save = '/';
- return made;
-}
-
-
-/*
-** Copy a file. Return false if error.
-*/
-static bool
-Copy(char *src, char *dest)
-{
- FILE *in;
- FILE *out;
- size_t i;
- char *p;
- char buff[BUFSIZ];
-
- /* Open the output file. */
- if ((out = fopen(dest, "w")) == NULL) {
- /* Failed; make any missing directories and try again. */
- if ((p = strrchr(dest, '/')) != NULL) {
- if (!MakeArchiveDirectory(dest)) {
- syswarn("cannot mkdir for %s", dest);
- return false;
- }
- out = fopen(dest, "w");
- }
- if (p == NULL || out == NULL) {
- syswarn("cannot open %s for writing", dest);
- return false;
- }
- }
-
- /* Opening the input file is easier. */
- if ((in = fopen(src, "r")) == NULL) {
- syswarn("cannot open %s for reading", src);
- fclose(out);
- unlink(dest);
- return false;
- }
-
- /* Write the data. */
- while ((i = fread(buff, 1, sizeof buff, in)) != 0)
- if (fwrite(buff, 1, i, out) != i) {
- syswarn("cannot write to %s", dest);
- fclose(in);
- fclose(out);
- unlink(dest);
- return false;
- }
- fclose(in);
-
- /* Flush and close the output. */
- if (ferror(out) || fflush(out) == EOF) {
- syswarn("cannot flush %s", dest);
- unlink(dest);
- fclose(out);
- return false;
- }
- if (fclose(out) == EOF) {
- syswarn("cannot close %s", dest);
- unlink(dest);
- return false;
- }
-
- return true;
-}
-
-
-/*
-** Copy an article from memory into a file.
-*/
-static bool
-CopyArt(ARTHANDLE *art, char *dest, bool Concat)
-{
- FILE *out;
- const char *p;
- char *q, *article;
- size_t i;
- const char *mode = "w";
-
- if (Concat) mode = "a";
-
- /* Open the output file. */
- if ((out = fopen(dest, mode)) == NULL) {
- /* Failed; make any missing directories and try again. */
- if ((p = strrchr(dest, '/')) != NULL) {
- if (!MakeArchiveDirectory(dest)) {
- syswarn("cannot mkdir for %s", dest);
- return false;
- }
- out = fopen(dest, mode);
- }
- if (p == NULL || out == NULL) {
- syswarn("cannot open %s for writing", dest);
- return false;
- }
- }
-
- /* Copy the data. */
- article = xmalloc(art->len);
- for (i=0, q=article, p=art->data; p<art->data+art->len;) {
- if (&p[1] < art->data + art->len && p[0] == '\r' && p[1] == '\n') {
- p += 2;
- *q++ = '\n';
- i++;
- if (&p[1] < art->data + art->len && p[0] == '.' && p[1] == '.') {
- p += 2;
- *q++ = '.';
- i++;
- }
- if (&p[2] < art->data + art->len && p[0] == '.' && p[1] == '\r' && p[2] == '\n') {
- break;
- }
- } else {
- *q++ = *p++;
- i++;
- }
- }
- *q++ = '\0';
-
- /* Write the data. */
- if (Concat) {
- /* Write a separator... */
- fprintf(out, "-----------\n");
- }
- if (fwrite(article, i, 1, out) != 1) {
- syswarn("cannot write to %s", dest);
- fclose(out);
- if (!Concat) unlink(dest);
- free(article);
- return false;
- }
- free(article);
-
- /* Flush and close the output. */
- if (ferror(out) || fflush(out) == EOF) {
- syswarn("cannot flush %s", dest);
- if (!Concat) unlink(dest);
- fclose(out);
- return false;
- }
- if (fclose(out) == EOF) {
- syswarn("cannot close %s", dest);
- if (!Concat) unlink(dest);
- return false;
- }
-
- return true;
-}
-
-
-/*
-** Write an index entry. Ignore I/O errors; our caller checks for them.
-*/
-static void
-WriteArtIndex(ARTHANDLE *art, char *ShortName)
-{
- const char *p;
- int i;
- char Subject[BUFSIZ];
- char MessageID[BUFSIZ];
-
- Subject[0] = '\0'; /* default to null string */
- p = wire_findheader(art->data, art->len, "Subject");
- if (p != NULL) {
- for (i=0; *p != '\r' && *p != '\n' && *p != '\0'; i++) {
- Subject[i] = *p++;
- }
- Subject[i] = '\0';
- }
-
- MessageID[0] = '\0'; /* default to null string */
- p = wire_findheader(art->data, art->len, "Message-ID");
- if (p != NULL) {
- for (i=0; *p != '\r' && *p != '\n' && *p != '\0'; i++) {
- MessageID[i] = *p++;
- }
- MessageID[i] = '\0';
- }
-
- printf("%s %s %s\n",
- ShortName,
- MessageID[0] ? MessageID : "<none>",
- Subject[0] ? Subject : "<none>");
-}
-
-
-/*
-** Crack an Xref line apart into separate strings, each of the form "ng:artnum".
-** Return in "lenp" the number of newsgroups found.
-**
-** This routine blatantly stolen from tradspool.c
-*/
-static char **
-CrackXref(const char *xref, unsigned int *lenp) {
- char *p;
- char **xrefs;
- char *q;
- unsigned int len, xrefsize;
-
- len = 0;
- xrefsize = 5;
- xrefs = xmalloc(xrefsize * sizeof(char *));
-
- /* skip pathhost */
- if ((p = strchr(xref, ' ')) == NULL) {
- warn("cannot find pathhost in Xref header");
- return NULL;
- }
- /* skip next spaces */
- for (p++; *p == ' ' ; p++) ;
- while (true) {
- /* check for EOL */
- /* shouldn't ever hit null w/o hitting a \r\n first, but best to be paranoid */
- if (*p == '\n' || *p == '\r' || *p == 0) {
- /* hit EOL, return. */
- *lenp = len;
- return xrefs;
- }
- /* skip to next space or EOL */
- for (q=p; *q && *q != ' ' && *q != '\n' && *q != '\r' ; ++q) ;
-
- xrefs[len] = xstrndup(p, q - p);
-
- if (++len == xrefsize) {
- /* grow xrefs if needed. */
- xrefsize *= 2;
- xrefs = xrealloc(xrefs, xrefsize * sizeof(char *));
- }
-
- p = q;
- /* skip spaces */
- for ( ; *p == ' ' ; p++) ;
- }
-}
-
-
-/*
-** Crack an groups pattern parameter apart into separate strings
-** Return in "lenp" the number of patterns found.
-*/
-static char **
-CrackGroups(char *group, unsigned int *lenp) {
- char *p;
- char **groups;
- char *q;
- unsigned int len, grpsize;
-
- len = 0;
- grpsize = 5;
- groups = xmalloc(grpsize * sizeof(char *));
-
- /* skip leading spaces */
- for (p=group; *p == ' ' ; p++) ;
- while (true) {
- /* check for EOL */
- /* shouldn't ever hit null w/o hitting a \r\n first, but best to be paranoid */
- if (*p == '\n' || *p == '\r' || *p == 0) {
- /* hit EOL, return. */
- *lenp = len;
- return groups;
- }
- /* skip to next comma, space, or EOL */
- for (q=p; *q && *q != ',' && *q != ' ' && *q != '\n' && *q != '\r' ; ++q) ;
-
- groups[len] = xstrndup(p, q - p);
-
- if (++len == grpsize) {
- /* grow groups if needed. */
- grpsize *= 2;
- groups = xrealloc(groups, grpsize * sizeof(char *));
- }
-
- p = q;
- /* skip commas and spaces */
- for ( ; *p == ' ' || *p == ',' ; p++) ;
- }
-}
-
-
-int
-main(int ac, char *av[])
-{
- char *Name;
- char *p;
- FILE *F;
- int i;
- bool Flat;
- bool Redirect;
- bool Concat;
- char *Index;
- char buff[BUFSIZ];
- char *spool;
- char dest[BUFSIZ];
- char **groups, *q, *ng;
- char **xrefs;
- const char *xrefhdr;
- ARTHANDLE *art;
- TOKEN token;
- unsigned int numgroups, numxrefs;
- int j;
- char *base = NULL;
- bool doit;
-
- /* First thing, set up our identity. */
- message_program_name = "archive";
-
- /* Set defaults. */
- if (!innconf_read(NULL))
- exit(1);
- Concat = false;
- Flat = false;
- Index = NULL;
- Redirect = true;
- umask(NEWSUMASK);
- ERRLOG = concatpath(innconf->pathlog, _PATH_ERRLOG);
- Archive = innconf->patharchive;
- groups = NULL;
- numgroups = 0;
-
- /* Parse JCL. */
- while ((i = getopt(ac, av, "a:cfi:p:r")) != EOF)
- switch (i) {
- default:
- die("usage error");
- break;
- case 'a':
- Archive = optarg;
- break;
- case 'c':
- Flat = true;
- Concat = true;
- break;
- case 'f':
- Flat = true;
- break;
- case 'i':
- Index = optarg;
- break;
- case 'p':
- groups = CrackGroups(optarg, &numgroups);
- break;
- case 'r':
- Redirect = false;
- break;
- }
-
- /* Parse arguments -- at most one, the batchfile. */
- ac -= optind;
- av += optind;
- if (ac > 2)
- die("usage error");
-
- /* Do file redirections. */
- if (Redirect)
- freopen(ERRLOG, "a", stderr);
- if (ac == 1 && freopen(av[0], "r", stdin) == NULL)
- sysdie("cannot open %s for input", av[0]);
- if (Index && freopen(Index, "a", stdout) == NULL)
- sysdie("cannot open %s for output", Index);
-
- /* Go to where the action is. */
- if (chdir(innconf->patharticles) < 0)
- sysdie("cannot chdir to %s", innconf->patharticles);
-
- /* Set up the destination. */
- strcpy(dest, Archive);
- Name = dest + strlen(dest);
- *Name++ = '/';
-
- if (!SMinit())
- die("cannot initialize storage manager: %s", SMerrorstr);
-
- /* Read input. */
- while (fgets(buff, sizeof buff, stdin) != NULL) {
- if ((p = strchr(buff, '\n')) == NULL) {
- warn("skipping %.40s: too long", buff);
- continue;
- }
- *p = '\0';
- if (buff[0] == '\0' || buff[0] == '#')
- continue;
-
- /* Check to see if this is a token... */
- if (IsToken(buff)) {
- /* Get a copy of the article. */
- token = TextToToken(buff);
- if ((art = SMretrieve(token, RETR_ALL)) == NULL) {
- warn("cannot retrieve %s", buff);
- continue;
- }
-
- /* Determine groups from the Xref header */
- xrefhdr = wire_findheader(art->data, art->len, "Xref");
- if (xrefhdr == NULL) {
- warn("cannot find Xref header");
- SMfreearticle(art);
- continue;
- }
-
- if ((xrefs = CrackXref(xrefhdr, &numxrefs)) == NULL || numxrefs == 0) {
- warn("bogus Xref header");
- SMfreearticle(art);
- continue;
- }
-
- /* Process each newsgroup... */
- if (base) {
- free(base);
- base = NULL;
- }
- for (i=0; (unsigned)i<numxrefs; i++) {
- /* Check for group limits... -p flag */
- if ((p=strchr(xrefs[i], ':')) == NULL) {
- warn("bogus Xref entry %s", xrefs[i]);
- continue; /* Skip to next xref */
- }
- if (numgroups > 0) {
- *p = '\0';
- ng = xrefs[i];
- doit = false;
- for (j=0; (unsigned)j<numgroups && !doit; j++) {
- if (uwildmat(ng, groups[j]) != 0) doit=true;
- }
- }
- else {
- doit = true;
- }
- *p = '/';
- if (doit) {
- p = Name;
- q = xrefs[i];
- while(*q) {
- *p++ = *q++;
- }
- *p='\0';
-
- if (!Flat) {
- for (p=Name; *p; p++) {
- if (*p == '.') {
- *p = '/';
- }
- }
- }
-
- if (Concat) {
- p = strrchr(Name, '/');
- q = DateString();
- p++;
- while (*q) {
- *p++ = *q++;
- }
- *p = '\0';
- }
-
- if (base && !Concat) {
- /* Try to link the file into the archive. */
- if (link(base, dest) < 0) {
-
- /* Make the archive directory. */
- if (!MakeArchiveDirectory(dest)) {
- syswarn("cannot mkdir for %s", dest);
- continue;
- }
-
- /* Try to link again; if that fails, make a copy. */
- if (link(base, dest) < 0) {
-#if defined(HAVE_SYMLINK)
- if (symlink(base, dest) < 0)
- syswarn("cannot symlink %s to %s",
- dest, base);
- else
-#endif /* defined(HAVE_SYMLINK) */
- if (!Copy(base, dest))
- continue;
- continue;
- }
- }
- } else {
- if (!CopyArt(art, dest, Concat))
- syswarn("copying %s to %s failed", buff, dest);
- base = xstrdup(dest);
- }
-
- /* Write index. */
- if (Index) {
- WriteArtIndex(art, Name);
- if (ferror(stdout) || fflush(stdout) == EOF)
- syswarn("cannot write index for %s", Name);
- }
- }
- }
-
- /* Free up the article storage space */
- SMfreearticle(art);
- art = NULL;
- /* Free up the xrefs storage space */
- for ( i=0; (unsigned)i<numxrefs; i++) free(xrefs[i]);
- free(xrefs);
- numxrefs = 0;
- xrefs = NULL;
- } else {
- warn("%s is not a token", buff);
- continue;
- }
- }
-
- /* close down the storage manager api */
- SMshutdown();
-
- /* If we read all our input, try to remove the file, and we're done. */
- if (feof(stdin)) {
- fclose(stdin);
- if (av[0])
- unlink(av[0]);
- exit(0);
- }
-
- /* Make an appropriate spool file. */
- p = av[0];
- if (p == NULL)
- spool = concatpath(innconf->pathoutgoing, "archive");
- else if (*p == '/')
- spool = concat(p, ".bch", (char *) 0);
- else
- spool = concat(innconf->pathoutgoing, "/", p, ".bch", (char *) 0);
- if ((F = xfopena(spool)) == NULL)
- sysdie("cannot spool to %s", spool);
-
- /* Write the rest of stdin to the spool file. */
- i = 0;
- if (fprintf(F, "%s\n", buff) == EOF) {
- syswarn("cannot start spool");
- i = 1;
- }
- while (fgets(buff, sizeof buff, stdin) != NULL)
- if (fputs(buff, F) == EOF) {
- syswarn("cannot write to spool");
- i = 1;
- break;
- }
- if (fclose(F) == EOF) {
- syswarn("cannot close spool");
- i = 1;
- }
-
- /* If we had a named input file, try to rename the spool. */
- if (p != NULL && rename(spool, av[0]) < 0) {
- syswarn("cannot rename spool");
- i = 1;
- }
-
- exit(i);
- /* NOTREACHED */
-}