/********************************************************
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1