home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
300-399
/
ff319.lzh
/
CNewsSrc
/
cnews.src.lzh
/
relay
/
procart.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-07-31
|
14KB
|
451 lines
/*
* process a single incoming article
*/
#ifdef DEBUG
# define STAT(x) { fprintf(stderr,"%s: %d, ",__FUNC__,__LINE__);\
fprintf x;\
fflush(stderr); }
#else
# define STAT(x)
#endif
#include <stdio.h>
#ifndef AMIGA
# include <sys/types.h>
#endif /* AMIGA */
#include "libc.h"
#include "news.h"
#include "active.h"
#include "control.h"
#include "headers.h"
#include "article.h"
#include "history.h"
#include "io.h"
#include "msgs.h"
#include "system.h"
#include "transmit.h"
/*
* COPYSIZE is the length of a bulk-copying buffer: the bigger the better,
* though fewer than 3% of articles exceed 8192 bytes (may 1988).
* It holds header lines first, and later holds bytes of the body.
* This buffer is allocated once at the start and never deallocated.
*/
#ifndef COPYSIZE
# ifdef SMALLMEM
# define COPYSIZE BUFSIZ /* conserve memory at expense of speed */
# else
# define COPYSIZE 8192 /* big enough for worst-case 4.2bsd blks */
# endif /* SMALLMEM */
#endif /* COPYSIZE */
extern char *exclude; /* for erik */
extern boolean okrefusal; /* flag from command line */
/* forwards */
extern void tossorfile(), surveydamage(), reject(), prefuse(), uninsart();
extern char *hdrcopy();
FORWARD void copyart(), cpybody(), insart();
FORWARD statust snuffmayreturn();
/*
* Copy the article on "in" to a temporary name in the news spool directory,
* unlink temp name; *or* copy into the final names, if known early enough.
* (Sets a_tmpf in or near hdrmunge() or hdrdump().)
* If the spool file opened, install the article it contains.
*/
statust cpinsart(in, inname, maxima, blvmax)
FILE *in;
register char *inname;
long maxima;
boolean blvmax; /* believe maxima? */
{
register struct article *artp;
register statust status;
struct article art;
artp = &art;
artinit(artp);
artp->a_blvmax = blvmax;
artp->a_unread = maxima;
/*
* copyart() may reject() the article, and may fill the disk.
* it calls fileart and logs rejected articles.
*/
copyart(artp, in, inname);
if (artp->a_status&ST_REFUSED) {
/* no good ngs (in fileart) or reject()ed; not serious */
warning("rejected article");
artp->a_status &= ~ST_REFUSED;
/* paranoia; shouldn't happen */
nnfclose(artp, &artp->a_artf, inname);
} else if (artp->a_artf == NULL) {
warning("can't open spool file `%s'", artp->a_tmpf);
artp->a_status |= ST_DROPPED;
} else {
nnfclose(artp, &artp->a_artf, inname);
insart(artp); /* logs accepted art.s during transmission */
if (artp->a_status&ST_JUNKED) { /* yer welcome, henry */
artp->a_status &= ~ST_JUNKED;
timestamp(stdout, (time_t *)NULL);
(void) printf(" %s j %s junked due to groups `%s'\n",
sendersite(nullify(artp->h.h_path)),
artp->h.h_msgid, artp->h.h_ngs);
}
}
status = artp->a_status;
artfree(artp);
return status;
}
/*
* Copy the next charcnt bytes of "in" (may be not a disk file)
* to a permanent file under a (possibly) temporary name.
* After the headers are seen, accept or reject the article.
* If rejected and the headers fit in core, no files will be opened.
* Must munge certain headers on the way & remember certain values.
* hdrmunge() or hdrdump() sets art->a_tmpf & art->a_artf.
* Unlink art->a_tmpf, if a temporary link.
*/
/* ARGSUSED inname */
STATIC void copyart(art, in, inname)
register struct article *art;
register FILE *in;
char *inname;
{
boolean installed = YES;
char *body;
body = hdrcopy(art, in);
hdrdeflt(&art->h);
STAT((stderr, "calling tossorfile()\n"));
tossorfile(art, &installed);
/* assertion: header values (art->h) can be forgotten here */
STAT((stderr, "calling cpybody()\n"));
cpybody(art, in, body);
STAT((stderr, "calling surveydamage()\n"));
surveydamage(art, &installed);
}
/*
* The loop copies header lines from input to output or a
* header output cache. On exit, hdr will contain the first
* non-header line, if any, left over from the end of header copying.
*
* Some people think the loop is ugly; I'm not sure why.
* If the byte count is positive, read a line; if it doesn't return
* EOF and is a header, then adjust byte count, stash and munge headers.
* strlen(line) must be computed before hdrstash is called,
* as hdrstash (and thus hdrdigest) removes newlines.
*/
char *hdrcopy(art, in) /* first body line, from gethdr */
register struct article *art;
FILE *in;
{
register char *hdr = NULL;
long limit;
int is_hdr = NO;
hdrwretch(); /* reset the header parser */
limit = (art->a_blvmax? art->a_unread+1: art->a_unread); /* 1 for NUL */
/* 1 is again for NUL */
while (limit > 1 &&
(hdr = gethdr(in, &limit, &is_hdr)) != NULL && is_hdr) {
hdrdigest(art, hdr, strlen(hdr));
hdr = NULL; /* freed inside gethdr */
}
/* If we read a body line, gethdr has adjusted limit appropriately. */
art->a_unread = limit - 1; /* limit updated by gethdr */
if (is_hdr) /* no body: header fills limit */
hdr = NULL;
return hdr;
}
/*
* Either reject the article described by art, or accept it and file it.
* If rejecting it, remove any links and give back assigned #'s
* (art->a_artf may still be open; arguably uninsart should close it).
* If accepting it, dump any saved headers and file the article.
* Unlink art->a_tmpf if it's a temporary link.
*/
void tossorfile(art, installedp)
register struct article *art;
boolean *installedp;
{
STAT((stderr, "calling reject()\n"));
reject(art); /* duplicate, etc.? */
if (art->a_status&(ST_DROPPED|ST_REFUSED)) {
STAT((stderr, "calling uninsart()\n"));
uninsart(art);
*installedp = NO;
} else {
STAT((stderr, "not rejected; calling hdrdump()\n"));
hdrdump(art, ALLHDRS); /* ALLHDRS triggers fileart */
}
STAT((stderr, "checking links\n"));
if (art->a_unlink) {
/* a_tmpf has had links made to it, so it can be removed. */
if (unlink(art->a_tmpf) < 0) {
warning("copyart can't unlink `%s'", art->a_tmpf);
art->a_status |= ST_ACCESS;
}
art->a_unlink = NO; /* caution */
}
}
/*
* Copy article body.
*
* body will contain the first non-header line, if any, left over from the
* end of header copying. Write it.
*
* Copy at most COPYSIZE bytes of body at a time and exactly art->a_unread
* bytes in total, barring EOF or a full disk. Then "block" is no longer
* needed. Force the article to disk, mostly for the benefit of control
* message processing.
*
* The copying buffer, block, is static because it is used repeatedly
* and persists through most of execution, so dynamic allocation and
* deallocation seems wasteful, but also for the benefit of compilers
* for odd machines (e.g. PE, 370s) which make implementing "large"
* automatic arrays difficult.
*/
STATIC void cpybody(art, in, body)
register struct article *art;
FILE *in;
register char *body;
{
register int readcnt;
static char block[COPYSIZE];
if (body != NULL) { /* read too far? */
register int bodylen = strlen(body);
STAT((stderr, "writing first line of body\n"));
if (art->a_artf && fwrite(body, 1, bodylen, art->a_artf) != bodylen)
fulldisk(art, spoolnm(art));
art->a_charswritten += bodylen;
}
STAT((stderr, "read blocks\n"));
#ifdef FJE
while (art->a_unread > 0 && !(art->a_status & ST_DISKFULL)) {
Chk_Abort(0L);
readcnt=fread(block,1, (int) min(art->a_unread, COPYSIZE),in);
STAT((stderr, "read %d bytes\n", readcnt));
if (readcnt < 1)
break;
STAT((stderr, "attempting to write block\n"));
if (art->a_artf && fwrite(block, 1, readcnt, art->a_artf) != readcnt)
fulldisk(art, spoolnm(art));
art->a_unread -= readcnt;
art->a_charswritten += readcnt;
}
#else
for (; art->a_unread > 0 && !(art->a_status&ST_DISKFULL) &&
(readcnt=fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) > 0;
art->a_unread -= readcnt, art->a_charswritten += readcnt) {
if (art->a_artf != NULL &&
fwrite(block, 1, readcnt, art->a_artf) != readcnt)
fulldisk(art, spoolnm(art));
fputc('.', stderr);
}
fputc('\n', stderr);
#endif /* FJE */
STAT((stderr, "about to fflush()\n"));
if (art->a_artf != NULL && fflush(art->a_artf) == EOF)
fulldisk(art, spoolnm(art));
STAT((stderr, "returning to caller\n"));
}
/*
* If not yet uninstalled, and the disk filled, uninstall this article
* to remove any zero-length links and decrement the active article number.
* The ST_DISKFULL status will prevent a history entry from being generated.
*/
void surveydamage(art, installedp)
register struct article *art;
register boolean *installedp;
{
if (art->a_unread > 0 && art->a_blvmax) {
(void) fprintf(stderr, "%s: article %s short by %ld bytes\n",
progname, (art->h.h_msgid != NULL? art->h.h_msgid: ""),
(long)art->a_unread);
art->a_status |= ST_SHORT; /* NB.: don't uninstall this art. */
}
if (*installedp && art->a_status&ST_DISKFULL) {
uninsart(art);
*installedp = NO;
}
#ifdef WATCHCORE
{
char stbot;
extern char *sbrk();
printf("debugging memory use: top of data=%u", (unsigned)sbrk(0));
printf(", bottom of stack=%u\n", (unsigned)&stbot);
}
#endif
}
/*
* Install the article on art->a_tmpf or art->a_files:
* The article should have been accepted and filed in copyart().
* Add history entries for the article. Log arrival.
* Transmit the article to our neighbours.
* Process control mess(age)es. ctlmsg can call transmit(fakeart,x)
* and generate log lines for cancels and ihave/sendme.
*/
STATIC void insart(art)
register struct article *art;
{
if (!(art->a_status&(ST_DROPPED|ST_REFUSED|ST_DISKFULL))) {
if (!art->a_filed) /* paranoia */
(void) fprintf(stderr, "%s: %s not filed by copyart!\n",
progname, art->h.h_msgid);
history(art, STARTLOG);
transmit(art, exclude); /* writes systems on stdout */
(void) putchar('\n'); /* ends the log line */
if (art->h.h_ctlcmd != NULL)
ctlmsg(art);
#ifdef notdef /* it's only a log file! */
(void) fflush(stdout); /* crash-proofness */
#endif
}
art->a_status &= ~ST_REFUSED; /* refusal is quite casual & common */
}
/*
* Reject articles. This can be arbitrarily picky.
* Only the headers are used to decide, so this can be called before
* the article is filed.
* Be sure to put the fastest tests first, especially if they often result
* in rejections.
*/
void reject(art)
register struct article *art;
{
if (art->h.h_path == NULL) {
prefuse(art);
(void) printf("no Path: header\n");
} else if (alreadyseen(art->h.h_msgid)) {
STAT((stderr, "called alreadyseen()\n"));
prefuse(art);
(void) printf("duplicate\n");
} else if (art->h.h_path != NULL && hopcount(art->h.h_path) > 0 &&
!ngmatch(oursys()->sy_ngs, art->h.h_ngs)) {
extern boolean histreject;
STAT((stderr, "called alreadyseen(), hopcount(), & ngmatch()\n"));
/*
* non-local article, with all bad groups.
* (local articles with bad groups will be bounced
* by fileart when the groups aren't in active.)
*/
if (histreject)
history(art, NOLOG);
prefuse(art);
(void) printf("no subscribed groups in `%s'\n", art->h.h_ngs);
} else if (art->h.h_approved == NULL && moderated(art->h.h_ngs)) {
STAT((stderr, "moderated and no approval\n"));
prefuse(art);
(void) printf("unapproved article in moderated group(s) `%s'\n",
art->h.h_ngs);
} else
return; /* art was accepted */
art->a_status |= ST_REFUSED;
if (!okrefusal)
art->a_status |= ST_DROPPED;
}
/*
* print the leader of a refusal message about the article in "art".
*/
void prefuse(art)
register struct article *art;
{
timestamp(stdout, (time_t *)NULL);
(void) printf(" %s - %s ", sendersite(nullify(art->h.h_path)),
art->h.h_msgid);
}
/*
* "Uninstall" an article: remove art->a_files (permanent names) and
* a_tmpf (temporary name if a_unlink set), and return assigned article #'s.
* If a_unlink isn't set, a_tmpf is a copy of the first link in art->a_files.
* Must be called before history() is called, else there will be a
* history entry for the article, but no spool files.
*/
void uninsart(art)
register struct article *art;
{
if (art->a_unlink && art->a_tmpf != NULL) {
(void) unlink(art->a_tmpf); /* I don't wanna know... */
art->a_unlink = NO;
}
/* return article numbers (YES) & ignore unlink errors */
(void) snuffmayreturn(art->a_files, YES);
}
statust snufffiles(filelist) /* just unlink all files in filelist */
char *filelist;
{
/* don't return article numbers (NO) & return unlink errors */
return snuffmayreturn(filelist, NO);
}
/*
* Unlink all files in filelist, and optionally return article numbers.
* When removing a link, note any failure, but don't issue an error message.
* For one thing, cancel controls fail routinely because the article has been
* removed manually or never existed (a previous cancel arrived before its
* subject and generated a fake history entry).
*/
STATIC statust snuffmayreturn(filelist, artret)
char *filelist;
boolean artret; /* return article numbers & note unlink errors? */
{
register statust status = ST_OKAY;
register char *arts, *spacep, *slashp, *artnm;
/* this is a deadly tedious job and I really should automate it */
for (arts = filelist; arts != NULL && arts[0] != '\0';
arts = (spacep == NULL? NULL: spacep+1)) {
spacep = index(arts, ' ');
if (spacep != NULL)
spacep[0] = '\0'; /* will be restored below */
artnm = strsave(arts);
if (spacep != NULL)
spacep[0] = ' '; /* restore space */
slashp = index(artnm, FNDELIM);
if (slashp != NULL)
slashp[0] = '\0'; /* will be restored below */
if (artret)
/* prevartnum will complain on i/o error to active */
(void) prevartnum(artnm); /* return assigned # */
if (slashp != NULL)
slashp[0] = FNDELIM; /* restore slash */
mkfilenm(artnm);
if (unlink(artnm) < 0)
status |= ST_ACCESS;
free(artnm);
}
return status;
}