/********************************************************
 * File: pkt.c
 * Created at Sun Jan 28 22:10:32 MSK 2001 by raorn // raorn@binec.ru
 * pkt processing
 * $Id: pkt.c,v 1.16 2002/03/03 21:05:57 raorn Exp $
 *******************************************************/
#include "crashecho.h"

/*
 *
 * Read Pkt
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define PKT_MINREADLEN 200

bool messageend;
bool shortread, longread;

ushort getuword(uchar * buf, ulong offset)
{
  return (ushort) (buf[offset] + 256 * buf[offset + 1]);
}

void putuword(uchar * buf, ulong offset, ushort num)
{
  buf[offset] = num % 256;
  buf[offset + 1] = num / 256;
}

ulong ReadNull(uchar * buf, ulong maxlen, FILE *fh)
{
  /* Reads from fh until buffer full or NULL */
  int ch, c = 0;

  if (shortread)
    return (0);

  ch = fgetc(fh);

  while (ch != EOF && ch != 0 && c != maxlen - 1) {
    buf[c++] = ch;
    ch = fgetc(fh);
  }
  buf[c] = 0;

  if (ch == EOF)
    shortread = TRUE;

  if (ch != 0 && c == maxlen - 1)
    longread = TRUE;

  return (c);
}

ulong ReadCR(uchar * buf, ulong maxlen, FILE *fh)
{
  /* Reads from fh until buffer full or CR */
  int ch, c = 0;

  ch = fgetc(fh);

  while (ch != EOF && ch != 0 && ch != 13 && c != maxlen - 2) {
    if(ch != 10)
      buf[c++] = ch;
    if (c != maxlen - 2)
      ch = fgetc(fh);
  }

  if (ch == 13)
    buf[c++] = ch;

  buf[c] = 0;

  if (ch == 0)
    messageend = TRUE;
  if (ch == EOF)
    shortread = TRUE;

  return (c);
}

