home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 1
/
GoldFishApril1994_CD2.img
/
d4xx
/
d473
/
cnewssrc
/
cnews_src.lzh
/
relay
/
procart.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-12-09
|
14KB
|
461 lines
/* :ts=4
* process a single incoming article
*
* $Log$
*/
#include <stdio.h>
#ifdef unix
# include <sys/types.h>
#endif /* unix */
#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.
*/
#ifdef AMIGA
# define COPYSIZE (BUFSIZ*8)
#else /* !AMIGA */
# ifndef COPYSIZE
# ifdef SMALLMEM
# define COPYSIZE BUFSIZ /* conserve memory at the expense of speed */
# else
# define COPYSIZE 8192 /* big enuf even for worst-case 4.2bsd blocks */
# endif /* SMALLMEM */
# endif /* COPYSIZE */
#endif /* AMIGA */
extern char *exclude; /* for erik */
/* imports */
extern void decline();
/* 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. it may call uninsart.
*/
copyart(artp, in, inname);
if (artp->a_status&ST_REFUSED) {
/* no good ngs (in fileart) or reject()ed; not serious */
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);
tossorfile(art, &installed);
/* assertion: header values (art->h) can be forgotten here */
cpybody(art, in, body);
surveydamage(art, &installed);
#ifdef AMIGA /* Duplicate code in tossorfile() */
if (art->a_unlink) {
/*
* On the Amiga, the unlink fails if the file is still open
* (the Lock still exists...) so we do the unlink here...
*
* 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 */
}
#endif /* AMIGA */
}
/*
* 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 * /* first body line, from gethdr */
hdrcopy(art, in)
register struct article *art;
FILE *in;
{
register char *hdr = NULL;
long limit = art->a_unread + SIZENUL;
int is_hdr = NO;
while (limit > SIZENUL && (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 - SIZENUL;
/* if is_hdr, there is no body: header fills limit */
return (is_hdr? NULL: 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;
{
reject(art); /* duplicate, etc.? */
if (art->a_status&(ST_DROPPED|ST_REFUSED)) {
uninsart(art);
*installedp = NO;
} else
hdrdump(art, ALLHDRS); /* ALLHDRS triggers fileart */
#ifndef AMIGA
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 */
}
#endif /* !AMIGA */
}
/*
* 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;
{
#ifdef AMIGA
register size_t linecnt = 0;
extern size_t input_lines;
#endif /* AMIGA */
register int readcnt;
static char block[COPYSIZE+1];
if (body != NULL) { /* read too far? */
register int bodylen = strlen(body);
if (art->a_artf != NULL &&
fwrite(body, 1, bodylen, art->a_artf) != bodylen)
fulldisk(art, spoolnm(art));
art->a_charswritten += bodylen;
}
#ifdef notdef
while (art->a_unread > 0 && !(art->a_status&ST_NEEDATTN) && !feof(in)) {
readcnt = fread(block, 1, (int) min(art->a_unread, COPYSIZE), in);
art->a_unread -= readcnt;
art->a_charswritten += readcnt;
if (art->a_artf && fwrite(block, 1, readcnt, art->a_artf) != readcnt)
fulldisk(art, spoolnm(art));
}
#else
block[COPYSIZE] = '\0';
for (; art->a_unread > 0 && !(art->a_status&ST_NEEDATTN) && !feof(in) &&
(readcnt=fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) > 0;
art->a_unread -= readcnt, art->a_charswritten += readcnt) {
register char *x = block;
while (x = strchr(x, '\n'))
linecnt++, x++;
if (art->a_artf != NULL &&
fwrite(block, 1, readcnt, art->a_artf) != readcnt)
fulldisk(art, spoolnm(art));
}
input_lines = linecnt;
#endif /* FJE */
if (art->a_artf != NULL && fflush(art->a_artf) == EOF)
fulldisk(art, spoolnm(art));
}
/*
* If not yet uninstalled, and the disk filled (or the news system was found
* to be otherwise unwell), uninstall this article
* to remove any (zero-length) links and decrement the active article number.
* The ST_NEEDATTN status will prevent a history entry being generated later.
*/
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_NEEDATTN) {
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
}
/*
* If nothing has gone wrong yet,
* 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_NEEDATTN))) {
if (!art->a_filed) /* paranoia */
(void) fprintf(stderr, "%s: %s not filed by copyart!\n",
progname, art->h.h_msgid);
history(art, STARTLOG); /* history may be unwritable */
if (art->a_status&(ST_DROPPED|ST_REFUSED|ST_NEEDATTN))
uninsart(art); /* it was; can't keep the article */
else {
transmit(art, exclude); /* writes systems on stdout */
(void) putchar('\n'); /* ends the log line */
ctlmsg(art); /* NCMP */
#ifdef FLUSHLOG
(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;
{
register char *msgid = art->h.h_msgid;
register char *path = art->h.h_path;
register char *ngs = art->h.h_ngs;
if (path == NULL) {
prefuse(art);
(void) printf("no Path: header\n");
} else if (!msgidok(art))
/* already complained */ ;
else if (alreadyseen(msgid)) {
prefuse(art);
(void) printf("duplicate\n");
} else if (path != NULL && hopcount(path) > 0 &&
!ngmatch(oursys()->sy_ngs, ngs)) {
extern boolean histreject;
/*
* 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", ngs);
} else if (art->h.h_approved == NULL && moderated(ngs)) {
prefuse(art);
(void) printf("unapproved article in moderated group(s) `%s'\n",
ngs);
} else
return; /* art was accepted */
decline(art);
}
/*
* 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 (or after it has failed),
* else there will be a history entry for the article, but no spool files.
* insart() need not be called first.
*/
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 = strchr(arts, ' ');
if (spacep != NULL)
spacep[0] = '\0'; /* will be restored below */
artnm = strsave(arts);
if (spacep != NULL)
spacep[0] = ' '; /* restore space */
slashp = strchr(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;
}