home *** CD-ROM | disk | FTP | other *** search
- /*
- **
- ** This software is Copyright (c) 1989 by Kent Landfield.
- **
- ** Permission is hereby granted to copy, distribute or otherwise
- ** use any part of this package as long as you do not try to make
- ** money from it or pretend that you wrote it. This copyright
- ** notice must be maintained in any copy made.
- **
- **
- ** History:
- ** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
- **
- */
- #ifndef lint
- static char SID[] = "@(#)news_arc.c 1.1 6/1/89";
- #endif
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <dirent.h>
- #include <stdio.h>
- #include <ctype.h>
- #include "article.h"
- #include "cfg.h"
-
- /*
- ** Defines for the type of "problems"
- ** encountered in saving the articles.
- */
- #define DUP_PROB 0
- #define NAME_PROB 1
- #define VOL_PROB 2
- #define TYPE_PROB 3
-
- int test = 0;
- int problem_article;
-
- extern struct group_archive *newsgrp;
- extern int overwrite;
-
- char *strchr();
- char *strcpy();
- char *strcat();
- char *do_problem();
- char *basename();
- char *suffix();
- FILE *efopen();
- void exit();
-
- get_header(filename)
- char *filename;
- {
- char *dp;
- int header_ok = 0;
- FILE *gfp;
-
- init_article();
-
- gfp = efopen(filename,"r");
-
- (void) strcpy(article.newsarticle, filename);
-
- while (fgets(s,sizeof s,gfp) != NULL) {
- if (debug)
- (void) fprintf(logfp, "BUF = [%s]",s);
-
- if (!isalpha(*s) || (strchr(s,':') == NULL)) {
- header_ok++;
- if (header_ok == 2)
- break;
- continue;
- }
-
- dp = s;
- while (*++dp)
- if (*dp == '\n')
- *dp = '\0';
-
- store_line();
- }
- (void) fclose(gfp);
-
- if (debug)
- dump_article();
- }
-
- /*
- ** check_archive_name
- **
- ** Assure the path specified is within the base directory
- ** specified by the archive administrator by assuring that
- ** a prankster could not have an article archived at a
- ** basedir/../../../etc/passwd
- ** location.
- **
- ** If an absoulte path is specified in the Archive-name, it
- ** is of no concern since a "checked" base directory and
- ** volume directory are prefixed.
- */
-
- check_archive_name(argstr)
- char *argstr;
- {
- char *substr();
- register char *rp;
- register char *dp;
-
- /*
- ** check to assure that the path specified
- ** does not contain the '..' sequence.
- */
-
- while ((rp = substr(argstr, "..")) != NULL) {
- dp = rp+2;
- while(*dp)
- *rp++ = *dp++;
- *rp = '\0';
- }
-
- /* I know this is not necessary but what the heck.. */
-
- while ((rp = substr(argstr, "//")) != NULL) {
- dp = rp+2;
- ++rp;
- while(*dp)
- *rp++ = *dp++;
- *rp = '\0';
- }
-
- /*
- ** strip the string of trailing '/'s
- */
-
- dp = argstr+(strlen(argstr)-1);
- while(*dp == '/' && dp > argstr)
- *dp = '\0';
- }
-
- /*
- ** IF YOU USE A COMPRESSION ROUTINE OTHER THAN COMPRESS
- ** OR PACK, ADD YOUR COMPRESSION SPECIFIC INFORMATION
- ** TO THE cprgs COMPRESS_TABLE ......
- */
-
- struct compress_tab {
- char *com_name;
- char *com_suffix;
- };
-
- struct compress_tab cprgs[] = {
- { "compress", ".Z" },
- { "pack", ".z" },
- { NULL, 0 },
- };
-
- char *suffix(compression)
- char *compression;
- {
- struct compress_tab *ct;
-
- ct = &cprgs[0];
- while ((ct->com_name) != NULL) {
- if (strcmp(compression, ct->com_name) == 0)
- return(ct->com_suffix);
- ct++;
- }
- return("");
- }
-
- int remove_suffix(path_str)
- char *path_str;
- {
- char *ss;
- struct compress_tab *ct;
-
- /*
- ** need to compare the filename passed in to
- ** the compression suffix table in order to
- ** determine if the file has a recognized,
- ** compression suffix attached.
- */
-
- ss = path_str + (strlen(path_str) -2);
-
- ct = &cprgs[0];
- while ((ct->com_name) != NULL) {
- if (strcmp(ss, ct->com_suffix) == 0) {
- *ss = '\0';
- return(TRUE);
- }
- ct++;
- }
- return(FALSE);
- }
-
- char *expand_name(filename,ng)
- char *filename;
- struct group_archive *ng;
- {
- char *comp_cmd;
- static char compress_path[MAXNAMLEN];
-
- (void) strcpy(compress_path, filename);
-
- /*
- ** Check to see if a group specific compress was specified.
- ** If so, then attach the suffix and return.
- ** Else check to see if a global compress was specified. If so,
- ** then attach the suffix and return.
- ** If both are NULL, return filename.
- */
-
- if (*(ng->compress)) {
- comp_cmd = basename(ng->compress);
- (void) strcat(compress_path, suffix(comp_cmd));
- }
- else if (*compress) {
- comp_cmd = basename(compress);
- (void) strcat(compress_path, suffix(comp_cmd));
- }
- return(compress_path);
- }
-
- #ifdef REDUCE_HEADERS
-
- struct hdrstokeep {
- char *ststr;
- int stbytes;
- };
-
- struct hdrstokeep hdrs[] = {
- { "From:", (sizeof "From:") },
- { "Newsgroups:", (sizeof "Newsgroups:") },
- { "Subject:", (sizeof "Subject:") },
- { "Message-ID:", (sizeof "Message-ID:") },
- { "Date:", (sizeof "Date:") },
- { NULL, 0 },
- };
-
- int keep_line(argstr)
- char *argstr;
- {
- struct hdrstokeep *pt;
-
- pt = &hdrs[0];
- while ((pt->ststr) != NULL) {
- if (strncmp(argstr, pt->ststr, (pt->stbytes-1)) == 0)
- return(TRUE);
- pt++;
- }
- return(FALSE);
- }
-
- int copy(source, target)
- char *source, *target;
- {
- char *strchr();
- FILE *from, *to;
- char fbuf[BUFSIZ];
- int inheader;
-
- inheader = TRUE;
-
- if (verbose) {
- (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
- if (test)
- return(0);
- }
- if ((from = fopen(source, "r")) == NULL) {
- (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
- return (-1);
- }
- if ((to = fopen(target, "w")) == NULL) {
- (void) fclose(from);
- (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
- return (-1);
- }
- /*
- ** Read the source and do not print any headers
- ** unless specified in the "keep" headers table.
- */
-
- while (fgets(fbuf, BUFSIZ, from) != NULL) {
- if (inheader) {
- /*
- ** Have I encountered a line without a line type ?
- */
- if (!isalpha(*fbuf) || (strchr(fbuf,':') == NULL))
- inheader = FALSE;
-
- else {
- /*
- ** Determine the type of the header line and
- ** decide if this is a line to be kept or pitched.
- */
- if (!keep_line(fbuf))
- continue;
- }
- }
- if (fputs(fbuf, to) == EOF) {
- (void) unlink(target);
- (void) fclose(from);
- (void) fclose(to);
- (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
- return (-1);
- }
- }
- (void) fclose(from);
- (void) fclose(to);
- return(0);
- }
-
- #else
-
- copy(source, target)
- char *source, *target;
- {
- int from, to, ct;
- char fbuf[BUFSIZ];
-
- if (verbose) {
- (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
- if (test)
- return(0);
- }
- if ((from = open(source, 0)) < 0) {
- (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
- return (-1);
- }
- if ((to = creat (target, 0644)) < 0) {
- (void) close(from);
- (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
- return (-1);
- }
- while ((ct = read(from, fbuf, BUFSIZ)) != 0) {
- if(ct < 0 || write(to, fbuf, (unsigned) ct) != ct) {
- (void) unlink(target);
- (void) close(from);
- (void) close(to);
- (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
- return (-1);
- }
- }
- (void) close(from);
- (void) close(to);
- return(0);
- }
-
- #endif /* REDUCE_HEADERS */
-
- /*
- ** mkparents:
- **
- ** If any parent directories in
- ** fullname don't exist, create them.
- */
-
- int mkparents(fullname)
- char *fullname;
- {
- char *strrchr();
-
- register char *p;
- char b[MAXNAMLEN];
- int rc;
-
- (void) strcpy(b, fullname);
-
- if ((p = strrchr(b, '/')) != NULL)
- *p = '\0';
- else /* no directories in fullname */
- return(0);
-
- if (*b == '\0') /* are we at the root ? */
- return(0);
-
- if (access(b, 0) == 0)
- return(0);
-
- (void) mkparents(b);
-
- if ((rc = makedir(b, 0755, newsgrp->owner, newsgrp->group)) != 0)
- error("makedir failed attempting to make", b);
-
- return(rc);
- }
-
-
- char *save_article (filename,ng)
- char *filename;
- struct group_archive *ng;
- {
- char *final_path;
- static char path[MAXNAMLEN];
- struct stat sb;
-
- problem_article = FALSE;
- path[0] = '\0';
-
- /*
- ** Read the news article file to extract the
- ** header information and fill appropriate
- ** data structures.
- */
- get_header(filename);
-
- /*
- ** Build the path string for the final resting spot
- ** for the new archive member.
- */
- switch(ng->type) {
- case ARCHIVE_NAME:
- /*
- ** The header's archive_name contains the filename in
- ** an "elm/part06" format.
- */
-
- if ((article.volume == -1) || (!header.archive_name[0]))
- return(do_problem(NAME_PROB, ng,filename,path));
-
- /*
- ** Assure the address is relative and
- ** that some prankster can not do nasty
- ** things to your system files by having
- ** an Archive-name line like:
- ** ../../../../../etc/passwd
- */
-
- check_archive_name(header.archive_name);
-
- /*
- ** Check to see if the article is a patch. If so,
- ** check to see if the administrator wishes to
- ** store the patch with the initially posted
- ** articles. This really relys on the archive name
- ** being correct.
- */
-
- if (article.rectype == PATCH && ng->patch_type == PACKAGE)
- /*
- ** Store the patch in the volume specified with the
- ** Archive-name: specified file name.
- */
- (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
- article.patch_volume, header.archive_name);
-
- else
- (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
- article.volume, header.archive_name);
- break;
- case VOLUME_ISSUE:
- /*
- ** The article filename contains the filename in
- ** a "v01i001" format.
- */
- if ((article.volume == -1) || (!article.filename[0]))
- return(do_problem(VOL_PROB,ng,filename,path));
-
- (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
- article.volume, article.filename);
- break;
- case ARTICLE_NUMBER:
- /*
- ** Store in same filename - thanks news...
- */
- (void) sprintf(path,"%s/%s", ng->location, filename);
- break;
- default:
- /*
- ** We have got problems....
- */
- return(do_problem(TYPE_PROB,ng,filename,path));
- }
-
- /*
- ** Check if the file is a patch. If so, log
- ** the patch information into the patch log
- ** in a *non-configurable* format so that
- ** applications can be written to access the
- ** file's "known format".
- */
-
- if (article.rectype == PATCH)
- write_patch_log(ng,path);
-
- #ifdef ADD_REPOST_SUFFIX
- if (article.repost == TRUE)
- /*
- ** The ADD_REPOST_SUFFIX code adds the REPOST_SUFFIX
- ** to any file that has been indicated as a repost
- ** by the moderator. This should not be used with
- ** Archive-Name archiving on a filesystem with 14
- ** character filename limits or filename truncation
- ** can occur. You have been warned... :-(
- **
- ** After adding the REPOST_SUFFIX, the filename is
- ** treated as any other file with the duplication
- ** checks and all...
- */
- (void) strcat(path,REPOST_SUFFIX);
- #endif /* ADD_REPOST_SUFFIX */
-
- /*
- ** expand the path to the file to include the
- ** compression suffix if necessary.
- */
-
- final_path = expand_name(path, ng);
-
- /*
- ** Make any necessary directories
- ** along the way.
- */
- (void) mkparents(path);
-
- /*
- ** Check to assure that there is not already
- ** a file with the same file name. If so
- ** copy (or archive) the file to the problems
- ** directory.
- **
- ** This works for REPOSTS as well.
- ** If the REPOST arrives and there is
- ** no file currently at the archive location, the
- ** REPOST is installed in the correct archive
- ** location.
- ** If there is a file that exists when a REPOST
- ** arrives, the REPOST is then handled in do_problem().
- */
-
- if ((stat(final_path ,&sb) == 0) && !overwrite) /* duplicate found */
- return(do_problem(DUP_PROB,ng, filename, final_path));
-
- if (copy(filename,path) != 0) {
- (void) fprintf(errfp,"copy failed for %s to %s\n",filename,path);
- return(NULL);
- }
- /*
- ** Write the filename to the .archived file in the newsgroup's
- ** BASEDIR directory since we do not want it rearchived tomorrow.
- */
- write_archived(filename, path);
-
- /*
- ** Return the path to the archived file.
- */
- return(path);
- }
-
-
-
- char *do_problem(type_of_problem, ng, file, path)
- int type_of_problem;
- struct group_archive *ng;
- char *file;
- char *path;
- {
-
- #ifdef MV_ORIGINAL
- char crnt_path[MAXNAMLEN];
- #endif /*MV_ORIGINAL */
-
- char pmess[BUFSIZ];
-
- problem_article = TRUE;
-
- /* ALERT THE ADMINISTRATOR THAT A PROBLEM WAS ENCOUNTERED
- **
- ** A problem has been encountered. It could be that there is an
- ** format mismatch or there is already a file with the same
- ** issue/archive/msg-id name.
- ** Copy the problem file to the problems directory.
- ** Alert the Administrator that a problem was received.
- */
-
- (void) sprintf(pmess,"PROBLEM: Article %s in %s ",file,ng->ng_name);
-
- switch( type_of_problem ) {
- case NAME_PROB:
- (void) strcat(pmess,"does not support Archive-Name Archiving\n.");
- break;
- case VOL_PROB:
- (void) strcat(pmess,"does not support Volume-Issue Archiving\n.");
- break;
- case TYPE_PROB:
- (void) strcat(pmess,"has an invalid archive TYPE specified\n.");
- break;
- case DUP_PROB:
- if (article.repost != TRUE)
- (void) strcat(pmess,"is a Duplicate article.\n");
- else
- (void) strcat(pmess,"is a Reposted article.\n");
- (void) sprintf(pmess,"%s\tExisting Archived path - %s", pmess,path);
- break;
- }
-
- /* print the message out to the screen, crontab output, etc */
-
- (void) fprintf(errfp,"%s\n",pmess);
-
- /* log the initial detection message. */
-
- record_problem(pmess, file, ng);
-
- /* Handling Repostings.
- **
- ** MV_ORIGINAL
- ** The original article is placed into a "original" directory in
- ** the problems directory (if duplicated). The inbound reposted
- ** article is placed into the archive in the correct position.
- **
- ** ADD_REPOST_SUFFIX
- ** If ADD_REPOST_SUFFIX is defined, all reposts will have the
- ** string specified in REPOST_SUFFIX appended to the archive
- ** filename so that a repost of elm/part07 would appear in
- ** the archive as elm/part07-repost prior to any compression.
- ** The addition of the suffix was done in save_article().
- ** Handle this as the true duplicated article that it is.
- **
- ** No Reposting Defines specified:
- ** The inbound article would be placed into the archive in the
- ** correct position only if the initial article is not in the archive.
- ** Otherwise the reposted article is placed in the problems directory
- ** as a normal duplicate article as it is now.
- */
-
- #ifdef MV_ORIGINAL
- if (article.repost == TRUE) {
- /*
- ** save the duplicated path
- ** Caution: may have compression suffix attached
- */
- (void) strcpy(crnt_path, path);
-
- /* create the storage path for original copy */
- /* no slash needed between Originals and crnt_path below.. */
-
- (void) sprintf(path,"%s/%s%s",problems_dir,"Originals",crnt_path);
-
- /* Display and record the actions */
- (void) sprintf(pmess,"\tMoving %s (original)\n\tto %s",crnt_path,path);
- (void) fprintf(errfp,"%s\n",pmess);
- record_problem(pmess, file, ng);
-
- /* Make any necessary directories along the way. */
- (void) mkparents(path);
-
- /* copy the original out of the way */
- if (copy(crnt_path,path) != 0) {
- (void) fprintf(errfp,"copy failed for %s to %s\n", crnt_path, path);
- return(NULL);
- }
-
- set_ownership(path, ng);
-
- /* restore the destination path for inbound article */
- (void) strcpy(path,crnt_path);
-
- /* remove the existing file */
- (void) unlink(path);
- /*
- ** Must assure that "path" does not have a .Z type
- ** of suffix used in compression. If it does, it must
- ** be removed before continuing. This is cheating and
- ** will probably break but what the hell.
- */
- (void) remove_suffix(path);
- }
- else
-
- #endif /* MV_ORIGINAL */
-
- /*
- ** Build the path string for the location of the article in
- ** the problems directory. Place the file in the appropriate
- ** directory in Article-Number format. In this manner, multiple
- ** problems will be stored as separate files.
- */
-
- (void) sprintf(path,"%s/%s/%s",problems_dir,ng->ng_name,file);
-
- /* Display and record the actions */
- (void) sprintf(pmess,"\tStoring Article %s at %s\n", file, path);
- (void) fprintf(errfp,"%s\n",pmess);
- record_problem(pmess, file, ng);
-
- /* Make any necessary directories along the way. */
- (void) mkparents(path);
-
- if (copy(file,path) != 0) {
- (void) fprintf(errfp,"copy failed for %s to %s\n", file, path);
- return(NULL);
- }
-
- /*
- ** Write the filename to the .archived file in the newsgroup's
- ** BASEDIR directory since we do not want it rearchived tomorrow.
- */
- write_archived(file, path);
-
- /*
- ** Return the path to the stored problem file.
- */
- return(path);
- }
-
- write_patch_log(ng, path)
- struct group_archive *ng;
- char *path;
- {
- char *sp;
- FILE *plfp;
- struct stat sb;
- int hn;
-
- /*
- ** The .patchlog file is used to record the
- ** information specific to patches that come
- ** through the newsgroup.
- **
- ** The format of the .patchlog file is:
- **
- ** path-to-patch initial-volume initial-issue volume issue
- ** bb/patch01 22 105 23 77
- ** v47i022 22 105 23 77
- */
-
- /*
- ** If this is the first time that an entry is written to the
- ** patch log, add a header on top of the file for informational
- ** purposes only...
- */
- if ((stat(ng->patchlog ,&sb) != 0)) {
- plfp = efopen(ng->patchlog,"a+");
-
- (void) fprintf(plfp,"#\n#\tPatch log for %s\n#\n",
- ng->ng_name);
-
- (void) fprintf(plfp,"# %-30s%-11s%-13s%-6s%10s\n",
- "Path To", "Initial", "Initial",
- "Current", "Current");
-
- (void) fprintf(plfp,"# %-30s%-11s%6s%13s%10s\n#\n",
- "Patchfile", "Volume", "Issue", "Volume", "Issue");
- (void) fclose(plfp);
- }
-
- /*
- ** Get rid of the base directory.
- */
- sp = path + (strlen(ng->location)+1);
-
- plfp = efopen(ng->patchlog,"a+");
- (void) fprintf(plfp,"%-24s%12d%12d%12d%11d\n", sp,
- article.patch_volume, article.patch_issue,
- article.volume, article.issue);
- (void) fclose(plfp);
- }
-