bool ReadPkt(uchar * pkt, struct FileEntry * fe, bool(*handlefunc) (struct MemMessage * mm, bool no_echomail), bool no_security, bool no_echomail)
{
  FILE *fh, *pktofsh;
  struct Aka *tmpaka;
  struct ConfigNode *tmpcnode;
  uchar pktofs[200];
  ulong rlen, msgnum, msgoffset, c;
  uchar buf[200];
  uchar PktHeader[SIZE_PKTHEADER];
  uchar PktMsgHeader[SIZE_PKTMSGHEADER];
  struct Node4D PktOrig, PktDest;
  uchar *monthnames[] =
    { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
    "Nov", "Dec", "???"
  };
  struct MemMessage *mm;
  struct TextChunk *chunk;
  bool pkt_pw, pkt_4d, pkt_5d;

  shortread = FALSE;
  longread = FALSE;

  pkt_pw = FALSE;
  pkt_4d = FALSE;
  pkt_5d = FALSE;

  PktOrig.Zone = 0;
  PktOrig.Net = 0;
  PktOrig.Node = 0;
  PktOrig.Point = 0;

  PktDest.Zone = 0;
  PktDest.Net = 0;
  PktDest.Node = 0;
  PktDest.Point = 0;

  if (!(mm = mmAlloc()))
    return (FALSE);

  if (!(fh = fopen(pkt, "rb"))) {
    LogWrite(1, SYSTEMERR, "Unable to open %s", pkt);
    LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno));
    mmFree(mm);
    return (TRUE);
  }

  if (fread(PktHeader, 1, SIZE_PKTHEADER, fh) != SIZE_PKTHEADER) {
    LogWrite(1, TOSSINGERR, "Packet header in %s is too short", pkt);
    fclose(fh);
    mmFree(mm);
    BadFile(pkt);
    return (TRUE);
  }

  if (getuword(PktHeader, PKTHEADER_PKTTYPE) != 0x0002) {
    LogWrite(1, TOSSINGERR, "%s is not a Type-2 packet", pkt);
    fclose(fh);
    mmFree(mm);
    BadFile(pkt);
    return (TRUE);
  }

  if (getuword(PktHeader, PKTHEADER_BAUD) == 2) {
    /* PktOrig och PktDest */
    PktOrig.Zone = getuword(PktHeader, PKTHEADER_ORIGZONE);
    PktOrig.Net = getuword(PktHeader, PKTHEADER_ORIGNET);
    PktOrig.Node = getuword(PktHeader, PKTHEADER_ORIGNODE);
    PktOrig.Point = getuword(PktHeader, PKTHEADER_ORIGPOINT);

    PktDest.Zone = getuword(PktHeader, PKTHEADER_DESTZONE);
    PktDest.Net = getuword(PktHeader, PKTHEADER_DESTNET);
    PktDest.Node = getuword(PktHeader, PKTHEADER_DESTNODE);
    PktDest.Point = getuword(PktHeader, PKTHEADER_DESTPOINT);

    pkt_5d = TRUE;
  } else {
    /* PktOrig och PktDest */
    if (getuword(PktHeader, PKTHEADER_ORIGZONE)) {
      PktOrig.Zone = getuword(PktHeader, PKTHEADER_ORIGZONE);
      PktDest.Zone = getuword(PktHeader, PKTHEADER_DESTZONE);
    } else if (getuword(PktHeader, PKTHEADER_QORIGZONE)) {
      PktOrig.Zone = getuword(PktHeader, PKTHEADER_QORIGZONE);
      PktDest.Zone = getuword(PktHeader, PKTHEADER_QDESTZONE);
    } else {
      PktOrig.Zone = 0;
      PktDest.Zone = 0;
    }

    PktOrig.Net = getuword(PktHeader, PKTHEADER_ORIGNET);
    PktOrig.Node = getuword(PktHeader, PKTHEADER_ORIGNODE);
    PktDest.Net = getuword(PktHeader, PKTHEADER_DESTNET);
    PktDest.Node = getuword(PktHeader, PKTHEADER_DESTNODE);

    if (PktHeader[PKTHEADER_CWVALIDCOPY] == PktHeader[PKTHEADER_CAPABILWORD + 1] &&
        PktHeader[PKTHEADER_CWVALIDCOPY + 1] == PktHeader[PKTHEADER_CAPABILWORD]) {
      pkt_4d = TRUE;

      if (getuword(PktHeader, PKTHEADER_ORIGPOINT) != 0 &&
          getuword(PktHeader, PKTHEADER_ORIGNET) == 0xffff)
        PktOrig.Net = getuword(PktHeader, PKTHEADER_AUXNET);

      PktOrig.Point = getuword(PktHeader, PKTHEADER_ORIGPOINT);
      PktDest.Point = getuword(PktHeader, PKTHEADER_DESTPOINT);
    }
  }

  /* Check packet destination */
  if ((config.cfg_Flags & CFG_CHECKPKTDEST) && !no_security) {
    for (tmpaka = (struct Aka *) config.AkaList.First; tmpaka; tmpaka = tmpaka->Next)
      if (Compare4D(&tmpaka->Node, &PktDest) == 0)
        break;

    if (!tmpaka) {
      LogWrite(1, TOSSINGERR, "Addressed to %u:%u/%u.%u, not to me",
               PktDest.Zone, PktDest.Net, PktDest.Node, PktDest.Point);
      fclose(fh);
      mmFree(mm);
      BadFile(pkt);
      return (TRUE);
    }
  }

  /* Fixa zone */
  /* XXX I think this code does nothing, since Compare4D will always fail... */
  if (PktOrig.Zone == 0)
    for (tmpcnode = (struct ConfigNode *) config.CNodeList.First; tmpcnode; tmpcnode = tmpcnode->Next) {
      if (Compare4D(&PktOrig, &tmpcnode->Node) == 0) {
        PktOrig.Zone = tmpcnode->Node.Zone;
        break;
      }
    }

  if (PktDest.Zone == 0)
    for (tmpaka = (struct Aka *) config.AkaList.First; tmpaka; tmpaka = tmpaka->Next) {
      if (Compare4D(&PktDest, &tmpaka->Node) == 0) {
        PktDest.Zone = tmpaka->Node.Zone;
        break;
      }
    }

  if (PktOrig.Zone == 0)
    PktOrig.Zone = config.cfg_DefaultZone;
  if (PktDest.Zone == 0)
    PktDest.Zone = config.cfg_DefaultZone;

  for (tmpcnode = (struct ConfigNode *) config.CNodeList.First; tmpcnode; tmpcnode = tmpcnode->Next)
    if (Compare4D(&PktOrig, &tmpcnode->Node) == 0)
      break;

  if (tmpcnode) {
    if (tmpcnode->PacketPW[0] != 0 && PktHeader[PKTHEADER_PASSWORD] != 0)
      pkt_pw = TRUE;
  }

  buf[0] = 0;

  if (pkt_pw)
    strcat(buf, ", pw");
  if (pkt_4d)
    strcat(buf, ", 4d");
  if (pkt_5d)
    strcat(buf, ", 5d");

  if (buf[0] != 0)
    buf[0] = '/';

  if (pkt_5d) {
    uchar domain[10];

    mystrncpy(domain, &PktHeader[PKTHEADER45_ORIGDOMAIN], 9);

    LogWrite(1, ACTIONINFO, "Tossing %s (%luK) from %d:%d/%d.%d@%s %s", fe->Name, (fe->Size + 512) / 1024, PktOrig.Zone, PktOrig.Net, PktOrig.Node, PktOrig.Point, domain, buf);
  } else {
    int month;

    month = getuword(PktHeader, PKTHEADER_MONTH);

    if (month > 11)
      month = 12;

    LogWrite(1, ACTIONINFO, "Tossing %s (%luK) from %d:%d/%d.%d (%02ld-%s-%02ld %02ld:%02ld:%02ld) %s", fe->Name, (fe->Size + 512) / 1024, PktOrig.Zone, PktOrig.Net, PktOrig.Node, PktOrig.Point, getuword(PktHeader, PKTHEADER_DAY), monthnames[month], getuword(PktHeader, PKTHEADER_YEAR) % 100, getuword(PktHeader, PKTHEADER_HOUR), getuword(PktHeader, PKTHEADER_MINUTE), getuword(PktHeader, PKTHEADER_SECOND), buf);
  }

  if (tmpcnode) {
    strncpy(buf, &PktHeader[PKTHEADER_PASSWORD], 8);
    buf[8] = 0;

    if (!no_security &&
        (tmpcnode->PacketPW[0] != 0 && stricmp(buf, tmpcnode->PacketPW)) &&
        (buf[0] != 0 || !(config.cfg_Flags & CFG_ALLOWNULLPASSWORDS))) {
      LogWrite(1, TOSSINGERR, "Wrong password");
      fclose(fh);
      mmFree(mm);
      BadFile(pkt);
      return (TRUE);
    }
  }

  snprintf(pktofs, 200, "%s.offset", pkt);

  if ((pktofsh=fopen(pktofs, "rb")) != NULL) {
    fread(&msgoffset, sizeof(msgoffset), 1, pktofsh);
    fread(&msgnum, sizeof(msgnum), 1, pktofsh);
    fseek(fh, msgoffset, SEEK_SET);
    fclose(pktofsh);
    LogWrite(1, ACTIONINFO, "Old packet with %ld processed messages found, continuing from offset %ld", msgnum, msgoffset);
  } else {
    msgoffset = ftell(fh);
    msgnum = 0;
  }

  while (1) {
    if ((pktofsh=fopen(pktofs, "wb")) != NULL) {
      fwrite(&msgoffset, sizeof(msgoffset), 1, pktofsh);
      fwrite(&msgnum, sizeof(msgnum), 1, pktofsh);
      fclose(pktofsh);
    }
    msgnum++;
    msgoffset = ftell(fh);

    if (fread(PktMsgHeader, 1, SIZE_PKTMSGHEADER, fh) < 2) {
      LogWrite(1, TOSSINGERR, "Packet header too short for msg #%lu (offset %ld)", msgnum + 1, msgoffset);
      fclose(fh);
      mmFree(mm);
      BadFile(pkt); /* FIXME should we delete pktofs file here? */
      return (TRUE);
    }

    if (getuword(PktMsgHeader, PKTMSGHEADER_PKTTYPE) != 2)
      break;

    printf("Message %lu              \015", msgnum);
    fflush(stdout);

    /* Init variables */
    mmClear(mm);

    mm->OrigNode.Net = getuword(PktMsgHeader, PKTMSGHEADER_ORIGNET);
    mm->OrigNode.Node = getuword(PktMsgHeader, PKTMSGHEADER_ORIGNODE);

    mm->DestNode.Net = getuword(PktMsgHeader, PKTMSGHEADER_DESTNET);
    mm->DestNode.Node = getuword(PktMsgHeader, PKTMSGHEADER_DESTNODE);

    mm->DestNode.Zone = PktDest.Zone;
    mm->DestNode.Zone = PktOrig.Zone;

    mm->Attr = getuword(PktMsgHeader, PKTMSGHEADER_ATTR);
    mm->Cost = getuword(PktMsgHeader, PKTMSGHEADER_COST);

    Copy4D(&mm->PktOrig, &PktOrig);
    Copy4D(&mm->PktDest, &PktDest);

    if (no_security)
      mm->no_security = TRUE;

    /* Get header strings */
    ReadNull(mm->DateTime, 20, fh);
    ReadNull(mm->To, 36, fh);
    ReadNull(mm->From, 36, fh);
    ReadNull(mm->Subject, 72, fh);

    /* Corrupt packet?  */
    if (shortread) {
      LogWrite(1, TOSSINGERR, "Message header for msg #%lu (offset %ld) is short", msgnum, msgoffset);
      fclose(fh);
      mmFree(mm);
      BadFile(pkt);
      return (TRUE);
    }

    if (longread) {
      LogWrite(1, TOSSINGERR, "Header strings too long in msg #%lu (offset %ld)", msgnum, msgoffset);
      fclose(fh);
      mmFree(mm);
      BadFile(pkt);
      return (TRUE);
    }

    /* Start reading message text */
    messageend = FALSE;

    rlen = ReadCR(buf, 200, fh);

    /* Echomail or netmail?  */
    if (strncmp(buf, "AREA:", 5) == 0) {
      mystrncpy(mm->Area, &buf[5], 80);
      /* Strip spaces from area name */
      stripleadtrail(mm->Area);
      /* Uppercase areatag */
      for (c = 0; mm->Area[c]; c++)
        if (islower(mm->Area[c]))
          mm->Area[c] = toupper(mm->Area[c]);
    } else {
      if (!mmAddLine(mm, buf, MM_ADD)) {
        fclose(fh);
        mmFree(mm);
        return (FALSE);
      }
    }

    /* Get rest of text */
    while (!messageend) {
      rlen = ReadCR(buf, 200, fh);

      if (shortread) {
        fclose(fh);
        mmFree(mm);
        LogWrite(1, TOSSINGERR, "Got unexpected EOF when reading msg #%lu (offset %ld)", msgnum, msgoffset);
        BadFile(pkt);
        return (TRUE);
      }

      if (buf[0]) {
        if (!mmAddLine(mm, buf, MM_ADD)) {
          fclose(fh);
          mmFree(mm);
          return (FALSE);
        }
      }
    }

    /* Stats */
    for (tmpcnode = (struct ConfigNode *) config.CNodeList.First; tmpcnode; tmpcnode = tmpcnode->Next)
      if (Compare4D(&tmpcnode->Node, &mm->PktOrig) == 0)
        break;

    if (tmpcnode) {
      ulong size;

      size = 0;

      for (chunk = (struct TextChunk *) mm->TextChunks.First; chunk; chunk = chunk->Next)
        if (chunk->Data)
          size += strlen(chunk->Data);

      if (mm->Area[0]) {
        tmpcnode->GotEchomails++;
        tmpcnode->GotEchomailBytes += size;
      } else {
        tmpcnode->GotNetmails++;
        tmpcnode->GotNetmailBytes += size;
      }
    }

    if (ctrlc) {
      fclose(fh);
      mmFree(mm);
      return (FALSE);
    }

    if (!(*handlefunc) (mm, no_echomail)) {
      fclose(fh);
      mmFree(mm);
      return (FALSE);
    }
  }

  if (getuword(PktMsgHeader, PKTMSGHEADER_PKTTYPE) != 0) {
    fclose(fh);
    mmFree(mm);
    LogWrite(1, TOSSINGERR, "Unknown message type %lu for message #%lu (offset %ld)", getuword(PktMsgHeader, PKTMSGHEADER_PKTTYPE), msgnum + 1, msgoffset);
    BadFile(pkt);
    return (TRUE);
  }

  printf("                      \015");
  fflush(stdout);

  unlink(pktofs);

  fclose(fh);
  mmFree(mm);

  return (TRUE);
}

