v19i101: Usenet sources archiver, Part04/04

Rich Salz rsalz at uunet.uu.net
Sat Jul 1 00:47:13 AEST 1989


Submitted-by: Kent Landfield <ssbell!kent>
Posting-number: Volume 19, Issue 101
Archive-name: rkive/part04

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 4)."
# Contents:  news_arc.c rkive.c
# Wrapped by kent at ssbell on Thu Jun  1 16:19:18 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'news_arc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'news_arc.c'\"
else
echo shar: Extracting \"'news_arc.c'\" \(20172 characters\)
sed "s/^X//" >'news_arc.c' <<'END_OF_FILE'
X/*
X**
X** This software is Copyright (c) 1989 by Kent Landfield.
X**
X** Permission is hereby granted to copy, distribute or otherwise 
X** use any part of this package as long as you do not try to make 
X** money from it or pretend that you wrote it.  This copyright 
X** notice must be maintained in any copy made.
X**
X**
X**  History:
X**	Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X**                                                               
X*/
X#ifndef lint
Xstatic char SID[] = "@(#)news_arc.c	1.1 6/1/89";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#include <stdio.h>
X#include <ctype.h>
X#include "article.h"
X#include "cfg.h"
X
X/*
X** Defines for the type of "problems"
X** encountered in saving the articles.
X*/
X#define DUP_PROB    0
X#define NAME_PROB   1
X#define VOL_PROB    2
X#define TYPE_PROB   3
X
Xint test = 0;
Xint problem_article;
X
Xextern struct group_archive *newsgrp;
Xextern int overwrite;
X
Xchar *strchr();
Xchar *strcpy();
Xchar *strcat();
Xchar *do_problem();
Xchar *basename();
Xchar *suffix();
XFILE *efopen();
Xvoid exit();
X
Xget_header(filename)
X    char *filename;
X{
X    char *dp;
X    int header_ok = 0;
X    FILE *gfp;
X
X    init_article();
X
X    gfp = efopen(filename,"r");
X
X    (void) strcpy(article.newsarticle, filename);
X
X    while (fgets(s,sizeof s,gfp) != NULL) {
X        if (debug)
X            (void) fprintf(logfp, "BUF = [%s]",s);
X
X        if (!isalpha(*s) || (strchr(s,':') == NULL)) {
X           header_ok++;
X           if (header_ok == 2) 
X               break;
X           continue;
X        }
X
X        dp = s;
X        while (*++dp)
X           if (*dp == '\n')
X               *dp = '\0';
X
X        store_line();
X    }
X    (void) fclose(gfp);
X
X    if (debug)
X        dump_article();
X}
X
X/*
X** check_archive_name
X**
X** Assure the path specified is within the base directory
X** specified by the archive administrator by assuring that
X** a prankster could not have an article archived at a
X**     basedir/../../../etc/passwd
X** location.
X**
X** If an absoulte path is specified in the Archive-name, it
X** is of no concern since a "checked" base directory and
X** volume directory are prefixed.
X*/
X
Xcheck_archive_name(argstr)
X    char *argstr;
X {
X    char *substr();
X    register char *rp;
X    register char *dp;
X
X    /* 
X    ** check to assure that the path specified
X    ** does not contain the '..' sequence.
X    */
X
X    while ((rp = substr(argstr, "..")) != NULL) {
X       dp = rp+2;
X       while(*dp)
X           *rp++ = *dp++;
X       *rp = '\0';
X    }
X
X    /* I know this is not necessary but what the heck.. */
X
X    while ((rp = substr(argstr, "//")) != NULL) {
X       dp = rp+2;
X       ++rp;
X       while(*dp)
X           *rp++ = *dp++;
X       *rp = '\0';
X    }
X
X    /* 
X    ** strip the string of trailing '/'s
X    */
X
X    dp = argstr+(strlen(argstr)-1);
X    while(*dp == '/' && dp > argstr)
X        *dp = '\0';
X}
X
X/*
X** IF YOU USE A COMPRESSION ROUTINE OTHER THAN COMPRESS
X** OR PACK, ADD YOUR COMPRESSION SPECIFIC INFORMATION
X** TO THE cprgs COMPRESS_TABLE ......
X*/
X
Xstruct compress_tab {
X    char     *com_name;
X    char     *com_suffix;
X};
X
Xstruct compress_tab cprgs[] = {
X{    "compress",        ".Z"    },
X{    "pack",            ".z"    },
X{    NULL,            0    },
X};
X
Xchar *suffix(compression)
X    char *compression;
X {
X    struct compress_tab *ct;
X
X    ct = &cprgs[0];
X    while ((ct->com_name) != NULL) {
X        if (strcmp(compression, ct->com_name) == 0) 
X            return(ct->com_suffix);
X        ct++;
X    }
X    return("");
X}
X
Xint remove_suffix(path_str)
Xchar *path_str;
X {
X    char *ss;
X    struct compress_tab *ct;
X
X    /*
X    ** need to compare the filename passed in to 
X    ** the compression suffix table in order to
X    ** determine if the file has a recognized,
X    ** compression suffix attached.
X    */
X    
X    ss = path_str + (strlen(path_str) -2);
X
X    ct = &cprgs[0];
X    while ((ct->com_name) != NULL) {
X        if (strcmp(ss, ct->com_suffix) == 0) {
X            *ss = '\0';
X            return(TRUE);
X        }
X        ct++;
X    }
X    return(FALSE);
X}
X
Xchar *expand_name(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X    char *comp_cmd;
X    static char compress_path[MAXNAMLEN];
X
X    (void) strcpy(compress_path, filename);
X
X    /*
X    ** Check to see if a group specific compress was specified.      
X    ** If so, then attach the suffix and return.                    
X    ** Else check to see if a global compress was specified. If so,
X    ** then attach the suffix and return.                         
X    ** If both are NULL, return filename.                        
X    */
X
X    if (*(ng->compress)) {
X        comp_cmd = basename(ng->compress);
X        (void) strcat(compress_path, suffix(comp_cmd));
X    }
X    else if (*compress) {
X        comp_cmd = basename(compress);
X        (void) strcat(compress_path, suffix(comp_cmd));
X    }
X    return(compress_path);
X}
X
X#ifdef REDUCE_HEADERS
X
Xstruct hdrstokeep {
X	char 	*ststr;
X	int	stbytes;
X};
X
Xstruct hdrstokeep hdrs[] = {
X{	"From:",		(sizeof "From:")	},
X{	"Newsgroups:",		(sizeof "Newsgroups:")	},
X{	"Subject:",		(sizeof "Subject:")	},
X{	"Message-ID:",		(sizeof "Message-ID:")	},
X{	"Date:",		(sizeof "Date:")	},
X{	NULL,			0			},
X};
X
Xint keep_line(argstr)
X    char *argstr;
X {
X    struct hdrstokeep *pt;
X
X    pt = &hdrs[0];
X    while ((pt->ststr) != NULL) {
X        if (strncmp(argstr, pt->ststr, (pt->stbytes-1)) == 0) 
X            return(TRUE);
X        pt++;
X    }
X    return(FALSE);
X}
X
Xint copy(source, target)
X    char *source, *target;
X{
X    char *strchr();
X    FILE *from, *to;
X    char fbuf[BUFSIZ];
X    int inheader;
X
X    inheader = TRUE;  
X
X    if (verbose) {
X        (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
X        if (test) 
X            return(0);
X    }
X    if ((from = fopen(source, "r")) == NULL) {
X        (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
X        return (-1);
X    }
X    if ((to = fopen(target, "w")) == NULL) {
X        (void) fclose(from);
X        (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
X        return (-1);
X    }
X    /*
X    ** Read the source and do not print any headers 
X    ** unless specified in the "keep" headers table.
X    */
X
X    while (fgets(fbuf, BUFSIZ, from) != NULL) {
X        if (inheader) {
X            /* 
X            ** Have I encountered a line without a line type ? 
X            */
X            if (!isalpha(*fbuf) || (strchr(fbuf,':') == NULL)) 
X                inheader = FALSE;
X
X            else {
X                /*
X                ** Determine the type of the header line and 
X                ** decide if this is a line to be kept or pitched.
X                */
X                if (!keep_line(fbuf))
X                    continue;
X            }
X        }
X        if (fputs(fbuf, to) == EOF) {
X            (void) unlink(target);
X            (void) fclose(from);
X            (void) fclose(to);
X            (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
X            return (-1);
X        }
X    }
X    (void) fclose(from);
X    (void) fclose(to);
X    return(0);
X}
X
X#else
X
Xcopy(source, target)
X    char *source, *target;
X{
X    int from, to, ct;
X    char fbuf[BUFSIZ];
X
X    if (verbose) {
X        (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
X        if (test) 
X            return(0);
X    }
X    if ((from = open(source, 0)) < 0) {
X        (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
X        return (-1);
X    }
X    if ((to = creat (target, 0644)) < 0) {
X        (void) close(from);
X        (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
X        return (-1);
X    }
X    while ((ct = read(from, fbuf, BUFSIZ)) != 0) {
X        if(ct < 0 || write(to, fbuf, (unsigned) ct) != ct) {
X            (void) unlink(target);
X            (void) close(from);
X            (void) close(to);
X            (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
X            return (-1);
X        }
X    }
X    (void) close(from);
X    (void) close(to);
X    return(0);
X}
X
X#endif /* REDUCE_HEADERS */
X
X/*
X** mkparents:
X**
X** If any parent directories in 
X** fullname don't exist, create them.
X*/
X
Xint mkparents(fullname)
Xchar *fullname;
X{
X    char *strrchr();
X
X    register char *p;
X    char b[MAXNAMLEN];
X    int rc;
X
X    (void) strcpy(b, fullname);
X
X    if ((p = strrchr(b, '/')) != NULL) 
X            *p = '\0';
X    else                  /* no directories in fullname */
X       return(0);
X
X    if (*b == '\0')           /* are we at the root ? */
X        return(0);
X
X    if (access(b, 0) == 0)
X        return(0);
X
X    (void) mkparents(b);
X
X    if ((rc = makedir(b, 0755, newsgrp->owner, newsgrp->group)) != 0) 
X        error("makedir failed attempting to make", b);
X
X    return(rc);
X}
X
X
Xchar *save_article (filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X    char *final_path;
X    static char path[MAXNAMLEN];
X    struct stat sb;
X
X    problem_article = FALSE;
X    path[0] = '\0';
X
X    /*
X    ** Read the news article file to extract the
X    ** header information and fill appropriate
X    ** data structures.
X    */
X    get_header(filename);
X
X    /*
X    ** Build the path string for the final resting spot
X    ** for the new archive member.
X    */
X    switch(ng->type) {
X    case ARCHIVE_NAME:
X            /*
X            ** The header's archive_name contains the filename in
X            ** an "elm/part06" format.
X            */
X
X            if ((article.volume == -1) || (!header.archive_name[0])) 
X                return(do_problem(NAME_PROB, ng,filename,path));
X    
X            /*
X            ** Assure the address is relative and
X            ** that some prankster can not do nasty
X            ** things to your system files by having
X            ** an Archive-name line like:
X            **    ../../../../../etc/passwd
X            */
X
X            check_archive_name(header.archive_name);
X
X            /* 
X            ** Check to see if the article is a patch. If so,
X            ** check to see if the administrator wishes to
X            ** store the patch with the initially posted
X            ** articles. This really relys on the archive name
X            ** being correct.
X            */
X            
X            if (article.rectype == PATCH && ng->patch_type == PACKAGE)
X                /*
X                ** Store the patch in the volume specified with the
X                ** Archive-name: specified file name.
X                */
X                (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
X		            article.patch_volume, header.archive_name);
X
X            else 
X                (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
X		        article.volume, header.archive_name);
X            break;
X    case VOLUME_ISSUE:
X            /*
X            ** The article filename contains the filename in
X            ** a "v01i001" format.
X            */
X            if ((article.volume == -1) || (!article.filename[0])) 
X                return(do_problem(VOL_PROB,ng,filename,path));
X
X            (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
X		        article.volume, article.filename);
X            break;
X    case ARTICLE_NUMBER:
X            /*
X            ** Store in same filename - thanks news...
X            */
X            (void) sprintf(path,"%s/%s", ng->location, filename);
X            break;
X    default:
X            /*
X            ** We have got problems....
X            */
X            return(do_problem(TYPE_PROB,ng,filename,path));
X    }
X
X    /*
X    ** Check if the file is a patch. If so, log
X    ** the patch information into the patch log
X    ** in a *non-configurable* format so that
X    ** applications can be written to access the
X    ** file's "known format".
X    */
X
X    if (article.rectype == PATCH)
X        write_patch_log(ng,path);
X
X#ifdef ADD_REPOST_SUFFIX
X    if (article.repost == TRUE)
X        /*
X        ** The ADD_REPOST_SUFFIX code adds the REPOST_SUFFIX
X	** to any file that has been indicated as a repost
X	** by the moderator. This should not be used with 
X	** Archive-Name archiving on a filesystem with 14
X	** character filename limits or filename truncation
X	** can occur. You have been warned... :-(
X	**
X 	** After adding the REPOST_SUFFIX, the filename is
X	** treated as any other file with the duplication
X	** checks and all...
X	*/
X	(void) strcat(path,REPOST_SUFFIX);
X#endif /* ADD_REPOST_SUFFIX */
X
X    /* 
X    ** expand the path to the file to include the 
X    ** compression suffix if necessary.
X    */
X
X    final_path = expand_name(path, ng);
X
X    /*
X    ** Make any necessary directories 
X    ** along the way. 
X    */
X    (void) mkparents(path);
X
X    /*
X    ** Check to assure that there is not already 
X    ** a file with the same file name. If so
X    ** copy (or archive) the file to the problems 
X    ** directory. 
X    **
X    ** This works for REPOSTS as well.
X    ** If the REPOST arrives and there is
X    ** no file currently at the archive location, the
X    ** REPOST is installed in the correct archive 
X    ** location.
X    ** If there is a file that exists when a REPOST
X    ** arrives, the REPOST is then handled in do_problem().
X    */
X
X    if ((stat(final_path ,&sb) == 0) && !overwrite)  /* duplicate found */
X        return(do_problem(DUP_PROB,ng, filename, final_path));
X
X    if (copy(filename,path) != 0) {  
X        (void) fprintf(errfp,"copy failed for %s to %s\n",filename,path);
X        return(NULL);
X    }  
X    /* 
X    ** Write the filename to the .archived file in the newsgroup's
X    ** BASEDIR directory since we do not want it rearchived tomorrow.
X    */
X    write_archived(filename, path);
X
X    /*
X    ** Return the path to the archived file.
X    */
X    return(path);
X}
X
X
X
Xchar *do_problem(type_of_problem, ng, file, path)
Xint type_of_problem;
Xstruct group_archive *ng;
Xchar *file;
Xchar *path;
X{
X
X#ifdef MV_ORIGINAL
X    char crnt_path[MAXNAMLEN];
X#endif /*MV_ORIGINAL */
X
X    char pmess[BUFSIZ];
X
X    problem_article = TRUE;
X
X    /* ALERT THE ADMINISTRATOR THAT A PROBLEM WAS ENCOUNTERED 
X    **
X    ** A problem has been encountered. It could be that there is an
X    ** format mismatch or there is already a file with the same 
X    ** issue/archive/msg-id name.
X    ** Copy the problem file to the problems directory. 
X    ** Alert the Administrator that a problem was received.
X    */
X
X    (void) sprintf(pmess,"PROBLEM: Article %s in %s ",file,ng->ng_name);
X
X    switch( type_of_problem ) {
X       case NAME_PROB:
X          (void) strcat(pmess,"does not support Archive-Name Archiving\n.");
X          break;
X       case VOL_PROB:
X          (void) strcat(pmess,"does not support Volume-Issue Archiving\n.");
X          break;
X       case TYPE_PROB:
X          (void) strcat(pmess,"has an invalid archive TYPE specified\n.");
X          break;
X       case DUP_PROB:
X          if (article.repost != TRUE) 
X              (void) strcat(pmess,"is a Duplicate article.\n");
X          else 
X             (void) strcat(pmess,"is a Reposted article.\n");
X          (void) sprintf(pmess,"%s\tExisting Archived path - %s", pmess,path);
X          break;
X    }
X
X    /* print the message out to the screen, crontab output, etc */
X
X    (void) fprintf(errfp,"%s\n",pmess);
X
X    /* log the initial detection message. */
X
X    record_problem(pmess, file, ng);
X
X    /* Handling Repostings.
X    **
X    ** MV_ORIGINAL
X    **     The original article is placed into a "original" directory in 
X    **     the problems directory (if duplicated). The inbound reposted
X    **     article is placed into the archive in the correct position.
X    **
X    ** ADD_REPOST_SUFFIX 
X    **     If ADD_REPOST_SUFFIX is defined, all reposts will have the 
X    **     string specified in REPOST_SUFFIX appended to the archive
X    **     filename so that a repost of elm/part07 would appear in
X    **     the archive as elm/part07-repost prior to any compression.
X    **     The addition of the suffix was done in save_article().
X    **     Handle this as the true duplicated article that it is.
X    **
X    ** No Reposting Defines specified:
X    **    The inbound article would be placed into the archive in the 
X    **    correct position only if the initial article is not in the archive.
X    **    Otherwise the reposted article is placed in the problems directory 
X    **    as a normal duplicate article as it is now.
X    */
X
X#ifdef MV_ORIGINAL
X    if (article.repost == TRUE) {
X        /*
X        ** save the duplicated path 
X        ** Caution: may have compression suffix attached
X        */
X        (void) strcpy(crnt_path, path);
X
X        /* create the storage path for original copy */
X        /* no slash needed between Originals and crnt_path below.. */
X
X        (void) sprintf(path,"%s/%s%s",problems_dir,"Originals",crnt_path);
X
X        /* Display and record the actions */ 
X        (void) sprintf(pmess,"\tMoving %s (original)\n\tto %s",crnt_path,path);
X        (void) fprintf(errfp,"%s\n",pmess);
X        record_problem(pmess, file, ng);
X
X        /* Make any necessary directories along the way. */
X        (void) mkparents(path);
X
X        /* copy the original out of the way */
X        if (copy(crnt_path,path) != 0) {
X            (void) fprintf(errfp,"copy failed for %s to %s\n", crnt_path, path);
X            return(NULL);
X        }
X
X        set_ownership(path, ng);
X
X        /* restore the destination path for inbound article */
X        (void) strcpy(path,crnt_path);
X
X        /* remove the existing file */
X        (void) unlink(path);
X        /*
X        ** Must assure that "path" does not have a .Z type
X        ** of suffix used in compression. If it does, it must 
X	** be removed before continuing. This is cheating and
X        ** will probably break but what the hell.
X        */
X        (void) remove_suffix(path);
X    }
X    else 
X
X#endif /* MV_ORIGINAL */
X
X    /*
X    ** Build the path string for the location of the article in 
X    ** the problems directory. Place the file in the appropriate 
X    ** directory in Article-Number format. In this manner, multiple 
X    ** problems will be stored as separate files. 
X    */
X
X        (void) sprintf(path,"%s/%s/%s",problems_dir,ng->ng_name,file);
X
X    /* Display and record the actions */ 
X    (void) sprintf(pmess,"\tStoring Article %s at %s\n", file, path);
X    (void) fprintf(errfp,"%s\n",pmess);
X    record_problem(pmess, file, ng);
X
X    /* Make any necessary directories along the way. */
X    (void) mkparents(path);
X
X    if (copy(file,path) != 0) {  
X        (void) fprintf(errfp,"copy failed for %s to %s\n", file, path);
X        return(NULL);
X    }  
X
X    /* 
X    ** Write the filename to the .archived file in the newsgroup's
X    ** BASEDIR directory since we do not want it rearchived tomorrow.
X    */
X    write_archived(file, path);
X
X    /*
X    ** Return the path to the stored problem file.
X    */
X    return(path);
X}
X
Xwrite_patch_log(ng, path)
X	struct group_archive *ng;
X	char *path;
X{
X        char *sp;
X	FILE *plfp;
X	struct stat sb;
X        int hn;
X
X	/* 
X	** The .patchlog file is used to record the
X	** information specific to patches that come
X	** through the newsgroup.
X	**
X	** The format of the .patchlog file is:
X	**
X	** path-to-patch  initial-volume  initial-issue  volume issue 
X	** bb/patch01          22              105         23    77
X	** v47i022             22              105         23    77
X	*/
X
X        /*
X        ** If this is the first time that an entry is written to the
X        ** patch log, add a header on top of the file for informational
X        ** purposes only...
X        */
X	if ((stat(ng->patchlog ,&sb) != 0)) {
X	    plfp = efopen(ng->patchlog,"a+");
X
X	    (void) fprintf(plfp,"#\n#\tPatch log for %s\n#\n",
X                    ng->ng_name);
X
X	    (void) fprintf(plfp,"# %-30s%-11s%-13s%-6s%10s\n", 
X                    "Path To", "Initial", "Initial",
X                    "Current", "Current");
X
X	    (void) fprintf(plfp,"# %-30s%-11s%6s%13s%10s\n#\n", 
X                    "Patchfile", "Volume", "Issue", "Volume", "Issue");
X	    (void) fclose(plfp);
X        }
X
X        /* 
X        ** Get rid of the base directory.
X        */
X        sp = path + (strlen(ng->location)+1);
X
X	plfp = efopen(ng->patchlog,"a+");
X	(void) fprintf(plfp,"%-24s%12d%12d%12d%11d\n", sp,
X			article.patch_volume, article.patch_issue,
X			article.volume, article.issue);
X	(void) fclose(plfp);
X}
END_OF_FILE
if test 20172 -ne `wc -c <'news_arc.c'`; then
    echo shar: \"'news_arc.c'\" unpacked with wrong size!
fi
# end of 'news_arc.c'
fi
if test -f 'rkive.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rkive.c'\"
else
echo shar: Extracting \"'rkive.c'\" \(17099 characters\)
sed "s/^X//" >'rkive.c' <<'END_OF_FILE'
X/*
X**                                                                
X**  Subsystem:   USENET Sources Archiver             
X**  File Name:   rkive.c               
X**                                                        
X**  usage: rkive [ -dgstuvV ] [ -f config_file ] [-n newsgroup ]
X**
X**
X** This software is Copyright (c) 1989 by Kent Landfield.
X**
X** Permission is hereby granted to copy, distribute or otherwise 
X** use any part of this package as long as you do not try to make 
X** money from it or pretend that you wrote it.  This copyright 
X** notice must be maintained in any copy made.
X**
X** Use of this software constitutes acceptance for use in an AS IS 
X** condition. There are NO warranties with regard to this software.  
X** In no event shall the author be liable for any damages whatsoever 
X** arising out of or in connection with the use or performance of this 
X** software.  Any use of this software is at the user's own risk.
X**
X**  If you make modifications to this software that you feel 
X**  increases it usefulness for the rest of the community, please 
X**  email the changes, enhancements, bug fixes as well as any and 
X**  all ideas to me. This software is going to be maintained and 
X**  enhanced as deemed necessary by the community.
X**
X**              Kent Landfield
X**              uunet!ssbell!kent
X**
X**  History:
X**    Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X**                                                               
X*/
Xchar sccsid[] = "@(#)rkive.c	1.1 6/1/89";
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#include <stdio.h>
X#include "article.h"
X#include "cfg.h"
X
X/* 
X** This is necessary since the builtin makedir call uses
X** mknod which is a superuser only call for directories.
X*/
X#if (!HAVE_MKDIR && !USE_SYSMKDIR)
X#define ROOT_ONLY
X#endif
X
X#define UFMT "usage: %s [ -dgstuvV ] [ -f config_file ] [ -n newsgroup ]\n"
X
Xint overwrite;
Xint status_only;
Xstruct stat sbuf;
Xstruct group_archive *newsgrp;
X
Xchar tmp_mailfile[] = "/tmp/rkive.mail";
Xchar global_mailfile[] = "/tmp/gbl.mail";
X
Xchar *save_article();
Xchar *compress_file();
Xchar *do_compress();
Xchar *basename();
Xchar *suffix();
Xvoid archive();
X
Xchar *strcpy();
Xchar *strcat();
Xchar *strchr();
XFILE *efopen();
Xvoid exit();
X
Xextern int debug;
Xextern int verbose;
Xextern int test;
Xextern int problem_article;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X   int c;
X   extern char *optarg;
X   char *nwsg = NULL;
X
X   progname = argv[0];
X   errfp = stderr;
X   logfp = stdout;
X
X   status_only = debug = verbose = 0;
X   test = overwrite = fill_in_defaults = 0;
X
X   /*
X   ** Setup the default config file to be used
X   ** unless the user specifies otherwise.
X   */
X   config_file = LOCATION;
X
X   if (argc > 1) {
X      while ((c = getopt(argc, argv, "dgstuvVn:f:")) != EOF) {
X         switch (c) {
X             case 'f':
X                 config_file = optarg;  
X                 break;  
X             case 'd':
X                 debug++;
X                 verbose++;
X                 break;
X             case 'g':
X                 fill_in_defaults++;
X                 break;
X             case 'n':
X                 nwsg = optarg;
X                 break;
X             case 's':
X                 status_only++;
X                 break;
X             case 't':
X                 test++;
X                 verbose++;
X                 break;
X             case 'u':
X                 overwrite++;
X                 break;
X             case 'v':
X                 verbose++;
X                 break;
X             case 'V':
X                 version();
X             default:
X                 (void) fprintf(errfp, UFMT, progname);
X                 return(1);
X         }
X      }
X   }
X
X   setup_defaults();
X
X   init_article();
X
X   for (c = 0; c <= num; c++)  {
X       newsgrp = &group[c];
X       /*
X       ** Was a newsgroup specified on the command line ?
X       */
X       if (nwsg != NULL) {
X          if (strcmp(nwsg, newsgrp->ng_name) != 0)
X              continue;
X       }
X       archive();
X   }
X
X   if (!status_only) {
X       /*
X       ** Mail notification of the archived members to the 
X       ** list of users specified in the configuration file
X       ** and remove the file containing the archived info.
X       */
X       mail_file(mail, global_mailfile, "Complete Archive Results ");
X       (void) unlink(global_mailfile);
X   }
X   return(0);
X}
X
Xvoid archive()
X{
X    struct dirent *dp;
X    int cct;
X    DIR *dfd;
X    char *rp, *rec;
X    char *dir = ".";
X    char *new_member;
X    char *archived_file;
X    char *get_archived_rec();
X    char newsgroup_directory[MAXNAMLEN];
X    
X#ifdef ROOT_ONLY
X    /*
X    ** check to assure that the user is root if 
X    ** actual archiving is to take place. This is necessary
X    ** if there is no mkdir system call.
X    */
X
X    if (!status_only && (getuid() != 0)) {
X        (void) fprintf(errfp, "%s: Sorry, Must be root to rkive.\n",
X                        progname);
X        exit(1);
X    }
X#endif
X
X    /* Remove any existing temporary mail file */
X
X    (void) unlink(tmp_mailfile);
X    cct = 0;  /* counter for newsgroup message in global mail */
X
X    /*
X    ** Assure that there something specified in the 
X    ** archive location variable...
X    */
X    if (!*newsgrp->location) {
X        (void) fprintf(errfp, "SKIPPING %s: No archive location specified..\n",
X                        newsgrp->ng_name);
X        return;
X    }
X
X    /*
X    ** print out the appropriate 
X    ** header for the newsgroup.
X    */
X
X    if (debug || (verbose && status_only)) {
X        (void) fprintf(logfp,"\n\n");
X        display_group_info(newsgrp);
X        (void) fprintf(logfp,"\n");
X    }
X    else if (status_only)
X        (void) fprintf(logfp, "%s\n",newsgrp->ng_name);
X
X    /* convert newsgroup name into a disk path */
X
X    rp = newsgrp->ng_name;
X
X    /*
X    ** convert all '.' to '/' to generate a path to the
X    ** newsgroup directory relative from the specified SPOOLDIR.
X    */
X
X    while (*rp) {             /* convert all */
X        if (*rp == '.')       /* '.'s to '/' */
X            *rp = '/';        /* to create   */
X        rp++;                 /* the disk    */
X    }                         /* location    */
X
X    (void) sprintf(newsgroup_directory,"%s/%s", spooldir,newsgrp->ng_name);
X
X    if (chdir(newsgroup_directory) != 0) {
X       (void) fprintf(errfp,"Can't change directory to %s, %s not archived\n",
X                     newsgroup_directory, newsgrp->ng_name);
X       return;
X    }
X
X    /*
X    ** Create a path to the .archived file for the newsgroup's archive.
X    ** This file is used to determine if an article has already been
X    ** archived.
X    */
X    (void) sprintf(newsgrp->arc_done,"%s/.archived",newsgrp->location);
X
X    /*
X    ** Create a path to the .patchlog file for the newsgroup's archive.
X    ** This file is used to record patches to posted software so that
X    ** it can easily be determined what the full set of software is.
X    */
X    (void) sprintf(newsgrp->patchlog,"%s/.patchlog",newsgrp->location);
X
X    /*
X    ** locate a file that needs to be archived. This is done by
X    ** a linear search of the directory with a linear search of
X    ** of the contents of the .archived file. If the file is not
X    ** specified in the .archived file, it has not been archived
X    ** before and we can proceed with the archiving.
X    */
X    if ((dfd  = opendir(dir)) == NULL) {
X        (void) fprintf(errfp, "can't open %s\n", newsgroup_directory);
X        return;
X    }
X    while ((dp = readdir(dfd)) != NULL) {
X       if (strcmp(dp->d_name,".") == 0
X           || strcmp(dp->d_name,"..") == 0)
X           continue;
X
X       if (stat(dp->d_name, &sbuf) != 0)  {
X           (void) fprintf(errfp, "can't stat %s/%s\n",
X                          newsgroup_directory, dp->d_name);
X           continue;
X       }
X
X       /* 
X       ** If its not a regular file, we cannot archive it. 
X       */
X
X       else if ((sbuf.st_mode & S_IFMT) != S_IFREG) 
X           continue; 
X        
X       /* 
X       ** If the user has specified that a quick status 
X       ** listing should be produced then hop to it....
X       */
X
X       if (status_only) {
X            if ((rec = get_archived_rec(dp->d_name)) == NULL) 
X                (void) fprintf(logfp,"\t<%s> Awaiting Archiving\n",dp->d_name);
X            else if ((rp = strchr(rec,' ')) == NULL)
X                (void) fprintf(logfp,"\t<%s> Archived\n",dp->d_name);
X            else {
X                rp++;
X                *(rp-1) = '\0';
X                (void) fprintf(logfp,"\t<%s> Archived as <%s>\n",rec,rp);
X            }
X            continue;
X       }
X
X       /* 
X       ** Archiving from here on out.
X       */
X
X       if (!needs_to_be_archived(dp->d_name)) 
X           continue;
X           
X       if ((new_member = save_article(dp->d_name,newsgrp)) != NULL) {
X           archived_file = compress_file(new_member,newsgrp);
X           set_ownership(archived_file,newsgrp);
X           
X           /*
X           ** If a problem has been encountered,
X           ** the function do_problem handles
X           ** the logging, and notifying.
X           */
X
X           if (!problem_article) {
X               log_activities(archived_file,newsgrp);
X               build_index(new_member,newsgrp);
X               notify_users(archived_file,newsgrp,cct++);
X           }
X       }
X       else 
X           (void) fprintf(logfp,"Unable to archive %s/%s!!!\n",
X                          newsgrp->ng_name, dp->d_name);
X    }
X    (void) closedir(dfd);
X
X    if (!status_only) {
X        /* Remove the expired entries from the .archived file */
X        /* stored in the newsgroup's BASEDIR directory.       */
X
X        remove_expired();
X
X        /* Mail notification of the archived members to the   */
X        /* list of users specified in the configuration file  */
X        /* and remove the file containing the archived info.  */
X
X        mail_file(newsgrp->mail_list, tmp_mailfile, newsgrp->ng_name);
X        (void) unlink(tmp_mailfile);
X    }
X    return;
X}
X
X/* 
X** Notify Users of Archiving.
X**      If users have been specified to be informed, check to see
X**      if they have requested a specific logging format. If so
X**      use the specified format to notify the user. If not, use
X**      "file archived at path" message.
X*/
Xnotify_users(filename,ng,num_msgs)
Xchar *filename;
Xstruct group_archive *ng;
Xint num_msgs;
X{
X    /*
X    ** Are there users specified in the 
X    ** newsgroup section ? 
X    */
X    if ( *(ng->mail_list) ) {
X        if ( *(ng->logformat) )
X           logit(tmp_mailfile, ng->logformat, filename);
X        else
X           logit(tmp_mailfile, DEFAULT_LOG_FORMAT, filename);
X    }
X
X    /* 
X    ** Are there users specified in the 
X    ** global section ? 
X    */
X    if ( *mail ) {
X        if (num_msgs == 0) /* print the newsgroup name out */
X            logit(global_mailfile, "\n\t\t:%G:\n",filename);
X        if (*log_format)
X            logit(global_mailfile, log_format,filename);
X        else 
X            logit(global_mailfile, DEFAULT_LOG_FORMAT, filename);
X    }
X}
X
X/*
X** Log_activities
X**
X** There are two possible logfiles that need to be written. 
X** The group specific logfile (ng->logfile) and the global 
X** log. If it has been configured to use a specific format
X** for the logging, do so. Else, just record the fact the
X** file was sucessfully archived and the date.          
X*/
Xlog_activities(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X   long clock;
X   long time();
X   char *ctime();
X   
X   char logbuf[BUFSIZ];
X   char dms_date[30];
X   
X   if ( !*(ng->logformat) || !*log_format) {
X       clock = time((long *)0);
X       (void) strcpy(dms_date, ctime(&clock));
X       *(dms_date+(strlen(dms_date)-1)) = '\0';
X       (void) sprintf(logbuf,"%s archived %s",filename, dms_date);
X   }
X
X   if ( *(ng->logformat) )
X       logit(ng->logfile, ng->logformat, filename);
X   else
X       logit(ng->logfile, logbuf, filename);
X
X   if ( *log_format )
X       logit(log, log_format, filename);
X   else
X       logit(log, logbuf, filename);
X}
X
X/*
X** logit
X**
X** This function is used to append a logfile record 
X** if there is a logfile name specified.
X**
X*/
X
Xlogit(filename, format_of_log, arch_file)
Xchar *filename;
Xchar *format_of_log;
Xchar *arch_file;
X{
X    FILE *fp, *fopen();
X
X    if ( *(filename) ) {   /* Is a logfile specified ? */
X        if ((fp = fopen(filename,"a")) != NULL) {
X            format_output(fp, format_of_log, arch_file, ARCHIVE);
X            (void) fclose(fp);
X        }
X    }
X}    
X
X
Xset_ownership(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X    if (verbose) {  /* Print out the actions about to be preformed */
X        (void) fprintf(logfp,"chown\t<%d> <%s>\n", ng->owner, filename);
X        (void) fprintf(logfp,"chgrp\t<%d> <%s>\n", ng->group, filename);
X    }
X
X    if (!test) {    /* chown the owner/group to the desired values */
X        if (chown(filename,ng->owner, ng->group) != 0)
X            error("Can't change ownership of", filename);
X    }
X
X    if (verbose) {  /* Print out the actions about to be preformed */
X        (void) fprintf(logfp,"chmod\t<%o> <%s>\n", ng->modes, filename);
X    }
X
X    if (!test) {    /* change the file modes to the specified modes */
X        if (chmod(filename,ng->modes) != 0)
X            error("Can't change modes of", filename);
X    }
X}
X
Xmail_file(user_list, file_to_mail, nwsgrp)
Xchar *user_list;
Xchar *file_to_mail;
Xchar *nwsgrp;
X{
X    char  *list, *name;
X    char  cmdstr[80];
X
X    /* Is there a list of users to mail to ? */
X    if ( !*user_list || (strlen(user_list) == 0))
X        return;
X
X    /* Was there a notification file created ? */
X    if (stat(file_to_mail, &sbuf) != 0) 
X        return;
X
X    name = user_list;
X    do {
X       if ((list = strchr(name,',')) != NULL) {
X            list++;
X            *(list-1) = '\0';
X        }
X
X#ifdef SUBJECT_LINE
X        (void) sprintf(cmdstr, "%s -s '%s' %s < %s", 
X                   MAIL, nwsgrp, name, file_to_mail);
X#else
X        (void) sprintf(cmdstr, "%s %s < %s", MAIL, name, file_to_mail);
X#endif
X        if (verbose)
X            (void) fprintf(logfp,"Mailing %s Archived results to %s\n",
X                           nwsgrp, name);
X        if (!test) 
X            (void) system(cmdstr);
X
X        name = list;
X
X    } while (name != NULL);
X    return;
X}
X
Xbuild_index(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X    if (*(ng->index)) {        /* Is there a newsgroup index file ?  */
X        if (*(ng->indformat))  /* Yes, Is there a index file format? */
X            logit(ng->index, ng->indformat, filename);
X        else if (*index_format)    /* No, is there a global format ? */
X            logit(ng->index, index_format, filename);
X        else                   /* No, use the default index format   */
X            logit(ng->index, DEFAULT_INDEX_FORMAT, filename);
X    }
X
X    if (*index) {            /* Is there a global index file ?       */
X        if (*index_format)   /* Yes, Is there a global file format ? */
X            logit(index, index_format, filename);
X        else                 /* No, so use the default index format  */
X            logit(ng->index, DEFAULT_INDEX_FORMAT , filename);
X    }
X}
X
X
Xchar *compress_file(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X    static char compressed[MAXNAMLEN];
X
X    (void) strcpy(compressed, filename);  /* store the filename */
X
X    /* Check to see if a group specific compress was specified.      */
X    /* If so, then execute the command with the filename passed in.  */
X    /* Else check to see if a global compress was specified. If so,  */
X    /* then execute the command with the filename passed in.         */
X    /* If both are NULL, no compression is done.                     */
X
X    if (*(ng->compress)) 
X        (void) strcat(compressed, do_compress(ng->compress, filename));
X    else if (*compress) 
X        (void) strcat(compressed, do_compress(compress, filename));
X
X    return(compressed);
X}
X
Xchar *do_compress(packit,filename)
Xchar *packit;
Xchar *filename;
X{
X    char *comp_cmd;
X    char cmd[BUFSIZ];
X
X    (void) sprintf(cmd,"%s %s", packit, filename);
X
X    /* 
X    ** get the basename of the command to use.
X    */
X    comp_cmd = basename(packit);
X
X    if (verbose)
X       (void) fprintf(logfp,"%s %s\n", comp_cmd, filename);
X
X    if (!test) 
X       (void) system(cmd);
X
X    return(suffix(comp_cmd));
X}
X
X
X/*
X** Record_problem()
X**	This function is used to log problems encountered
X**	to the designated parties.
X*/
Xrecord_problem(msg_fmt,filename,ng)
Xchar *msg_fmt;
Xchar *filename;
Xstruct group_archive *ng;
X{
X    /* 
X    ** This function is used in the event that a problem
X    ** has occurred during archiving. It mails a message
X    ** to the newsgroup speecified list and it mails a 
X    ** message to the globally specified users.
X    ** 
X    ** It then logs the fact into both the newsgroup 
X    ** and the global logfiles if they have been specified.
X    */
X
X    if ( *(ng->mail_list) ) 
X        logit(tmp_mailfile, msg_fmt, filename);
X    
X    if ( *mail ) 
X        logit(global_mailfile, msg_fmt,filename);
X    
X    logit(ng->logfile, msg_fmt, filename);
X    logit(log, msg_fmt, filename);
X}
END_OF_FILE
if test 17099 -ne `wc -c <'rkive.c'`; then
    echo shar: \"'rkive.c'\" unpacked with wrong size!
fi
# end of 'rkive.c'
fi
echo shar: End of archive 4 \(of 4\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list