kent@sparky.imd.sterling.com (Kent Landfield) (02/25/91)
Submitted-by: Kent Landfield <kent@sparky.imd.sterling.com> Posting-number: Volume 17, Issue 18 Archive-name: rkive/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # If this archive is complete, you will see the following message at the end: # "End of archive 2 (of 6)." # Contents: rkive/rkive.c rkive/setup.c # Wrapped by kent@sparky on Sun Feb 24 16:11:22 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'rkive/rkive.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rkive/rkive.c'\" else echo shar: Extracting \"'rkive/rkive.c'\" \(27854 characters\) sed "s/^X//" >'rkive/rkive.c' <<'END_OF_FILE' X/* X** Subsystem: USENET Sources Archiver X** File Name: rkive.c X** X** usage: rkive [-?dgstuvV] [-f config_file] [-n newsgroup] X** [-B batchfile] [-S newsgroup] X** X** X** This software is Copyright (c) 1989, 1990, 1991 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!sparky!kent X** kent@sparky.imd.sterling.com X*/ X X#include <sys/types.h> X#include <sys/stat.h> X#include <stdio.h> X#include "article.h" X#include "cfg.h" X X#ifdef NNTP Xchar sccsid[] = "@(#)rkive.c 2.2 2/23/91 - NNTP Version"; X#else Xchar sccsid[] = "@(#)rkive.c 2.2 2/23/91"; X#endif /*!NNTP*/ 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/* X** The following define is use for compilation X** so that format_output can use the extended X** fomating characters that are not available X** in article. X*/ X#define RKIVE X Xchar tmp_mailfile[] = "/tmp/rkive.mail"; Xchar global_mailfile[] = "/tmp/gbl.mail"; X Xchar *batch_file = NULL; Xchar article_name[MAXNAMLEN]; X Xint retrieve = FROM_DISK; /* default archiving is done from local disk */ Xint overwrite; Xint status_only; X Xstruct stat sbuf; X XFILE *inputfp; X Xint needs_to_be_archived(); Xchar *save_article(); Xchar *compress_file(); Xchar *do_compress(); Xchar *basename(); Xchar *suffix(); Xchar *format_output(); Xvoid archive(); Xvoid build_index(); Xvoid display_group_info(); Xvoid logit(); Xvoid log_activities(); Xvoid mail_file(); Xvoid notify_users(); Xvoid record_problem(); Xvoid set_ownership(); X Xint fclose(); Xint stat(); Xint strcmp(); Xint strlen(); Xint system(); Xint unlink(); Xchar *strcpy(); Xchar *strcat(); Xchar *strchr(); XFILE *efopen(); Xvoid exit(); X Xextern int yydebug; Xextern char inputstring[]; Xextern parser_return_value; X Xextern int debug; Xextern int verbose; Xextern int test; Xextern int inum; Xextern int problem_article; Xextern char *nntp_server; X Xvoid usage() X{ X (void)fprintf(stderr,"usage: %s [-?dgstuvV] [-f config_file]\n", progname); X (void)fprintf(stderr," [-n newsgroup] [-B batchfile] [-S newsgroup]\n"); X (void)fprintf(stderr,"options:\n"); X (void)fprintf(stderr," -? Display this message.\n"); X (void)fprintf(stderr," -d Debug, assumes verbose flag as well.\n"); X (void)fprintf(stderr," -g Fill in default values with group display.\n"); X (void)fprintf(stderr," Only effective with verbose mode flag.\n"); X (void)fprintf(stderr," -s Display status of article archiving.\n"); X (void)fprintf(stderr," -t Test mode, display what would be done but do no archiving.\n"); X (void)fprintf(stderr," -u Unconditionally overwrite dupliclate articles.\n"); X (void)fprintf(stderr," -v Verbose archiving information printed.\n"); X (void)fprintf(stderr," -V Display %s software version information.\n", X progname); X (void)fprintf(stderr," -f config_file\n"); X (void)fprintf(stderr," Specify alternate configuration file to be used.\n"); X (void)fprintf(stderr," -n newsgroup\n"); X (void)fprintf(stderr," Specify newsgroup to archive or display status for.\n"); X (void)fprintf(stderr," -B batchfile\n"); X (void)fprintf(stderr," Read names of articles to archive from batchfile.\n"); X (void)fprintf(stderr," Note: Use of the -B option requires a newsgroup\n"); X (void)fprintf(stderr," be specified using the -n option.\n"); X (void)fprintf(stderr," -S newsgroup\n"); X (void)fprintf(stderr," Take the name of articles to archive from stdin.\n"); X} X Xint main(argc, argv) Xint argc; Xchar **argv; X{ X int getopt(); X void version(); X void setup_defaults(); X void init_article(); X X int c; X extern char *optarg; X extern int opterr; X char *nwsg = NULL; X X yydebug = FALSE; X X opterr = 0; X progname = argv[0]; X inputfp = stdin; X logfp = stdout; X errfp = stderr; 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:B:S:y")) != EOF) { X switch (c) { X case 'B': /* take filenames from batch file */ X retrieve = FROM_BATCHFILE; X batch_file = optarg; X break; X case 'd': /* for extremely verbose output */ X debug++; X verbose++; X break; X case 'f': /* alternate configuration file */ X config_file = optarg; X break; X case 'g': /* display defaults filled in */ X fill_in_defaults++; X break; X case 'n': /* specify an individual newsgroup */ X nwsg = optarg; X break; X case 's': /* display article archive status */ X status_only++; X break; X case 'S': /* single article archiving */ X retrieve = FROM_NAME; X nwsg = optarg; X break; X case 't': /* test mode, show but do nothing */ X test++; X verbose++; X break; X case 'u': /* overwrite existing duplicates */ X overwrite++; X break; X case 'v': /* display expanded output */ X verbose++; X break; X case 'V': /* display software version only */ X version(); X break; X case 'y': /* turn on yydebug facilities */ X yydebug++; X break; X case '?': /* display usage information */ X default: /* display usage, invalid option */ X usage(); X return(1); X } X } X } X X /* X ** If the user has specified that input is be filenames X ** retrieved from a batch file, the user must specify a X ** a newsgroup in addition to the batch filename. X */ X if (retrieve == FROM_BATCHFILE) { X if (nwsg == NULL) { X (void) fprintf(errfp, X "%s: Must specify a newsgroup when using batchfile mode\n", X progname); X (void) fprintf(errfp,"Sample command line...\n"); X (void) fprintf(errfp,"\t%s -B %s -n newsgroup-here\n", X progname, batch_file); X return(1); 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, "Rkive results"); X (void) unlink(global_mailfile); X } X return(0); X} X Xvoid archive() X{ X char *get_archived_rec(); X int retrieve_article(); X void get_header(); X X int cct; X int first; X int val; X char *rp, *rec; X char *new_member; X char *archived_file; 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 /* ROOT_ONLY */ X X inum = 0; /* initialize chronological issue counter */ 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 ** If the user wishes to see the newsgroup info as well as all the X ** information about a file archived, the user must specify X ** rkive -svv to see it all... X */ X X if (debug || (verbose == 2 && status_only)) { X (void) fprintf(logfp,"\n\n"); X display_group_info(newsgrp); X (void) fprintf(logfp,"\n"); X } X else if (status_only || verbose == 2) X (void) fprintf(logfp, "%s\n",newsgrp->ng_name); 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. If the user did not specify one ... X */ X if (!*newsgrp->arc_done) 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 if (!*newsgrp->patchlog) X (void) sprintf(newsgrp->patchlog,"%s/.patchlog",newsgrp->location); X X /* X ** first indicates that archiving has just begun for this group X ** so that any necessary setup can be done in retrieve_article(). X */ X X first = 1; X X#ifdef NNTP X /* X ** Determine if the newsgroup being archived is to be accessed via X ** nntp or via local disk access. X */ X if (*newsgrp->nntp) { X nntp_server = newsgrp->nntp; X retrieve = FROM_NNTP; X } X else if (*nntp) { X nntp_server = nntp; X retrieve = FROM_NNTP; X } X#endif /*NNTP*/ X X while ((val = retrieve_article(article_name,first)) != DONE) { X first = 0; X if (val == ERROR_ENCOUNTERED) X return; X else if (val != RETRIEVED) { X /* X ** Invalid return value encountered, we're in truble now.. X */ X (void) fprintf(errfp,"Invalid return from retrieve_article..?? Skipping %s\n", X newsgrp->ng_path); X return; X } X X /* X ** Read the news article file to extract the X ** header information and fill appropriate X ** data structures. X */ X get_header(article_name); 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(header.ident)) == NULL) { X /* X ** Assure that the article that we are going X ** to give status on is one that would be archived X ** if archiving was occuring. (e.g. does it match X ** the user supplied criteria for archiving...) X */ X if (*newsgrp->match) { X parser_return_value = 0; X (void) strcpy(inputstring, newsgrp->match); X if (yyparse()) X (void) fprintf(logfp, "Parse failed and returned %d\n", X parser_return_value); X else { X /* Did match string produce true as result? */ X X if (debug) X (void) fprintf(logfp, "Parser returned %d\n", X parser_return_value); X } X if (!parser_return_value) { X#ifdef NNTP X /* X ** Since they only want status, remove the X ** nntp transfer tmpfile if newsgroup is X ** being archived via nntp. X */ X if (retrieve == FROM_NNTP) X (void) unlink(article_name); X#endif /*NNTP*/ X continue; X } X } X#ifdef NNTP X if (retrieve == FROM_NNTP) X (void) fprintf(logfp, X "\t[%s] Awaiting Archiving\n",header.ident); X else X (void) fprintf(logfp, X "\t[%s] Awaiting Archiving\n",article_name); X#else /*!NNTP*/ X (void) fprintf(logfp,"\t[%s] Awaiting Archiving\n",article_name); X#endif /*NNTP*/ X } X else if (verbose) { X if ((rp = strchr(rec,' ')) == NULL) { X#ifdef NNTP X if (retrieve == FROM_NNTP) X (void) fprintf(logfp,"\t[%s] Archived\n",header.ident); X else X (void) fprintf(logfp,"\t[%s] Archived\n",article_name); X#else /*!NNTP*/ X (void) fprintf(logfp,"\t[%s] Archived\n",article_name); X#endif /*NNTP*/ X } X else { X rp++; X *(rp-1) = '\0'; X (void) fprintf(logfp,"\t%s Archived as %s\n",rec,rp); X } X } X#ifdef NNTP X /* X ** Since they only want status, remove the X ** nntp transfer tmpfile if newsgroup is X ** being archived via nntp. X */ X if (retrieve == FROM_NNTP) X (void) unlink(article_name); X#endif /*NNTP*/ X continue; X } /* End of status only... */ X X /* X ** X ** We have located a file that may need to be archived. X ** First read the header information from the found file. X ** Next, determine if the file needs to be archived by X ** doing a linear search of of the contents of the .archived file X ** comparing the message-id of the new article with the message-ids X ** recorded in the .archived file. If the new message-id is not X ** specified in the .archived file, it has not been archived X ** before and we can proceed with the archiving. X ** Can we say slow... maybe dbm in the future ??? X */ X X if (!needs_to_be_archived(header.ident)) { X#ifdef NNTP X /* X ** Remove the nntp transfer tmpfile if the X ** newsgroup is being archived via nntp. X */ X if (retrieve == FROM_NNTP) X (void) unlink(article_name); X#endif /*NNTP*/ X continue; X } X X if (*newsgrp->match) { X (void) strcpy(inputstring, newsgrp->match); X X if (yyparse()) { X (void) fprintf(logfp, "Parse failed and returned %d\n", X parser_return_value); X#ifdef NNTP X /* X ** Remove the nntp transfer tmpfile if the X ** newsgroup is being archived via nntp. X */ X if (retrieve == FROM_NNTP) X (void) unlink(article_name); X#endif /*NNTP*/ X continue; X } X X /* Did match string produce true as result? */ X X if (debug) X (void) fprintf(logfp, "Parser returned %d\n", parser_return_value); X X if (!parser_return_value) { X#ifdef NNTP X /* X ** Remove the nntp transfer tmpfile if the X ** newsgroup is being archived via nntp. X */ X if (retrieve == FROM_NNTP) X (void) unlink(article_name); X#endif /*NNTP*/ X continue; X } X } X X /* X ** Archiving from here on out. X */ X X if ((new_member = save_article(article_name,newsgrp)) != NULL) { X /* X ** To not do any compression of setting of ownership X ** for newsgroup articles that are archived by an X ** external application. X */ X if (newsgrp->type != EXTERNAL_COMMAND) { X archived_file = compress_file(new_member,newsgrp); X set_ownership(archived_file,new_member,newsgrp); X } X else X archived_file = new_member; 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 if (newsgrp->type != ONLY_ARCHIVE_NAME) { X (void) fprintf(logfp,"Unable to archive %s/%s!!!\n", X newsgrp->ng_path, article_name); X } X } X#ifdef NNTP X /* X ** Remove the nntp transfer tmpfile if the X ** newsgroup is being archived via nntp. X */ X if (retrieve == FROM_NNTP) X (void) unlink(article_name); X#endif /*NNTP*/ X } X X if (!status_only) { 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*/ Xvoid notify_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*/ Xvoid log_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 Xvoid logit(filename, format_of_log, arch_file) Xchar *filename; Xchar *format_of_log; Xchar *arch_file; X{ X FILE *fp, *fopen(); X char *qp; X X if (!test) { X if ( *(filename) ) { /* Is a logfile specified ? */ X if ((fp = fopen(filename,"a")) != NULL) { X qp = format_output(format_of_log, arch_file, ARCHIVE); X (void) fprintf(fp,"%s\n",qp); X (void) fclose(fp); X } X } X } X} X X/* X** Set_ownership X** X** This functions is responsible for setting the owner, group X** and modes of a file just put into the archive. Two file names X** are passed to this function. "filename" contains the compression X** suffix if the file is to be compressed. "flname" is the name of X** the file without the compression suffix. If the ownership or X** modes can not be set on the target "filename", this function X** then tries to set the original file, "flname". In this manner, X** the file permissions get set even if the compression routine fails. X*/ X Xvoid set_ownership(filename,flname,ng) Xchar *filename; /* filename with compression suffix */ Xchar *flname; /* filename without compression suffix */ Xstruct group_archive *ng; X{ X int chmod(); X int chown(); 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 if (!test) { /* change the file modes to the specified modes */ X if (chmod(filename,ng->modes) != 0) { X /* Assume the compress failed and try the original... */ X if (chmod(flname,ng->modes) != 0) X record_problem("Can't change modes of %O", filename, ng); X } X } 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 /* Assume the compress failed and try the original... */ X if (chown(flname, ng->owner, ng->group) != 0) { X /* X ** Are we on a system that has a braindamaged chown X ** and does not let a general user give files away ? X ** Assume so. Quota be gone!! X */ X if (getuid() == 0) X record_problem("Can't change ownership of %O",filename,ng); X } X } X } X} X Xvoid mail_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 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 Xvoid build_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 (*mindex) { /* Is there a global index file ? */ X if (*index_format) /* Yes, Is there a global file format ? */ X logit(mindex, 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) || (*compress)) { 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 /* Check to see if the compression worked. If not, return the */ X /* original file name passed to this function. The check is */ X /* done by assuring the compressed file exists. If not then */ X /* it is assumed that it failed. */ X X if (stat(compressed, &sbuf) == -1) X (void) strcpy(compressed, filename); /* restore filename */ X } X return(compressed); X} X Xchar *do_compress(packit,filename) Xchar *packit; Xchar *filename; X{ X char *comp_cmd; X char *kp; 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 /* X ** Need to remove any compression command X ** options if they exist. (compress -f) X */ X X (void) sprintf(cmd,"%s", comp_cmd); X X if ((kp = strchr(cmd,' ')) != NULL) { X *kp = '\0'; X } X return(suffix(cmd)); X} X X X/* X** Record_problem() X** This function is used to log problems encountered X** to the designated parties. X*/ X Xvoid record_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 27854 -ne `wc -c <'rkive/rkive.c'`; then echo shar: \"'rkive/rkive.c'\" unpacked with wrong size! fi # end of 'rkive/rkive.c' fi if test -f 'rkive/setup.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rkive/setup.c'\" else echo shar: Extracting \"'rkive/setup.c'\" \(23815 characters\) sed "s/^X//" >'rkive/setup.c' <<'END_OF_FILE' X/* X** This software is Copyright (c) 1989, 1990, 1991 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 X#if !defined(lint) && !defined(SABER) Xstatic char SID[] = "@(#)setup.c 2.2 2/23/91"; X#endif X X#include <sys/types.h> X#include <sys/stat.h> X#include <stdio.h> X#include <ctype.h> X#include <pwd.h> X#include <grp.h> X#include "cfg.h" X X#define GAG(b) ((void) fprintf(errfp,"%s invalid variable, ignoring.\n",b)) X Xchar spooldir[MAXNAMLEN] = { SPOOLDIR }; Xchar problems_dir[MAXNAMLEN] = { PROBLEMS_DIR }; X Xint default_owner = OWNER; Xint default_group = GROUP; Xint default_modes = MODES; Xint default_type = CHRONOLOGICAL; Xint default_patch_type = HISTORICAL; X Xchar default_match[MAXMATCHLEN]; X X/* X** compress - X** Used to determine whether or not articles should be compressed X** to save space. The command to execute is stored in compress. X*/ Xchar compress[MAXNAMLEN] = { '\0' }; X X/* X** arch_command - X** Used to determine whether or not articles should be piped X** to an external command for the actual archiving. X*/ Xchar arch_command[MAXNAMLEN] = { '\0' }; X X/* X** mail - X** If specified, all actions logged are mailed to the list of users X** specified. The user names are a comma seperated list. X*/ Xchar mail[MAXNAMLEN] = { '\0' }; X X/* X** checkhash - X** If specified, command to feed article to for transit damage. X*/ Xchar checkhash[MAXNAMLEN] = { '\0' }; X X#ifdef NNTP X/* X** nntp - X** If specified, nntp contains the system name of the nntp server. X*/ Xchar nntp[MAXNAMLEN] = { '\0' }; X#endif /*NNTP*/ X X/* X** log - X** The location of the master log in which all actions are logged. X** If not specified, all logged events are printed on stdout. X*/ Xchar log[MAXNAMLEN] = { '\0' }; X X/* X** log_format - X** The format of each individual log file record. The format is X** then filled with information contained in the headers. X*/ Xchar log_format[BUFSIZ] = { '\0' }; X X/* X** mindex - X** The location of the master index. X*/ Xchar mindex[MAXNAMLEN] = { '\0' }; X X/* X** index_format - X** The format of each individual master index record. The format X** is then filled with information contained in the headers. X*/ Xchar index_format[BUFSIZ] = { '\0' }; X Xchar *config_file; XFILE *config; X Xstruct stat stbuf; Xstruct passwd *pwent; X Xint get_group(); Xint get_owner(); Xint correct_modes(); Xint get_patch_type(); Xint get_archive_type(); Xint fclose(); Xint sscanf(); Xint stat(); Xint strlen(); Xint strcmp(); Xint strncmp(); Xchar *strstrip(); Xchar *strchr(); Xchar *strcpy(); Xvoid error(); Xvoid get_spooldir(); Xvoid get_archive_basedir(); Xstruct passwd *getpwnam(); X Xstruct restricted_dirs { X char *dirstr; /* path of restricted directory */ X}; X Xstatic struct restricted_dirs base_dirs[] = { X{ "/" }, X{ "/bin" }, X{ "/dev" }, X{ "/dev/dsk" }, X{ "/dev/rdsk" }, X{ "/etc" }, X{ "/lib" }, X{ "/stand" }, X{ "/sbin" }, X{ "/sys" }, X{ "/usr/5bin" }, X{ "/usr/5include" }, X{ "/usr/5lib" }, X{ "/usr/adm" }, X{ "/usr/adm" }, X{ "/usr/boot" }, X{ "/usr/diag" }, X{ "/usr/etc" }, X{ "/usr/include" }, X{ "/usr/kvm" }, X{ "/usr/spool/uucp" }, X{ "/usr/man" }, X{ "/usr/sccs" }, X{ "/usr/sys" }, X{ "/usr/ucb" }, X{ "/usr/ucbinclude" }, X{ "/usr/ucblib" }, X{ "/usr/xpg2bin" }, X{ "/usr/xpg2include" }, X{ "/usr/xpg2lib" }, X{ NULL }, X}; X Xint config_line_count = 0; X X/* X** Reads lines from file, catting lines X** ending with a backslash together. X*/ X Xchar *long_fgets(buffer, size, fp) Xchar *buffer; Xint size; XFILE *fp; X{ X static char buf[BUFSIZ]; X extern char *strcat(); X register char *p; X X *buffer = 0; X X for (;;) { X if (fgets(buf, BUFSIZ, fp) == NULL) { X if (*buffer) { X if ((p = strchr(buf, '\n')) != NULL) X *p = 0; X return buffer; X } X else X return NULL; X } X X if ((p = strchr(buf, '\n')) != NULL) X *p = 0; X X if (*buf) { X if (*(buf + strlen(buf) - 1) != '\\') { X /* X ** If line would be too long, X ** print warning and return empty X */ X if (strlen(buffer) + strlen(buf) >= size) { X (void) fprintf(errfp,"Config %d: Input line overflow, %d max\n", X config_line_count, size); X *buffer = 0; X return buffer; X } X (void) strcat(buffer, buf); X return buffer; X } X else { X *(buf + strlen(buf) - 1) = 0; /* Remove backslash */ X X /* X ** If too long, print warning, read X ** all continuation lines and ignore X ** them, return empty line X */ X X if (strlen(buffer) + strlen(buf) >= size) { X (void) fprintf(errfp,"Config %d: Input line overflow, %d max\n", X config_line_count, size); X while (*(buf + strlen(buf) - 1) == '\\' && fgets(buf, BUFSIZ, fp)) X ; X *buffer = 0; X return buffer; X } X X /* Add new line and read next one */ X X (void) strcat(buffer, buf); X } X } X else X return buffer; X X } /* End for(ever) */ X} X X Xvoid setup_defaults() X{ X char *sp; X char *buf; X char buffer[BUFSIZ]; X char mode_str[128]; X X char *sav_format(); X char *get_cmd(); X char *get_users(); X#ifdef NNTP X char *getnntp(); X#endif /*NNTP*/ X FILE *efopen(); X X config = efopen(config_file,"r"); X X num = -1; /* initialize group structure index */ X X while (long_fgets(buffer, sizeof buffer, config) != NULL) { X config_line_count++; X /* ignore comments and blank lines */ X if (*buffer == '#' || !*buffer) X continue; X X buf = buffer; X X /* strip leading spaces and tabs */ X while(*buf == ' ' || *buf == '\t') X ++buf; X X /* if embedded comments, truncate at the comment */ X if ((sp = strchr(buf,'#')) != NULL) X *sp = '\0'; X X /* check to see if newsgroup entry */ X X if (*buf == '$' && *(buf+1) == '$') { X if (++num >= NUM_NEWSGROUPS) X error("Maximum number of newsgroups exceeded!!\n", X "Please increase the NUM_NEWSGROUPS define..."); X X sp = buf+2; X while (*sp && !isspace(*sp)) X ++sp; X *sp = '\0'; X X group[num].owner = default_owner; X group[num].group = default_group; X group[num].modes = default_modes; X group[num].type = default_type; X group[num].patch_type = default_patch_type; X (void) strcpy (group[num].ng_name, strstrip(buf+2)); X group[num].location[0] = '\0'; X group[num].mail_list[0] = '\0'; X group[num].arc_done[0] = '\0'; X group[num].logfile[0] = '\0'; X group[num].index[0] = '\0'; X group[num].patchlog[0] = '\0'; X group[num].logformat[0] = '\0'; X group[num].indformat[0] = '\0'; X group[num].compress[0] = '\0'; X group[num].checkhash[0] = '\0'; X group[num].match[0] = '\0'; X group[num].arch_command[0] = '\0'; X X#ifdef NNTP X group[num].nntp[0] = '\0'; X#endif /*NNTP*/ X } X X else if ((sp = strchr(buf,'=')) != NULL) { X sp++; X /* Global assignment */ X while (*sp == ' ' || *sp == '\t') X sp++; X X if (!*sp) /* is something still there ? */ X continue; X X switch(*buf) { X case 'A': if (strncmp(buf, "ARCHIVE_CMD", 11) == 0) X (void) strcpy(arch_command, get_cmd(sp)); X else X GAG(buf); X break; X case 'C': if (strncmp(buf, "COMPRESS", 8) == 0) X (void) strcpy(compress, get_cmd(sp)); X else if (strncmp(buf, "CHECKHASH", 9) == 0) X (void) strcpy(checkhash, get_cmd(sp)); X else X GAG(buf); X break; X case 'G': if (strncmp(buf,"GROUP",5) == 0) X default_group = get_group(sp); X else X GAG(buf); X break; X case 'I': if (strncmp(buf, "INDEX_FORMAT", 12) == 0) X (void) strcpy(index_format, sav_format(sp)); X else if (strncmp(buf, "INDEX", 3) == 0) X (void) strcpy(mindex, strstrip(sp)); X else X GAG(buf); X break; X case 'L': if (strncmp(buf, "LOG_FORMAT", 10) == 0) X (void) strcpy(log_format, sav_format(sp)); X else if (strncmp(buf, "LOG", 3) == 0) X (void) strcpy(log, strstrip(sp)); X else X GAG(buf); X break; X case 'M': if (strncmp(buf, "MAIL",4) == 0) X (void) strcpy(mail,get_users(sp)); X else if (strncmp(buf, "MODE",4) == 0) X default_modes = correct_modes(sp, mode_str); X else if (strncmp(buf, "MATCH", 5) == 0) X (void) strcpy(default_match, sp); X else X GAG(buf); X break; X#ifdef NNTP X case 'N': if (strncmp(buf, "NNTP",4) == 0) X (void) strcpy(nntp, getnntp(sp)); X else X GAG(buf); X break; X#endif /*NNTP*/ X case 'O': if (strncmp(buf,"OWNER",5) == 0) X default_owner = get_owner(sp); X else X GAG(buf); X break; X case 'P': if (strncmp(buf, "PROBLEMS", 8) == 0) X (void) strcpy(problems_dir, strstrip(sp)); X else if (strncmp(buf,"PATCHES",7) == 0) X default_patch_type = get_patch_type("Global",sp); X else X GAG(buf); X break; X case 'S': if (strncmp(buf,"SPOOLDIR",8) == 0) X get_spooldir(sp); X else X GAG(buf); X break; X case 'T': if (strncmp(buf,"TYPE",4) == 0) X default_type = get_archive_type("Global", sp); X else X GAG(buf); X break; X default : (void) fprintf(errfp, X "%s invalid global assignment, ignoring.\n", X buf); X } X } X else if ((sp = strchr(buf,':')) != NULL) { X sp++; X /* group variable assignment */ X while (*sp == ' ' || *sp == '\t') X sp++; X X if (!*sp) /* is something still there ? */ X continue; X X switch(*buf) { X case 'A': if (strncmp(buf, "ARCHIVE_CMD", 11) == 0) X (void)strcpy(group[num].arch_command,get_cmd(sp)); X else if (strncmp(buf, "ARCHIVED_LOG", 11) == 0) X (void) sprintf(group[num].arc_done, strstrip(sp)); X else X GAG(buf); X break; X case 'B': if (strncmp(buf, "BASEDIR",7) == 0) X get_archive_basedir(sp); X break; X case 'C': if (strncmp(buf, "COMPRESS", 8) == 0) X (void) strcpy(group[num].compress,get_cmd(sp)); X else if (strncmp(buf, "CHECKHASH", 9) == 0) X (void) strcpy(group[num].checkhash,get_cmd(sp)); X else X GAG(buf); X break; X case 'G': if (strncmp(buf,"GROUP",5) == 0) X group[num].group = get_group(sp); X else X GAG(buf); X break; X case 'I': if (strncmp(buf, "INDEX_FORMAT", 12) == 0) X (void) strcpy(group[num].indformat,sav_format(sp)); X else if (strncmp(buf, "INDEX", 3) == 0) X (void) strcpy(group[num].index, strstrip(sp)); X else X GAG(buf); X break; X case 'L': if (strncmp(buf, "LOG_FORMAT", 10) == 0) X (void) strcpy(group[num].logformat, sav_format(sp)); X else if (strncmp(buf, "LOG", 3) == 0) X (void) strcpy(group[num].logfile, strstrip(sp)); X else X GAG(buf); X break; X case 'M': if (strncmp(buf, "MAIL",4) == 0) X (void) strcpy(group[num].mail_list, get_users(sp)); X else if (strncmp(buf, "MODE",4) == 0) X group[num].modes = correct_modes(sp, mode_str); X else if (strncmp(buf, "MATCH", 5) == 0) X /* Copy as it is! */ X (void) strcpy(group[num].match, sp); X else X GAG(buf); X break; X#ifdef NNTP X case 'N': if (strncmp(buf, "NNTP",4) == 0) X (void) strcpy(group[num].nntp, getnntp(sp)); X else X GAG(buf); X break; X#endif /*NNTP*/ X case 'O': if (strncmp(buf,"OWNER",5) == 0) X group[num].owner = get_owner(sp); X else X GAG(buf); X break; X case 'P': if (strncmp(buf,"PATCHLOG",8) == 0) X (void) sprintf(group[num].patchlog, strstrip(sp)); X else if (strncmp(buf,"PATCHES",7) == 0) X group[num].patch_type = get_patch_type(group[num].ng_name,sp); X else X GAG(buf); X break; X case 'T': if (strncmp(buf,"TYPE",4) == 0) X group[num].type = get_archive_type(group[num].ng_name,sp); X else X GAG(buf); X break; X default : (void) fprintf(errfp, X "%s invalid group assignment, ignoring.\n", X buf); X } X } X else /* no idea what it is */ X error("unknown line type", buf); X } X (void) fclose(config); X config_line_count = 0; X} X Xvoid error(msg1,msg2) X char *msg1; X char *msg2; X{ X if (config_line_count) X (void) fprintf(errfp,"%s: config line %d: %s %s\n", X progname, config_line_count, msg1, msg2); X else X (void) fprintf(errfp,"%s: %s %s\n",progname,msg1, msg2); X X exit(1); X/*NOTREACHED*/ X} X X/* X** valid_base_directory X** X** Assure the directory specified in the configuration file X** as the base directory for a newsgroup archive is not found X** in the table of restricted base directories. X** X** This kind of checking is almost insulting to me as an X** administrator but, enough people asked me to put it in X** so "this duds for you".. X*/ X Xint valid_base_directory(argstr) X char *argstr; X { X register char *rp; X register char *dp; X char wpath[MAXNAMLEN]; X char lastchar; X struct restricted_dirs *pt; X X /* X ** First check to see if the base directory is any X ** character other than a slash. We need to assure X ** that "../../../etc" or ./etc is not allowed. We X ** need a valid absolute path with which to do relative X ** path addressing. (Have I confused myself yet ?) X */ X X if (*argstr != '/') X return(FALSE); X X /* X ** Strip the string of duplicate '/'s. X ** Also check to assure that the path specified X ** does not contain the '..' sequence. X */ X X dp = argstr; X rp = wpath; X lastchar = ' '; X X while (*dp) { X if (*dp != '/' || lastchar != '/') { X lastchar = *dp; X *rp++ = *dp; X } X if (*dp == '.' && lastchar == '.') { X if ((*(dp+1) == '/') || (*(dp+1) == '\0')) X return(FALSE); X } X ++dp; X } X *rp = '\0'; X X /* X ** strip the string of trailing '/'s so X ** I can use the simple checking below. X */ X X dp = wpath+(strlen(wpath)-1); X while(*dp == '/' && dp > wpath) X *dp = '\0'; X X /* X ** check if they match X */ X X pt = &base_dirs[0]; X while ((pt->dirstr) != NULL) { X X if (strcmp(wpath, pt->dirstr) == 0) X return(FALSE); X X pt++; X } X return(TRUE); X} X Xvoid get_archive_basedir(s) Xchar *s; X{ X (void) strcpy(group[num].location, strstrip(s)); X X if (!valid_base_directory(group[num].location)) X error(group[num].ng_name," - Invalid archive base directory!"); X} X Xint correct_modes(s,mode_string) Xchar *s; Xchar *mode_string; X{ X register int c; X register int i; X X i = 0; X (void) sscanf(s, "%s", mode_string); X while ((c = *mode_string++) >= '0' && c <= '7') X i = (i << 3) + (c - '0'); X mode_string--; X return(i); X} X X Xchar *get_cmd(cmd) Xchar *cmd; X{ X static char *rp; X char *kp; X X rp = strstrip(cmd); X X /* X ** Here an external command needs to be verified. X ** To do so, the options must be removed. I am being X ** real lazy here but what the hey.. X ** If a space is found after the cmdline is striped X ** put a null there and then replace it with a X ** space after the check... X */ X X if ((kp = strchr(rp,' ')) != NULL) X *kp = '\0'; X else if ((kp = strchr(rp,'\t')) != NULL) X *kp = '\0'; X X /* need to assure the user has specified */ X /* a valid executable path. */ X X if (stat(rp, &stbuf) != 0) X error("Can't find specified command -", rp); X X if (kp != NULL) /* replace the space.. */ X *kp = ' '; X X return(rp); X} X X#ifdef NNTP Xchar *getnntp(loc) Xchar *loc; X{ X static char *rp; X X rp = strstrip(loc); X X /* X ** check to assure that the user does not wish to negate X ** a global declaration for the nntp server. X */ X X if ((strncmp(rp,"LOCAL",5) == 0) || (strncmp(rp, "local",5) == 0)) X return(""); X X return(rp); X} X#endif /*NNTP*/ X X X Xint get_group(valstr) Xchar *valstr; X{ X char *wp; X struct group *grent; X struct group *getgrnam(); X X /* group specified by names but */ X /* needs to be numbers */ X X wp = strstrip(valstr); X X if ((grent = getgrnam(wp)) == NULL) X error("Invalid system group:",wp); X return(grent->gr_gid); X} X X Xint get_owner(valstr) Xchar *valstr; X{ X char *wp; X X /* owner specified by names but */ X /* needs to be numbers */ X X wp = strstrip(valstr); X X if ((pwent = getpwnam(wp)) == NULL) X error("Invalid user:",wp); X return(pwent->pw_uid); X} X Xint get_archive_type(ngname, s) Xchar *ngname; Xchar *s; X{ X int return_type = default_type; X X if (strcmp(s, "Archive-Name") == 0) X return_type = ARCHIVE_NAME; X else if (strcmp(s, "Volume-Issue") == 0) X return_type = VOLUME_ISSUE; X else if (strcmp(s, "Chronological") == 0) X return_type = CHRONOLOGICAL; X else if (strcmp(s, "Article-Number") == 0) X return_type = ARTICLE_NUMBER; X else if (strcmp(s, "Comp-Archives") == 0) X return_type = COMP_ARCHIVES; X else if (strcmp(s, "External-Command") == 0) X return_type = EXTERNAL_COMMAND; X else if (strcmp(s, "Only-Archive-Name") == 0) X return_type = ONLY_ARCHIVE_NAME; X else { X (void) fprintf(errfp,"%s: %s: %s %s\n", progname, X ngname, "Invalid Archive Type:", s); X (void) fprintf(errfp,"\tTYPE Must be %s, %s, %s, %s, %s, %s or %s\n", X "Archive-Name", "Volume-Issue", "Comp-Archives", X "External-Command", "Only-Archive-Name", X "Chronological", "Article-Number"); X exit(1); X } X return(return_type); X} X Xvoid get_spooldir(s) Xchar *s; X{ X static char *rp; X X rp = strstrip(s); X X /* need to assure the user has specified */ X /* a valid directory path for the base */ X /* directory for the news subsystem.. */ X X if (stat(rp, &stbuf) != 0) X error("Can't find SPOOLDIR -", rp); X X (void) strcpy(spooldir, rp); X} X Xchar *get_users(s) Xchar *s; X{ X char *strcat(); X X static char users[512]; X char tmp_users[512]; X char *list, *name; X char *cp, *dp; X register int i; X X /* prepare the string for saving by stripping any spaces */ X X for (i = 0; i < sizeof users; i++) X users[i] = '\0'; X X cp = s; X dp = users; X while (*cp) { X if (*cp != ' ' && *cp != '\t') X *dp++ = *cp; X ++cp; X } X X /* Need to check the specified user list */ X /* to assure that all users are valid. */ X X (void) strcpy(tmp_users, users); X *users = '\0'; X X name = tmp_users; X X while (name != NULL) { X /* is there additional users specified ? */ X if ((list = strchr(name,',')) != NULL) { X list++; X *(list-1) = '\0'; X } X X#ifdef CHECK_LOGNAME X /* check if user is found in passwd file */ X if ((pwent = getpwnam(name)) == NULL) X error("Invalid user:",name); X#endif /* CHECK_LOGNAME */ X X if (*users != '\0') { X (void) strcat(users, ","); X (void) strcat(users, name); X } X else X (void) strcpy(users, name); X name = list; X } X return(users); X} X X/* X** get a specified format from the buffer X** Must allow for spaces and tabs so they X** need to be passed intact in the format. X*/ Xchar *sav_format(s) X char *s; X{ X static char *cp; X char *dp; X X if ((cp = strchr(s,'"')) != NULL && X (dp = strchr(++cp,'"')) != NULL) { X *dp = '\0'; X } X else X cp = NULL; X return(cp); X} X Xint get_patch_type(ngname,s) Xchar *ngname; Xchar *s; X{ X int return_type = default_type; X X if (strcmp(s, "Package") == 0) X return_type = PACKAGE; X else if (strcmp(s, "Historical") == 0) X return_type = HISTORICAL; X else { X (void) fprintf(errfp,"%s: %s: %s %s\n", progname, X ngname, "Invalid Patches Type:", s); X (void) fprintf(errfp,"\tPATCHES Must be %s, or %s\n", X "Historical", "Package"); X exit(1); X } X return(return_type); X} END_OF_FILE if test 23815 -ne `wc -c <'rkive/setup.c'`; then echo shar: \"'rkive/setup.c'\" unpacked with wrong size! fi # end of 'rkive/setup.c' fi echo shar: End of archive 2 \(of 6\). cp /dev/null ark2isdone MISSING="" for I in 1 2 3 4 5 6 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 6 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 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.