/*
 *
 * Write Pkt
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void pktWrite(struct Pkt *pkt, uchar * buf, ulong len)
{
  if (fwrite(buf, 1, len, pkt->fh) != len) {
    ioerror = TRUE;
    ioerrornum = errno;
  }

  pkt->Len += len;
}

void WriteNull(struct Pkt *pkt, uchar * str)
{
  pktWrite(pkt, str, (ulong) (strlen(str) + 1));
}

struct Pkt *FindPkt(struct Node4D *node, struct Node4D *mynode, ushort type)
{
  struct Pkt *pkt;

  for (pkt = (struct Pkt *) PktList.First; pkt; pkt = pkt->Next)
    if (Compare4D(node, &pkt->Dest) == 0 && Compare4D(mynode, &pkt->Orig) == 0
        && type == pkt->Type)
      return (pkt);

  return (NULL);
}

static time_t lastt = 0;
static ulong serial = 0;

struct Pkt *CreatePkt(struct Node4D *dest, struct ConfigNode *node, struct Aka *aka, ushort type)
{
  uchar buf[100], buf2[100];
  struct Pkt *pkt;
  ulong num, c;
  time_t t;
  struct tm *tp;
  uchar PktHeader[SIZE_PKTHEADER];

  do {
    t = time(NULL);
    if (t == lastt)
      serial++;
    else
      serial = 0;
    if (serial == 256)
      serial = 0;
    lastt = t;
    num = (t << 8) + serial;
    snprintf(buf2, 100, "%08lx.newpkt", num);

    MakeFullPath(config.cfg_PacketCreate, buf2, buf, 100);
  } while (Exists(buf));

  if (!(pkt = (struct Pkt *) calloc(1, sizeof(struct Pkt)))) {
    nomem = TRUE;
    return (NULL);
  }

  if (!(pkt->fh = fopen(buf, "wb"))) {
    LogWrite(1, SYSTEMERR, "Unable to create packet %s", buf);
    LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno));
    free(pkt);
    return (NULL);
  }

  pkt->hexnum = num;
  pkt->Type = type;
  Copy4D(&pkt->Dest, dest);
  Copy4D(&pkt->Orig, &aka->Node);

  putuword(PktHeader, PKTHEADER_ORIGNODE, aka->Node.Node);
  putuword(PktHeader, PKTHEADER_DESTNODE, dest->Node);

  time(&t);
  tp = localtime(&t);

  putuword(PktHeader, PKTHEADER_DAY, tp->tm_mday);
  putuword(PktHeader, PKTHEADER_MONTH, tp->tm_mon);
  putuword(PktHeader, PKTHEADER_YEAR, tp->tm_year + 1900);
  putuword(PktHeader, PKTHEADER_HOUR, tp->tm_hour);
  putuword(PktHeader, PKTHEADER_MINUTE, tp->tm_min);
  putuword(PktHeader, PKTHEADER_SECOND, tp->tm_sec);

  putuword(PktHeader, PKTHEADER_BAUD, 0);
  putuword(PktHeader, PKTHEADER_PKTTYPE, 2);
  putuword(PktHeader, PKTHEADER_ORIGNET, aka->Node.Net);
  putuword(PktHeader, PKTHEADER_DESTNET, dest->Net);
  PktHeader[PKTHEADER_PRODCODELOW] = 0xfe;
  PktHeader[PKTHEADER_REVMAJOR] = VERSION_MAJOR;
  putuword(PktHeader, PKTHEADER_QORIGZONE, aka->Node.Zone);
  putuword(PktHeader, PKTHEADER_QDESTZONE, dest->Zone);
  putuword(PktHeader, PKTHEADER_AUXNET, 0);
  putuword(PktHeader, PKTHEADER_CWVALIDCOPY, 0x0100);
  PktHeader[PKTHEADER_PRODCODEHIGH] = 0;
  PktHeader[PKTHEADER_REVMINOR] = VERSION_MINOR;
  putuword(PktHeader, PKTHEADER_CAPABILWORD, 0x0001);
  putuword(PktHeader, PKTHEADER_ORIGZONE, aka->Node.Zone);
  putuword(PktHeader, PKTHEADER_DESTZONE, dest->Zone);
  putuword(PktHeader, PKTHEADER_ORIGPOINT, aka->Node.Point);
  putuword(PktHeader, PKTHEADER_DESTPOINT, dest->Point);
  PktHeader[PKTHEADER_PRODDATA] = 0;
  PktHeader[PKTHEADER_PRODDATA + 1] = 0;
  PktHeader[PKTHEADER_PRODDATA + 2] = 0;
  PktHeader[PKTHEADER_PRODDATA + 3] = 0;

  for (c = 0; c < 8; c++)
    PktHeader[PKTHEADER_PASSWORD + c] = 0;

  if (node)
    strncpy(&PktHeader[PKTHEADER_PASSWORD], node->PacketPW, 8);

  pktWrite(pkt, PktHeader, SIZE_PKTHEADER);

  if (ioerror) {
    fclose(pkt->fh);
    free(pkt);
    return (NULL);
  }

  return (pkt);
}

void FinishPacket(struct Pkt *pkt)
{
  pktWrite(pkt, "", 1);
  pktWrite(pkt, "", 1);
  fclose(pkt->fh);

  if (pkt->hexnum) {
    uchar oldname[200], newname[200], buf1[100], buf2[100];

    /* Create packet name */
    snprintf(buf1, 100, "%08lx.newpkt", pkt->hexnum);

    /* FIXME packet name may be too long */
    snprintf(buf2, 100, "%08lx_%d_%d_%d_%d_%d_%d_%d_%d.newpkt", pkt->hexnum,
            pkt->Orig.Zone, pkt->Orig.Net, pkt->Orig.Node, pkt->Orig.Point,
            pkt->Dest.Zone, pkt->Dest.Net, pkt->Dest.Node, pkt->Dest.Point);

    MakeFullPath(config.cfg_PacketCreate, buf1, oldname, 200);
    MakeFullPath(config.cfg_PacketCreate, buf2, newname, 200);

    if (rename(oldname, newname)) {
      LogWrite(1, SYSTEMERR, "Failed to rename %s to %s", oldname, newname);
      LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno));
    }
  }

  jbFreeNode(&PktList, (struct jbNode *) pkt);
}

