/******************************************************** * File: crashmaint.c * Created at Sun Jan 28 22:10:33 MSK 2001 by raorn // raorn@binec.ru * CrashMaint - messagebase maintenance * $Id: crashmaint.c,v 1.33 2002/10/11 21:36:19 raorn Exp $ *******************************************************/ #include #include #include #include #include #include #ifdef HAVE_GETOPT_H # include #else # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include #include #include #include uchar verstr[] = "CrashMaint/" PLATFORM_NAME " " VERSION; uchar LogFile[100] = {0}; ulong LogLevel = 3; mode_t MsgbaseUmask; struct Area { struct Area *Next; uchar Tagname[80]; uchar Path[200]; uchar Messagebase[20]; ulong KeepNum, KeepDays; }; struct jbList AreaList, ProcessList; #define CMD_PURGE 0 #define CMD_PACK 1 #define CMD_LINK 2 #define CMD_MAX 3 struct Command { uchar *Arg, *Str; }; struct Messagebase { uchar *Name; bool (*processfunc[CMD_MAX]) (struct Area * area); }; bool PurgeAreaMSG(struct Area *area); bool PackAreaMSG(struct Area *area); #ifdef MSGBASE_JAM bool PurgeAreaJAM(struct Area *area); bool PackAreaJAM(struct Area *area); bool LinkAreaJAM(struct Area *area); time_t jam_utcoffset; bool jam_quicklink = FALSE; #endif bool relink = FALSE; struct Command Commands[CMD_MAX] = { {"purge", "purge"}, {"pack", "pack"}, {"link", "link"} }; struct Messagebase Messagebases[] = { #ifdef MSGBASE_JAM {"JAM", { PurgeAreaJAM, PackAreaJAM, LinkAreaJAM } }, #endif {"MSG", { PurgeAreaMSG, PackAreaMSG, NULL } }, {NULL, {NULL, NULL, NULL}} }; #define SHOWVER 128 bool ctrlc; RETSIGTYPE breakfunc(int x) { ctrlc = TRUE; } /******************** *.msg *********************/ struct Msg { struct Msg *Next; ulong Num, NewNum, Day; }; struct jbList MsgList; int Compare(const void *a1, const void *a2) { struct Msg **m1, **m2; m1 = (struct Msg **) a1; m2 = (struct Msg **) a2; if ((*m1)->Num > (*m2)->Num) return (1); if ((*m1)->Num < (*m2)->Num) return (-1); return (0); } bool Sort(struct jbList * list) { struct Msg *msg, **buf, **work; ulong nc; nc = 0; for (msg = (struct Msg *) list->First; msg; msg = msg->Next) nc++; if (nc == 0) return (TRUE); if (!(buf = (struct Msg **) malloc(nc * sizeof(struct StatsNode *)))) return (FALSE); work = buf; for (msg = (struct Msg *) list->First; msg; msg = msg->Next) *work++ = msg; qsort(buf, nc, 4, Compare); jbNewList(list); for (work = buf; nc--;) jbAddNode(list, (struct jbNode *) *work++); free(buf); return (TRUE); } void MakeFidoDate(time_t tim, uchar * dest) { struct tm *tp; time_t t; static uchar *monthnames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" }; t = tim; tp = localtime(&t); snprintf(dest, 20, "%02d %s %02d %02d:%02d:%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100, tp->tm_hour, tp->tm_min, tp->tm_sec); } uchar *scanfuncarea; bool nomem; void scanfunc(uchar * str) { uchar buf[200]; struct FileEntry *fe; ulong num, day; struct Msg *msg; if (strlen(str) < 5) return; if (stricmp(&str[strlen(str) - 4], ".msg") != 0) return; if (atol(str) < 2) return; MakeFullPath(scanfuncarea, str, buf, 200); if (!(fe = GetFileEntry(buf))) return; num = atol(str); day = fe->Date / (24 * 60 * 60); free(fe); if (!(msg = (struct Msg *) malloc(sizeof(struct Msg)))) { nomem = TRUE; return; } jbAddNode(&MsgList, (struct jbNode *) msg); msg->Num = num; msg->Day = day; } bool PurgeAreaMSG(struct Area *area) { time_t today; ulong num, del, highwater, oldhighwater; struct Msg *msg; uchar buf[200], buf2[100]; struct StoredMsg StoredMsg; FILE *fh; mode_t oldumask; highwater = 0; oldhighwater = 0; jbNewList(&MsgList); LogWrite(2, MISCINFO, "Purging %s...", area->Tagname); scanfuncarea = area->Path; if (!(ScanDir(area->Path, scanfunc))) { LogWrite(1, SYSTEMERR, "Error: Couldn't scan directory %s", area->Path); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); jbFreeList(&MsgList); return (TRUE); } if (nomem) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&MsgList); return (FALSE); } if (!Sort(&MsgList)) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&MsgList); return (FALSE); } if (!MsgList.First) { LogWrite(3, MISCINFO, " Area is empty"); return (TRUE); } if (ctrlc) { jbFreeList(&MsgList); return (TRUE); } MakeFullPath(area->Path, "1.msg", buf, 200); if ((fh = fopen(buf, "rb"))) { if (fread(&StoredMsg, 1, sizeof(struct StoredMsg), fh) == sizeof(struct StoredMsg)) { highwater = StoredMsg.ReplyTo; oldhighwater = StoredMsg.ReplyTo; } fclose(fh); } if (area->KeepNum != 0) { num = 0; for (msg = (struct Msg *) MsgList.First; msg; msg = msg->Next) num++; msg = (struct Msg *) MsgList.First; del = 0; while (num > area->KeepNum && !ctrlc) { while (msg->Num == 0) msg = msg->Next; snprintf(buf2, 100, "%lu.msg", msg->Num); MakeFullPath(area->Path, buf2, buf, 200); if (msg->Num == highwater) highwater = 0; LogWrite(6, DEBUG, " Deleting message #%lu by number", msg->Num); unlink(buf); msg->Num = 0; num--; del++; } if (ctrlc) { jbFreeList(&MsgList); return (TRUE); } LogWrite(3, MISCINFO, " %lu messages deleted by number, %lu messages left", del, num); } if (area->KeepDays != 0) { del = 0; num = 0; today = time(NULL) / (24 * 60 * 60); for (msg = (struct Msg *) MsgList.First; msg && !ctrlc; msg = msg->Next) { if (today - msg->Day > area->KeepDays && msg->Num != 0) { snprintf(buf2, 100, "%lu.msg", msg->Num); MakeFullPath(area->Path, buf2, buf, 200); if (msg->Num == highwater) highwater = 0; LogWrite(6, DEBUG, " Deleting message #%lu by date", msg->Num); unlink(buf); msg->Num = 0; del++; } else { num++; } } if (ctrlc) { jbFreeList(&MsgList); return (TRUE); } LogWrite(3, MISCINFO, " %lu messages deleted by date, %lu messages left", del, num); } jbFreeList(&MsgList); if (highwater != oldhighwater) { strcpy(StoredMsg.From, "CrashEcho"); strcpy(StoredMsg.To, "All"); strcpy(StoredMsg.Subject, "HighWater mark"); MakeFidoDate(time(NULL), StoredMsg.DateTime); StoredMsg.TimesRead = 0; StoredMsg.DestNode = 0; StoredMsg.OrigNode = 0; StoredMsg.Cost = 0; StoredMsg.OrigNet = 0; StoredMsg.DestNet = 0; StoredMsg.DestZone = 0; StoredMsg.OrigZone = 0; StoredMsg.OrigPoint = 0; StoredMsg.DestPoint = 0; StoredMsg.ReplyTo = highwater; StoredMsg.Attr = FLAG_SENT | FLAG_PVT; StoredMsg.NextReply = 0; MakeFullPath(area->Path, "1.msg", buf, 200); oldumask = umask(MsgbaseUmask); if ((fh = fopen(buf, "wb"))) { fwrite(&StoredMsg, sizeof(struct StoredMsg), 1, fh); fwrite("", 1, 1, fh); fclose(fh); } umask(oldumask); } return (TRUE); } bool PackAreaMSG(struct Area *area) { ulong num, highwater, oldhighwater; struct Msg *msg; uchar buf[200], newbuf[200], buf2[100]; struct StoredMsg StoredMsg; FILE *fh; mode_t oldumask; highwater = 0; oldhighwater = 0; jbNewList(&MsgList); LogWrite(2, MISCINFO, "Renumbering %s...", area->Tagname); scanfuncarea = area->Path; if (!(ScanDir(area->Path, scanfunc))) { LogWrite(1, SYSTEMERR, "Error: Couldn't scan directory %s", area->Path); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); jbFreeList(&MsgList); return (TRUE); } if (nomem) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&MsgList); return (FALSE); } if (!Sort(&MsgList)) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&MsgList); return (FALSE); } if (!MsgList.First) { LogWrite(3, MISCINFO, " Area is empty"); return (TRUE); } if (ctrlc) { jbFreeList(&MsgList); return (TRUE); } MakeFullPath(area->Path, "1.msg", buf, 200); if ((fh = fopen(buf, "rb"))) { if (fread(&StoredMsg, 1, sizeof(struct StoredMsg), fh) == sizeof(struct StoredMsg)) { highwater = StoredMsg.ReplyTo; oldhighwater = StoredMsg.ReplyTo; } fclose(fh); } num = 2; msg = (struct Msg *) MsgList.First; while (msg && !ctrlc) { while (msg && msg->Num == 0) msg = msg->Next; if (msg) { msg->NewNum = num++; msg = msg->Next; } } for (msg = (struct Msg *) MsgList.First; msg && !ctrlc; msg = msg->Next) if (msg->Num != 0 && msg->Num != msg->NewNum) { snprintf(buf2, 100, "%lu.msg", msg->Num); MakeFullPath(area->Path, buf2, buf, 200); snprintf(buf2, 100, "%lu.msg", msg->NewNum); MakeFullPath(area->Path, buf2, newbuf, 200); if (highwater == msg->Num) highwater = msg->NewNum; LogWrite(6, DEBUG, " Renaming message %lu to %lu\n", msg->Num, msg->NewNum); rename(buf, newbuf); } if (ctrlc) { jbFreeList(&MsgList); return (TRUE); } LogWrite(3, MISCINFO, " Area renumbered"); jbFreeList(&MsgList); if (highwater != oldhighwater) { strcpy(StoredMsg.From, "CrashEcho"); strcpy(StoredMsg.To, "All"); strcpy(StoredMsg.Subject, "HighWater mark"); MakeFidoDate(time(NULL), StoredMsg.DateTime); StoredMsg.TimesRead = 0; StoredMsg.DestNode = 0; StoredMsg.OrigNode = 0; StoredMsg.Cost = 0; StoredMsg.OrigNet = 0; StoredMsg.DestNet = 0; StoredMsg.DestZone = 0; StoredMsg.OrigZone = 0; StoredMsg.OrigPoint = 0; StoredMsg.DestPoint = 0; StoredMsg.ReplyTo = highwater; StoredMsg.Attr = FLAG_SENT | FLAG_PVT; StoredMsg.NextReply = 0; MakeFullPath(area->Path, "1.msg", buf, 200); oldumask = umask(MsgbaseUmask); if ((fh = fopen(buf, "wb"))) { fwrite(&StoredMsg, sizeof(struct StoredMsg), 1, fh); fwrite("", 1, 1, fh); fclose(fh); } umask(oldumask); } return (TRUE); } /*************************** JAM ************************/ #ifdef MSGBASE_JAM bool PurgeAreaJAM(struct Area * area) { time_t today; ulong active, basenum, total, del, num, day; s_JamBase *Base_PS; s_JamBaseHeader BaseHeader_S; s_JamMsgHeader Header_S; int res; LogWrite(2, MISCINFO, "Purging %s...", area->Tagname); if (JAM_OpenMB(area->Path, &Base_PS)) { LogWrite(1, USERERR, "Failed to open messagebase \"%s\"", area->Path); return (TRUE); } if (JAM_LockMB(Base_PS, 10)) { LogWrite(1, USERERR, "Timeout when trying to lock messagebase \"%s\"", area->Path); JAM_CloseMB(Base_PS); return (TRUE); } if (JAM_ReadMBHeader(Base_PS, &BaseHeader_S)) { LogWrite(1, USERERR, "Failed to read header of messagebase \"%s\"", area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } if (JAM_GetMBSize(Base_PS, &total)) { LogWrite(1, USERERR, "Failed to get size of messagebase \"%s\"", area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } basenum = BaseHeader_S.BaseMsgNum; active = BaseHeader_S.ActiveMsgs; if (total == 0) { LogWrite(3, MISCINFO, " Area is empty"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } if (area->KeepNum != 0) { num = 0; del = 0; while (num < total && active > area->KeepNum && !ctrlc) { res = JAM_ReadMsgHeader(Base_PS, num, &Header_S, NULL); if (res == 0) { /* Read success */ if (!(Header_S.Attribute & MSG_DELETED) && !(Header_S.Attribute & MSG_LOCKED)) { /* Not already deleted or locked */ LogWrite(6, DEBUG, " Deleting message #%lu by number", basenum + num); Header_S.Attribute |= MSG_DELETED; JAM_ChangeMsgHeader(Base_PS, num, &Header_S); BaseHeader_S.ActiveMsgs--; JAM_WriteMBHeader(Base_PS, &BaseHeader_S); active--; del++; } } num++; } if (ctrlc) { JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } LogWrite(3, MISCINFO, " %lu messages deleted by number, %lu messages left", del, active); } if (area->KeepDays != 0) { del = 0; num = 0; today = (time(NULL) - jam_utcoffset) / (24 * 60 * 60); while (num < total && !ctrlc) { res = JAM_ReadMsgHeader(Base_PS, num, &Header_S, NULL); if (res == 0) { /* Read success */ day = Header_S.DateReceived / (24 * 60 * 60); if (day == 0) day = Header_S.DateProcessed / (24 * 60 * 60); if (day == 0) day = Header_S.DateWritten / (24 * 60 * 60); if (today - day > area->KeepDays && !(Header_S.Attribute & MSG_DELETED) && !(Header_S.Attribute & MSG_LOCKED)) { /* Not already deleted or locked and too old */ LogWrite(6, DEBUG, " Deleting message #%lu by date\n", basenum + num); Header_S.Attribute |= MSG_DELETED; JAM_ChangeMsgHeader(Base_PS, num, &Header_S); BaseHeader_S.ActiveMsgs--; JAM_WriteMBHeader(Base_PS, &BaseHeader_S); del++; active--; } } num++; } if (ctrlc) { JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } LogWrite(3, MISCINFO, " %lu messages deleted by date, %lu messages left", del, active); } JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } bool PackAreaJAM(struct Area * area) { ulong active, basenum, total, del, num; s_JamBase *Base_PS, *NewBase_PS; s_JamBaseHeader BaseHeader_S; s_JamMsgHeader Header_S; s_JamSubPacket *SubPacket_PS; int res, res1, res2; uchar buf[200], oldname[200], tmpname[200]; bool firstwritten; uchar *msgtext; mode_t oldumask; LogWrite(2, MISCINFO, "Packing %s...", area->Tagname); if (JAM_OpenMB(area->Path, &Base_PS)) { LogWrite(1, USERERR, "Failed to open messagebase \"%s\"", area->Path); return (TRUE); } if (JAM_LockMB(Base_PS, 10)) { LogWrite(1, USERERR, "Timeout when trying to lock messagebase \"%s\"", area->Path); JAM_CloseMB(Base_PS); return (TRUE); } if (JAM_ReadMBHeader(Base_PS, &BaseHeader_S)) { LogWrite(1, USERERR, "Failed to read header of messagebase \"%s\"", area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } if (JAM_GetMBSize(Base_PS, &total)) { LogWrite(1, USERERR, "Failed to get size of messagebase \"%s\"", area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } basenum = BaseHeader_S.BaseMsgNum; active = BaseHeader_S.ActiveMsgs; if (total == 0) { LogWrite(3, MISCINFO, " Area is empty"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } snprintf(buf, 200, "%s.cmtemp", area->Path); oldumask = umask(MsgbaseUmask); if (JAM_CreateMB(buf, 1, &NewBase_PS)) { umask(oldumask); LogWrite(1, USERERR, "Failed to create new messagebase \"%s\"", buf); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } umask(oldumask); if (JAM_LockMB(NewBase_PS, 10)) { LogWrite(1, USERERR, "Timeout when trying to lock messagebase \"%s\"", buf); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } /* Copy messages */ del = 0; num = 0; firstwritten = FALSE; BaseHeader_S.ActiveMsgs = 0; while (num < total && !ctrlc) { res = JAM_ReadMsgHeader(Base_PS, num, &Header_S, NULL); if (res) { if (res == JAM_NO_MESSAGE) { if (firstwritten) { JAM_AddEmptyMessage(NewBase_PS); } else { BaseHeader_S.BaseMsgNum++; del++; } } else { LogWrite(1, USERERR, "Failed to read message %ld, cannot pack messagebase", num + basenum); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } } else { if (Header_S.Attribute & MSG_DELETED) { if (firstwritten) { JAM_AddEmptyMessage(NewBase_PS); } else { BaseHeader_S.BaseMsgNum++; del++; } } else { if (!firstwritten) { /* Set basenum */ res = JAM_WriteMBHeader(NewBase_PS, &BaseHeader_S); if (res) { LogWrite(1, USERERR, "Failed to write messagebase header, cannot pack messagebase"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } firstwritten = TRUE; } /* Read header with all subpackets */ res = JAM_ReadMsgHeader(Base_PS, num, &Header_S, &SubPacket_PS); if (res) { LogWrite(1, USERERR, "Failed to read message %ld, cannot pack messagebase", num + basenum); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } /* Read message text */ msgtext = NULL; if (Header_S.TxtLen) { if (!(msgtext = malloc(Header_S.TxtLen))) { LogWrite(1, SYSTEMERR, "Out of memory"); JAM_DelSubPacket(SubPacket_PS); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (FALSE); } res = JAM_ReadMsgText(Base_PS, Header_S.TxtOffset, Header_S.TxtLen, msgtext); if (res) { LogWrite(1, USERERR, "Failed to read message %ld, cannot pack messagebase", num + basenum); JAM_DelSubPacket(SubPacket_PS); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } } /* Write new message */ res = JAM_AddMessage(NewBase_PS, &Header_S, SubPacket_PS, msgtext, Header_S.TxtLen); if (msgtext) free(msgtext); JAM_DelSubPacket(SubPacket_PS); BaseHeader_S.ActiveMsgs++; if (res) { LogWrite(1, USERERR, "Failed to copy message %ld (disk full?), cannot pack messagebase", num + basenum); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } } } num++; } /* Write back header */ BaseHeader_S.ModCounter++; res = JAM_WriteMBHeader(NewBase_PS, &BaseHeader_S); if (res) { LogWrite(1, USERERR, "Failed to write messagebase header, cannot pack messagebase"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); JAM_UnlockMB(NewBase_PS); JAM_CloseMB(NewBase_PS); if (ctrlc) { JAM_RemoveMB(NewBase_PS, buf); return (TRUE); } /* This could not be done with JAMLIB... */ snprintf(oldname, 200, "%s%s", area->Path, EXT_HDRFILE); snprintf(tmpname, 200, "%s.cmtemp%s", area->Path, EXT_HDRFILE); res1 = unlink(oldname); res2 = rename(tmpname, oldname); if (!res1 && !res2) { snprintf(oldname, 200, "%s%s", area->Path, EXT_TXTFILE); snprintf(tmpname, 200, "%s.cmtemp%s", area->Path, EXT_TXTFILE); res1 = unlink(oldname); res2 = rename(tmpname, oldname); } if (!res1 && !res2) { snprintf(oldname, 200, "%s%s", area->Path, EXT_IDXFILE); snprintf(tmpname, 200, "%s.cmtemp%s", area->Path, EXT_IDXFILE); res1 = unlink(oldname); res2 = rename(tmpname, oldname); } if (!res1 && !res2) { /* snprintf(oldname, 200, "%s%s", area->Path, EXT_LRDFILE); */ snprintf(tmpname, 200, "%s.cmtemp%s", area->Path, EXT_LRDFILE); /* Keep lastread file */ res2 = unlink(tmpname); } if (res1 || res2) { LogWrite(1, SYSTEMERR, "Failed to update area. The area might be in use by another program"); return (FALSE); } LogWrite(3, MISCINFO, " %ld deleted messages removed from messagebase", del); return (TRUE); } /************************** Linking ***********************/ struct jMsg { unsigned long MsgIdCRC; unsigned long ReplyCRC; uchar *MsgIdData; uchar *ReplyData; unsigned long ReplyTo; unsigned long Reply1st; unsigned long ReplyNext; unsigned long OldReplyTo; unsigned long OldReply1st; unsigned long OldReplyNext; }; int jam_CompareMsgIdReply(s_JamBase * Base_PS, struct jMsg *msgs, ulong msgidmsg, ulong replymsg) { if (msgs[msgidmsg].MsgIdCRC != msgs[replymsg].ReplyCRC) return FALSE; if (jam_quicklink || (msgs[msgidmsg].MsgIdData && msgs[replymsg].ReplyData && !strcmp(msgs[replymsg].ReplyData, msgs[msgidmsg].MsgIdData))) return TRUE; return FALSE; } /* dest is a reply to num */ void jam_setreply(struct jMsg *msgs, ulong nummsgs, ulong base, ulong num, ulong dest) { int n, times; if (msgs[dest].ReplyTo) /* Already linked */ return; LogWrite(6, DEBUG, " Linking #%lu as a reply to #%ld", base + dest, base + num); msgs[dest].ReplyTo = num + base; if (msgs[num].Reply1st == 0) { msgs[num].Reply1st = dest + base; } else { n = msgs[num].Reply1st - base; if (n == dest) return; if (n < 0 || n >= nummsgs) { /* Oops! Base seems to be b0rken */ LogWrite(2, ACTIONINFO, "Warning: message #%ld is linked to something outside the base", num + base); return; } times = 0; while (msgs[n].ReplyNext) { times++; if (times > 1000) { /* Something appears to have gone wrong */ LogWrite(2, ACTIONINFO, "Warning: >1000 replies to message %ld or circular reply links", num + base); return; } n = msgs[n].ReplyNext - base; if (n == dest) return; if (n < 0 || n >= nummsgs) { /* Oops! Base seems to be b0rken */ LogWrite(2, ACTIONINFO, "Warning: message #%ld is linked to something outside the base", num + base); return; } } msgs[n].ReplyNext = dest + base; } } int LinkAreaJAM(struct Area *area) { ulong nummsgs, basenum, res; int c, d; s_JamBase *Base_PS; s_JamBaseHeader BaseHeader_S; struct jMsg *msgs; LogWrite(2, MISCINFO, "Linking %s...", area->Tagname); fflush(stdout); if (JAM_OpenMB(area->Path, &Base_PS)) { LogWrite(1, USERERR, "Failed to open messagebase \"%s\"", area->Path); return (TRUE); } if (JAM_LockMB(Base_PS, 10)) { LogWrite(1, USERERR, "Timeout when trying to lock JAM messagebase \"%s\"", area->Path); JAM_CloseMB(Base_PS); return (TRUE); } if (JAM_ReadMBHeader(Base_PS, &BaseHeader_S)) { LogWrite(1, USERERR, "Failed to read header of messagebase \"%s\"", area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } basenum = BaseHeader_S.BaseMsgNum; if (JAM_GetMBSize(Base_PS, &nummsgs)) { LogWrite(1, USERERR, "Failed to get size of JAM area \"%s\"", area->Path); return (TRUE); } if (nummsgs == 0) return (TRUE); /* Nothing to do */ /* Read msgid/reply */ if (!(msgs = calloc(nummsgs, sizeof(struct jMsg)))) { LogWrite(1, SYSTEMERR, "Out of memory, cannot link %s", area->Tagname); return (TRUE); } for (c = 0; c < nummsgs; c++) { s_JamMsgHeader Header_S; s_JamSubPacket *SubPacket_PS; res = JAM_ReadMsgHeader(Base_PS, c, &Header_S, &SubPacket_PS); msgs[c].MsgIdCRC = -1; msgs[c].ReplyCRC = -1; if (!res) { msgs[c].MsgIdCRC = Header_S.MsgIdCRC; msgs[c].ReplyCRC = Header_S.ReplyCRC; if (!relink) { msgs[c].ReplyTo = Header_S.ReplyTo; msgs[c].Reply1st = Header_S.Reply1st; msgs[c].ReplyNext = Header_S.ReplyNext; } msgs[c].OldReplyTo = Header_S.ReplyTo; msgs[c].OldReply1st = Header_S.Reply1st; msgs[c].OldReplyNext = Header_S.ReplyNext; if (!jam_quicklink) { s_JamSubfield *Field_PS; for (Field_PS = JAM_GetSubfield(SubPacket_PS); Field_PS; Field_PS = JAM_GetSubfield(NULL)) switch(Field_PS->LoID) { case JAMSFLD_MSGID: msgs[c].MsgIdData = calloc(Field_PS->DatLen + 1, 1); if (msgs[c].MsgIdData) mystrncpy(msgs[c].MsgIdData, Field_PS->Buffer, Field_PS->DatLen + 1); break; case JAMSFLD_REPLYID: msgs[c].ReplyData = calloc(Field_PS->DatLen + 1, 1); if (msgs[c].ReplyData) mystrncpy(msgs[c].ReplyData, Field_PS->Buffer, Field_PS->DatLen + 1); break; } JAM_DelSubPacket(SubPacket_PS); } } } for (c = 0; c < nummsgs; c++) { for (d = c+1; d < nummsgs; d++) { /* See if this is a reply to a message */ if (msgs[c].ReplyCRC != -1 && jam_CompareMsgIdReply(Base_PS, msgs, d, c)) jam_setreply(msgs, nummsgs, basenum, d, c); /* See if there are any replies to this message */ if (msgs[c].MsgIdCRC != -1 && jam_CompareMsgIdReply(Base_PS, msgs, c, d)) jam_setreply(msgs, nummsgs, basenum, c, d); } } /* Update links */ for (c = 0; c < nummsgs; c++) { if (msgs[c].ReplyTo != msgs[c].OldReplyTo || msgs[c].Reply1st != msgs[c].OldReply1st || msgs[c].ReplyNext != msgs[c].OldReplyNext) { s_JamMsgHeader Header_S; LogWrite(6, DEBUG, " Updating message #%lu", basenum + c); res = JAM_ReadMsgHeader(Base_PS, c, &Header_S, NULL); if (!res) { Header_S.ReplyTo = msgs[c].ReplyTo; Header_S.Reply1st = msgs[c].Reply1st; Header_S.ReplyNext = msgs[c].ReplyNext; JAM_ChangeMsgHeader(Base_PS, c, &Header_S); } } if (msgs[c].MsgIdData) free(msgs[c].MsgIdData); if (msgs[c].ReplyData) free(msgs[c].ReplyData); } free(msgs); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return (TRUE); } #endif /************************** end of messagebases *******************/ bool ReadConfig(uchar * file) { FILE *fh; uchar cfgword[20]; uchar tag[80], aka[80], path[200], mb[20], areafile[100]; struct Area *tmparea, *LastArea; ulong jbcpos; uchar *cfgbuf; if (!(cfgbuf = malloc(4000))) { fprintf(stderr, "Out of memory\n"); return (FALSE); } if (!(fh = fopen(file, "rt"))) { fprintf(stderr, "Failed to open %s for reading\n", file); fprintf(stderr, "Error: %s\n", strerror(errno)); free(cfgbuf); return (FALSE); } areafile[0] = 0; while (fgets(cfgbuf, 4000, fh)) { jbcpos = 0; jbstrcpy(cfgword, cfgbuf, 20, &jbcpos); if (stricmp(cfgword, "AREAFILE") == 0) jbstrcpy(areafile, cfgbuf, 100, &jbcpos); if (stricmp(cfgword, "LOGFILE") == 0) jbstrcpy(LogFile, cfgbuf, 100, &jbcpos); if (stricmp(cfgword, "LOGLEVEL") == 0) if (jbstrcpy(tag, cfgbuf, 80, &jbcpos)) LogLevel = atol(tag); if (stricmp(cfgword, "MSGBASEUMASK") == 0) if (jbstrcpy(tag, cfgbuf, 200, &jbcpos)) sscanf(tag, "%o", &MsgbaseUmask); #ifdef MSGBASE_JAM if (stricmp(cfgword, "JAM_QUICKLINK") == 0) jam_quicklink = TRUE; #endif } fclose(fh); if (areafile[0] == 0) { fprintf(stderr, "AREAFILE not defined in %s\n", file); free(cfgbuf); return (FALSE); } if (!(fh = fopen(areafile, "rt"))) { fprintf(stderr, "Failed to open %s for reading\n", areafile); fprintf(stderr, "Error: %s\n", strerror(errno)); free(cfgbuf); return (FALSE); } LastArea = NULL; while (fgets(cfgbuf, 4000, fh)) { jbcpos = 0; jbstrcpy(cfgword, cfgbuf, 20, &jbcpos); if (stricmp(cfgword, "AREA") == 0 || stricmp(cfgword, "NETMAIL") == 0 || stricmp(cfgword, "LOCALAREA") == 0) { jbstrcpy(tag, cfgbuf, 80, &jbcpos); jbstrcpy(aka, cfgbuf, 80, &jbcpos); if (stricmp(tag, "DEFAULT") != 0 && strnicmp(tag, "DEFAULT_", 8) != 0) { if (jbstrcpy(mb, cfgbuf, 20, &jbcpos)) { jbstrcpy(path, cfgbuf, 200, &jbcpos); if (!(tmparea = (struct Area *) calloc(1, sizeof(struct Area)))) { fprintf(stderr, "Out of memory\n"); fclose(fh); return (FALSE); } jbAddNode(&AreaList, (struct jbNode *) tmparea); LastArea = tmparea; strcpy(tmparea->Tagname, tag); strcpy(tmparea->Messagebase, mb); strcpy(tmparea->Path, path); } } } if (stricmp(cfgword, "KEEPDAYS") == 0 && LastArea) if (jbstrcpy(tag, cfgbuf, 80, &jbcpos)) LastArea->KeepDays = atol(tag); if (stricmp(cfgword, "KEEPNUM") == 0 && LastArea) if (jbstrcpy(tag, cfgbuf, 80, &jbcpos)) LastArea->KeepNum = atol(tag); } fclose(fh); free(cfgbuf); return (TRUE); } int main(int argc, char **argv) { struct Area *area; uchar *cfg = CONFIG_NAME, *afile = NULL, *pattern = NULL; int command, criteria = 0; int i; bool delafter = FALSE; uchar *only_type = NULL; #ifdef MSGBASE_JAM time_t t1, t2; struct tm *tp; #endif static struct option lopts[] = { {"help", 0, NULL, 'h'}, {"config", required_argument, NULL, 'c'}, {"file", required_argument, NULL, 'f'}, {"delete-after", 0, NULL, 'd'}, {"relink", 0, NULL, 'r'}, {"msg", 0, NULL, 'M'}, #ifdef MSGBASE_JAM {"jam", 0, NULL, 'J'}, #endif {"version", 0, NULL, SHOWVER}, {0, 0, 0, 0} }; #ifdef MSGBASE_JAM static uchar optstr[] = "+hc:f:drMJ"; #else static uchar optstr[] = "+hc:f:drM"; #endif static uchar helpstr[] = "Usage: crashmaint [OPTION]... COMMAND [PATTERN]\n" "Display CrashEcho statistics.\n" "\n" " -h, --help display this help and exit\n" " -c, --config=FILE use this configuration file instead of the default\n" " -f, --file=FILE read list of areas from FILE\n" " -d, --delete-after delete list of areas after processing\n" " -r, --relink do full relink of messagebase instead of updating\n" " new replylinks\n" " -M, --msg process only MSG areas\n" #ifdef MSGBASE_JAM " -J, --jam process only JAM areas\n" #endif " --version print version number and exit\n" "\n" "The following commands rcognized:\n" "\n" " purge [JM] delete messages according to age and/or number\n" " pack [JM] pack messagebase and optionaly renumber messages\n" " link [J-] link messagebase\n" "\n" "You can use only one of -f, -M" #ifdef MSGBASE_JAM ", -J" #endif " or PATTERN." "\n"; int ch, lopt_index; signal(SIGINT, breakfunc); while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){ switch(ch){ case 'h': case '?': default: fprintf(stderr, helpstr); exit(EXIT_OK); /* Not reached */ case SHOWVER: fprintf(stderr, "%s\n", verstr); exit(EXIT_OK); /* Not reached */ case 'c': cfg = optarg; break; case 'f': afile = optarg; criteria++; break; case 'd': delafter = TRUE; break; case 'r': relink = TRUE; break; case 'M': only_type = "MSG"; /* FIXME this is UGLY */ criteria++; break; #ifdef MSGBASE_JAM case 'J': only_type = "JAM"; /* FIXME this is UGLY */ criteria++; break; #endif } } argc -= optind; argv += optind; if(argc < 1){ fprintf(stderr, helpstr); exit(EXIT_ERROR); } jbNewList(&AreaList); jbNewList(&ProcessList); for (command = 0; command < CMD_MAX; command++) if (!stricmp(argv[0], Commands[command].Arg)) break; if (command == CMD_MAX) { fprintf(stderr, helpstr); exit(EXIT_ERROR); } if (argc > 1) { if (!(CheckPattern(pattern=argv[1]))) { fprintf(stderr, "Invalid pattern \"%s\"\n", pattern); exit(EXIT_ERROR); } criteria++; } if (criteria > 1) { fprintf(stderr, helpstr); exit(EXIT_ERROR); } MsgbaseUmask = umask(0777); umask(MsgbaseUmask); if (!(ReadConfig(cfg))) { jbFreeList(&AreaList); exit(EXIT_ERROR); } if (!LogFile[0]) { fprintf(stderr, "No logfile defined.\n"); exit(EXIT_ERROR); } if (!OpenLogfile(LogFile, LogLevel)) { exit(EXIT_ERROR); } #ifdef MSGBASE_JAM t1 = time(NULL); tp = gmtime(&t1); tp->tm_isdst = -1; t2 = mktime(tp); jam_utcoffset = t2 - t1; #endif if (!afile && delafter) { fprintf(stderr, helpstr); jbFreeList(&AreaList); exit(EXIT_ERROR); } LogWrite(2, SYSTEMINFO, "%s started successfully!", verstr); if (pattern) { for (area = (struct Area *) AreaList.First; area && !ctrlc; area = area->Next) if (MatchPattern(pattern, area->Tagname)) { struct Area *tmparea; if (!(tmparea = (struct Area *) calloc(1, sizeof(struct Area)))) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&AreaList); jbFreeList(&ProcessList); return (FALSE); } memcpy(tmparea, area, sizeof(struct Area)); jbAddNode(&ProcessList, (struct jbNode *) tmparea); jbFreeNode(&AreaList, (struct jbNode *) area); } } else if (only_type) { for (area = (struct Area *) AreaList.First; area && !ctrlc; area = area->Next) if (stricmp(only_type, area->Messagebase) == 0) { struct Area *tmparea; if (!(tmparea = (struct Area *) calloc(1, sizeof(struct Area)))) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&AreaList); jbFreeList(&ProcessList); return (FALSE); } memcpy(tmparea, area, sizeof(struct Area)); jbAddNode(&ProcessList, (struct jbNode *) tmparea); jbFreeNode(&AreaList, (struct jbNode *) area); } } else if (afile && Exists(afile)){ FILE *fh; uchar *buf; struct Area *tmparea; if (!(buf = malloc(1024))) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&AreaList); exit(EXIT_ERROR); } if ((fh=fopen(afile, "rt")) == NULL){ LogWrite(1, SYSTEMERR, "Failed to open file %s for reading", afile); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); jbFreeList(&AreaList); exit(EXIT_ERROR); } while (fgets(buf, 1024, fh)) { if (buf[0] && buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; for (area = (struct Area *) AreaList.First; area; area = area->Next) if (!stricmp(buf, area->Tagname)) { if (!(tmparea = (struct Area *) calloc(1, sizeof(struct Area)))) { LogWrite(1, SYSTEMERR, "Out of memory"); jbFreeList(&AreaList); jbFreeList(&ProcessList); fclose(fh); free(buf); return (FALSE); } memcpy(tmparea, area, sizeof(struct Area)); jbAddNode(&ProcessList, (struct jbNode *) tmparea); jbFreeNode(&AreaList, (struct jbNode *) area); } } fclose(fh); free(buf); } for (area = (struct Area *) (criteria==0 ? AreaList.First : ProcessList.First); area && !ctrlc; area = area->Next) { for(i=0;Messagebases[i].Name;i++) if(stricmp(Messagebases[i].Name,area->Messagebase)==0) break; if (Messagebases[i].processfunc[command]) { if (!Messagebases[i].processfunc[command](area)) { jbFreeList(&AreaList); jbFreeList(&ProcessList); exit(EXIT_ERROR); } } /* else { fprintf(stderr, "Area %s skipped: can\'t %s %s messagebase\n", area->Tagname, Commands[command].Str, area->Messagebase); } */ } if (ctrlc) LogWrite(1, SYSTEMERR, "*** User Break ***"); else if (delafter && Exists(afile)) unlink(afile); jbFreeList(&AreaList); jbFreeList(&ProcessList); LogWrite(2, SYSTEMINFO, "CrashMaint end"); CloseLogfile(); exit(EXIT_OK); }