/*
+ * $Id$
+ *
* Citadel/UX Intelligent Network Processor for IGnet/Open networks
* See copyright.txt for copyright information
- * $Id$
+ *
*/
/* How long it takes for an old node to drop off the network map */
#define EXPIRY_TIME (2592000L)
+/* How long we keep recently arrived messages in the use table */
+#define USE_TIME (604800L)
+
/* Where do we keep our lock file? */
-#define LOCKFILE "/var/lock/LCK.netproc"
+#define LOCKFILE "/tmp/netproc.LCK"
/* Path to the 'uudecode' utility (needed for network file transfers) */
#define UUDECODE "/usr/bin/uudecode"
+/* Files used by the networker */
+#define MAILSYSINFO "./network/mail.sysinfo"
+
/* Uncomment the DEBUG def to see noisy traces */
-/* #define DEBUG 1 */
+#define DEBUG 1
#include "sysdep.h"
#include <signal.h>
#include <errno.h>
#include <syslog.h>
-#include <gdbm.h>
#include "citadel.h"
#include "tools.h"
+#include "ipc.h"
/* A list of users you wish to filter out of incoming traffic can be kept
* in ./network/filterlist -- messages from these users will be automatically
struct filterlist *next;
char f_person[64];
char f_room[64];
+ char f_system[64];
};
struct syslist {
};
-void attach_to_server(int argc, char **argv);
+
+/*
+ * This structure is used to hold all of the fields of a message
+ * during conversion, processing, or whatever.
+ */
+struct minfo {
+ char A[512];
+ char B[512];
+ char C[512];
+ char D[512];
+ char E[512];
+ char G[512];
+ char H[512];
+ char I[512];
+ char N[512];
+ char O[512];
+ char P[512];
+ char R[512];
+ char S[512];
+ long T;
+ char U[512];
+ char Z[512];
+ char nexthop[512];
+ };
+
+
+struct usetable {
+ struct usetable *next;
+ char msgid[256];
+ time_t timestamp;
+};
+
+
+
+
void serv_read(char *buf, int bytes);
void serv_write(char *buf, int nbytes);
void get_config(void);
struct filterlist *filter = NULL;
struct syslist *slist = NULL;
+struct msglist *purgelist = NULL;
+struct usetable *usetable = NULL;
struct config config;
extern char bbs_home_directory[];
/*
- * we also load the network/mail.sysinfo table into memory, make changes
+ * we also load the mail.sysinfo table into memory, make changes
* as we learn more about the network from incoming messages, and write
* the table back to disk when we're done.
*/
char insys = 0;
char buf[128];
- fp = fopen("network/mail.sysinfo", "r");
+ fp = fopen(MAILSYSINFO, "r");
if (fp == NULL)
return (1);
time_t now;
time(&now);
- newfp = fopen("network/mail.sysinfo", "w");
+ newfp = fopen(MAILSYSINFO, "w");
for (stemp = slist; stemp != NULL; stemp = stemp->next) {
if (!strcasecmp(stemp->s_name, config.c_nodename)) {
time(&stemp->s_lastcontact);
return 1;
}
lfp = fopen(LOCKFILE, "w");
+ if (lfp == NULL) {
+ syslog(LOG_NOTICE, "Cannot create %s: %s", LOCKFILE,
+ strerror(errno));
+ return(1);
+ }
fprintf(lfp, "%ld\n", (long) getpid());
fclose(lfp);
return (0);
fp = fopen("./network/filterlist", "r");
if (fp == NULL)
return;
- while (fgets(sbuf, 256, fp) != NULL) {
+ while (fgets(sbuf, sizeof sbuf, fp) != NULL) {
if (sbuf[0] != '#') {
sbuf[strlen(sbuf) - 1] = 0;
fbuf = (struct filterlist *)
return (0);
}
-int get_sysinfo_type(char *name)
-{ /* determine routing from sysinfo file */
+/*
+ * Determine routing from sysinfo file
+ */
+int get_sysinfo_type(char *name) {
struct syslist *stemp;
- GETSN:for (stemp = slist; stemp != NULL; stemp = stemp->next) {
+
+GETSN: for (stemp = slist; stemp != NULL; stemp = stemp->next) {
if (!strcasecmp(stemp->s_name, name)) {
if (!strcasecmp(stemp->s_type, "use")) {
strcpy(name, stemp->s_nexthop);
}
-void fpgetfield(FILE * fp, char *string)
+void fpgetfield(FILE *fp, char *string, int limit)
{
int a, b;
a = 0;
do {
b = getc(fp);
- if (b < 1) {
+ if ((b < 1) || (a >= limit)) {
string[a] = 0;
return;
}
* Load all of the fields of a message, except the actual text, into a
* table in memory (so we know how to process the message).
*/
-void msgfind(char *msgfile, struct minfo *buffer)
+void fpmsgfind(FILE *fp, struct minfo *buffer)
{
int b, e, mtype, aflag;
char bbb[1024];
char userid[1024];
- FILE *fp;
strcpy(userid, "");
- fp = fopen(msgfile, "rb");
- if (fp == NULL) {
- syslog(LOG_ERR, "can't open message file: %s", strerror(errno));
- return;
- }
e = getc(fp);
if (e != 255) {
- syslog(LOG_ERR, "incorrect message format");
+ syslog(LOG_ERR, "Magic number check failed for this message");
goto END;
}
+
+ memset(buffer, 0, sizeof(struct minfo));
mtype = getc(fp);
aflag = getc(fp);
- buffer->I = 0L;
- buffer->R[0] = 0;
- buffer->E[0] = 0;
- buffer->H[0] = 0;
- buffer->S[0] = 0;
- buffer->B[0] = 0;
- buffer->G[0] = 0;
-
- BONFGM:b = getc(fp);
+
+BONFGM: b = getc(fp);
if (b < 0)
goto END;
if (b == 'M')
goto END;
- fpgetfield(fp, bbb);
+ fpgetfield(fp, bbb, sizeof bbb);
while ((bbb[0] == ' ') && (strlen(bbb) > 1))
strcpy(bbb, &bbb[1]);
if (b == 'A') {
if (b == 'T')
buffer->T = atol(bbb);
if (b == 'I')
- buffer->I = atol(bbb);
+ strcpy(buffer->I, bbb);
if (b == 'H')
strcpy(buffer->H, bbb);
if (b == 'B')
strcpy(buffer->G, bbb);
if (b == 'E')
strcpy(buffer->E, bbb);
+ if (b == 'Z')
+ strcpy(buffer->Z, bbb);
goto BONFGM;
- END:if (buffer->I == 0L)
- buffer->I = buffer->T;
+END:
+
+}
+
+
+/*
+ * msgfind() is the same as fpmsgfind() except it accepts a filename
+ * instead of a file handle.
+ */
+void msgfind(char *msgfile, struct minfo *buffer) {
+ FILE *fp;
+
+ fp = fopen(msgfile, "rb");
+ if (fp == NULL) {
+ syslog(LOG_ERR, "can't open %s: %s", msgfile, strerror(errno));
+ return;
+ }
+
+ fpmsgfind(fp, buffer);
fclose(fp);
}
+
+
+
+
void ship_to(char *filenm, char *sysnm)
{ /* send spool file filenm to system sysnm */
char sysflnm[100];
do {
a = getc(tfp);
if (a != 'M') {
- fpgetfield(tfp, buf);
+ fpgetfield(tfp, buf, sizeof buf);
if (a == 'O') {
strcpy(dest_room, buf);
}
+/*
+ * Generate a Message-ID string for the use table
+ */
+void strmsgid(char *buf, struct minfo *msginfo) {
+ int i;
+
+ strcpy(buf, msginfo->I);
+ if (strchr(buf, '@') == NULL) {
+ strcat(buf, "@");
+ strcat(buf, msginfo->N);
+ }
+
+ for (i=0; i<strlen(buf); ++i) {
+ if (isspace(buf[i])) {
+ strcpy(&buf[i], &buf[i+1]);
+ }
+ buf[i] = tolower(buf[i]);
+ }
+}
+
+
+
+/*
+ * Check the use table to see if a message has been here before.
+ * Returns 1 if the message is a duplicate; otherwise, it returns
+ * 0 and the message ID is added to the use table.
+ */
+int already_received(struct minfo *msginfo) {
+ char buf[256];
+ struct usetable *u;
+ time_t now;
+
+ /* We can't check for dups on a zero msgid, so just pass them through */
+ if (strlen(msginfo->I)==0) {
+ return 0;
+ }
+
+ strmsgid(buf, msginfo);
+ now = time(NULL);
+
+ /* Set return value to 1 if message exists */
+ for (u=usetable; u!=NULL; u=u->next) {
+ if (!strcasecmp(buf, u->msgid)) {
+ u->timestamp = time(NULL); /* keep it fresh */
+ return(1);
+ }
+ }
+
+ /* Not found, so we're ok, but add it to the use table now */
+ u = (struct usetable *) malloc(sizeof (struct usetable));
+ u->next = usetable;
+ u->timestamp = time(NULL);
+ strncpy(u->msgid, buf, 255);
+ usetable = u;
+
+ return(0);
+}
+
/*
- * Purge any old entries out of the use table
+ * Load the use table from disk
*/
-void purge_use_table(GDBM_FILE ut) {
+void read_use_table(void) {
+ struct usetable *u;
+ struct usetable ubuf;
+ FILE *fp;
+
+ unlink("data/usetable.gdbm"); /* we don't use this anymore */
+
+ fp = fopen("usetable", "rb");
+ if (fp == NULL) return;
+
+ while (fread(&ubuf, sizeof (struct usetable), 1, fp) > 0) {
+ u = (struct usetable *) malloc(sizeof (struct usetable));
+ memcpy(u, &ubuf, sizeof (struct usetable));
+ u->next = usetable;
+ usetable = u;
+ }
+
+ fclose(fp);
}
-/* FIX ... this isn't even close to being finished yet.
- * Here's what needs to be done:
- * 1. Write an entry for each received message into the table
- * 2. Check incoming messages against the table
- * 3. Post a list of rejected messages
- * 4. Purge entries more than a few days old
+
+
+/*
+ * Purge any old entries out of the use table as we write them back to disk.
+ *
*/
+void write_use_table(void) {
+ struct usetable *u;
+ time_t now;
+ FILE *fp;
+
+ now = time(NULL);
+ fp = fopen("usetable", "wb");
+ if (fp == NULL) return;
+ for (u=usetable; u!=NULL; u=u->next) {
+ if ((now - u->timestamp) <= USE_TIME) {
+ fwrite(u, sizeof(struct usetable), 1, fp);
+ }
+ }
+ fclose(fp);
+}
*/
void inprocess(void)
{
- FILE *fp, *message, *testfp, *ls;
+ FILE *fp, *message, *testfp, *ls, *duplist;
static struct minfo minfo;
- struct recentmsg recentmsg;
char tname[128], aaa[1024], iname[256], sfilename[256], pfilename[256];
int a, b;
int FieldID;
char buf[256];
long msglen;
int bloklen;
- GDBM_FILE use_table;
+ int valid_msg;
/* temp file names */
- sprintf(tname, tmpnam(NULL));
- sprintf(iname, tmpnam(NULL));
+ sprintf(tname, "%s.netproc.%d", tmpnam(NULL), __LINE__);
+ sprintf(iname, "%s.netproc.%d", tmpnam(NULL), __LINE__);
load_filterlist();
/* Make sure we're in the right directory */
chdir(bbs_home_directory);
- /* Open the use table */
- use_table = gdbm_open("./data/usetable.gdbm", 512,
- GDBM_WRCREAT, 0600, 0);
- if (use_table == NULL) {
- syslog(LOG_ERR, "could not open use table: %s",
- strerror(errno));
- }
+ /* temporary file to contain a log of rejected dups */
+ duplist = tmpfile();
+
/* Let the shell do the dirty work. Get all data from spoolin */
do {
sprintf(aaa, "cd %s/network/spoolin; ls", bbs_home_directory);
SKIP: ptr = fgets(sfilename, sizeof sfilename, ls);
if (ptr != NULL) {
sfilename[strlen(sfilename) - 1] = 0;
+#ifdef DEBUG
+ syslog(LOG_DEBUG,
+ "Trying <%s>", sfilename);
+#endif
if (!strcmp(sfilename, ".")) goto SKIP;
if (!strcmp(sfilename, "..")) goto SKIP;
if (!strcmp(sfilename, "CVS")) goto SKIP;
goto ENDSTR;
/* This crates the temporary file. */
+ valid_msg = 1;
message = fopen(tname, "wb");
if (message == NULL) {
- syslog(LOG_ERR, "error creating %s: %s", tname, strerror(errno));
+ syslog(LOG_ERR, "error creating %s: %s",
+ tname, strerror(errno));
goto ENDSTR;
}
putc(255, message); /* 0xFF (start-of-message) */
a = getc(fp);
putc(a, message); /* mode */
do {
- FieldID = getc(fp); /* Header field ID */
- putc(FieldID, message);
- do {
- a = getc(fp);
- putc(a, message);
- } while (a > 0);
+ FieldID = getc(fp); /* Header field ID */
+ if (isalpha(FieldID)) {
+ putc(FieldID, message);
+ do {
+ a = getc(fp);
+ if (a < 127) putc(a, message);
+ } while (a > 0);
+ if (a != 0) putc(0, message);
+ }
+ else { /* Invalid field ID; flush it */
+ do {
+ a = getc(fp);
+ } while (a > 0);
+ valid_msg = 0;
+ }
} while ((FieldID != 'M') && (a >= 0));
/* M is always last */
+ if (FieldID != 'M') valid_msg = 0;
msglen = ftell(message);
fclose(message);
+ if (!valid_msg) {
+ unlink(tname);
+ goto NXMSG;
+ }
+
/* process the individual mesage */
- minfo.D[0] = 0;
- minfo.C[0] = 0;
- minfo.B[0] = 0;
- minfo.G[0] = 0;
- minfo.R[0] = 0;
msgfind(tname, &minfo);
- strncpy(recentmsg.RMnodename, minfo.N, 9);
- recentmsg.RMnodename[9] = 0;
- recentmsg.RMnum = minfo.I;
syslog(LOG_NOTICE, "#%ld fm <%s> in <%s> @ <%s>",
minfo.I, minfo.A, minfo.O, minfo.N);
if (strlen(minfo.R) > 0) {
if (strlen(minfo.G) > 0)
strcpy(stemp->s_gdom, minfo.G);
}
+
+ /* Check the use table; reject message if it's been here before */
+ if (already_received(&minfo)) {
+ syslog(LOG_NOTICE, "rejected duplicate message");
+ fprintf(duplist, "#<%s> fm <%s> in <%s> @ <%s>\n",
+ minfo.I, minfo.A, minfo.O, minfo.N);
+ }
+
+
/* route the message if necessary */
- if ((strcasecmp(minfo.D, NODENAME)) && (minfo.D[0] != 0)) {
+ else if ((strcasecmp(minfo.D, NODENAME)) && (minfo.D[0] != 0)) {
a = get_sysinfo_type(minfo.D);
syslog(LOG_NOTICE, "routing message to system <%s>", minfo.D);
fflush(stdout);
/* message falls into the bit bucket? */
}
}
+
/* check to see if it's a file transfer */
else if (!strncasecmp(minfo.S, "FILE", 4)) {
proc_file_transfer(tname);
}
+
/* otherwise process it as a normal message */
else {
-
if (!strcasecmp(minfo.R, "postmaster")) {
strcpy(minfo.R, "");
strcpy(minfo.C, "Aide");
}
fclose(message);
+
}
unlink(tname);
goto NXMSG;
- ENDSTR:fclose(fp);
+ENDSTR: fclose(fp);
unlink(pfilename);
}
} while (ptr != NULL);
unlink(iname);
- purge_use_table(use_table);
- gdbm_close(use_table);
+
+ /*
+ * If dups were rejected, post a message saying so
+ */
+ if (ftell(duplist)!=0L) {
+ fp = fopen("./network/spoolin/ctdl_rejects", "ab");
+ if (fp != NULL) {
+ fprintf(fp, "%cA%c", 255, 1);
+ fprintf(fp, "T%ld%c", time(NULL), 0);
+ fprintf(fp, "ACitadel%c", 0);
+ fprintf(fp, "OAide%cM", 0);
+ fprintf(fp, "The following duplicate messages"
+ " were rejected:\n \n");
+ rewind(duplist);
+ while (fgets(buf, sizeof(buf), duplist) != NULL) {
+ buf[strlen(buf)-1] = 0;
+ fprintf(fp, " %s\n", buf);
+ }
+ fprintf(fp, "%c", 0);
+ pclose(fp);
+ }
+ }
+
+ fclose(duplist);
+
}
}
/*
- * implement split horizon algorithm
+ * Implement split horizon algorithm (prevent infinite spooling loops
+ * by refusing to send any node a message which already contains its
+ * nodename in the path).
*/
-int ismsgok(long int mpos, FILE * mmfp, char *sysname)
+int ismsgok(FILE *mmfp, char *sysname)
{
int a;
int ok = 0; /* fail safe - no path, don't send it */
char fbuf[256];
- fseek(mmfp, mpos, 0);
+ fseek(mmfp, 0L, 0);
if (getc(mmfp) != 255)
return (0);
getc(mmfp);
getc(mmfp);
while (a = getc(mmfp), ((a != 'M') && (a != 0))) {
- fpgetfield(mmfp, fbuf);
+ fpgetfield(mmfp, fbuf, sizeof fbuf);
if (a == 'P') {
ok = checkpath(fbuf, sysname);
}
+
+/*
+ * Add a message to the list of messages to be deleted off the local server
+ * at the end of this run.
+ */
+void delete_locally(long msgid, char *roomname) {
+ struct msglist *mptr;
+
+ mptr = (struct msglist *) malloc(sizeof(struct msglist));
+ mptr->next = purgelist;
+ mptr->m_num = msgid;
+ strcpy(mptr->m_rmname, roomname);
+ purgelist = mptr;
+}
+
+
+
+/*
+ * Delete all messages on the purge list from the local server.
+ */
+void process_purgelist(void) {
+ char curr_rm[ROOMNAMELEN];
+ char buf[256];
+ struct msglist *mptr;
+
+
+ strcpy(curr_rm, "__nothing__");
+ while (purgelist != NULL) {
+ if (strcasecmp(curr_rm, purgelist->m_rmname)) {
+ sprintf(buf, "GOTO %s", purgelist->m_rmname);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0] == '2') {
+ extract(curr_rm, &buf[4], 0);
+ }
+ else {
+ syslog(LOG_ERR, "%s", buf);
+ }
+ }
+ if (!strcasecmp(curr_rm, purgelist->m_rmname)) {
+ syslog(LOG_NOTICE, "Purging <%ld> in <%s>",
+ purgelist->m_num, purgelist->m_rmname);
+ sprintf(buf, "DELE %ld", purgelist->m_num);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0] != '2') {
+ syslog(LOG_ERR, "%s", buf);
+ }
+
+ }
+ mptr = purgelist->next;
+ free(purgelist);
+ purgelist = mptr;
+ }
+}
+
+
+
+
/* spool list of messages to a file */
/* returns # of msgs spooled */
int spool_out(struct msglist *cmlist, FILE * destfp, char *sysname)
{
struct msglist *cmptr;
FILE *mmfp;
- char fbuf[128];
+ char fbuf[1024];
int a;
int msgs_spooled = 0;
long msg_len;
int blok_len;
+ static struct minfo minfo;
char buf[256];
char curr_rm[256];
}
/* download the message from the server... */
mmfp = tmpfile();
+ if (mmfp == NULL) {
+ syslog(LOG_NOTICE, "tmpfile() failed: %s\n",
+ strerror(errno) );
+ }
sprintf(buf, "MSG3 %ld", cmptr->m_num);
serv_puts(buf);
serv_gets(buf);
rewind(mmfp);
- if (ismsgok(0L, mmfp, sysname)) {
+ if (ismsgok(mmfp, sysname)) {
++msgs_spooled;
fflush(stdout);
fseek(mmfp, 0L, 0);
fread(fbuf, 3, 1, mmfp);
fwrite(fbuf, 3, 1, destfp);
while (a = getc(mmfp), ((a != 0) && (a != 'M'))) {
- if (a != 'C')
+ if (a != 'C') {
putc(a, destfp);
- fpgetfield(mmfp, fbuf);
- if (a == 'P')
+ }
+ fpgetfield(mmfp, fbuf, sizeof fbuf);
+ if (a == 'P') {
fprintf(destfp, "%s!", NODENAME);
- if (a != 'C')
+ }
+ if (a != 'C') {
fwrite(fbuf, strlen(fbuf) + 1, 1, destfp);
+ }
+ if (a == 'S') if (!strcasecmp(fbuf, "CANCEL")) {
+ delete_locally(cmptr->m_num, cmptr->m_rmname);
+ }
}
if (a == 'M') {
fprintf(destfp, "C%s%c",
putc(a, destfp);
} while (a > 0);
}
+
+ /* Get this message into the use table, so we can reject it
+ * if a misconfigured remote system sends it back to us.
+ */
+ fseek(mmfp, 0L, 0);
+ fpmsgfind(mmfp, &minfo);
+ already_received(&minfo);
+
}
fclose(mmfp);
}
char tempflnm[64];
char buf[256];
struct msglist *cmlist = NULL;
+ struct msglist *cmlast = NULL;
struct rmlist *crmlist = NULL;
struct rmlist *rmptr, *rmptr2;
- struct msglist *cmptr, *cmptr2;
+ struct msglist *cmptr;
FILE *sysflfp, *tempflfp;
- int outgoing_msgs;
+ int outgoing_msgs = 0;
long thismsg;
- sprintf(tempflnm, tmpnam(NULL));
+ sprintf(tempflnm, "%s.netproc.%d", tmpnam(NULL), __LINE__);
tempflfp = fopen(tempflnm, "w");
if (tempflfp == NULL)
return;
cmptr->m_num = thismsg;
strcpy(cmptr->m_rmname, rmptr->rm_name);
- if (cmlist == NULL)
+ if (cmlist == NULL) {
cmlist = cmptr;
+ }
else {
- cmptr2 = cmlist;
- while (cmptr2->next != NULL)
- cmptr2 = cmptr2->next;
- cmptr2->next = cmptr;
+ cmlast->next = cmptr;
}
+ cmlast = cmptr;
+ ++outgoing_msgs;
}
} else { /* print error from "msgs all" */
syslog(LOG_ERR, "%s", buf);
}
}
- outgoing_msgs = 0;
- cmptr2 = cmlist; /* this loop counts the messages */
- while (cmptr2 != NULL) {
- ++outgoing_msgs;
- cmptr2 = cmptr2->next;
- }
syslog(LOG_NOTICE, "%d messages to be spooled to %s",
outgoing_msgs, sysname);
/*
* Spool out the messages, but only if there are any.
*/
- if (outgoing_msgs != 0)
+ if (outgoing_msgs != 0) {
outgoing_msgs = spool_out(cmlist, tempflfp, sysname);
- syslog(LOG_NOTICE, "%d messages actually spooled",
- outgoing_msgs);
+ }
+
+ syslog(LOG_NOTICE, "%d messages actually spooled", outgoing_msgs);
/*
* Deallocate list of spooled messages.
void np_attach_to_server(void)
{
char buf[256];
- char portname[8];
char *args[] =
- {"netproc", "localhost", NULL, NULL};
+ { "netproc", NULL };
syslog(LOG_NOTICE, "Attaching to server...");
- sprintf(portname, "%d", config.c_port_number);
- args[2] = portname;
- attach_to_server(3, args);
+ attach_to_server(1, args, NULL, NULL);
serv_gets(buf);
syslog(LOG_NOTICE, "%s", &buf[4]);
sprintf(buf, "IPGM %d", config.c_ipgm_secret);
}
}
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "Calling get_config()");
+#endif
get_config();
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "Creating lock file");
+#endif
if (set_lockfile() != 0) {
syslog(LOG_NOTICE, "lock file exists: already running");
cleanup(1);
syslog(LOG_ERR, "cannot load sysinfo");
setup_special_nodes();
- inprocess(); /* first collect incoming stuff */
+ /* Open the use table */
+ read_use_table();
+
+ /* first collect incoming stuff */
+ inprocess();
+ /* Now process outbound messages, but NOT if this is just a
+ * quick import-only run (i.e. the -i command-line option
+ * was specified)
+ */
if (import_only != 1) {
allfp = (FILE *) popen("cd ./network/systems; ls", "r");
if (allfp != NULL) {
while (fgets(allst, 32, allfp) != NULL) {
allst[strlen(allst) - 1] = 0;
- outprocess(allst);
+ if (strcmp(allst, "CVS"))
+ outprocess(allst);
}
pclose(allfp);
}
/* import again in case anything new was generated */
inprocess();
}
+
+ /* Update mail.sysinfo with new information we learned */
rewrite_syslist();
+
+ /* Delete any messages which need to be purged locally */
+ syslog(LOG_NOTICE, "calling process_purgelist()");
+ process_purgelist();
+
+ /* Close the use table */
+ write_use_table();
+
syslog(LOG_NOTICE, "processing ended.");
cleanup(0);
return 0;