void ClosePackets(void)
{
  struct Pkt *pkt, *pkt2;

  pkt = (struct Pkt *) PktList.First;

  while (pkt) {
    pkt2 = pkt->Next;
    FinishPacket(pkt);
    pkt = pkt2;
  }
}

bool WriteMsgHeader(struct Pkt *pkt, struct MemMessage *mm)
{
  uchar PktMsgHeader[SIZE_PKTMSGHEADER];

  putuword(PktMsgHeader, PKTMSGHEADER_PKTTYPE, 0x0002);
  putuword(PktMsgHeader, PKTMSGHEADER_ORIGNODE, mm->OrigNode.Node);
  putuword(PktMsgHeader, PKTMSGHEADER_DESTNODE, mm->DestNode.Node);
  putuword(PktMsgHeader, PKTMSGHEADER_ORIGNET, mm->OrigNode.Net);
  putuword(PktMsgHeader, PKTMSGHEADER_DESTNET, mm->DestNode.Net);
  putuword(PktMsgHeader, PKTMSGHEADER_ATTR, mm->Attr);
  putuword(PktMsgHeader, PKTMSGHEADER_COST, mm->Cost);

  pktWrite(pkt, PktMsgHeader, SIZE_PKTMSGHEADER);

  WriteNull(pkt, mm->DateTime);
  WriteNull(pkt, mm->To);
  WriteNull(pkt, mm->From);
  WriteNull(pkt, mm->Subject);

  if (ioerror)
    return (FALSE);

  return (TRUE);
}

