/********************************************************
 * File: handle.c
 * Created at Sun Jan 28 22:10:29 MSK 2001 by raorn // raorn@binec.ru
 * Message handling
 * $Id: handle.c,v 1.23 2002/05/12 22:18:36 raorn Exp $
 *******************************************************/
#include "crashecho.h"

bool WriteBad(struct MemMessage * mm, uchar * reason);

bool HandleMessage(struct MemMessage *mm, bool no_echomail)
{
  LogWrite(6, DEBUG, "Is in HandleMessage()"); 

  if (nowdoing == DOING_TOSS || nowdoing == DOING_TOSSBAD)
    toss_total++;

  if (mm->Area[0] == 0)
    return HandleNetmail(mm);
  else if (mm->no_security || !no_echomail)
    return HandleEchomail(mm);
  else {
    LogWrite(3, TOSSINGERR, "Refusing echomail message from insecure inbound");

    toss_bad++;

    return WriteBad(mm, "Echomail message from insecure inbound");
  }
}

/**************************** auto-add *****************************/

bool GetDescription(uchar * area, struct ConfigNode * node, uchar * desc)
{
  struct Arealist *arealist;
  uchar buf[200];
  ulong c, d;
  FILE *fh;
  for (arealist = (struct Arealist *) config.ArealistList.First; arealist; arealist = arealist->Next) {
    if (arealist->Node == node && (arealist->Flags & AREALIST_DESC)) {
      if ((fh = fopen(arealist->AreaFile, "rt"))) {
        while (fgets(buf, 199, fh)) {
          for (c = 0; buf[c] > 32; c++);

          if (buf[c] != 0) {
            buf[c] = 0;

            if (stricmp(buf, area) == 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;
                fclose(fh);
                return (TRUE);
              }
            }
          }
        }
        fclose(fh);
      } else {
        LogWrite(1, SYSTEMERR, "Failed to open file \"%s\"\n", arealist->AreaFile);
        LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno));
      }
    }
  }
  return (FALSE);
}

bool AddTossNode(struct Area * area, struct ConfigNode * cnode, ushort flags)
{
  struct TossNode *tnode;

  /* Check if it already exists */
  for (tnode = (struct TossNode *) area->TossNodes.First; tnode; tnode = tnode->Next)
    if (tnode->ConfigNode == cnode)
      return (TRUE);

  if (!(tnode = (struct TossNode *) calloc(1, sizeof(struct TossNode)))) {
    nomem = TRUE;
    return (FALSE);
  }

  jbAddNode((struct jbList *) &area->TossNodes, (struct jbNode *) tnode), tnode->ConfigNode = cnode;
  tnode->Flags = flags;

  return (TRUE);
}

static time_t lastt=0;

void MakeDirectory(uchar * dest, ulong destsize, uchar * defdir, uchar * areaname)
{
  ulong c, d;
  uchar lowercase[200], shortname[50];

  /* Convert to lower case */
  mystrncpy(lowercase, areaname, 200);

  for (c = 0; lowercase[c] != 0; c++)
    lowercase[c] = tolower(lowercase[c]);

  /* Make 8 digit serial number */
  if (lastt == 0)
    lastt = time(NULL);
  else
    lastt++;

  snprintf(shortname, 50, "%08lx", lastt);

  d = 0;
  for (c = 0; c < strlen(defdir) && d != destsize - 1; c++) {
    if (defdir[c] == '%' && (defdir[c + 1] | 32) == 'a') {
      strncpy(&dest[d], areaname, (size_t) (destsize - 1 - d));
      dest[destsize - 1] = 0;
      d = strlen(dest);
      c++;
    } else if (defdir[c] == '%' && (defdir[c + 1] | 32) == 'l') {
      strncpy(&dest[d], lowercase, (size_t) (destsize - 1 - d));
      dest[destsize - 1] = 0;
      d = strlen(dest);
      c++;
    } else if (defdir[c] == '%' && (defdir[c + 1] | 32) == '8') {
      strncpy(&dest[d], shortname, (size_t) (destsize - 1 - d));
      dest[destsize - 1] = 0;
      d = strlen(dest);
      c++;
    } else
      dest[d++] = defdir[c];
  }
  dest[d] = 0;
}

