home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume19
/
rkive
/
part04
/
news_arc.c
next >
Wrap
C/C++ Source or Header
|
1989-06-29
|
20KB
|
760 lines
/*
**
** 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);
}