bool WritePath(struct Pkt * pkt, struct jbList * list)
{
  ushort c;
  struct Path *path;

  for (path = (struct Path *) list->First; path; path = path->Next)
    for (c = 0; c < path->Paths; c++)
      if (path->Path[c][0] != 0) {
        pktWrite(pkt, "\001PATH: ", 7);
        pktWrite(pkt, path->Path[c], (ulong) strlen(path->Path[c]));
        pktWrite(pkt, "\015", 1);
      }

  if (ioerror)
    return (FALSE);

  return (TRUE);
}

bool WriteSeenBy(struct Pkt * pkt, struct jbList * list)
{
  uchar *buf;

  if (!(buf = mmMakeSeenByBuf(list)))
    return (FALSE);

  pktWrite(pkt, buf, (ulong) strlen(buf));

  free(buf);

  return (TRUE);
}

bool WriteEchoMail(struct MemMessage * mm, struct ConfigNode * node, struct Aka * aka)
{
  uchar buf[200];
  struct Pkt *pkt;
  struct TextChunk *chunk;
  ulong size;

  toss_written++;

  mm->Type = PKTS_ECHOMAIL;

  size = 0;

  for (chunk = (struct TextChunk *) mm->TextChunks.First; chunk; chunk = chunk->Next)
    if (chunk->Data)
      size += strlen(chunk->Data);

  node->SentEchomails++;
  node->SentEchomailBytes += size;

  pkt = FindPkt(&node->Node, &aka->Node, mm->Type);

  if (!pkt || (pkt && config.cfg_MaxPktSize != 0 && pkt->Len > config.cfg_MaxPktSize)) {
    if (pkt)
      FinishPacket(pkt);

    if (!(pkt = CreatePkt(&node->Node, node, aka, mm->Type))) {
      return FALSE;
    }

    jbAddNode(&PktList, (struct jbNode *) pkt);
  }

  Copy4D(&mm->DestNode, &node->Node);
  Copy4D(&mm->OrigNode, &aka->Node);

  if (!WriteMsgHeader(pkt, mm))
    return FALSE;

  snprintf(buf, 200, "AREA:%s\015", mm->Area);
  pktWrite(pkt, buf, (ulong) strlen(buf));

  if (ioerror)
    return (FALSE);

  if (mm->Rescanned) {
    snprintf(buf, 200, "\001RESCANNED %u:%u/%u.%u\015", aka->Node.Zone, aka->Node.Net, aka->Node.Node, aka->Node.Point);
    pktWrite(pkt, buf, (ulong) strlen(buf));
  }

  if (ioerror)
    return (FALSE);

  for (chunk = (struct TextChunk *) mm->TextChunks.First; chunk; chunk = chunk->Next) {
    if (chunk->Data)
      pktWrite(pkt, chunk->Data, strlen(chunk->Data));

    if (ioerror)
      return (FALSE);
  }

  if (node->Node.Zone != aka->Node.Zone || (node->Flags & NODE_TINYSEENBY)) {
    struct jbList seenbylist;
    struct Nodes2D *seenby;

    if (!(seenby = (struct Nodes2D *) malloc(sizeof(struct Nodes2D)))) {
      nomem = TRUE;
      return (FALSE);
    }

    seenby->Nodes = 0;
    seenby->Next = NULL;

    jbNewList(&seenbylist);
    jbAddNode(&seenbylist, (struct jbNode *) seenby);

    if (node->Node.Point == 0)
      if (!mmAddNodes2DList(&seenbylist, node->Node.Net, node->Node.Node)) {
        jbFreeList(&seenbylist);
        return (FALSE);
      }

    if (aka->Node.Point == 0)
      if (!mmAddNodes2DList(&seenbylist, aka->Node.Net, aka->Node.Node)) {
        jbFreeList(&seenbylist);
        return (FALSE);
      }

    if (!mmSortNodes2D(&seenbylist)) {
      jbFreeList(&seenbylist);
      return (FALSE);
    }

    if (!WriteSeenBy(pkt, &seenbylist)) {
      jbFreeList(&seenbylist);
      return (FALSE);
    }

    jbFreeList(&seenbylist);
  } else if (!(node->Flags & NODE_NOSEENBY)) {
    if (!mmSortNodes2D(&mm->SeenBy))
      return (FALSE);

    if (!WriteSeenBy(pkt, &mm->SeenBy))
      return (FALSE);
  }

  if (!WritePath(pkt, &mm->Path))
    return (FALSE);

  pktWrite(pkt, "", 1);

  return (TRUE);
}


syntax highlighted by Code2HTML, v. 0.9.1