/******************************************************** * File: areafix.c * Created at Sun Jan 28 22:10:29 MSK 2001 by raorn // raorn@binec.ru * AreaFix * $Id: areafix.c,v 1.25 2002/10/15 20:18:58 raorn Exp $ *******************************************************/ #include "crashecho.h" #ifdef HAVE_STDARG_H # include #endif bool AreaFixHandle(struct MemMessage * mm); void SendRemoteAreafix(void); struct Arealist *FindForward(uchar * tagname, uchar * flags); void RemoteAreafix(uchar * area, struct ConfigNode *node); bool CheckFlags(uchar group, uchar * node); struct afReply { struct MemMessage *mm; uchar subject[72]; ulong lines; ulong part; }; struct afReply *afInitReply(uchar * fromname, struct Node4D *from4d, uchar * toname, struct Node4D *to4d, uchar * subject); void afFreeReply(struct afReply *af); void afAddLine(struct afReply *af, uchar * fmt, ...); void afSendMessage(struct afReply *af); void AddCommandReply(struct afReply *af, uchar * cmd, uchar * reply); void rawSendList(short type, struct Node4D *from4d, uchar * toname, struct ConfigNode *cnode); void rawSendHelp(struct Node4D *from4d, uchar * toname, struct ConfigNode *cnode); void rawSendInfo(struct Node4D *from4d, uchar * toname, struct ConfigNode *cnode); bool ScanAreaFix(void) { LogWrite(2, ACTIONINFO, "Scanning NETMAIL for AreaFix requests"); if (config.Netmail.Messagebase->scanfunc) { if (!(*config.Netmail.Messagebase->scanfunc) (&config.Netmail, FALSE, TRUE, 0, NULL, AcceptNotRecd, AreaFixHandle)) { return FALSE; } } return TRUE; } bool CheckAreaFix(void) { ulong day; struct Area *area; day = time(NULL) / (24 * 60 * 60); LogWrite(3, AREAFIX, "Checking for expired requests"); for (area = (struct Area *) config.AreaList.First; area; area = area->Next) if (area->Messagebase == NULL && area->Expires && area->Expires < day) { LogWrite(4, AREAFIX, "AreaFix: Forward request for \"%s\" expired - removing", area->Tagname); SendRemoveMessages(area); area->AreaType=AREATYPE_DELETED; } RemoveDeletedAreas(); LogWrite(3, AREAFIX, "Checking for deleted areas"); for (area = (struct Area *) config.AreaList.First; area; area = area->Next) if (area->Removed && area->Removed < day) { LogWrite(4, AREAFIX, "AreaFix: Area \"%s\" removed - deleting from config", area->Tagname); jbFreeList(&area->TossNodes); jbFreeNode(&config.AreaList, (struct jbNode *)area); config.changed=TRUE; } return TRUE; } bool AreaFixHandle(struct MemMessage * mm) { struct AreaFixName *areafixname; struct Aka *tmpaka; for (tmpaka = (struct Aka *) config.AkaList.First; tmpaka; tmpaka = tmpaka->Next) if (Compare4D(&tmpaka->Node, &mm->DestNode) == 0) break; if (!tmpaka) return TRUE; for (areafixname = (struct AreaFixName *) config.AreaFixList.First; areafixname; areafixname = areafixname->Next) if (stricmp(areafixname->Name, mm->To) == 0) break; if (!areafixname) return TRUE; if (!AreaFix(mm)) return FALSE; mm->Changed = TRUE; if (config.cfg_Flags & CFG_KEEPAREAFIX) mm->Attr |= FLAG_RECD; else mm->Deleted = TRUE; return (TRUE); } #define COMMAND_UPDATE 1 #define COMMAND_ADD 2 #define COMMAND_REMOVE 3 bool AreaFix(struct MemMessage * mm) { struct Arealist *arealist; struct ConfigNode *cnode; struct Area *area; struct TossNode *temptnode; struct BannedNode *bannednode; struct TextChunk *chunk; ulong c, d, q, jbcpos; uchar password[100], buf[100], buf2[100]; bool stop, sendareaquery, sendarealist, sendavail, sendareaunlinked, sendhelp, sendinfo, done, iswild; bool globalrescan, wasfeed; uchar *opt, areaname[100], command; struct afReply *afr; Print4D(&mm->OrigNode, buf, 100); LogWrite(4, AREAFIX, "AreaFix: Request from %s", buf); /* Init reply structure */ for (cnode = (struct ConfigNode *) config.CNodeList.First; cnode; cnode = cnode->Next) if (Compare4D(&cnode->Node, &mm->OrigNode) == 0) break; if (!cnode) { Print4D(&mm->OrigNode, buf, 100); LogWrite(2, AREAFIX, "AreaFix: Unknown node %s", buf); if (!(afr = afInitReply(((struct AreaFixName *) config.AreaFixList.First)->Name, &(((struct Aka *) (config.AkaList.First))->Node), mm->From, &mm->OrigNode, "AreaFix response"))) return (FALSE); afAddLine(afr, "Sorry, your node is not configured here."); afSendMessage(afr); afFreeReply(afr); return (TRUE); } else if (!(afr = afInitReply(((struct AreaFixName *) config.AreaFixList.First)->Name, &cnode->Aka->Node, mm->From, &mm->OrigNode, "AreaFix response"))) return (FALSE); jbcpos = 0; jbstrcpy(password, mm->Subject, 100, &jbcpos); if (stricmp(password, cnode->AreafixPW) != 0) { Print4D(&mm->OrigNode, buf, 100); LogWrite(2, AREAFIX, "AreaFix: Wrong password \"%s\" from %s", password, buf); afAddLine(afr, "Sorry, wrong password."); afSendMessage(afr); afFreeReply(afr); return (TRUE); } sendarealist = FALSE; sendavail = FALSE; sendareaquery = FALSE; sendareaunlinked = FALSE; sendinfo = FALSE; sendhelp = FALSE; globalrescan = FALSE; done = FALSE; while (jbstrcpy(password, mm->Subject, 100, &jbcpos)) { if (!stricmp(password, "-L")) { sendarealist = TRUE; done = TRUE; AddCommandReply(afr, password, "Sending list of all areas"); } else if (!stricmp(password, "-A")) { sendavail = TRUE; done = TRUE; AddCommandReply(afr, password, "Sending list of areas available from uplink"); } else if (!stricmp(password, "-Q")) { sendareaquery = TRUE; done = TRUE; AddCommandReply(afr, password, "Sending query"); } else if (!stricmp(password, "-I")) { sendinfo = TRUE; done = TRUE; AddCommandReply(afr, password, "Sending configuration info"); } else if (!stricmp(password, "-U")) { sendareaunlinked = TRUE; done = TRUE; AddCommandReply(afr, password, "Sending list of all unlinked areas"); } else if (!stricmp(password, "-R")) { if (config.cfg_Flags & CFG_ALLOWRESCAN) { AddCommandReply(afr, password, "Will rescan all areas"); globalrescan = TRUE; } else { AddCommandReply(afr, password, "No rescanning allowed"); } } } stop = FALSE; for (chunk = (struct TextChunk *) mm->TextChunks.First; chunk && !stop; chunk = chunk->Next) { if (chunk->Data) { ulong clen = strlen(chunk->Data); for (c = 0; c < clen && !stop; c++) { for (d = 0; d < 100 && chunk->Data[c + d] != 13 && chunk->Data[c + d] != 10 && c + d < clen; d++) buf[d] = chunk->Data[c + d]; buf[d] = 0; c += d; if (strncmp(buf, "---", 3) == 0) { stop = TRUE; break; } stripleadtrail(buf); if (buf[0] == '%') { jbcpos = 0; jbstrcpy(buf2, buf, 100, &jbcpos); if (stricmp(buf2, "%PAUSE") == 0) { if (cnode->Flags & NODE_PASSIVE) { AddCommandReply(afr, buf, "Your system is already passive"); } else { cnode->Flags |= NODE_PASSIVE; cnode->changed = TRUE; config.changed = TRUE; done = TRUE; AddCommandReply(afr, buf, "Your system is marked as passive"); AddCommandReply(afr, "", "Send %RESUME to get echomail again"); } } else if (stricmp(buf2, "%RESUME") == 0) { if (cnode->Flags & NODE_PASSIVE) { cnode->Flags &= ~NODE_PASSIVE; cnode->changed = TRUE; config.changed = TRUE; done = TRUE; AddCommandReply(afr, buf, "Your system is active again"); } else { AddCommandReply(afr, buf, "Your system is not paused"); } } else if (stricmp(buf2, "%PWD") == 0) { if (jbstrcpy(buf2, buf, 40, &jbcpos)) { mystrncpy(cnode->AreafixPW, buf2, 40); cnode->changed = TRUE; config.changed = TRUE; done = TRUE; AddCommandReply(afr, buf, "AreaFix password changed"); } else { AddCommandReply(afr, buf, "No new password specified"); } } else if (stricmp(buf2, "%RESCAN") == 0) { if (config.cfg_Flags & CFG_ALLOWRESCAN) { AddCommandReply(afr, buf, "Will rescan all areas added after this line"); globalrescan = TRUE; } else { AddCommandReply(afr, buf, "No rescanning allowed"); } } else if (stricmp(buf2, "%COMPRESS") == 0) { if (jbstrcpy(buf2, buf, 40, &jbcpos)) { bool gotpacker; struct Packer *tmppacker; gotpacker = FALSE; tmppacker = NULL; if (buf2[0] != '?') { if (stricmp(buf2, "NONE") == 0) { tmppacker = NULL; gotpacker = TRUE; } else { for (tmppacker = (struct Packer *) config.PackerList.First; tmppacker; tmppacker = tmppacker->Next) if (stricmp(buf2, tmppacker->Name) == 0 && tmppacker->Packer[0]) break; if (tmppacker) gotpacker = TRUE; else AddCommandReply(afr, buf, "Unknown packer. Choose from this list:"); } } else { AddCommandReply(afr, buf, "Sending list of packers:"); } if (gotpacker) { cnode->Packer = tmppacker; cnode->changed = TRUE; config.changed = TRUE; AddCommandReply(afr, buf, "Packed changed"); } else { for (tmppacker = (struct Packer *) config.PackerList.First; tmppacker; tmppacker = tmppacker->Next) { if (tmppacker->Packer[0]) AddCommandReply(afr, "", tmppacker->Name); } AddCommandReply(afr, "", "NONE"); } done = TRUE; } else { AddCommandReply(afr, buf, "No new method specified"); } } else if (stricmp(buf2, "%LIST") == 0) { sendarealist = TRUE; done = TRUE; AddCommandReply(afr, buf, "Sending list of all areas"); } else if (stricmp(buf2, "%AVAIL") == 0) { sendavail = TRUE; done = TRUE; AddCommandReply(afr, buf, "Sending list of areas available from uplink"); } else if (stricmp(buf2, "%QUERY") == 0) { sendareaquery = TRUE; done = TRUE; AddCommandReply(afr, buf, "Sending query"); } else if (stricmp(buf2, "%UNLINKED") == 0) { sendareaunlinked = TRUE; done = TRUE; AddCommandReply(afr, buf, "Sending list of all unlinked areas"); } else if (stricmp(buf2, "%HELP") == 0) { sendhelp = TRUE; done = TRUE; AddCommandReply(afr, buf, "Sending help file"); } else if (stricmp(buf2, "%INFO") == 0) { sendinfo = TRUE; done = TRUE; AddCommandReply(afr, buf, "Sending configuration info"); } else { done = TRUE; AddCommandReply(afr, buf, "Unknown command"); } } else if (buf[0] != 1 && buf[0] != 0) { ulong rescannum; bool patterndone, dorescan, areaexists, success; struct Area *rescanarea; done = TRUE; rescannum = 0; dorescan = FALSE; /* Separate command, name and opt */ mystrncpy(areaname, buf, 100); opt = ""; for (q = 0; areaname[q]; q++) if (areaname[q] == ',') opt = &areaname[q]; if (opt[0] == ',') { opt[0] = 0; opt = &opt[1]; while (opt[0] == 32) opt = &opt[1]; } striptrail(areaname); striptrail(opt); if (areaname[0] == '-') { command = COMMAND_REMOVE; strcpy(areaname, &areaname[1]); } else if (areaname[0] == '=') { command = COMMAND_UPDATE; strcpy(areaname, &areaname[1]); } else { command = COMMAND_ADD; if (areaname[0] == '+') strcpy(areaname, &areaname[1]); } if (!stricmp(areaname, "NETMAIL")) { afAddLine(afr, "%-30.30s Oops!.. I don't recommend you to do this again...", buf); } else if (!CheckPattern(areaname)) { afAddLine(afr, "%-30.30s Invalid pattern", buf); } else { iswild = IsPattern(areaname); if (iswild) { afAddLine(afr, "%s", buf); afAddLine(afr, ""); } patterndone = FALSE; areaexists = FALSE; rescanarea = NULL; for (area = (struct Area *) config.AreaList.First; area; area = area->Next) if (area->AreaType == AREATYPE_ECHOMAIL) { if (MatchPattern(areaname, area->Tagname)) { areaexists = TRUE; for (temptnode = (struct TossNode *) area->TossNodes.First; temptnode; temptnode = temptnode->Next) if (temptnode->ConfigNode == cnode) break; switch (command) { case COMMAND_ADD: if (!temptnode) { /* Do we have access? */ if (CheckFlags(area->Group, cnode->Groups) || CheckFlags(area->Group, cnode->ReadOnlyGroups)) { patterndone = TRUE; for (bannednode = (struct BannedNode *) area->BannedNodes.First; bannednode; bannednode = bannednode->Next) if (bannednode->ConfigNode == cnode) break; /* Are we banned? */ if (bannednode) { if (iswild) afAddLine(afr, " You have been banned from %s", area->Tagname); else AddCommandReply(afr, buf, "You have been banned from that area"); LogWrite(3, AREAFIX, "AreaFix: This node is banned in %s", area->Tagname); } else { if ((area->Flags & AREA_DEFREADONLY) || CheckFlags(area->Group, cnode->ReadOnlyGroups)) { LogWrite(4, AREAFIX, "AreaFix: Attached to %s as read-only", area->Tagname); if (iswild) afAddLine(afr, " Attached to %s as read-only", area->Tagname); else AddCommandReply(afr, buf, "Attached as read-only"); } else { LogWrite(4, AREAFIX, "AreaFix: Attached to %s", area->Tagname); if (iswild) afAddLine(afr, " Attached to %s", area->Tagname); else AddCommandReply(afr, buf, "Attached"); } if (!(temptnode = calloc(1, sizeof(struct TossNode)))) { afFreeReply(afr); nomem = TRUE; return (FALSE); } temptnode->ConfigNode = cnode; if ((area->Flags & AREA_DEFREADONLY) || CheckFlags(area->Group, cnode->ReadOnlyGroups)) temptnode->Flags = TOSSNODE_READONLY; jbAddNode(&area->TossNodes, (struct jbNode *) temptnode); rescanarea = area; area->changed = TRUE; config.changed = TRUE; } } else if (!iswild) { AddCommandReply(afr, buf, "You don't have access to that area"); } } else { patterndone = TRUE; if (iswild) afAddLine(afr, " You are already attached to %s", area->Tagname); else AddCommandReply(afr, buf, "You are already attached to that area"); } break; case COMMAND_REMOVE: if (!temptnode) { if (!iswild) { AddCommandReply(afr, buf, "You are not attached to that area"); patterndone = TRUE; } } else { patterndone = TRUE; if ((area->Flags & AREA_MANDATORY) && !(temptnode->Flags & TOSSNODE_FEED)) { if (iswild) afAddLine(afr, " You are not allowed to detach from %s", area->Tagname); else AddCommandReply(afr, buf, "You are not allowed to detach from that area"); } else { LogWrite(4, AREAFIX, "AreaFix: Detached from %s", area->Tagname); if (iswild) afAddLine(afr, " Detached from %s", area->Tagname); else AddCommandReply(afr, buf, "Detached"); wasfeed = FALSE; if (temptnode->Flags & TOSSNODE_FEED) wasfeed = TRUE; jbFreeNode(&area->TossNodes, (struct jbNode *) temptnode); area->changed = TRUE; config.changed = TRUE; if (wasfeed && (config.cfg_Flags & CFG_REMOVEWHENFEED)) { LogWrite(2, AREAFIX, "AreaFix: Feed disconnected, removing area %s", area->Tagname); SendRemoveMessages(area); area->AreaType=AREATYPE_DELETED; } else if (config.cfg_Flags & CFG_AREAFIXREMOVE) { if (area->TossNodes.First == NULL || (((struct TossNode *) area->TossNodes.First)->Next == NULL && ((struct TossNode *) area->TossNodes.First)->Flags & TOSSNODE_FEED)) { if (!area->Messagebase) { if (area->TossNodes.First) { LogWrite(3, AREAFIX, "AreaFix: Area %s removed, message sent to areafix", area->Tagname); snprintf(buf, 100, "-%s", area->Tagname); RemoteAreafix(buf, ((struct TossNode *) area->TossNodes.First)->ConfigNode); } else { LogWrite(3, AREAFIX, "AreaFix: Area %s removed", area->Tagname); } area->AreaType=AREATYPE_DELETED; } } } } } break; case COMMAND_UPDATE: if (temptnode) { patterndone = TRUE; if (globalrescan) { if (iswild) { afAddLine(afr, " Will rescan %s", area->Tagname); rescanarea = area; } else { AddCommandReply(afr, buf, "Will rescan area"); rescanarea = area; } } else { if (iswild) { afAddLine(afr, " Nothing to do with %s", area->Tagname); rescanarea = area; } else { AddCommandReply(afr, buf, "Nothing to do"); rescanarea = area; } } } break; } } if (command == COMMAND_UPDATE || command == COMMAND_ADD) { if (patterndone && rescanarea) { if (strnicmp(opt, "r=", 2) == 0) { rescannum = atoi(&opt[2]); dorescan = TRUE; } else if (opt[0]) { afAddLine(afr, "%-30.30s Unknown option %s", "", opt); } if (globalrescan || dorescan) { if (config.cfg_Flags & CFG_ALLOWRESCAN) { if (!rescanarea->Messagebase) { afAddLine(afr, "%-30.30s Can't rescan, area is pass-through", ""); } else if (!rescanarea->Messagebase->scanfunc) { afAddLine(afr, "%-30.30s Can't rescan, messagebase does not support rescan", ""); } else { LogWrite(4, AREAFIX, "AreaFix: Rescanning %s", rescanarea->Tagname); RescanNode = cnode; rescan_total = 0; nowdoing = DOING_RESCAN; success = (*rescanarea->Messagebase->scanfunc) (rescanarea, FALSE, FALSE, rescannum, NULL, AcceptAny, HandleRescan); RescanNode = NULL; rescanarea = NULL; nowdoing = DOING_AFIX; if (!success) { afFreeReply(afr); return (FALSE); } LogWrite(4, AREAFIX, "AreaFix: Rescanned %lu messages", rescan_total); afAddLine(afr, "%-30.30s Rescanned %lu messages", "", rescan_total); } } else { afAddLine(afr, "%-30.30s No rescanning allowed", ""); } } } } } switch (command) { case COMMAND_ADD: if (!patterndone) { if (iswild) { afAddLine(afr, " There were no matching areas to connect to"); } else { if (!areaexists) { /* Check if area autoremoved */ for (area = (struct Area *) config.AreaList.First; area; area = area->Next) if (area->AreaType == AREATYPE_DELETED) if (!stricmp(areaname, area->Tagname)) break; if (cnode->Flags & NODE_FORWARDREQ) { arealist = FindForward(areaname, cnode->Groups); if (arealist) { uchar buf2[100]; LogWrite(3, AREAFIX, "AreaFix: %s requested from %u:%u/%u.%u", areaname, arealist->Node->Node.Zone, arealist->Node->Node.Net, arealist->Node->Node.Node, arealist->Node->Node.Point); snprintf(buf2, 100, "Request sent to %u:%u/%u.%u", arealist->Node->Node.Zone, arealist->Node->Node.Net, arealist->Node->Node.Node, arealist->Node->Node.Point); AddCommandReply(afr, buf, buf2); snprintf(buf2, 100, "+%s", areaname); RemoteAreafix(buf2, arealist->Node); if (area) { /* FIXME: Delete area before autocreation... */ jbFreeList(&area->TossNodes); jbFreeNode(&config.AreaList, (struct jbNode *)area); config.changed=TRUE; } /* FIXME: ... but AddArea may fail if calloc fails */ area = AddArea(areaname, &arealist->Node->Node, &arealist->Node->Aka->Node, TRUE, config.cfg_Flags & CFG_FORWARDPASSTHRU); if (area) { ushort flags; area->Group = arealist->Group; if (config.cfg_FwdTimeout != 0) area->Expires = (time(NULL) / (24 * 60 * 60)) + config.cfg_FwdTimeout; flags = 0; if (CheckFlags(area->Group, cnode->ReadOnlyGroups)) flags |= TOSSNODE_READONLY; AddTossNode(area, cnode, flags); config.changed = TRUE; area->changed = TRUE; areaexists = TRUE; } } } } if (!areaexists) { AddCommandReply(afr, buf, "Unknown area"); LogWrite(3, AREAFIX, "AreaFix: Unknown area %s", areaname); } } } break; case COMMAND_REMOVE: if (!patterndone) { if (iswild) afAddLine(afr, " There were no matching areas to detach from"); else AddCommandReply(afr, buf, "Unknown area"); } break; case COMMAND_UPDATE: if (!patterndone) { if (iswild) afAddLine(afr, " There were no matching areas"); else AddCommandReply(afr, buf, "You are not attached to this area"); } break; } if (iswild) afAddLine(afr, ""); } } RemoveDeletedAreas(); } } } if (done == FALSE) afAddLine(afr, "Nothing to do."); if (exitprg || nomem) { afFreeReply(afr); return (FALSE); } afSendMessage(afr); afFreeReply(afr); if (sendarealist) rawSendList(SENDLIST_FULL, &cnode->Aka->Node, mm->From, cnode); if (sendavail) rawSendList(SENDLIST_AVAIL, &cnode->Aka->Node, mm->From, cnode); if (sendareaquery) rawSendList(SENDLIST_QUERY, &cnode->Aka->Node, mm->From, cnode); if (sendareaunlinked) rawSendList(SENDLIST_UNLINKED, &cnode->Aka->Node, mm->From, cnode); if (sendhelp) rawSendHelp(&cnode->Aka->Node, mm->From, cnode); if (sendinfo) rawSendInfo(&cnode->Aka->Node, mm->From, cnode); /* Restore old MemMessage */ SendRemoteAreafix(); return (TRUE); } void SendRemoveMessages(struct Area *area) { struct TossNode *tn; uchar buf[100]; struct MemMessage *mm; for (tn = (struct TossNode *) area->TossNodes.First; tn; tn = tn->Next) { if (tn->ConfigNode->Flags & NODE_SENDAREAFIX) { LogWrite(5, AREAFIX, "AreaFix: Sending message to AreaFix at %d:%d/%d.%d", tn->ConfigNode->Node.Zone, tn->ConfigNode->Node.Net, tn->ConfigNode->Node.Node, tn->ConfigNode->Node.Point); if (!(mm = mmAlloc())) { nomem = TRUE; return; } Copy4D(&mm->DestNode, &tn->ConfigNode->Node); Copy4D(&mm->OrigNode, &area->Aka->Node); mystrncpy(mm->From, ((struct AreaFixName *) config.AreaFixList.First)->Name, 36); mystrncpy(mm->To, tn->ConfigNode->RemoteAFName, 36); mystrncpy(mm->Subject, tn->ConfigNode->RemoteAFPw, 72); mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL), mm->DateTime); MakeNetmailKludges(mm); snprintf(buf, 100, "-%s\015", area->Tagname); mmAddLine(mm, buf, MM_ADD); sprintf(buf, "--- Generated by CrashEcho " VERSION "\015"); mmAddLine(mm, buf, MM_ADD); HandleNetmail(mm); mmFree(mm); } if ((tn->ConfigNode->Flags & NODE_SENDTEXT) && !(tn->Flags & TOSSNODE_FEED)) { LogWrite(5, AREAFIX, "AreaFix: Notifying sysop at %d:%d/%d.%d", tn->ConfigNode->Node.Zone, tn->ConfigNode->Node.Net, tn->ConfigNode->Node.Node, tn->ConfigNode->Node.Point); if (!(mm = mmAlloc())) return; Copy4D(&mm->DestNode, &tn->ConfigNode->Node); Copy4D(&mm->OrigNode, &area->Aka->Node); mystrncpy(mm->From, config.cfg_Sysop, 36); mystrncpy(mm->To, tn->ConfigNode->SysopName, 36); mystrncpy(mm->Subject, "Area removed", 72); mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL), mm->DateTime); MakeNetmailKludges(mm); snprintf(buf, 100, "The area \"%s\" has been removed by the uplink.\015", area->Tagname); mmAddLine(mm, buf, MM_ADD); if (tn->ConfigNode->Flags & NODE_SENDAREAFIX) { sprintf(buf, "A message has been sent to your AreaFix.\015"); mmAddLine(mm, buf, MM_ADD); } HandleNetmail(mm); mmFree(mm); } } } void RemoveDeletedAreas(void) { struct Area *area; for (area=(struct Area *)config.AreaList.First; area; area=area->Next) if (area->AreaType == AREATYPE_DELETED) { if (config.cfg_RemoveTimeout == 0) { jbFreeList(&area->TossNodes); jbFreeNode(&config.AreaList, (struct jbNode *)area); config.changed=TRUE; } else if (area->Removed == 0) { area->Expires = 0; area->Removed = (time(NULL) / (24 * 60 * 60)) + config.cfg_RemoveTimeout; area->changed=TRUE; config.changed=TRUE; } } } struct StatsNode { struct StatsNode *Next; uchar *Tagname; uchar *Desc; uchar Group; bool Attached; bool Feed; ulong WeekKB; /* -1 means unknown */ }; struct jbList SortList; bool AddSortList(uchar * tagname, uchar * desc, uchar group, bool attached, bool feed, long weekkb) { uchar *mtagname, *mdesc; struct StatsNode *ss; mtagname = (uchar *) malloc(strlen(tagname) + 1); mdesc = (uchar *) malloc(strlen(desc) + 1); ss = (struct StatsNode *) malloc(sizeof(struct StatsNode)); if (!mtagname || !mdesc || !ss) { if (mtagname) free(mtagname); if (mdesc) free(mdesc); if (ss) free(ss); return (FALSE); } strcpy(mtagname, tagname); strcpy(mdesc, desc); ss->Tagname = mtagname; ss->Desc = mdesc; ss->Group = group; ss->Attached = attached; ss->Feed = feed; ss->WeekKB = weekkb; jbAddNode(&SortList, (struct jbNode *) ss); return (TRUE); } void FreeSortList(void) { struct StatsNode *ss; for (ss = (struct StatsNode *) SortList.First; ss; ss = ss->Next) { if (ss->Tagname) free(ss->Tagname); if (ss->Desc) free(ss->Desc); } jbFreeList(&SortList); } int CompareAreas(const void *a1, const void *a2) { struct StatsNode **s1, **s2; s1 = (struct StatsNode **) a1; s2 = (struct StatsNode **) a2; if ((*s1)->Group < (*s2)->Group) return (-1); if ((*s1)->Group > (*s2)->Group) return (1); return (stricmp((*s1)->Tagname, (*s2)->Tagname)); } void SortSortList(void) { ulong nc; struct StatsNode *ss, **buf, **work; nc = 0; for (ss = (struct StatsNode *) SortList.First; ss; ss = ss->Next) nc++; if (nc == 0) return; if (!(buf = (struct StatsNode **) malloc(nc * sizeof(struct StatsNode *)))) { nomem = TRUE; return; } work = buf; for (ss = (struct StatsNode *) SortList.First; ss; ss = ss->Next) *work++ = ss; qsort(buf, nc, 4, CompareAreas); jbNewList(&SortList); for (work = buf; nc--;) jbAddNode(&SortList, (struct jbNode *) *work++); free(buf); } long CalculateWeekKB(struct Area *area) { if (area->FirstTime == 0 || DayStatsWritten == 0) { return (-1); } else { int days, c; unsigned long sum; days = DayStatsWritten - area->FirstTime / (24 * 60 * 60); if (days > 7) days = 7; sum = 0; for (c = 1; c < days + 1; c++) sum += area->Last8Days[c]; if (sum == 0 && area->Texts != 0) { days = DayStatsWritten - area->FirstTime / (24 * 60 * 60); if (days == 0) days = 1; return (area->Texts / days); } else { if (days == 0) days = 1; return (sum / days); } } } bool AddForwardList(struct Arealist * arealist) { bool res; FILE *fh; uchar buf[200]; uchar desc[100]; ulong c, d; struct Area *area; struct StatsNode *ss; if (!(fh = fopen(arealist->AreaFile, "rt"))) { LogWrite(1, SYSTEMERR, "AreaFix: File %s not found", arealist->AreaFile); LogWrite(1, SYSTEMERR, "AreaFix: Error: %s", strerror(errno)); return (TRUE); } while (fgets(buf, 199, fh)) { desc[0] = 0; for (c = 0; buf[c] > 32; c++); if (buf[c] != 0) { buf[c] = 0; c++; while (buf[c] <= 32 && buf[c] != 0) c++; if (buf[c] != 0) { d = 0; while (buf[c] != 0 && buf[c] != 10 && buf[c] != 13 && d < 77) desc[d++] = buf[c++]; desc[d] = 0; } } if (buf[0] != 0) { /* Don't add areas that exist locally */ for (area = (struct Area *) config.AreaList.First; area; area = area->Next) if (stricmp(buf, area->Tagname) == 0) break; for (ss = (struct StatsNode *) SortList.First; ss; ss = ss->Next) if (stricmp(buf, ss->Tagname) == 0) break; if (!area && !ss) { if (arealist->Flags & AREALIST_DESC) res = AddSortList(buf, desc, arealist->Group, FALSE, FALSE, -1); else res = AddSortList(buf, "", arealist->Group, FALSE, FALSE, -1); if (!res) { fclose(fh); return (FALSE); } } } } fclose(fh); return (TRUE); } void AddCommandReply(struct afReply *afr, uchar * cmd, uchar * reply) { if (strlen(cmd) <= 30) { afAddLine(afr, "%-30s %s", cmd, reply); } else { afAddLine(afr, "%s", cmd); afAddLine(afr, "%-30s %s", "", reply); } } void rawSendList(short type, struct Node4D *from4d, uchar * toname, struct ConfigNode *cnode) { uchar buf[50]; struct TossNode *tn; struct Area *area; struct StatsNode *ss, *lastss; struct Arealist *arealist; short sendlisttotal, sendlistlinked; uchar ast; struct afReply *afr; /* Log action */ switch (type) { case SENDLIST_QUERY: LogWrite(4, AREAFIX, "AreaFix: Sending query to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); break; case SENDLIST_AVAIL: LogWrite(4, AREAFIX, "AreaFix: Sending list of areas available from uplink to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); break; case SENDLIST_UNLINKED: LogWrite(4, AREAFIX, "AreaFix: Sending list of unlinked areas to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); break; case SENDLIST_FULL: LogWrite(4, AREAFIX, "AreaFix: Sending list of areas to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); break; } /* Start building reply message */ if (!(afr = afInitReply(((struct AreaFixName *) config.AreaFixList.First)->Name, from4d, toname, &cnode->Node, "AreaFix list of areas"))) return; switch (type) { case SENDLIST_QUERY: afAddLine(afr, "This is a list of all connected areas at %u:%u/%u.%u:", from4d->Zone, from4d->Net, from4d->Node, from4d->Point); break; case SENDLIST_AVAIL: afAddLine(afr, "This is a list of areas available from uplink at %u:%u/%u.%u:", from4d->Zone, from4d->Net, from4d->Node, from4d->Point); break; case SENDLIST_FULL: afAddLine(afr, "This is a list of all available areas at %u:%u/%u.%u:", from4d->Zone, from4d->Net, from4d->Node, from4d->Point); break; case SENDLIST_UNLINKED: afAddLine(afr, "This is a list of all unlinked areas at %u:%u/%u.%u:", from4d->Zone, from4d->Net, from4d->Node, from4d->Point); break; } afAddLine(afr, ""); /* Init list */ jbNewList(&SortList); if (type != SENDLIST_AVAIL) { /* Add local areas */ for (area = (struct Area *) config.AreaList.First; area; area = area->Next) if (area->AreaType == AREATYPE_ECHOMAIL) { short add; bool attached, feed; for (tn = (struct TossNode *) area->TossNodes.First; tn; tn = tn->Next) if (tn->ConfigNode == cnode) break; add = FALSE; switch (type) { case SENDLIST_QUERY: if (tn) add = TRUE; break; case SENDLIST_UNLINKED: if (!tn && (CheckFlags(area->Group, cnode->Groups) || CheckFlags(area->Group, cnode->ReadOnlyGroups))) add = TRUE; break; case SENDLIST_FULL: if (tn || (CheckFlags(area->Group, cnode->Groups) || CheckFlags(area->Group, cnode->ReadOnlyGroups))) add = TRUE; break; } if (add) { attached = FALSE; feed = FALSE; if (tn) attached = TRUE; if (tn && tn->Flags & TOSSNODE_FEED) feed = TRUE; if (!AddSortList(area->Tagname, area->Description, area->Group, attached, feed, CalculateWeekKB(area))) { LogWrite(1, SYSTEMERR, "AreaFix: Out of memory when building list of areas"); afAddLine(afr, "Failed to build list of areas, out of memory"); afSendMessage(afr); afFreeReply(afr); FreeSortList(); return; } } } /* Add forward-requestable areas */ if (config.cfg_Flags & CFG_INCLUDEFORWARD && (type == SENDLIST_UNLINKED || type == SENDLIST_FULL) && cnode->Flags & NODE_FORWARDREQ) { for (arealist = (struct Arealist *) config.ArealistList.First; arealist; arealist = arealist->Next) if ((arealist->Flags & AREALIST_FORWARD) && CheckFlags(arealist->Group, cnode->Groups)) { if (!AddForwardList(arealist)) { LogWrite(1, SYSTEMERR, "AreaFix: Out of memory when building list of areas"); afAddLine(afr, "Failed to build list of areas, out of memory"); afSendMessage(afr); afFreeReply(afr); FreeSortList(); return; } } } } else { for (arealist = (struct Arealist *) config.ArealistList.First; arealist; arealist = arealist->Next) if ((arealist->Flags & AREALIST_FORWARD) && CheckFlags(arealist->Group, cnode->Groups)) { if (!AddForwardList(arealist)) { LogWrite(1, SYSTEMERR, "AreaFix: Out of memory when building list of areas"); afAddLine(afr, "Failed to build list of areas, out of memory"); afSendMessage(afr); afFreeReply(afr); FreeSortList(); return; } } } /* Generate list */ SortSortList(); lastss = NULL; sendlisttotal = 0; sendlistlinked = 0; for (ss = (struct StatsNode *) SortList.First; ss; ss = ss->Next) { if (!lastss || lastss->Group != ss->Group) { if (lastss) afAddLine(afr, ""); if (ss->Group) afAddLine(afr, " Group: %s", config.cfg_GroupNames[ss->Group - 'A']); else afAddLine(afr, " Group: %s", ""); afAddLine(afr, ""); afAddLine(afr, " Tagname Description KB/week"); afAddLine(afr, " ---------------------------- --------------------------------- -------"); } ast = ' '; if (type == SENDLIST_FULL && ss->Attached) { ast = '*'; sendlistlinked++; } if (ss->Feed) ast = '%'; sendlisttotal++; if (ss->WeekKB == -1) strcpy(buf, "?"); else snprintf(buf, 50, "%ld", ss->WeekKB); if (strlen(ss->Tagname) <= 28) { afAddLine(afr, "%c%-28.28s %-33.33s %8.8s", ast, ss->Tagname, ss->Desc, buf); } else { afAddLine(afr, "%c%-70.70s", ast, ss->Tagname); afAddLine(afr, "%c%-28.28s %-33.33s %8.8s", ' ', "", ss->Desc, buf); } lastss = ss; } switch (type) { case SENDLIST_QUERY: afAddLine(afr, "\015%lu linked area%s.", sendlisttotal, sendlisttotal == 1 ? "" : "s"); afAddLine(afr, "A '%%' means that you are the feed for the area."); break; case SENDLIST_AVAIL: afAddLine(afr, "\015Totally %lu area%s.", sendlisttotal, sendlisttotal == 1 ? "" : "s"); break; case SENDLIST_UNLINKED: afAddLine(afr, "\015%lu unlinked area%s.", sendlisttotal, sendlisttotal == 1 ? "" : "s"); break; case SENDLIST_FULL: afAddLine(afr, "\015Totally %lu area%s, you are connected to %lu of them.", sendlisttotal, sendlisttotal == 1 ? "" : "s", sendlistlinked); afAddLine(afr, "A '*' means that you are connected to the area."); afAddLine(afr, "A '%%' means that you are the feed for the area."); break; } afSendMessage(afr); afFreeReply(afr); FreeSortList(); } void rawSendHelp(struct Node4D *from4d, uchar * toname, struct ConfigNode *cnode) { uchar helpbuf[100]; FILE *fh; struct afReply *afr; LogWrite(4, AREAFIX, "AreaFix: Sending help file to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); if (!(afr = afInitReply(((struct AreaFixName *) config.AreaFixList.First)->Name, from4d, toname, &cnode->Node, "AreaFix help"))) return; if (!(fh = fopen(config.cfg_AreaFixHelp, "rt"))) { LogWrite(1, SYSTEMERR, "AreaFix: Unable to open %s", config.cfg_AreaFixHelp); LogWrite(1, SYSTEMERR, "AreaFix: Error: %s", strerror(errno)); afAddLine(afr, "*** Error *** : Couldn't open help file"); } else { while (fgets(helpbuf, 100, fh) && !nomem && !exitprg) { if (helpbuf[0] != 0) helpbuf[strlen(helpbuf) - 1] = 0; afAddLine(afr, "%s", helpbuf); } fclose(fh); } afSendMessage(afr); afFreeReply(afr); } void rawSendInfo(struct Node4D *from4d, uchar * toname, struct ConfigNode *cnode) { int c; struct afReply *afr; LogWrite(4, AREAFIX, "AreaFix: Sending configuration info to %u:%u/%u.%u", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); if (!(afr = afInitReply(((struct AreaFixName *) config.AreaFixList.First)->Name, from4d, toname, &cnode->Node, "AreaFix configuration info"))) return; afAddLine(afr, "Configuration for %u:%u/%u.%u:", cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); afAddLine(afr, ""); afAddLine(afr, " Sysop: %s", cnode->SysopName); afAddLine(afr, "Packet password: %s", cnode->PacketPW); afAddLine(afr, "Areafix password: %s", cnode->AreafixPW); if (!cnode->Packer) { afAddLine(afr, " Packer: No packer"); } else { afAddLine(afr, " Packer: %s", cnode->Packer->Name); } afAddLine(afr, ""); if (cnode->Flags & NODE_PASSIVE) afAddLine(afr, " * You are passive and will not receive any echomail messages"); if (cnode->Flags & NODE_TINYSEENBY) afAddLine(afr, " * You receive messages with tiny SEEN-BY lines"); if (cnode->Flags & NODE_NOSEENBY) afAddLine(afr, " * You receive messages without SEEN-BY lines"); if (cnode->Flags & NODE_FORWARDREQ) afAddLine(afr, " * You may do forward-requests"); if (cnode->Flags & NODE_NOTIFY) afAddLine(afr, " * You will receive notifications"); if (cnode->Flags & NODE_AUTOADD) afAddLine(afr, " * New areas from you will be auto-added"); afAddLine(afr, ""); afAddLine(afr, "You have full access to these groups:"); afAddLine(afr, ""); for (c = 'A'; c <= 'Z'; c++) if (CheckFlags(c, cnode->Groups) && !CheckFlags(c, cnode->ReadOnlyGroups)) { if (config.cfg_GroupNames[c - 'A'][0] != 0) afAddLine(afr, "%c: %s", c, config.cfg_GroupNames[c - 'A']); else afAddLine(afr, "%c", c); } afAddLine(afr, ""); afAddLine(afr, "You have read-only access to these groups:"); afAddLine(afr, ""); for (c = 'A'; c <= 'Z'; c++) if (CheckFlags(c, cnode->ReadOnlyGroups)) { if (config.cfg_GroupNames[c - 'A']) afAddLine(afr, "%c: %s", c, config.cfg_GroupNames[c - 'A']); else afAddLine(afr, "%c", c); } afSendMessage(afr); afFreeReply(afr); } void afRawPrepareMessage(void); struct afReply *afInitReply(uchar * fromname, struct Node4D *from4d, uchar * toname, struct Node4D *to4d, uchar * subject) { struct afReply *afr; if (!(afr = calloc(1, sizeof(struct afReply)))) { nomem = TRUE; return (NULL); } if (!(afr->mm = mmAlloc())) { nomem = TRUE; free(afr); return (NULL); } mystrncpy(afr->mm->From, fromname, 36); Copy4D(&afr->mm->OrigNode, from4d); mystrncpy(afr->mm->To, toname, 36); Copy4D(&afr->mm->DestNode, to4d); mystrncpy(afr->subject, subject, 72); afr->mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL), afr->mm->DateTime); MakeNetmailKludges(afr->mm); afr->lines = 0; afr->part = 1; return (afr); } void afFreeReply(struct afReply *afr) { mmFree(afr->mm); } void afAddLine(struct afReply *afr, uchar * fmt, ...) { va_list args; uchar buf[200]; if (afr->lines >= config.cfg_AreaFixMaxLines - 2 && config.cfg_AreaFixMaxLines != 0) { strcpy(buf, "\015(Continued in next message)\015"); mmAddLine(afr->mm, buf, MM_ADD); snprintf(afr->mm->Subject, 72, "%s (part %ld)", afr->subject, afr->part); afSendMessage(afr); jbFreeList(&afr->mm->TextChunks); MakeFidoDate(time(NULL), afr->mm->DateTime); MakeNetmailKludges(afr->mm); strcpy(buf, "(Continued from previous message)\015\015"); mmAddLine(afr->mm, buf, MM_ADD); afr->lines = 2; afr->part++; } va_start(args, fmt); vsnprintf(buf, 200, fmt, args); va_end(args); strcat(buf, "\015"); mmAddLine(afr->mm, buf, MM_ADD); afr->lines++; } void afSendMessage(struct afReply *afr) { mmAddLine(afr->mm, "\015--- CrashEcho's AreaFix\015", MM_ADD); if (afr->part != 1) snprintf(afr->mm->Subject, 72, "%s (part %ld)", afr->subject, afr->part); else mystrncpy(afr->mm->Subject, afr->subject, 72); if(!(config.cfg_Flags & CFG_KEEPRECEIPT)) afr->mm->Attr |= FLAG_KILLSENT; HandleNetmail(afr->mm); } void RemoteAreafix(uchar * area, struct ConfigNode *node) { struct RemoteAFCommand *cmd; if (!(cmd = (struct RemoteAFCommand *) calloc(1, sizeof(struct RemoteAFCommand)))) { nomem = TRUE; return; } strncpy(cmd->Command, area, 70); cmd->Command[70] = 0; jbAddNode(&node->RemoteAFList, (struct jbNode *) cmd); } void SendRemoteAreafix(void) { struct ConfigNode *node; struct RemoteAFCommand *cmd; uchar buf[200]; struct MemMessage *mm; for (node = (struct ConfigNode *) config.CNodeList.First; node; node = node->Next) if (node->RemoteAFList.First) { if (!(mm = mmAlloc())) { nomem = TRUE; return; } Copy4D(&mm->DestNode, &node->Node); Copy4D(&mm->OrigNode, &node->Aka->Node); mystrncpy(mm->From, config.cfg_Sysop, 36); mystrncpy(mm->To, node->RemoteAFName, 36); mystrncpy(mm->Subject, node->RemoteAFPw, 72); mm->Attr = FLAG_PVT; MakeFidoDate(time(NULL), mm->DateTime); MakeNetmailKludges(mm); for (cmd = (struct RemoteAFCommand *) node->RemoteAFList.First; cmd; cmd = cmd->Next) { snprintf(buf, 200, "%s\015", cmd->Command); mmAddLine(mm, buf, MM_ADD); } mmAddLine(mm, "\015--- CrashEcho's AreaFix\015", MM_ADD); HandleNetmail(mm); mmFree(mm); jbFreeList(&node->RemoteAFList); } } bool CheckFlags(uchar group, uchar * node) { uchar c; for (c = 0; c < strlen(node); c++) { if (group == node[c]) return (TRUE); } return (FALSE); } struct Arealist *FindForward(uchar * tagname, uchar * flags) { struct Arealist *arealist; uchar buf[200]; ulong c; FILE *fh; for (arealist = (struct Arealist *) config.ArealistList.First; arealist; arealist = arealist->Next) { if ((arealist->Flags & AREALIST_FORWARD) && CheckFlags(arealist->Group, flags)) { if ((fh = fopen(arealist->AreaFile, "rt"))) { while (fgets(buf, 199, fh)) { for (c = 0; buf[c] > 32; c++); buf[c] = 0; if (stricmp(buf, tagname) == 0) { fclose(fh); return (arealist); } } fclose(fh); } else { LogWrite(1, SYSTEMERR, "Failed to open file %s", arealist->AreaFile); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); } } } return (NULL); } void DoSendAFList(short type, struct ConfigNode *cnode) { switch (type) { case SENDLIST_FULL: rawSendList(SENDLIST_FULL, &cnode->Aka->Node, cnode->SysopName, cnode); break; case SENDLIST_AVAIL: rawSendList(SENDLIST_AVAIL, &cnode->Aka->Node, cnode->SysopName, cnode); break; case SENDLIST_QUERY: rawSendList(SENDLIST_QUERY, &cnode->Aka->Node, cnode->SysopName, cnode); break; case SENDLIST_UNLINKED: rawSendList(SENDLIST_UNLINKED, &cnode->Aka->Node, cnode->SysopName, cnode); break; case SENDLIST_INFO: rawSendInfo(&cnode->Aka->Node, cnode->SysopName, cnode); break; case SENDLIST_HELP: rawSendHelp(&cnode->Aka->Node, cnode->SysopName, cnode); break; } }