/***************************************************************************** * * $Id$ * Purpose ...............: Message Base Maintenance * ***************************************************************************** * 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, 675 Mass Ave, Cambridge, MA 02139, USA. *****************************************************************************/ #include "../config.h" #include "../lib/libs.h" #include "../lib/memwatch.h" #include "../lib/structs.h" #include "../lib/users.h" #include "../lib/records.h" #include "../lib/common.h" #include "../lib/clcomm.h" #include "../lib/msg.h" #include "../lib/dbcfg.h" #include "../lib/mberrors.h" #include "post.h" #include "mbmsg.h" int do_pack = FALSE; /* Pack flag */ int do_kill = FALSE; /* Kill flag (age and maxmsgs) */ int do_index = FALSE; /* Index flag */ int do_link = FALSE; /* Link messages */ int do_post = FALSE; /* Post a Message */ extern int do_quiet; /* Quiet flag */ extern int show_log; /* Show loglines */ long do_area = 0; /* Do only one area */ time_t t_start; /* Start time */ time_t t_end; /* End time */ int are_tot = 0; /* Total areas */ int are_proc = 0; /* Areas processed */ int msg_tot = 0; /* Total messages */ int msg_del = 0; /* Deleted messages */ int msg_link = 0; /* Linked messages */ int processed = FALSE; /* Did process something */ int oldmask; /* * Header, only if not quiet */ void ProgName() { if (do_quiet) return; colour(15, 0); printf("\nMBMSG: MBSE BBS %s - Message Base Maintenance Utility\n", VERSION); colour(14, 0); printf(" %s\n", COPYRIGHT); colour(7, 0); } int main(int argc, char **argv) { int i; char *cmd, *too = NULL, *subj = NULL, *mfile = NULL, *flavor = NULL; struct passwd *pw; long tarea = 0; #ifdef MEMWATCH mwInit(); #endif InitConfig(); TermInit(1); oldmask = umask(007); t_start = time(NULL); /* * Catch all signals we can, and ignore or catch them */ for (i = 0; i < NSIG; i++) { if ((i == SIGHUP) || (i == SIGBUS) || (i == SIGILL) || (i == SIGSEGV) || (i == SIGTERM) || (i == SIGKILL)) signal(i, (void (*))die); else signal(i, SIG_IGN); } if (argc < 2) Help(); cmd = xstrcpy((char *)"Cmd:"); for (i = 1; i < argc; i++) { cmd = xstrcat(cmd, (char *)" "); cmd = xstrcat(cmd, argv[i]); if (strncasecmp(argv[i], "i", 1) == 0) do_index = TRUE; if (strncasecmp(argv[i], "l", 1) == 0) do_link = TRUE; if (strncasecmp(argv[i], "k", 1) == 0) do_kill = TRUE; if (strncasecmp(argv[i], "pa", 2) == 0) do_pack = TRUE; if (strncasecmp(argv[i], "po", 2) == 0) { do_post = TRUE; too = argv[++i]; cmd = xstrcat(cmd, (char *)" \""); cmd = xstrcat(cmd, too); tarea = atoi(argv[++i]); cmd = xstrcat(cmd, (char *)"\" "); cmd = xstrcat(cmd, argv[i]); subj = argv[++i]; cmd = xstrcat(cmd, (char *)" \""); cmd = xstrcat(cmd, subj); mfile = argv[++i]; cmd = xstrcat(cmd, (char *)"\" "); cmd = xstrcat(cmd, mfile); flavor = argv[++i]; cmd = xstrcat(cmd, (char *)" "); cmd = xstrcat(cmd, flavor); } if (strncasecmp(argv[i], "-a", 2) == 0) { i++; do_area = atoi(argv[i]); } if (strncasecmp(argv[i], "-q", 2) == 0) do_quiet = TRUE; } if (!(do_index || do_link || do_kill || do_pack || do_post)) Help(); ProgName(); pw = getpwuid(getuid()); InitClient(pw->pw_name, (char *)"mbmsg", CFG.location, CFG.logfile, CFG.util_loglevel, CFG.error_log, CFG.mgrlog); Syslog(' ', " "); Syslog(' ', "MBMSG v%s", VERSION); Syslog(' ', cmd); free(cmd); if (!do_quiet) { printf("\n"); colour(3, 0); } if (do_index || do_link || do_kill || do_pack) { memset(&MsgBase, 0, sizeof(MsgBase)); DoMsgBase(); } if (do_post) { Post(too, tarea, subj, mfile, flavor); } die(MBERR_OK); return 0; } void Help() { do_quiet = FALSE; ProgName(); colour(12, 0); printf("\n Usage: mbmsg [command(s)] \n\n"); colour(9, 0); printf(" Commands are:\n\n"); colour(3, 0); // printf(" i index Create new index files\n"); printf(" l link Link messages by subject\n"); printf(" k kill Kill messages (age & count)\n"); printf(" pa pack Pack deleted messages\n"); printf(" po post <#> Post file in message area #\n\n"); colour(9, 0); printf(" Options are:\n\n"); colour(3, 0); printf(" -a -area <#> Process area <#> only\n"); printf(" -q -quiet Quiet mode\n"); printf("\n"); die(MBERR_COMMANDLINE); } void die(int onsig) { signal(onsig, SIG_IGN); if (!do_quiet) { printf("\r"); colour(3, 0); } if (MsgBase.Locked) Msg_UnLock(); if (MsgBase.Open) Msg_Close(); if (onsig) { if (onsig <= NSIG) WriteError("Terminated on signal %d (%s)", onsig, SigName[onsig]); else WriteError("Terminated with error %d", onsig); } if (onsig == SIGSEGV) Syslog('+', "Last msg area %s", msgs.Name); if (are_tot || are_proc || msg_link) Syslog('+', "Areas [%6d] Processed [%6d] Linked [%6d]", are_tot, are_proc, msg_link); if (msg_tot || msg_del) Syslog('+', "Msgs [%6d] Deleted [%6d]", msg_tot, msg_del); t_end = time(NULL); Syslog(' ', "MBMSG finished in %s", t_elapsed(t_start, t_end)); umask(oldmask); if (!do_quiet) { colour(7, 0); printf("\r \n"); } ExitClient(onsig); } void DoMsgBase() { FILE *pAreas; char *sAreas, *Name; long arearec; int Del = 0; sAreas = calloc(81, sizeof(char)); Name = calloc(81, sizeof(char )); IsDoing("Msg Maintenance"); if (do_area) Syslog('+', "Processing message area %ld", do_area); else Syslog('+', "Processing all message areas"); if (do_kill) { Syslog('-', " Total Max. Days/Killed Max. Nr/Killed Area name"); Syslog('-', "------ ------ ------ ------ ------ ----------------------------------"); } sprintf(sAreas, "%s/etc/mareas.data", getenv("MBSE_ROOT")); if(( pAreas = fopen (sAreas, "r")) == NULL) { WriteError("$Can't open Messages Areas File."); die(MBERR_INIT_ERROR); } fread(&msgshdr, sizeof(msgshdr), 1, pAreas); if (do_area) { if (fseek(pAreas, (msgshdr.recsize + msgshdr.syssize) * (do_area - 1), SEEK_CUR) == 0) { fread(&msgs, msgshdr.recsize, 1, pAreas); if (msgs.Active) { if (!diskfree(CFG.freespace)) die(MBERR_DISK_FULL); if (!do_quiet) { colour(3, 0); printf("\r%5ld .. %-40s", do_area, msgs.Name); fflush(stdout); } are_tot++; mkdirs(msgs.Base, 0770); if (do_kill) KillArea(msgs.Base, msgs.Name, msgs.DaysOld, msgs.MaxMsgs, do_area); if (do_pack || msg_del) PackArea(msgs.Base, do_area); if (do_index) IndexArea(msgs.Base, do_area); if (do_link) LinkArea(msgs.Base, do_area); if (processed) are_proc++; } } } else { arearec = 0; while (fread(&msgs, msgshdr.recsize, 1, pAreas) == 1) { fseek(pAreas, msgshdr.syssize, SEEK_CUR); arearec++; if (msgs.Active) { if (!diskfree(CFG.freespace)) die(MBERR_DISK_FULL); Nopper(); if (!do_quiet) { colour(3, 0); printf("\r%5ld .. %-40s", arearec, msgs.Name); fflush(stdout); } are_tot++; mkdirs(msgs.Base, 0770); processed = FALSE; if (do_kill) KillArea(msgs.Base, msgs.Name, msgs.DaysOld, msgs.MaxMsgs, arearec); if (do_pack || (Del != msg_del)) { PackArea(msgs.Base, arearec); } Del = msg_del; if (do_index) IndexArea(msgs.Base, arearec); if (do_link) LinkArea(msgs.Base, arearec); if (processed) are_proc++; } } } fclose(pAreas); if (!do_area) { sprintf(sAreas, "%s/etc/users.data", getenv("MBSE_ROOT")); if ((pAreas = fopen (sAreas, "r")) == NULL) { WriteError("$Can't open Messages Areas File."); die(SIGILL); } fread(&usrconfighdr, sizeof(usrconfighdr), 1, pAreas); while (fread(&usrconfig, usrconfighdr.recsize, 1, pAreas) == 1) { if (usrconfig.Email && strlen(usrconfig.Name)) { Nopper(); sprintf(Name, "User %s email area: mailbox", usrconfig.Name); if (!do_quiet) { colour(3, 0); printf("\r .. %-40s", Name); fflush(stdout); } sprintf(sAreas, "%s/%s/mailbox", CFG.bbs_usersdir, usrconfig.Name); are_tot++; processed = FALSE; if (do_kill) KillArea(sAreas, Name, 0, CFG.defmsgs, 0); if (do_pack || (Del != msg_del)) { PackArea(sAreas, 0); } Del = msg_del; if (do_index) IndexArea(sAreas, 0); if (do_link) LinkArea(sAreas, 0); if (processed) are_proc++; sprintf(sAreas, "%s/%s/archive", CFG.bbs_usersdir, usrconfig.Name); sprintf(Name, "User %s email area: archive", usrconfig.Name); are_tot++; processed = FALSE; if (do_kill) KillArea(sAreas, Name, 0, CFG.defmsgs, 0); if (do_pack || (Del != msg_del)) PackArea(sAreas, 0); Del = msg_del; if (do_index) IndexArea(sAreas, 0); if (do_link) LinkArea(sAreas, 0); if (processed) are_proc++; sprintf(sAreas, "%s/%s/trash", CFG.bbs_usersdir, usrconfig.Name); sprintf(Name, "User %s email area: trash", usrconfig.Name); are_tot++; processed = FALSE; if (do_kill) KillArea(sAreas, Name, CFG.defdays, CFG.defmsgs, 0); if (do_pack || (Del != msg_del)) PackArea(sAreas, 0); Del = msg_del; if (do_index) IndexArea(sAreas, 0); if (do_link) LinkArea(sAreas, 0); if (processed) are_proc++; } } fclose(pAreas); } if (do_link) RemoveSema((char *)"msglink"); free(sAreas); free(Name); die(MBERR_OK); } typedef struct { unsigned long Subject; unsigned long Number; } MSGLINK; void LinkArea(char *Path, long Areanr) { int i, m; unsigned long Number, Prev, Next, Crc, Total; char Temp[128], *p; MSGLINK *Link; IsDoing("Linking %ld", Areanr); if (Msg_Open(Path)) { if (!do_quiet) { colour(12, 0); printf(" (linking)"); colour(13, 0); fflush(stdout); } if ((Total = Msg_Number()) != 0L) { if (Msg_Lock(30L)) { if ((Link = (MSGLINK *)malloc(Total * sizeof(MSGLINK))) != NULL) { memset(Link, 0, Total * sizeof(MSGLINK)); Number = Msg_Lowest(); i = 0; do { Msg_ReadHeader(Number); strcpy(Temp, Msg.Subject); p = strupr(Temp); if (!strncmp(p, "RE:", 3)) { p += 3; if (*p == ' ') p++; } Link[i].Subject = StringCRC32(p); Link[i].Number = Number; i++; if (CFG.slow_util && do_quiet && ((i % 5) == 0)) usleep(1); if (((i % 10) == 0) && (!do_quiet)) { printf("%6d / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i, Total); fflush(stdout); } } while(Msg_Next(&Number) == TRUE); if (!do_quiet) { printf("%6d / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i, Total); fflush(stdout); } Number = Msg_Lowest(); i = 0; do { Msg_ReadHeader(Number); Prev = Next = 0; Crc = Link[i].Subject; for (m = 0; m < Total; m++) { if (m == i) continue; if (Link[m].Subject == Crc) { if (m < i) Prev = Link[m].Number; else if (m > i) { Next = Link[m].Number; break; } } } if (CFG.slow_util && do_quiet && ((i % 5) == 0)) usleep(1); if (((i % 10) == 0) && (!do_quiet)) { printf("%6d / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i, Total); fflush(stdout); } if (Msg.Original != Prev || Msg.Reply != Next) { Msg.Original = Prev; Msg.Reply = Next; Msg_WriteHeader(Number); processed = TRUE; msg_link++; } i++; } while(Msg_Next(&Number) == TRUE); if (!do_quiet) { printf("%6d / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i, Total); fflush(stdout); } free(Link); } if (!do_quiet) { printf(" \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); fflush(stdout); } Msg_UnLock(); } else { Syslog('+', "Can't lock %s", Path); } } Msg_Close(); if (!do_quiet) { printf("\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b"); fflush(stdout); } } } /* * Kill messages according to age and max messages. */ void KillArea(char *Path, char *Name, int DaysOld, int MaxMsgs, long Areanr) { unsigned long Number, TotalMsgs = 0, Highest, *Active, Counter = 0; int i, DelCount = 0, DelAge = 0, Done; time_t Today, MsgDate; IsDoing("Killing %ld", Areanr); Today = time(NULL) / 86400L; if (Msg_Open(Path)) { if (!do_quiet) { colour(12, 0); printf(" (Killing)"); colour(13, 0); fflush(stdout); } if (Msg_Lock(30L)) { TotalMsgs = Msg_Number(); if (TotalMsgs) { if ((Active = (unsigned long *)malloc((size_t)((TotalMsgs + 100L) * sizeof(unsigned long)))) != NULL) { i = 0; Number = Msg_Lowest(); do { Active[i++] = Number; } while (Msg_Next(&Number) == TRUE); } } else Active = NULL; Number = Msg_Lowest(); Highest = Msg_Highest(); do { if (CFG.slow_util && do_quiet) usleep(1); if ((!do_quiet) && ((Counter % 10L) == 0)) { printf("%6lu / %6lu\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", Counter, TotalMsgs); fflush(stdout); } if ((Counter % 10L) == 0) DoNop(); Counter++; msg_tot++; if (Msg_ReadHeader(Number) == TRUE) { Done = FALSE; if (DaysOld) { /* * GoldED doesn't fill the Msg.Arrived field, use the * written date instead. */ if (Msg.Arrived == 0L) MsgDate = Msg.Written / 86400L; else MsgDate = Msg.Arrived / 86400L; if ((Today - MsgDate) > DaysOld) { Msg_Delete(Number); Done = TRUE; DelAge++; msg_del++; if (Active != NULL) { for (i = 0; i < TotalMsgs; i++) { if (Active[i] == Number) Active[i] = 0L; } } } } if (Done == FALSE && (MaxMsgs) && Msg_Number() > MaxMsgs) { Msg_Delete(Number); DelCount++; msg_del++; if (Active != NULL) { for (i = 0; i < TotalMsgs; i++) { if (Active[i] == Number) Active[i] = 0L; } } } } } while (Msg_Next(&Number) == TRUE); if (Active != NULL) free(Active); Msg_UnLock(); } else { Syslog('+', "Can't lock msgbase %s", Path); } if (!do_quiet) { printf(" \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); fflush(stdout); } Msg_Close(); Syslog('-', "%6d %6d %6d %6d %6d %s", TotalMsgs, DaysOld, DelAge, MaxMsgs, DelCount, Name); if (!do_quiet) { printf("\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b"); fflush(stdout); } } else Syslog('+', "Failed to open %s", Path); } void IndexArea(char *Path, long Areanr) { } /* * Pack message area if there are deleted messages. */ void PackArea(char *Path, long Areanr) { IsDoing("Packing %ld", Areanr); if (Msg_Open(Path)) { if (!do_quiet) { colour(12, 0); printf(" (Packing)"); fflush(stdout); } if (Msg_Lock(30L)) { Msg_Pack(); Msg_UnLock(); } else Syslog('+', "Can't lock %s", Path); Msg_Close(); if (!do_quiet) { printf("\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b"); fflush(stdout); } } if (CFG.slow_util && do_quiet) usleep(1); }