rsalz@uunet.uu.net (Rich Salz) (06/30/89)
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@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@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.