struct Area *AddArea(uchar * name, struct Node4D *node, struct Node4D *mynode, ulong active, ulong forcepassthru)
{
  struct Area *temparea, *defarea;
  struct Aka *tempaka;
  struct ConfigNode *tempcnode;

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

  jbNewList(&temparea->TossNodes);
  jbNewList(&temparea->BannedNodes);

  jbAddNode(&config.AreaList, (struct jbNode *) temparea);

  for (tempaka = (struct Aka *) config.AkaList.First; tempaka; tempaka = tempaka->Next)
    if (Compare4D(&tempaka->Node, mynode) == 0)
      break;

  if (!tempaka)
    tempaka = (struct Aka *) config.AkaList.First;

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

  /* Find default area to use */
  defarea = NULL;

  /* First we try to find one for specific groups */
  if (tempcnode && tempcnode->DefaultGroup) {
    uchar groups[100];

    for (defarea = (struct Area *) config.AreaList.First; defarea; defarea = defarea->Next)
      if (strnicmp(defarea->Tagname, "DEFAULT_", 8) == 0) {
        mystrncpy(groups, &defarea->Tagname[8], 50);

        if (MatchFlags(tempcnode->DefaultGroup, groups))
          break;
      }
  }

  /* If not found, we try to find the general default area */
  if (!defarea) {
    for (defarea = (struct Area *) config.AreaList.First; defarea; defarea = defarea->Next)
      if (stricmp(defarea->Tagname, "DEFAULT") == 0)
        break;
  }

