/***************************************************************************** * * $Id$ * Purpose: Process Fidonet style mail and files. * ***************************************************************************** * Copyright (C) 1997-2002 * * Michiel Broek FIDO: 2:280/2802 * Beekmansbos 10 * 1971 BV IJmuiden * the Netherlands * * This file is part of MBSE BBS. * * This BBS is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * MBSE BBS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MBSE BBS; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #include "../config.h" #include "../lib/libs.h" #include "../lib/mbse.h" #include "../lib/structs.h" #include "../lib/users.h" #include "../lib/records.h" #include "../lib/common.h" #include "../lib/nodelist.h" #include "../lib/clcomm.h" #include "../lib/dbdupe.h" #include "../lib/dbcfg.h" #include "../lib/dbnode.h" #include "../lib/dbmsgs.h" #include "../lib/dbuser.h" #include "../lib/dbftn.h" #include "../lib/dbtic.h" #include "../lib/msg.h" #include "../lib/mberrors.h" #include "flock.h" #include "tosspkt.h" #include "unpack.h" #include "tic.h" #include "fsort.h" #include "scan.h" #include "mbfido.h" #include "tracker.h" #include "notify.h" #include "rollover.h" #include "hatch.h" #include "scannews.h" #include "maketags.h" #include "makestat.h" #include "newspost.h" #include "rnews.h" #include "mgrutil.h" #include "backalias.h" #include "rfc2ftn.h" #include "dirsession.h" #include "dirlock.h" #include "queue.h" #define UNPACK_FACTOR 300 int do_areas = FALSE; /* Process area taglists */ int do_toss = FALSE; /* Toss flag */ int do_scan = FALSE; /* Scan flag */ int do_tic = FALSE; /* Process .tic files */ int do_notify = FALSE; /* Create notify messages */ int do_roll = FALSE; /* Rollover only */ int do_full = FALSE; /* Full mailscan */ int do_tags = FALSE; /* Create taglists */ int do_stat = FALSE; /* Create statistic HTML pages */ int do_test = FALSE; /* Test routing */ int do_news = FALSE; /* Process NNTP news */ int do_uucp = FALSE; /* Process UUCP newsbatch */ int do_mail = FALSE; /* Process MTA email message */ int do_unsec = FALSE; /* Unsecure tossing */ int do_learn = FALSE; /* News articles learnmode */ int check_crc = TRUE; /* Check .tic crc values */ int check_dupe = TRUE; /* Check duplicates */ int do_flush = FALSE; /* Flush outbound queue */ int flushed = FALSE; /* If anything was flushed */ extern int do_quiet; /* Quiet flag */ extern int e_pid; /* Pid of child process */ extern int show_log; /* Show logging on screen */ int do_unprot = FALSE; /* Unprotected inbound flag */ time_t t_start; /* Start time */ time_t t_end; /* End time */ int packets = 0; /* Tossed packets */ int packets_ok = 0; /* Tossed packets Ok. */ char *envptr = NULL; extern int net_in, net_imp, net_out, net_bad; extern int echo_in, echo_imp, echo_out, echo_bad, echo_dupe; extern int email_in, email_imp, email_out, email_bad; extern int news_in, news_imp, news_out, news_bad, news_dupe; extern int tic_in, tic_imp, tic_out, tic_bad, tic_dup; extern int Magics, Hatched; extern int notify, filemgr, areamgr; /* * If we don't know what to type */ void Help(void) { do_quiet = FALSE; ProgName(); colour(LIGHTCYAN, BLACK); printf("\nUsage: mbfido [command(s)] \n\n"); colour(LIGHTBLUE, BLACK); printf(" Commands are:\n\n"); colour(CYAN, BLACK); printf(" a areas Process Areas taglists\n"); printf(" m mail ... MTA Mail mode\n"); printf(" ne news Scan for new news\n"); printf(" no notify Send notify messages\n"); printf(" r roll Rollover statistic counters\n"); printf(" s scan Scan outgoing Fido mail\n"); printf(" ta tag Create taglists\n"); printf(" te test Do routing test for node\n"); printf(" ti tic Process .tic files\n"); printf(" to toss Toss incoming Fido mail\n"); printf(" u uucp Process UUCP batchfile\n"); printf(" w web Create WWW statistics\n\n"); colour(LIGHTBLUE, BLACK); printf(" Options are:\n\n"); colour(CYAN, BLACK); printf(" -f -full Full Mailscan\n"); printf(" -l -learn Learn News dupes\n"); printf(" -noc -nocrc Skip CRC checking\n"); printf(" -nod -nodupe Skip dupe checking\n"); printf(" -q -quiet Quiet mode\n"); printf(" -uns -unsecure Toss unsecure\n"); printf(" -unp -unprotect Toss unprotected inbound\n"); colour(LIGHTGRAY, BLACK); ExitClient(MBERR_COMMANDLINE); } /* * Header, only if not quiet. */ void ProgName(void) { if (do_quiet) return; colour(WHITE, BLACK); printf("\nMBFIDO: MBSE BBS %s - Fidonet File and Mail processor\n", VERSION); colour(YELLOW, BLACK); printf(" %s\n", COPYRIGHT); } void die(int onsig) { /* * First check if there is a child running, if so, kill it. */ if (e_pid) { if ((kill(e_pid, SIGTERM)) == 0) Syslog('+', "SIGTERM to pid %d succeeded", e_pid); else { if ((kill(e_pid, SIGKILL)) == 0) Syslog('+', "SIGKILL to pid %d succeeded", e_pid); else WriteError("Failed to kill pid %d", e_pid); } /* * In case the child had the tty in raw mode, reset the tty. */ system("stty sane"); } if (onsig != MBERR_NO_PROGLOCK) CloseDupes(); /* * Check for locked and open message base. */ if (MsgBase.Locked) Msg_UnLock(); if (MsgBase.Open) Msg_Close(); signal(onsig, SIG_IGN); if (!do_quiet) { show_log = TRUE; colour(CYAN, BLACK); } if (onsig) { if (onsig <= NSIG) WriteError("Terminated on signal %d (%s)", onsig, SigName[onsig]); else WriteError("Terminated with error %d", onsig); } if (echo_imp + net_imp + net_out + echo_out) CreateSema((char *)"msglink"); if ((echo_out + net_out + tic_out) || flushed) CreateSema((char *)"scanout"); if (tic_imp) CreateSema((char *)"reqindex"); if (net_in + net_imp + net_out + net_bad) Syslog('+', "Netmail [%4d] import [%4d] out [%4d] bad [%4d]", net_in, net_imp, net_out, net_bad); if (email_in + email_imp + email_out + email_bad) Syslog('+', "Email [%4d] import [%4d] out [%4d] bad [%4d]", email_in, email_imp, email_out, email_bad); if (echo_in + echo_imp + echo_out + echo_bad + echo_dupe) Syslog('+', "Echomail [%4d] import [%4d] out [%4d] bad [%4d] dupe [%4d]", echo_in, echo_imp, echo_out, echo_bad, echo_dupe); if (news_in + news_imp + news_out + news_bad + news_dupe) Syslog('+', "News [%4d] import [%4d] out [%4d] bad [%4d] dupe [%4d]", news_in, news_imp, news_out, news_bad, news_dupe); if (tic_in + tic_imp + tic_out + tic_bad + tic_dup) Syslog('+', "TICfiles [%4d] import [%4d] out [%4d] bad [%4d] dupe [%4d]", tic_in, tic_imp, tic_out, tic_bad, tic_dup); if (Magics + Hatched) Syslog('+', " Magics [%4d] hatch [%4d]", Magics, Hatched); if (notify + areamgr + filemgr) Syslog('+', "Notify msgs [%4d] AreaMgr [%4d] FileMgr [%4d]", notify, areamgr, filemgr); /* * There should be no locks anymore, but in case of a crash try to unlock * all possible directories. Only if onsig <> 110, this was a lock error * and there should be no lock. We prevent removing the lock of another * mbfido this way. */ if (onsig != MBERR_NO_PROGLOCK) { ulockdir(CFG.inbound); ulockdir(CFG.pinbound); ulockdir(CFG.out_queue); } t_end = time(NULL); Syslog(' ', "MBFIDO finished in %s", t_elapsed(t_start, t_end)); if (!do_quiet) colour(LIGHTGRAY, BLACK); ExitClient(onsig); } int main(int argc, char **argv) { int i, Loop, envrecip_count = 0; char *p, *cmd, *temp, Options[81]; struct passwd *pw; struct tm *t; fa_list **envrecip, *envrecip_start = NULL; faddr *taddr = NULL; FILE *ofp; /* * The next trick is to supply a fake environment variable * MBSE_ROOT in case we are started from UUCP or the MTA. * this will setup the variable so InitConfig() will work. * The /etc/passwd must point to the correct homedirectory. * Some programs can't set uid to mbse, so mbfido is installed * setuid mbse. */ if (getenv("MBSE_ROOT") == NULL) { pw = getpwuid(getuid()); if (strcmp(pw->pw_name, "mbse")) { /* * We are not running as user mbse. */ pw = getpwnam("mbse"); if (setuid(pw->pw_uid)) { printf("Fatal error: can't set uid to user mbse\n"); } } envptr = xstrcpy((char *)"MBSE_ROOT="); envptr = xstrcat(envptr, pw->pw_dir); putenv(envptr); } InitConfig(); memset(&Options, 0, sizeof(Options)); /* * Initialize global variables, data records. */ InitNode(); InitMsgs(); InitTic(); InitUser(); InitFidonet(); TermInit(1); t_start = time(NULL); t = localtime(&t_start); Diw = t->tm_wday; Miy = t->tm_mon; umask(002); /* * Catch all the signals we can, and ignore the rest. */ for(i = 0; i < NSIG; i++) { if ((i == SIGINT) || (i == SIGBUS) || (i == SIGILL) || (i == SIGSEGV) || (i == SIGTERM) || (i == SIGKILL)) signal(i, (void (*))die); else signal(i, SIG_IGN); } if ((p = strrchr(argv[0], '/'))) p++; else p = argv[0]; if (!strcmp(p, "mbmail")) { do_quiet = TRUE; do_mail = TRUE; cmd = xstrcpy((char *)"Cmd: mbmail"); } else if (!strcmp(p, "mbnews")) { do_quiet = TRUE; do_uucp = TRUE; cmd = xstrcpy((char *)"Cmd: mbnews"); } else { if (argc < 2) Help(); cmd = xstrcpy((char *)"Cmd: mbfido"); } envrecip = &envrecip_start; for (i = 1; i < argc; i++) { cmd = xstrcat(cmd, (char *)" "); cmd = xstrcat(cmd, argv[i]); if (strncmp(tl(argv[i]), "ne", 2) == 0) do_news = TRUE; if (strncmp(tl(argv[i]), "no", 2) == 0) { do_notify = TRUE; if (((i + 1) < argc) && ((strchr(argv[i + 1], ':') != NULL) || (atoi(argv[i + 1])) || (strncmp(argv[i + 1], "*", 1) == 0))) { sprintf(Options, "%s", argv[i + 1]); i++; } } if (strncmp(tl(argv[i]), "r", 1) == 0) do_roll = TRUE; else if (strncmp(tl(argv[i]), "a", 1) == 0) do_areas = TRUE; else if (strncmp(tl(argv[i]), "s", 1) == 0) do_scan = TRUE; else if (strncmp(tl(argv[i]), "ta", 2) == 0) do_tags = TRUE; else if (strncmp(tl(argv[i]), "ti", 2) == 0) do_tic = TRUE; else if (strncmp(tl(argv[i]), "te", 2) == 0) { do_test = TRUE; if ((i + 1) < argc) { if ((taddr = parsefaddr(argv[i + 1])) == NULL) { Help(); } i++; cmd = xstrcat(cmd, (char *)" "); cmd = xstrcat(cmd, argv[i]); } } else if (strncmp(tl(argv[i]), "to", 2) == 0) do_toss = TRUE; else if (strncmp(tl(argv[i]), "u", 1) == 0) do_uucp = TRUE; else if (strncmp(tl(argv[i]), "m", 1) == 0) do_mail = TRUE; else if (strncmp(tl(argv[i]), "w", 1) == 0) do_stat = TRUE; else if (strncmp(tl(argv[i]), "-f", 2) == 0) do_full = TRUE; else if (strncmp(tl(argv[i]), "-l", 2) == 0) do_learn = TRUE; else if (strncmp(tl(argv[i]), "-noc", 4) == 0) check_crc = FALSE; else if (strncmp(tl(argv[i]), "-nod", 4) == 0) check_dupe = FALSE; else if (strncmp(tl(argv[i]), "-q", 2) == 0) do_quiet = TRUE; else if (strncmp(tl(argv[i]), "-a", 2) == 0) WriteError("The -a option is obsolete, adjust your setup"); else if (strncmp(tl(argv[i]), "-unp", 4) == 0) do_unprot = TRUE; else if (strncmp(tl(argv[i]), "-uns", 4) == 0) do_unsec = TRUE; else if (do_mail) { /* * Possible recipient address(es). */ if ((taddr = parsefaddr(argv[i]))) { (*envrecip) = (fa_list*)malloc(sizeof(fa_list)); (*envrecip)->next = NULL; (*envrecip)->addr = taddr; envrecip = &((*envrecip)->next); envrecip_count++; } else { cmd = strcat(cmd, (char *)" <- unparsable recipient! "); } } } ProgName(); pw = getpwuid(getuid()); InitClient(pw->pw_name, (char *)"mbfido", CFG.location, CFG.logfile, CFG.util_loglevel, CFG.error_log, CFG.mgrlog, CFG.debuglog); Syslog(' ', " "); Syslog(' ', "MBFIDO v%s", VERSION); Syslog(' ', cmd); free(cmd); /* * Not yet locked, if anything goes wrong, exit with die(MBERR_NO_PROGLOCK) */ if (!diskfree(CFG.freespace)) die(MBERR_DISK_FULL); if (do_mail) { /* * Try to get a lock for a long time, another mbfido may be legally * running since mbmail is started by the MTA instead of by mbtask. * The timeout is 10 minutes. If mbmail times out, the MTA will * bounce the message. What happens during the time we wait is * unknown, will the MTA be patient enough? */ i = 30; while (TRUE) { if (do_unprot) { if (lockdir(CFG.inbound)) break; } else { if (lockdir(CFG.pinbound)) break; } i--; if (! i) { WriteError("Lock timeout, aborting"); die(MBERR_NO_PROGLOCK); } sleep(20); Nopper(); } } else { /* * Started under control of mbtask, that means if there is a lock then * there is something wrong; abort. */ if (do_unprot) { if (! lockdir(CFG.inbound)) die(MBERR_NO_PROGLOCK); } else { if (! lockdir(CFG.pinbound)) die(MBERR_NO_PROGLOCK); } } /* * Locking succeeded, no more abort alowed with die(110) */ if (initnl()) die(MBERR_INIT_ERROR); if (!do_mail && !do_uucp) Rollover(); if (!do_quiet) printf("\n"); /* * Read alias file */ cmd = calloc(PATH_MAX, sizeof(char)); sprintf(cmd, "%s/etc/aliases", getenv("MBSE_ROOT")); if ((do_news || do_scan || do_toss || do_mail) && file_exist(cmd, R_OK) == 0) readalias(cmd); free(cmd); if (do_mail) { if (!envrecip_count) { WriteError("No valid receipients specified, aborting"); die(MBERR_NO_RECIPIENTS); } umask(066); if ((ofp = tmpfile()) == NULL) { WriteError("$Can't open tmpfile for RFC message"); die(MBERR_INIT_ERROR); } temp = calloc(10240, sizeof(char)); while (fgets(temp, 10240, stdin)) fprintf(ofp, temp); free(temp); for (envrecip = &envrecip_start; *envrecip; envrecip = &((*envrecip)->next)) { Syslog('+', "Message to: %s", ascfnode((*envrecip)->addr, 0x7f)); rfc2ftn(ofp, (*envrecip)->addr); } fclose(ofp); flush_queue(); die(MBERR_OK); } InitDupes(); if (do_notify) if (Notify(Options)) { do_flush = TRUE; } if (do_tic) { if (IsSema((char *)"mailin")) RemoveSema((char *)"mailin"); /* * Hatch new files and process .tic files * until nothing left to do. */ Loop = TRUE; do { Hatch(); switch (Tic()) { case -1: die(MBERR_OK); break; case 0: Loop = FALSE; break; default: break; } } while (Loop); } if (do_news) { ScanNews(); if (IsSema((char *)"newnews")) RemoveSema((char *)"newnews"); } if (do_scan) ScanMail(do_full); if (do_toss) { if (IsSema((char *)"mailin")) RemoveSema((char *)"mailin"); if (TossMail() == FALSE) die(MBERR_OK); } if (do_tic || do_toss) { /* * Do inbound direcory sessions */ if (dirinbound()) { if (do_tic) { /* * Hatch new files and process .tic files * until nothing left to do. */ Loop = TRUE; do { Hatch(); switch (Tic()) { case -1: die(MBERR_OK); break; case 0: Loop = FALSE; break; default: break; } } while (Loop); } if (do_toss) { TossMail(); } } } if (!do_uucp) newspost(); if (do_test) { if (taddr == NULL) Help(); TestTracker(taddr); tidy_faddr(taddr); } if (do_tags) MakeTags(); if (do_stat) MakeStat(); if (do_uucp) NewsUUCP(); if (do_areas) Areas(); if (do_flush) flush_queue(); die(MBERR_OK); return 0; } /* * Toss Fidonet mail */ int TossMail(void) { char *inbound, *fname; DIR *dp; struct dirent *de; struct stat sbuf; int files = 0, files_ok = 0, rc = 0, maxrc = 0; fd_list *fdl = NULL; if (do_unprot) inbound = xstrcpy(CFG.inbound); else inbound = xstrcpy(CFG.pinbound); Syslog('+', "Pass: toss netmail (%s)", inbound); if (chdir(inbound) == -1) { WriteError("$Can't chdir(%s)", inbound); die(MBERR_INIT_ERROR); } /* * First toss any netmail packets. */ maxrc = rc = TossPkts(); chdir(inbound); /* * Scan the directory for ARCmail archives. The archive extension * numbering doesn't matter, as long as there is something, so * all kind of ARCmail naming schemes are recognized. */ if ((dp = opendir(inbound)) == NULL) { WriteError("$Can't opendir(%s)", inbound); die(MBERR_INIT_ERROR); } Syslog('+', "Pass: toss ARCmail (%s)", inbound); /* * Add all ARCmail filenames to the memory array. */ sync(); while ((de=readdir(dp))) if ((strlen(de->d_name) == 12) && ((strncasecmp(de->d_name+8,".su",3) == 0) || (strncasecmp(de->d_name+8,".mo",3) == 0) || (strncasecmp(de->d_name+8,".tu",3) == 0) || (strncasecmp(de->d_name+8,".we",3) == 0) || (strncasecmp(de->d_name+8,".th",3) == 0) || (strncasecmp(de->d_name+8,".fr",3) == 0) || (strncasecmp(de->d_name+8,".sa",3) == 0))) { stat(de->d_name, &sbuf); fill_fdlist(&fdl, de->d_name, sbuf.st_mtime); } closedir(dp); sort_fdlist(&fdl); /* * Now process the archives, the oldest first. */ while ((fname = pull_fdlist(&fdl)) != NULL) { files++; IsDoing("Unpack arcmail"); if (IsSema((char *)"upsalarm")) { Syslog('+', "Detected upsalarm semafore, aborting toss"); break; } if (!diskfree(CFG.freespace)) { rc = MBERR_DISK_FULL; break; } if (checkspace(inbound, fname, UNPACK_FACTOR)) if ((rc = unpack(fname)) == 0) { files_ok++; sync(); rc = TossPkts(); chdir(inbound); } else WriteError("Error unpacking file %s", fname); else Syslog('!', "Insufficient space to unpack file %s", fname); if (rc > maxrc) maxrc = rc; } free(inbound); if ((files || packets) && ((files_ok != files) || (packets_ok != packets))) Syslog('!', "Processed %d of %d files, %d of %d packets, rc=%d", files_ok, files, packets_ok, packets, maxrc); return TRUE; } /* * Toss all packets currently in the inbound. Tossing is sorted by * age of the files. */ int TossPkts(void) { char *inbound = NULL, *fname; DIR *dp; struct dirent *de; struct stat sbuf; int rc = 0, maxrc = 0; fd_list *fdl = NULL; IsDoing("Tossing mail"); if (do_unprot) inbound = xstrcpy(CFG.inbound); else inbound = xstrcpy(CFG.pinbound); if ((dp = opendir(inbound)) == NULL) { WriteError("$Can't opendir(%s)", inbound); return FALSE; } /* * Read all .pkt filenames, get the timestamp and add them * to the memory array for later sort on filedate. */ while ((de = readdir(dp))) if ((strlen(de->d_name) == 12) && (strncasecmp(de->d_name+8,".pkt",4) == 0)) { stat(de->d_name, &sbuf); fill_fdlist(&fdl, de->d_name, sbuf.st_mtime); } closedir(dp); sort_fdlist(&fdl); /* * Get the filenames, the oldest first until nothing left. */ while ((fname = pull_fdlist(&fdl)) != NULL) { if (!diskfree(CFG.freespace)) return FALSE; packets++; /* * See if "pktdate" from Tobias Ernst (or another preprocessor) is installed. */ if (strlen(CFG.pktdate)) { rc = execute(CFG.pktdate, fname, (char *)NULL, (char *)"/dev/null", (char *)"/dev/null", (char *)"/dev/null"); if (rc) Syslog('+', "%s preprocessing rc=%d", fname, rc); } if ((rc = toss(fname)) == 0) packets_ok++; else WriteError("Error tossing packet %s", fname); if (rc > maxrc) maxrc = rc; } free(inbound); do_flush = TRUE; return maxrc; } /* * Toss one packet */ int toss(char *fn) { int rc = 0, ld; char newname[16]; /* * Lock the packet */ if ((ld = f_lock(fn)) == -1) return 1; rc = TossPkt(fn); if (rc == 0) { unlink(fn); } else { strncpy(newname,fn,sizeof(newname)-1); strcpy(newname+8,".bad"); rename(fn,newname); } funlock(ld); return rc; }