#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PLATFORM_AMIGA char *ver="$VER: CrashMaint "VERSION" ("__COMMODORE_DATE__")"; #endif static size_t ptrsize = sizeof(void *); struct Area { struct Area *Next; char Tagname[80]; char Path[80]; char Messagebase[20]; uint32_t KeepNum,KeepDays; }; struct jbList AreaList; struct Messagebase { char *Name; bool (*processfunc)(struct Area *area,bool maint,bool pack,bool verbose); }; #ifdef MSGBASE_MSG bool ProcessAreaMSG(struct Area *area,bool maint, bool pack, bool verbose); #endif #ifdef MSGBASE_JAM bool ProcessAreaJAM(struct Area *area,bool maint, bool pack, bool verbose); #endif struct Messagebase Messagebases[] = { #ifdef MSGBASE_JAM { "JAM", ProcessAreaJAM }, #endif #ifdef MSGBASE_MSG { "MSG", ProcessAreaMSG }, #endif { NULL, NULL } }; #define ARG_MAINT 0 #define ARG_PACK 1 #define ARG_VERBOSE 2 #define ARG_SETTINGS 3 #define ARG_PATTERN 4 struct argument args[] = { { ARGTYPE_BOOL, "MAINT", 0, NULL }, { ARGTYPE_BOOL, "PACK", 0, NULL }, { ARGTYPE_BOOL, "VERBOSE", 0, NULL }, { ARGTYPE_STRING, "SETTINGS", 0, NULL }, { ARGTYPE_STRING, "PATTERN", 0, NULL }, { ARGTYPE_END, NULL, 0, 0 } }; bool ctrlc; void breakfunc(int x) { ctrlc=TRUE; } /******************** *.msg *********************/ #ifdef MSGBASE_MSG struct Msg { struct Msg *Next; uint32_t 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; uint32_t nc; nc=0; for(msg=(struct Msg *)list->First;msg;msg=msg->Next) nc++; if(nc == 0) return(TRUE); if(!(buf=(struct Msg **)osAlloc(nc*sizeof(struct StatsNode *)))) return(FALSE); work=buf; for(msg=(struct Msg *)list->First;msg;msg=msg->Next) *work++=msg; qsort(buf,nc,ptrsize,Compare); jbNewList(list); for(work=buf;nc--;) jbAddNode(list,(struct jbNode *)*work++); osFree(buf); return(TRUE); } void MakeFidoDate(time_t tim,char *dest) { struct tm *tp; time_t t; char *monthnames[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"}; t=tim; tp=localtime(&t); sprintf(dest,"%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); } char *scanfuncarea; bool nomem; void scanfunc(char *str) { char buf[200]; struct osFileEntry *fe; uint32_t 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=osGetFileEntry(buf))) return; num=atol(str); day=fe->Date / (24*60*60); osFree(fe); if(!(msg=(struct Msg *)osAlloc(sizeof(struct Msg)))) { nomem=TRUE; return; } jbAddNode(&MsgList,(struct jbNode *)msg); msg->Num=num; msg->Day=day; } bool ProcessAreaMSG(struct Area *area,bool maint, bool pack, bool verbose) { uint32_t today,num,del,highwater,oldhighwater; struct Msg *msg; char buf[200],newbuf[200],buf2[100]; struct StoredMsg StoredMsg; osFile fh; highwater=0; oldhighwater=0; jbNewList(&MsgList); printf("Processing %s...\n",area->Tagname); scanfuncarea=area->Path; if(!(osScanDir(area->Path,scanfunc))) { uint32_t err=osError(); printf(" Error: Couldn't scan directory %s\n",area->Path); printf(" Error: %s\n",osErrorMsg(err)); jbFreeList(&MsgList); return(TRUE); } if(nomem) { printf("Out of memory\n"); jbFreeList(&MsgList); return(FALSE); } if(!Sort(&MsgList)) { printf("Out of memory\n"); jbFreeList(&MsgList); return(FALSE); } if(!MsgList.First) { printf(" Area is empty\n"); return(TRUE); } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } MakeFullPath(area->Path,"1.msg",buf,200); if((fh=osOpen(buf,MODE_OLDFILE))) { if(osRead(fh,&StoredMsg,sizeof(struct StoredMsg))==sizeof(struct StoredMsg)) { highwater=StoredMsg.ReplyTo; oldhighwater=StoredMsg.ReplyTo; } osClose(fh); } if(maint && 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; sprintf(buf2,"%u.msg",msg->Num); MakeFullPath(area->Path,buf2,buf,200); if(msg->Num == highwater) highwater=0; if(verbose) printf(" Deleting message #%u by number\n",msg->Num); osDelete(buf); msg->Num=0; num--; del++; } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } printf(" %u messages deleted by number, %u messages left\n",del,num); } if(maint && 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) { sprintf(buf2,"%u.msg",msg->Num); MakeFullPath(area->Path,buf2,buf,200); if(msg->Num == highwater) highwater=0; if(verbose) printf(" Deleting message #%u by date\n",msg->Num); osDelete(buf); msg->Num=0; del++; } else { num++; } } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } printf(" %u messages deleted by date, %u messages left\n",del,num); } if(pack) { 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) { sprintf(buf2,"%u.msg",msg->Num); MakeFullPath(area->Path,buf2,buf,200); sprintf(buf2,"%u.msg",msg->NewNum); MakeFullPath(area->Path,buf2,newbuf,200); if(highwater == msg->Num) highwater=msg->NewNum; if(verbose) printf(" Renaming message %u to %u\n",msg->Num,msg->NewNum); osRename(buf,newbuf); } if(ctrlc) { jbFreeList(&MsgList); return(TRUE); } printf(" Area renumbered\n"); } jbFreeList(&MsgList); if(highwater!=oldhighwater) { strcpy(StoredMsg.From,"MagiMail"); 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); if((fh=osOpen(buf,MODE_NEWFILE))) { osWrite(fh,&StoredMsg,sizeof(struct StoredMsg)); osWrite(fh,"",1); osClose(fh); } } return(TRUE); } #endif /*************************** JAM ************************/ #ifdef MSGBASE_JAM long jam_utcoffset = 0xbaadf00d; bool ProcessAreaJAM(struct Area *area,bool maint, bool pack, bool verbose) { uint32_t today,active,basenum,total,del,num,day; s_JamBase *Base_PS,*NewBase_PS; s_JamBaseHeader BaseHeader_S; s_JamMsgHeader Header_S; s_JamSubPacket* SubPacket_PS; int res,res1,res2; char buf[200],oldname[200],tmpname[200]; bool firstwritten; char *msgtext; /* Some timezone tricks */ if(jam_utcoffset == 0xbaadf00d) { time_t t1,t2; struct tm *tp; t1=time(NULL); tp=gmtime(&t1); tp->tm_isdst=-1; t2=mktime(tp); jam_utcoffset=t2-t1; } printf("Processing %s...\n",area->Tagname); if(JAM_OpenMB(area->Path,&Base_PS)) { printf(" Failed to open messagebase \"%s\"\n",area->Path); return(TRUE); } if(JAM_LockMB(Base_PS,10)) { printf(" Timeout when trying to lock messagebase \"%s\"\n",area->Path); JAM_CloseMB(Base_PS); return(TRUE); } if(JAM_ReadMBHeader(Base_PS,&BaseHeader_S)) { printf(" Failed to read header of messagebase \"%s\"\n",area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } if(JAM_GetMBSize(Base_PS,&total)) { printf(" Failed to get size of messagebase \"%s\"\n",area->Path); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } basenum=BaseHeader_S.BaseMsgNum; active=BaseHeader_S.ActiveMsgs; if(total == 0) { printf(" Area is empty\n"); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } if(maint && 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 & JAM_MSG_DELETED)) { /* Not already deleted */ if(verbose) printf(" Deleting message #%u by number\n",basenum+num); Header_S.Attribute |= JAM_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); } printf(" %u messages deleted by number, %u messages left\n",del,active); } if(maint && 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 & JAM_MSG_DELETED)) { /* Not already deleted and too old*/ if(verbose) printf(" Deleting message #%u by date\n",basenum+num); Header_S.Attribute |= JAM_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); } printf(" %u messages deleted by date, %u messages left\n",del,active); } if(pack) { strcpy(buf,area->Path); strcat(buf,".cmtemp"); if(JAM_CreateMB(buf,1,&NewBase_PS)) { printf(" Failed to create new messagebase \"%s\"\n",buf); JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); return(TRUE); } if(JAM_LockMB(NewBase_PS,10)) { printf(" Timeout when trying to lock messagebase \"%s\"\n",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 { printf(" Failed to read message %d, cannot pack messagebase\n",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 & JAM_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) { printf(" Failed to write messagebase header, cannot pack messagebase\n"); 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) { printf(" Failed to read message %d, cannot pack messagebase\n",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=osAlloc(Header_S.TxtLen))) { printf("Out of memory\n"); 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) { printf(" Failed to read message %d, cannot pack messagebase\n",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) osFree(msgtext); JAM_DelSubPacket(SubPacket_PS); BaseHeader_S.ActiveMsgs++; if(res) { printf(" Failed to copy message %d (disk full?), cannot pack messagebase\n",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) { printf(" Failed to write messagebase header, cannot pack messagebase\n"); 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... */ sprintf(oldname,"%s%s",area->Path,EXT_HDRFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_HDRFILE); res1=osDelete(oldname); res2=osRename(tmpname,oldname); if(res1 && res2) { sprintf(oldname,"%s%s",area->Path,EXT_TXTFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_TXTFILE); res1=osDelete(oldname); res2=osRename(tmpname,oldname); } if(res1 && res2) { sprintf(oldname,"%s%s",area->Path,EXT_IDXFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_IDXFILE); res1=osDelete(oldname); res2=osRename(tmpname,oldname); } if(res1 && res2) { sprintf(oldname,"%s%s",area->Path,EXT_LRDFILE); sprintf(tmpname,"%s.cmtemp%s",area->Path,EXT_LRDFILE); /* Keep lastread file */ res2=osDelete(tmpname); } if(!res1 || !res2) { printf(" Failed to update area. The area might be in use by another program.\n"); return(FALSE); } printf(" %d deleted messages removed from messagebase\n",del); } else { JAM_UnlockMB(Base_PS); JAM_CloseMB(Base_PS); } return(TRUE); } #endif /************************** end of messagebases *******************/ char cfgbuf[4000]; bool ReadConfig(char *file) { osFile fh; char cfgword[20]; char tag[80],aka[80],path[80],mb[20]; struct Area *tmparea,*LastArea; uint32_t jbcpos; if(!(fh=osOpen(file,MODE_OLDFILE))) { uint32_t err=osError(); printf("Failed to open file %s for reading\n",file); printf("Error: %s\n",osErrorMsg(err)); return(FALSE); } LastArea=NULL; while(osFGets(fh,cfgbuf,4000)) { jbcpos=0; jbstrcpy(cfgword,cfgbuf,20,&jbcpos); 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); } 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,80,&jbcpos); if(!(tmparea=(struct Area *)osAllocCleared(sizeof(struct Area)))) { printf("Out of memory\n"); osClose(fh); return(FALSE); } jbAddNode(&AreaList,(struct jbNode *)tmparea); LastArea=tmparea; strcpy(tmparea->Tagname,tag); strcpy(tmparea->Messagebase,mb); strcpy(tmparea->Path,path); } } } } osClose(fh); return(TRUE); } int main(int argc, char **argv) { struct Area *area; char *cfg; bool maint,pack,verbose; int i; signal(SIGINT,breakfunc); if(!osInit()) exit(OS_EXIT_ERROR); if(argc > 1 && (strcmp(argv[1],"?")==0 || strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"help")==0 || strcmp(argv[1],"/h")==0 || strcmp(argv[1],"/?")==0 )) { printargs(args); osEnd(); exit(OS_EXIT_OK); } if(!parseargs(args,argc,argv)) { osEnd(); exit(OS_EXIT_ERROR); } jbNewList(&AreaList); maint=FALSE; pack=FALSE; verbose=FALSE; if(args[ARG_MAINT].data) maint=TRUE; if(args[ARG_PACK].data) pack=TRUE; if(args[ARG_VERBOSE].data) verbose=TRUE; if(!maint && !pack) { printf("Nothing to do.\n"); osEnd(); exit(OS_EXIT_OK); } if(args[ARG_PATTERN].data) { if(!(osCheckPattern((char *)args[ARG_PATTERN].data))) { printf("Invalid pattern \"%s\"\n",(char *)args[ARG_PATTERN].data); osEnd(); exit(OS_EXIT_ERROR); } } cfg=getenv(OS_CONFIG_VAR); if(!cfg) cfg=OS_CONFIG_NAME; if(args[ARG_SETTINGS].data) cfg=(char *)args[ARG_SETTINGS].data; if(!(ReadConfig(cfg))) { jbFreeList(&AreaList); osEnd(); exit(OS_EXIT_ERROR); } for(area=(struct Area *)AreaList.First;area && !ctrlc;area=area->Next) { bool match; match=FALSE; if(!args[ARG_PATTERN].data) match=TRUE; else if(osMatchPattern((char *)args[ARG_PATTERN].data,area->Tagname)) match=TRUE; if(match) { for(i=0;Messagebases[i].Name;i++) if(stricmp(Messagebases[i].Name,area->Messagebase)==0) break; if(Messagebases[i].processfunc) { if(!Messagebases[i].processfunc(area,maint,pack,verbose)) exit(OS_EXIT_ERROR); } else { printf("Cannot process area %s, messagebase %s not supported by CrashMaint\n", area->Tagname, area->Messagebase); } } } if(ctrlc) printf("*** User Break ***\n"); jbFreeList(&AreaList); osEnd(); exit(OS_EXIT_OK); }