/******************************************************** * File: outbound.c * Created at Sun Jan 28 22:10:31 MSK 2001 by raorn // raorn@binec.ru * Outbound handling * $Id: outbound.c,v 1.13 2002/10/27 22:47:52 raorn Exp $ *******************************************************/ #include "crashecho.h" struct jbList ArcList; bool doAddFlow(uchar * filename, uchar * basename, uchar type, long mode); void MakeBaseName(struct Node4D *n4d, uchar * basename) { struct Aka *firstaka; uchar *ospathchars; ulong num, c; uchar buf[50]; ospathchars = PATH_CHARS; firstaka = (struct Aka *) config.AkaList.First; /* Main domain */ strcpy(basename, config.cfg_Outbound); if (basename[0]) { if (strchr(ospathchars, basename[strlen(basename) - 1])) basename[strlen(basename) - 1] = 0; /* Strip */ } if (config.cfg_Flags & CFG_USEASO) { /* Add slash */ c = strlen(basename); basename[c++] = ospathchars[0]; basename[c++] = 0; /* Add ASO name */ snprintf(buf, 50, "%u.%u.%u.%u", n4d->Zone, n4d->Net, n4d->Node, n4d->Point); strcat(basename, buf); } else { if (n4d->Zone != firstaka->Node.Zone) { /* Not in main zone */ num = n4d->Zone; if (!(config.cfg_Flags & CFG_NOMAXOUTBOUNDZONE)) { if (num > 0xfff) num = 0xfff; } snprintf(buf, 50, ".%03lx", num); strcat(basename, buf); } if (!Exists(basename)) mkdir(basename, 0777); /* Add slash */ c = strlen(basename); basename[c++] = ospathchars[0]; basename[c++] = 0; /* Add net/node */ snprintf(buf, 50, "%04x%04x", n4d->Net, n4d->Node); strcat(basename, buf); if (n4d->Point) { strcat(basename, ".pnt"); if (!Exists(basename)) mkdir(basename, 0777); /* Add slash */ c = strlen(basename); basename[c++] = ospathchars[0]; basename[c++] = 0; /* Add point */ snprintf(buf, 50, "%08x", n4d->Point); strcat(basename, buf); } } } void WriteIndex(void) { FILE *fh; uchar buf[200]; struct ConfigNode *cnode; MakeFullPath(config.cfg_PacketDir, "ceindex", buf, 200); /* Get basenum */ if (!(fh = fopen(buf, "wt"))) return; for (cnode = (struct ConfigNode *) config.CNodeList.First; cnode; cnode = cnode->Next) if (cnode->LastArcName[0]) { Print4D(&cnode->Node, buf, 200); fprintf(fh, "%s %s\n", buf, cnode->LastArcName); } fclose(fh); } void ReadIndex(void) { FILE *fh; uchar buf[200], buf2[200]; ulong jbcpos; struct ConfigNode *cnode, *c1, *c2; struct Node4D n4d; MakeFullPath(config.cfg_PacketDir, "ceindex", buf, 200); /* Get basenum */ if (!(fh = fopen(buf, "rt"))) return; while (fgets(buf, 200, fh)) { striptrail(buf); jbcpos = 0; jbstrcpy(buf2, buf, 200, &jbcpos); if (Parse4D(buf2, &n4d)) { jbstrcpy(buf2, buf, 200, &jbcpos); for (cnode = (struct ConfigNode *) config.CNodeList.First; cnode; cnode = cnode->Next) if (Compare4D(&cnode->Node, &n4d) == 0) mystrncpy(cnode->LastArcName, buf2, 13); } } fclose(fh); /* Check for duplicates */ for (c1 = (struct ConfigNode *) config.CNodeList.First; c1; c1 = c1->Next) for (c2 = c1->Next; c2; c2 = c2->Next) if (c1->LastArcName[0] && hextodec(c1->LastArcName) == hextodec(c2->LastArcName)) { LogWrite(1, TOSSINGINFO, "Warning: The same bundle name is used for %u:%u/%u.%u and %u:%u/%u.%u", c1->Node.Zone, c1->Node.Net, c1->Node.Node, c1->Node.Point, c2->Node.Zone, c2->Node.Net, c2->Node.Node, c2->Node.Point); LogWrite(1, TOSSINGINFO, "Cleared bundle name for %u:%u/%u.%u", c2->Node.Zone, c2->Node.Net, c2->Node.Node, c2->Node.Point); c2->LastArcName[0] = 0; WriteIndex(); } } bool ExistsBasenum(ulong num) { uchar name[20]; struct FileEntry *fe; struct ConfigNode *cnode; snprintf(name, 20, "%08lx.", num); for (fe = (struct FileEntry *) ArcList.First; fe; fe = fe->Next) if (IsArc(fe->Name) && hextodec(fe->Name) == num) return (TRUE); for (cnode = (struct ConfigNode *) config.CNodeList.First; cnode; cnode = cnode->Next) if (cnode->LastArcName[0] && hextodec(cnode->LastArcName) == num) return (TRUE); return (FALSE); } bool ExistsBundle(ulong basenum, ulong num) { uchar name[20]; struct FileEntry *fe; uchar *daynames[] = { "su", "mo", "tu", "we", "th", "fr", "sa" }; snprintf(name, 20, "%08lx.%s%ld", basenum, daynames[num / 10], num % 10); for (fe = (struct FileEntry *) ArcList.First; fe; fe = fe->Next) if (stricmp(fe->Name, name) == 0) return (TRUE); return (FALSE); } void MakeArcName(struct ConfigNode *cnode, uchar * dest, size_t destlen) { struct FileEntry *fe, *fe2, *foundfe; uchar arc[20], buf[200], ext[10]; ulong basenum; long suffix, newsuffix, day, i; uchar *daynames[] = { "su", "mo", "tu", "we", "th", "fr", "sa" }; time_t t; struct tm *tp; time(&t); tp = localtime(&t); day = tp->tm_wday; /* Get basenum */ if (!cnode->LastArcName[0]) { basenum = time(NULL); while (ExistsBasenum(basenum)) basenum++; } else { basenum = hextodec(cnode->LastArcName); } /* Get latest in list */ foundfe = NULL; if (cnode->LastArcName[0]) { for (fe = (struct FileEntry *) ArcList.First; fe; fe = fe->Next) if (stricmp(cnode->LastArcName, fe->Name) == 0) foundfe = fe; } /* Get extension of latest */ suffix = -1; if (foundfe) { strncpy(ext, &foundfe->Name[strlen(foundfe->Name) - 3], 3); ext[2] = 0; for (i = 0; i < 7; i++) { if (stricmp(ext, daynames[i]) == 0) { suffix = i * 10; suffix += foundfe->Name[strlen(foundfe->Name) - 1] - '0'; } } } if (suffix == -1) { if ((config.cfg_Flags & CFG_WEEKDAYNAMING)) newsuffix = day * 10; else newsuffix = 0; } else { /* There was an old bundle */ newsuffix = suffix; if (foundfe->Size == 0) newsuffix = -1; if (foundfe->Size > config.cfg_MaxBundleSize) newsuffix = -1; if ((config.cfg_Flags & CFG_WEEKDAYNAMING) && suffix / 10 != day) newsuffix = -1; if (newsuffix == -1) { newsuffix = suffix + 1; if (newsuffix == 70) newsuffix = 0; if ((config.cfg_Flags & CFG_WEEKDAYNAMING) && newsuffix / 10 != day) newsuffix = day * 10; if (ExistsBundle(basenum, newsuffix)) newsuffix = suffix; } } /* Delete zero length bundles for this node */ fe = (struct FileEntry *) ArcList.First; snprintf(arc, 20, "%08lx.", basenum); while (fe) { fe2 = fe->Next; if (strnicmp(arc, fe->Name, 9) == 0 && fe->Size == 0) { MakeFullPath(config.cfg_PacketDir, fe->Name, buf, 200); unlink(buf); jbFreeNode(&ArcList, (struct jbNode *) fe); } fe = fe2; } snprintf(dest, destlen, "%08lx.%s%ld", basenum, daynames[newsuffix / 10], newsuffix % 10); if (stricmp(cnode->LastArcName, dest) != 0) { mystrncpy(cnode->LastArcName, dest, 13); WriteIndex(); } } void HandleOrphan(uchar * name) { FILE *fh; uchar buf[200], buf2[200]; char type; bool mode; ulong jbcpos; struct Node4D n4d; if (!(fh = fopen(name, "rt"))) { LogWrite(1, SYSTEMERR, "Failed to open orphan file \"%s\"", name); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); return; } if (!fgets(buf, 100, fh)) { LogWrite(1, SYSTEMERR, "Orphan file \"%s\" contains no information", name); fclose(fh); return; } fclose(fh); jbcpos = 0; jbstrcpy(buf2, buf, 100, &jbcpos); if (stricmp(buf2, "Normal") == 0) type = PKTS_NORMAL; else if (stricmp(buf2, "Hold") == 0) type = PKTS_HOLD; else if (stricmp(buf2, "Direct") == 0) type = PKTS_DIRECT; else if (stricmp(buf2, "Crash") == 0) type = PKTS_CRASH; else { LogWrite(1, SYSTEMERR, "Unknown flavour \"%s\" in \"%s\"", buf2, name); return; } jbstrcpy(buf2, buf, 100, &jbcpos); if (!Parse4D(buf2, &n4d)) { LogWrite(1, SYSTEMERR, "Invalid node \"%s\" in \"%s\"", buf2, name); return; } mode = FLOW_NONE; jbstrcpy(buf2, buf, 100, &jbcpos); if (stricmp(buf2, "Truncate") == 0) mode = FLOW_TRUNC; if (stricmp(buf2, "Delete") == 0) mode = FLOW_DELETE; mystrncpy(buf, name, 200); buf[strlen(buf) - 7] = 0; /* Remove .orphan */ if (AddFlow(buf, &n4d, type, mode)) unlink(name); /* Orphan file no longer needed */ } void MakeOrphan(uchar * file, struct Node4D *n4d, char type, long mode) { uchar buf[200]; FILE *fh; snprintf(buf, 200, "%s.orphan", file); if (!(fh = fopen(buf, "wt"))) { LogWrite(1, SYSTEMERR, "Failed to open \"%s\", cannot make .orphan file", buf); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); return; } snprintf(buf, 200, "%s %d:%d/%d.%d", prinames[(int) type], n4d->Zone, n4d->Net, n4d->Node, n4d->Point); if (mode == FLOW_TRUNC) strcat(buf, " Truncate"); if (mode == FLOW_DELETE) strcat(buf, " Delete"); fputs(buf, fh); fclose(fh); } /* Only call if file is already locked */ /* MakeOrphan() should be called if necessary */ bool doAddFlow(uchar * filename, uchar * basename, uchar type, long mode) { uchar buf[200], letter, *prefix; FILE *fh; switch (type) { case PKTS_NORMAL: case PKTS_ECHOMAIL: letter = 'f'; break; case PKTS_HOLD: letter = 'h'; break; case PKTS_DIRECT: letter = 'd'; break; case PKTS_CRASH: letter = 'c'; break; default: letter = 'f'; } snprintf(buf, 200, "%s.%clo", basename, letter); if (!(fh = fopen(buf, "r+t")) && !(fh = fopen(buf, "w+t"))) { LogWrite(1, SYSTEMERR, "Failed to open \"%s\"", buf); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); return (FALSE); } while (fgets(buf, 200, fh)) { striptrail(buf); if (buf[0] == '#') strcpy(buf, &buf[1]); if (buf[0] == '~') continue; if (buf[0] == '^') strcpy(buf, &buf[1]); if (stricmp(buf, filename) == 0) { fclose(fh); return (TRUE); /* Was already in flow file */ } } fseek(fh, 0, SEEK_END); prefix = ""; if (mode == FLOW_TRUNC) prefix = "#"; if (mode == FLOW_DELETE) prefix = "^"; fprintf(fh, "%s%s\n", prefix, filename); fclose(fh); return (TRUE); } /* Handles locking and MakeOrphan() */ bool AddFlow(uchar * filename, struct Node4D * n4d, uchar type, long mode) { uchar basename[200]; MakeBaseName(n4d, basename); if (!Lock(basename, config.cfg_Flags & CFG_LOCKBYLINK, TRUE)) { LogWrite(1, SYSTEMERR, "Cannot add to %s, node is busy...\n", GetFilePart(basename)); MakeOrphan(filename, n4d, type, mode); return (FALSE); } if (!doAddFlow(filename, basename, type, mode)) MakeOrphan(filename, n4d, type, mode); Unlock(basename); return (TRUE); } bool MakePktTmp(uchar * name) { uchar buf[200]; MakeFullPath(config.cfg_PacketDir, GetFilePart(name), buf, 200); strcpy(&buf[strlen(buf) - 6], "pkttmp"); /* Change suffix */ if (!movefile(name, buf)) { LogWrite(1, SYSTEMERR, "Failed to move file \"%s\" to \"%s\"", name, buf); return (FALSE); } return (TRUE); } void UpdateFile(uchar * name) { struct FileEntry *newfe, *fe; if (!(newfe = GetFileEntry(name))) return; for (fe = (struct FileEntry *) ArcList.First; fe; fe = fe->Next) if (stricmp(fe->Name, name) == 0) break; if (fe) { fe->Date = newfe->Date; fe->Size = newfe->Size; free(newfe); } else { jbAddNode(&ArcList, (struct jbNode *) newfe); } } #define COPYBUFSIZE 5000 bool PackFile(char *file) { uchar basename[200], arcname[200], pktname[200], buf[200], buf2[200]; ulong jbcpos; int c, res, rc; struct Node4D n4d; struct ConfigNode *cnode; /* Parse filename */ mystrncpy(buf, GetFilePart(file), 200); for (c = 0; buf[c]; c++) if (buf[c] == '_') buf[c] = ' '; jbcpos = 0; /* Skip hexnum */ jbstrcpy(buf2, buf, 100, &jbcpos); /* Skip pktorig */ jbstrcpy(buf2, buf, 100, &jbcpos); jbstrcpy(buf2, buf, 100, &jbcpos); jbstrcpy(buf2, buf, 100, &jbcpos); jbstrcpy(buf2, buf, 100, &jbcpos); /* Get pktdest */ jbstrcpy(buf2, buf, 100, &jbcpos); n4d.Zone = atol(buf2); jbstrcpy(buf2, buf, 100, &jbcpos); n4d.Net = atol(buf2); jbstrcpy(buf2, buf, 100, &jbcpos); n4d.Node = atol(buf2); jbstrcpy(buf2, buf, 100, &jbcpos); n4d.Point = atol(buf2); /* Make basename for this node */ MakeBaseName(&n4d, basename); if (!Lock(basename, config.cfg_Flags & CFG_LOCKBYLINK, TRUE)) { LogWrite(1, TOSSINGINFO, "Cannot add \"%s\" to outbound, node is busy...", GetFilePart(file)); return (FALSE); } /* Handle echomail packet */ for (cnode = (struct ConfigNode *) config.CNodeList.First; cnode; cnode = cnode->Next) if (Compare4D(&cnode->Node, &n4d) == 0) break; if (cnode && cnode->Packer) { /* Pack echomail */ MakeArcName(cnode, buf, 200); MakeFullPath(config.cfg_PacketDir, buf, arcname, 200); mystrncpy(pktname, file, 200); GetFilePart(pktname)[8] = 0; strcat(pktname, ".pkt"); LogWrite(4, TOSSINGINFO, "Packing %s for %d:%d/%d.%d with %s", GetFilePart(pktname), cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point, cnode->Packer->Name); ExpandPacker(cnode->Packer->Packer, buf, 200, arcname, pktname); rename(file, pktname); res = Execute(buf, &rc); if (!res && rc == 0) { UpdateFile(arcname); unlink(pktname); if (!doAddFlow(arcname, basename, cnode->EchomailPri, FLOW_TRUNC)) MakeOrphan(arcname, &n4d, cnode->EchomailPri, FLOW_TRUNC); } else { rename(pktname, file); if (rc) LogWrite(1, SYSTEMERR, "Packer failed: %lu", rc); else LogWrite(1, SYSTEMERR, "Failed to run packer"); Unlock(basename); return (FALSE); } } else { /* Send unpacked echomail */ MakeFullPath(config.cfg_PacketDir, GetFilePart(file), pktname, 200); GetFilePart(pktname)[8] = 0; strcat(pktname, ".pkt"); LogWrite(4, TOSSINGINFO, "Sending %s unpacked to %d:%d/%d.%d", GetFilePart(pktname), cnode->Node.Zone, cnode->Node.Net, cnode->Node.Node, cnode->Node.Point); if (!movefile(file, pktname)) { LogWrite(1, SYSTEMERR, "Failed to move file \"%s\" to \"%s\"", file, pktname); Unlock(basename); return (FALSE); } else { if (!doAddFlow(pktname, basename, cnode->EchomailPri, FLOW_DELETE)) MakeOrphan(pktname, &n4d, cnode->EchomailPri, FLOW_DELETE); } } Unlock(basename); if (ioerror) return (FALSE); return (TRUE); } bool ArchiveOutbound(void) { struct jbList PktList; struct FileEntry *fe; uchar buf[200]; /* Orphan files */ LogWrite(3, ACTIONINFO, "Scanning for orphan files"); if (!(ReadDir(config.cfg_PacketDir, &ArcList, IsOrphan))) { LogWrite(1, SYSTEMERR, "Failed to read directory \"%s\"", config.cfg_PacketDir); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); return (FALSE); } SortFEList(&ArcList); for (fe = (struct FileEntry *) ArcList.First; fe && !ctrlc; fe = fe->Next) { LogWrite(1, SYSTEMINFO, "Found orphan file \"%s\", retrying...", fe->Name); MakeFullPath(config.cfg_PacketDir, fe->Name, buf, 200); HandleOrphan(buf); } jbFreeList(&ArcList); /* Read ArcList */ if (!(ReadDir(config.cfg_PacketDir, &ArcList, IsArc))) { LogWrite(1, SYSTEMERR, "Failed to read directory \"%s\"", config.cfg_PacketDir); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); return (FALSE); } /* Read index */ ReadIndex(); /* Old packets */ LogWrite(3, ACTIONINFO, "Scanning for old packets"); if (!(ReadDir(config.cfg_PacketDir, &PktList, IsPktTmp))) { LogWrite(1, SYSTEMERR, "Failed to read directory \"%s\"", config.cfg_PacketDir); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); jbFreeList(&ArcList); return (FALSE); } SortFEList(&PktList); for (fe = (struct FileEntry *) PktList.First; fe; fe = fe->Next) { LogWrite(1, SYSTEMINFO, "Found old packet file \"%s\", retrying...", fe->Name); MakeFullPath(config.cfg_PacketDir, fe->Name, buf, 200); PackFile(buf); } jbFreeList(&PktList); /* New packets */ LogWrite(3, ACTIONINFO, "Scanning for new files to pack"); if (!(ReadDir(config.cfg_PacketCreate, &PktList, IsNewPkt))) { LogWrite(1, SYSTEMERR, "Failed to read directory \"%s\"", config.cfg_PacketCreate); LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno)); jbFreeList(&ArcList); return (FALSE); } SortFEList(&PktList); for (fe = (struct FileEntry *) PktList.First; fe; fe = fe->Next) { MakeFullPath(config.cfg_PacketCreate, fe->Name, buf, 200); if (!PackFile(buf)) if (!MakePktTmp(buf)) { jbFreeList(&PktList); jbFreeList(&ArcList); return (FALSE); } } jbFreeList(&PktList); jbFreeList(&ArcList); return (TRUE); }