  if (defarea) {
    struct TossNode *tnode;
    ulong c;
    uchar *forbiddenchars = "\"#'`()*,/:;<>|";
    uchar buf[100], buf2[200];

    mystrncpy(buf, name, 100);

    for (c = 0; buf[c] != 0; c++)
      if (buf[c] < 33 || buf[c] > 126 || strchr(forbiddenchars, buf[c]))
        buf[c] = '_';

    /*
     * Cannot create directory directly into temparea->Path.
     * MakeDirectory checks for duplicate area names in the
     * AreaList and would get confused
     * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    MakeDirectory(buf2, 200, defarea->Path, buf);
    mystrncpy(temparea->Path, buf2, 200);

    if (!forcepassthru)
      temparea->Messagebase = defarea->Messagebase;

    strcpy(temparea->Description, defarea->Description);

    if (defarea->Flags & AREA_MANDATORY)
      temparea->Flags |= AREA_MANDATORY;

    if (defarea->Flags & AREA_DEFREADONLY)
      temparea->Flags |= AREA_DEFREADONLY;

    if (defarea->Flags & AREA_IGNOREDUPES)
      temparea->Flags |= AREA_IGNOREDUPES;

    if (defarea->Flags & AREA_IGNORESEENBY)
      temparea->Flags |= AREA_IGNORESEENBY;

    if (defarea->Flags & AREA_IMPORTSEENBY)
      temparea->Flags |= AREA_IMPORTSEENBY;

    temparea->KeepDays = defarea->KeepDays;
    temparea->KeepNum = defarea->KeepNum;

    for (tnode = (struct TossNode *) defarea->TossNodes.First; tnode; tnode = tnode->Next)
      AddTossNode(temparea, tnode->ConfigNode, tnode->Flags);
  }

  GetDescription(name, tempcnode, temparea->Description);

  if (!active)
    temparea->Flags |= AREA_UNCONFIRMED;

  mystrncpy(temparea->Tagname, name, 80);

  temparea->Aka = tempaka;
  temparea->AreaType = AREATYPE_ECHOMAIL;

  if (tempcnode) {
    temparea->Group = tempcnode->DefaultGroup;
    AddTossNode(temparea, tempcnode, TOSSNODE_FEED);

    for (tempcnode = (struct ConfigNode *) config.CNodeList.First; tempcnode; tempcnode = tempcnode->Next)
      if (MatchFlags(temparea->Group, tempcnode->AddGroups)) {
        ushort flags;

        flags = 0;

        if ((temparea->Flags & AREA_DEFREADONLY) || MatchFlags(temparea->Group, tempcnode->ReadOnlyGroups))
          flags = TOSSNODE_READONLY;

        AddTossNode(temparea, tempcnode, flags);
      }
  }

  config.changed = TRUE;
  temparea->changed = TRUE;

  return (temparea);
}

/**************************** Echomail *****************************/

bool FindNodes2D(struct jbList * list, struct Node4D * node)
{
  struct Nodes2D *tmp;
  ushort c;

  for (tmp = (struct Nodes2D *) list->First; tmp; tmp = tmp->Next)
    for (c = 0; c < tmp->Nodes; c++)
      if (tmp->Net[c] == node->Net && tmp->Node[c] == node->Node)
        return (TRUE);

  return (FALSE);
}

bool WriteBad(struct MemMessage * mm, uchar * reason)
{
  struct Area *temparea;
/*  struct TextChunk *chunk;*/
  uchar buf[200];

  LogWrite(6, DEBUG, "Is in WriteBad(..., \"%s\")", reason); 

  for (temparea = (struct Area *) config.AreaList.First; temparea; temparea = temparea->Next)
    if (temparea->AreaType == AREATYPE_BAD)
      break;

  if (!temparea) {
    LogWrite(2, TOSSINGERR, "No BAD area configured, message lost");
    return (TRUE);
  }

  /* Insert a new textchunk with information first in the message */
/*  if (!(chunk = (struct TextChunk *) malloc(sizeof(struct TextChunk)))) {*/
/*    nomem = TRUE;*/
/*    return (FALSE);*/
/*  }*/

/*  chunk->Next = (struct TextChunk *) mm->TextChunks.First;*/
/*  mm->TextChunks.First = (struct jbNode *) chunk;*/
/*  if (!mm->TextChunks.Last)*/
/*    mm->TextChunks.Last = (struct jbNode *) chunk;*/

  /* Lines must be inserted in reverse order */
  snprintf(buf, 200, "\001REASON: %s\r", reason);
  mmAddLine(mm, buf, MM_HEAD);
  snprintf(buf, 200, "\001FROM: %u:%u/%u.%u\r", mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point);
  mmAddLine(mm, buf, MM_HEAD);
  snprintf(buf, 200, "\001AREA:%s\r", mm->Area);
  mmAddLine(mm, buf, MM_HEAD);

  if (temparea->Messagebase) {
    if (!((*temparea->Messagebase->importfunc) (mm, temparea)))
      return (FALSE);
  }
  temparea->NewTexts++;

  return (TRUE);
}

bool AddNodePath(struct jbList * list, struct Node4D * node)
{
  uchar buf[40];
  struct Path *path;
  struct Node4D n4d;
  ushort lastnet, num;
  bool lastok;
  ulong jbcpos;

  lastok = FALSE;
  lastnet = 0;

  /* Find last entry in Path */
  path = (struct Path *) list->Last;
  if (path && path->Paths != 0) {
    num = path->Paths - 1;
    jbcpos = 0;

    while (jbstrcpy(buf, path->Path[num], 40, &jbcpos)) {
      if (Parse4D(buf, &n4d)) {
        if (n4d.Net == 0)
          n4d.Net = lastnet;
        else
          lastnet = n4d.Net;
        lastok = TRUE;
      } else {
        lastok = FALSE;
      }
    }
  }

  /* Are we already in the PATH line?  */
  if (lastok) {
    if (n4d.Net == node->Net && n4d.Node == node->Node && n4d.Point == node->Point)
      return (TRUE);
  }

  /* Make address */
  if (lastok && n4d.Net == node->Net)
    snprintf(buf, 40, "%u", node->Node);
  else
    snprintf(buf, 40, "%u/%u", node->Net, node->Node);

  /* Add new */
  path = (struct Path *) list->Last;

  if (path) {
    if (path->Paths != 0) {
      if (strlen(buf) + strlen(path->Path[path->Paths - 1]) <= 70) {
        /* Add to old path */
        strcat(path->Path[path->Paths - 1], " ");
        strcat(path->Path[path->Paths - 1], buf);
        return (TRUE);
      }
    }
  }

  if (path && path->Paths == PKT_NUMPATH)
    path = NULL;                /* Chunk is full */

  if (!path) {
    /* Alloc new path */
    if (!(path = (struct Path *) malloc(sizeof(struct Path)))) {
      nomem = TRUE;
      return (FALSE);
    }

    jbAddNode(list, (struct jbNode *) path);
    path->Next = NULL;
    path->Paths = 0;
  }

  /* Always net/node when a new line */
  snprintf(path->Path[path->Paths], 100, "%u/%u", node->Net, node->Node);

  path->Paths++;

  return (TRUE);
}

uchar *StripRe(uchar * str)
{
  for (;;) {
    if (strnicmp(str, "Re:", 3) == 0) {
      str += 3;
      if (*str == ' ')
        str++;
    } else if (strnicmp(str, "Re^", 3) == 0 && str[4] == ':') {
      str += 5;
      if (*str == ' ')
        str++;
    } else if (strnicmp(str, "Re[", 3) == 0 && str[4] == ']' && str[5] == ':') {
      str += 6;
      if (*str == ' ')
        str++;
    } else
      break;
  }
  return (str);
}

bool HandleEchomail(struct MemMessage * mm)
{
  struct Area *temparea;
  struct TossNode *temptnode;
  struct AddNode *tmpaddnode;
  struct RemNode *tmpremnode;
  struct ConfigNode *tempcnode;
  struct Carbon *tempcarbon;
  uchar buf[200];

  LogWrite(6, DEBUG, "Is in HandleEchomail()"); 

  mm->Type = PKTS_ECHOMAIL;
  mm->Attr = 0;

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

  if (!stricmp(mm->Area, "NETMAIL")) {
    LogWrite(1, TOSSINGERR, "Echomail message to NETMAIL area. CrashEcho confused :-/", mm->Area);
    toss_bad++;

    if (!WriteBad(mm, "Echomail message to \"NETMAIL\" area."))
      return (FALSE);
  }

  /* Find area */
  for (temparea = (struct Area *) config.AreaList.First; temparea; temparea = temparea->Next)
    if (stricmp(temparea->Tagname, mm->Area) == 0)
      break;

  /* Auto-add */
  if (!temparea) {
    if (tempcnode)
      temparea = AddArea(mm->Area, &mm->PktOrig, &mm->PktDest, tempcnode->Flags & NODE_AUTOADD, FALSE);
    else
      temparea = AddArea(mm->Area, &mm->PktOrig, &mm->PktDest, FALSE, FALSE);

    if (!temparea)
      return (FALSE);

    if (temparea->Flags & AREA_UNCONFIRMED)
      LogWrite(3, TOSSINGERR, "Unknown area %s", mm->Area);

    else
      LogWrite(3, TOSSINGINFO, "Unknown area %s -- auto-adding", mm->Area);
  }

  /* Don't toss in auto-added areas */
  if (temparea->Flags & AREA_UNCONFIRMED) {
    toss_bad++;

    if (!WriteBad(mm, "Unknown area tag"))
      return (FALSE);

    return (TRUE);
  }

  /* Don't toss in removed areas */
  if (temparea->AreaType == AREATYPE_DELETED) {
    LogWrite(1, TOSSINGERR, "Posting not allowed to removed area %s", mm->Area);
    toss_bad++;

    if (!WriteBad(mm, "Posting not allowed to removed area"))
      return (FALSE);

    return (TRUE);
  }

  /* Check if the node receives this area */
  if ((nowdoing == DOING_TOSS || nowdoing == DOING_TOSSBAD) && !mm->no_security) {
    for (temptnode = (struct TossNode *) temparea->TossNodes.First; temptnode; temptnode = temptnode->Next)
      if (Compare4D(&temptnode->ConfigNode->Node, &mm->PktOrig) == 0)
        break;

    if (!temptnode) {
      LogWrite(1, TOSSINGERR, "%u:%u/%u.%u not active for %s", mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point, mm->Area);
      toss_bad++;

      if (!WriteBad(mm, "Sender not active for this area"))
        return (FALSE);

      return (TRUE);
    }

    if (temptnode->Flags & TOSSNODE_READONLY || (temparea->Expires && !(temptnode->Flags & TOSSNODE_FEED))) {
      LogWrite(1, TOSSINGERR, "%u:%u/%u.%u not allowed to post in %s", mm->PktOrig.Zone, mm->PktOrig.Net, mm->PktOrig.Node, mm->PktOrig.Point, mm->Area);
      toss_bad++;

      if (!WriteBad(mm, "Sender not allowed to post in this area"))
        return (FALSE);

      return (TRUE);
    }
  }

  /* We got message, so clear the EXPIRES field */
  if (temparea->Expires) {
    /* FIXME check if message comes from FEED */
    temparea->Expires = 0;

    config.changed = TRUE;
    temparea->changed = TRUE;
  }

  /* Remove all SEEN-BY:s if the message comes from an other zone */
  if (temparea->Aka->Node.Zone != mm->PktOrig.Zone && mm->SeenBy.First)
    jbFreeList(&mm->SeenBy);

  /* Check if a node already is in seen-by */
  if ((config.cfg_Flags & CFG_CHECKSEENBY) && !(temparea->Flags & AREA_IGNORESEENBY)) {
    for (temptnode = (struct TossNode *) temparea->TossNodes.First; temptnode; temptnode = temptnode->Next) {
      temptnode->ConfigNode->IsInSeenBy = FALSE;

      if (temptnode->ConfigNode->Node.Zone == temparea->Aka->Node.Zone)
        if (temptnode->ConfigNode->Node.Point == 0)
          if (FindNodes2D(&mm->SeenBy, &temptnode->ConfigNode->Node))
            temptnode->ConfigNode->IsInSeenBy = TRUE;
    }
  }

  /* Add nodes to seen-by */
  for (temptnode = (struct TossNode *) temparea->TossNodes.First; temptnode; temptnode = temptnode->Next)
    if (temptnode->ConfigNode->Node.Point == 0 && temparea->Aka->Node.Zone == temptnode->ConfigNode->Node.Zone)
      if (!(temptnode->ConfigNode->Flags & NODE_PASSIVE))
        if (!(temptnode->Flags & TOSSNODE_WRITEONLY))
          if (!mmAddNodes2DList (&mm->SeenBy, temptnode->ConfigNode->Node.Net, temptnode->ConfigNode->Node.Node))
            return (FALSE);

  /* Remove nodes specified in config from seen-by and path */
  for (tmpremnode = (struct RemNode *) temparea->Aka->RemList.First; tmpremnode; tmpremnode = tmpremnode->Next)
    mmRemNodes2DListPat(&mm->SeenBy, &tmpremnode->NodePat);

  /* Add nodes specified in config to seen-by */
  for (tmpaddnode = (struct AddNode *) temparea->Aka->AddList.First; tmpaddnode; tmpaddnode = tmpaddnode->Next)
    mmAddNodes2DList(&mm->SeenBy, tmpaddnode->Node.Net, tmpaddnode->Node.Node);

  /* Add own node to seen-by */
  if (temparea->Aka->Node.Point == 0) {
    if (!mmAddNodes2DList (&mm->SeenBy, temparea->Aka->Node.Net, temparea->Aka->Node.Node))
      return (FALSE);
  }

  /* Add own node to path */
  if (temparea->Aka->Node.Point == 0) {
    if (!AddNodePath(&mm->Path, &temparea->Aka->Node))
      return (FALSE);
  }

  /* Dupecheck */
  if (config.cfg_DupeMode != DUPE_IGNORE && (nowdoing == DOING_TOSS || nowdoing == DOING_TOSSBAD) && !(temparea->Flags & AREA_IGNOREDUPES)) {
    if (CheckDupe(mm)) {
      LogWrite(4, TOSSINGERR, "Duplicate message in %s", mm->Area);

      toss_dupes++;
      temparea->NewDupes++;

      if (nowdoing == DOING_TOSS && tempcnode)
        tempcnode->Dupes++;

      if (config.cfg_DupeMode == DUPE_BAD) {
        if (!WriteBad(mm, "Duplicate message"))
          return (FALSE);
      }

      return (TRUE);
    }
  }

  temparea->NewTexts++;

  if (!mmSortNodes2D(&mm->SeenBy))
    return (FALSE);

  /* Write to all nodes */
  if (!mm->Rescanned)
    /* not rescanned */
    for (temptnode = (struct TossNode *) temparea->TossNodes.First; temptnode; temptnode = temptnode->Next)
      /* is not sender of packet */
      if (Compare4D(&mm->PktOrig, &temptnode->ConfigNode->Node) != 0)
        /* is not passive */
        if (!(temptnode->ConfigNode->Flags & NODE_PASSIVE))
          /* is not write-only */
          if (!(temptnode->Flags & TOSSNODE_WRITEONLY))
            /* is not already in seen-by */
            if (!(temptnode->ConfigNode->IsInSeenBy == TRUE && (config.cfg_Flags & CFG_CHECKSEENBY)))
              if (!WriteEchoMail(mm, temptnode->ConfigNode, temparea->Aka))
                return (FALSE);

  if (nowdoing == DOING_TOSS || nowdoing == DOING_TOSSBAD) {
    if (temparea->Messagebase) {
      toss_import++;

      if (config.cfg_Flags & CFG_STRIPRE)
        strcpy(mm->Subject, StripRe(mm->Subject));

      if (!(*temparea->Messagebase->importfunc) (mm, temparea))
        return (FALSE);
    }
  }

  if ((nowdoing == DOING_TOSS || nowdoing == DOING_TOSSBAD) || (nowdoing == DOING_SCAN && (config.cfg_Flags & CFG_CCONSCAN))) {
    /* Add ^aAREA */
    snprintf(buf, 200, "\001AREA:%s\r", mm->Area);
    mmAddLine(mm, buf, MM_HEAD);

    for (tempcarbon = (struct Carbon *) config.CarbonList.First; tempcarbon; tempcarbon = tempcarbon->Next)
      if (MatchPattern(tempcarbon->FromArea, mm->Area) &&
          MatchPattern(tempcarbon->Pattern, tempcarbon->CCType == CC_SUBJ ? mm->Subject : tempcarbon->CCType == CC_FROM ? mm->From : mm->To)) {
        for (temparea = (struct Area *) config.AreaList.First; temparea; temparea = temparea->Next)
          if (stricmp(temparea->Tagname, tempcarbon->ToArea) == 0)
            break;

        /* Copy message */
        if (temparea && temparea->Messagebase) {
          toss_cc++;

          if (!(*temparea->Messagebase->importfunc) (mm, temparea))
            return (FALSE);
        }
      }
  }

  return (TRUE);
}

/**************************** Netmail *****************************/

/* Main netmail handling */
bool HandleNetmail(struct MemMessage * mm)
{
  struct ConfigNode *pktcnode;
  struct TextChunk *tmpchunk, *chunk;
  uchar buf[400];
  ulong size;

  LogWrite(6, DEBUG, "Is in HandleNetmail()"); 

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

  /* Calculate size */
  size = 0;

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

  /* Statistics */
  if (nowdoing == DOING_TOSS && pktcnode) {
    pktcnode->GotNetmails++;
    pktcnode->GotNetmailBytes += size;
  }

  /* Set zones if they are zero */
  if (mm->DestNode.Zone == 0)
    mm->DestNode.Zone = mm->PktDest.Zone;

  if (mm->OrigNode.Zone == 0)
    mm->OrigNode.Zone = mm->PktOrig.Zone;

  /* Add CR if last line doesn't end with CR */
  chunk = (struct TextChunk *) mm->TextChunks.First;

  if (chunk && chunk->Data) {
    if (chunk->Data[strlen(chunk->Data) - 1] != 13 && chunk->Data[strlen(chunk->Data) - 1])
      mmAddBuf(&mm->TextChunks, "\015", 1, MM_ADD);
  }

  if (nowdoing == DOING_TOSS) {
    mm->Attr &= FLAG_IMPORT;
    mm->Attr |= FLAG_PVT;
  } else if (nowdoing == DOING_SCAN) {
    mm->Attr &= FLAG_EXPORT;
    mm->Attr |= (FLAG_PVT | FLAG_KILLSENT);
  }

  /* Import netmail */
  if (config.cfg_Flags & CFG_STRIPRE)
    strcpy(mm->Subject, StripRe(mm->Subject));

  if (nowdoing == DOING_TOSS) {
    Print4D(&mm->OrigNode, buf, 400);
    LogWrite(4, TOSSINGINFO, "Importing message from %s at %s", mm->From, buf);
  }

  config.Netmail.NewTexts++;

  if (config.Netmail.Messagebase) {
    toss_import++;

    if (config.cfg_Flags & CFG_STRIPRE)
      strcpy(mm->Subject, StripRe(mm->Subject));

    if (!(config.Netmail.Messagebase->importfunc) (mm, &config.Netmail))
      return (FALSE);
  }

  return (TRUE);
}


/******************************* end netmail **********************************/

/********************************** Rescan *******************************/

bool HandleRescan(struct MemMessage * mm)
{
  struct Area *temparea;

  rescan_total++;

  /* Find area */
  for (temparea = (struct Area *) config.AreaList.First; temparea; temparea = temparea->Next)
    if (stricmp(temparea->Tagname, mm->Area) == 0)
      break;

  /* Add own node to seen-by to be on the safe side */
  if (temparea->Aka->Node.Point == 0) {
    if (!mmAddNodes2DList (&mm->SeenBy, temparea->Aka->Node.Net, temparea->Aka->Node.Node))
      return (FALSE);
  }

  /* Add destination node to seen-by to be on the safe side */
  if (RescanNode->Node.Point == 0) {
    if (!mmAddNodes2DList (&mm->SeenBy, RescanNode->Node.Net, RescanNode->Node.Node))
      return (FALSE);
  }

  /* Add own node to path */
  if (temparea->Aka->Node.Point == 0) {
    if (!AddNodePath(&mm->Path, &temparea->Aka->Node))
      return (FALSE);
  }

  if (!mmSortNodes2D(&mm->SeenBy))
    return (FALSE);

  mm->Rescanned = TRUE;

  if (!WriteEchoMail(mm, RescanNode, temparea->Aka))
    return (FALSE);

  return (TRUE);
}

/********************************** TossBad ******************************/

bool HandleBad(struct MemMessage * mm)
{
  if (HandleEchomail(mm)) {
    mm->Changed = TRUE;
    mm->Deleted = TRUE;
    return TRUE;
  }

  return FALSE;
}


syntax highlighted by Code2HTML, v. 0.9.1