/********************************************************
 * File: config.c
 * Created at Sun Jan 28 22:10:29 MSK 2001 by raorn // raorn@binec.ru
 * Configuration
 * $Id: cfg.c,v 1.15 2002/07/07 12:56:10 raorn Exp $
 *******************************************************/
#include "crashecho.h"

bool CorrectFlags(uchar * flags)
{
  ulong c;

  for (c = 0; c < strlen(flags); c++) {
    flags[c] = toupper(flags[c]);

    if (flags[c] < 'A' || flags[c] > 'Z')
      return (FALSE);
  }

  return (TRUE);
}

uchar *cfgbuf;

bool ReadConfig(uchar * filename, struct Config * cfg, short *seconderr, ulong * cfgline, uchar * cfgerr, size_t errlen)
{
  uchar buf2[200], cfgword[30];
  ulong jbcpos;
  FILE *cfgfh;

  struct Aka *tmpaka, *LastAka = NULL;
  struct ConfigNode *tmpnode, *LastCNode = NULL;
  struct Packer *tmppacker, *LastPacker = NULL;
  struct Carbon *tmpcarbon;
  struct AreaFixName *tmpareafixname;
  struct Arealist *tmparealist;

  struct AddNode *tmpaddnode;
  struct RemNode *tmpremnode;
  struct Node4D tmp4d;

  cfg->changed = FALSE;
  mystrncpy(cfg->filename, filename, 100);

  *cfgline = 0;

  if (!(cfgbuf = malloc(4000))) {
    *seconderr = READCONFIG_NO_MEM;
    return (FALSE);
  }

  if (!(cfgfh = fopen(filename, "rt"))) {
    *seconderr = READCONFIG_NOT_FOUND;
    free(cfgbuf);
    return (FALSE);
  }

  *seconderr = READCONFIG_INVALID;

  while (fgets(cfgbuf, 4000, cfgfh)) {
    jbcpos = 0;
    (*cfgline)++;

    jbstrcpy(cfgword, cfgbuf, 30, &jbcpos);

    if (cfgword[0] == 0 || strchr(";#", cfgword[0])) 
      continue;

    if (stricmp(cfgword, "AKA") == 0) {
      if (!(tmpaka = (struct Aka *) calloc(1, sizeof(struct Aka)))) {
        *seconderr = READCONFIG_NO_MEM;
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      jbAddNode(&cfg->AkaList, (struct jbNode *) tmpaka);
      LastAka = tmpaka;

      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(Parse4D(buf2, &LastAka->Node))) {
        snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      jbNewList(&LastAka->AddList);
      jbNewList(&LastAka->RemList);
    } else if (stricmp(cfgword, "ADDNODE") == 0) {
      if (!LastAka) {
        mystrncpy(cfgerr, "No previous AKA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      while (jbstrcpy(buf2, cfgbuf, 100, &jbcpos)) {
        if (!(tmpaddnode = (struct AddNode *) calloc(1, sizeof(struct AddNode)))) {
          *seconderr = READCONFIG_NO_MEM;
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        jbAddNode(&LastAka->AddList, (struct jbNode *) tmpaddnode);

        if (!(Parse4D(buf2, &tmpaddnode->Node))) {
          snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }
    } else if (stricmp(cfgword, "REMNODE") == 0) {
      if (!LastAka) {
        mystrncpy(cfgerr, "No previous AKA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      while (jbstrcpy(buf2, cfgbuf, 100, &jbcpos)) {
        if (!(tmpremnode = (struct RemNode *) calloc(1, sizeof(struct RemNode)))) {
          *seconderr = READCONFIG_NO_MEM;
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        jbAddNode(&LastAka->RemList, (struct jbNode *) tmpremnode);

        if (!Parse2DPat(buf2, &tmpremnode->NodePat)) {
          snprintf(cfgerr, errlen, "Invalid node pattern \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }
    } else if (stricmp(cfgword, "DOMAIN") == 0) {
      if (!LastAka) {
        mystrncpy(cfgerr, "No previous AKA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastAka->Domain, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }
    } else if (stricmp(cfgword, "GROUPNAME") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 2, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!CorrectFlags(buf2)) {
        snprintf(cfgerr, errlen, "Invalid group \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(cfg->cfg_GroupNames[buf2[0] - 'A'], cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "NODE") == 0) {
      if (!(tmpnode = (struct ConfigNode *) calloc(1, sizeof(struct ConfigNode)))) {
        *seconderr = READCONFIG_NO_MEM;
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      jbNewList(&tmpnode->RemoteAFList);
      jbAddNode(&cfg->CNodeList, (struct jbNode *) tmpnode);
      LastCNode = tmpnode;

      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(Parse4D(buf2, &LastCNode->Node))) {
        snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      /* Aka */
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(Parse4D(buf2, &tmp4d))) {
        snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      for (tmpaka = (struct Aka *) cfg->AkaList.First; tmpaka; tmpaka = tmpaka->Next)
        if (Compare4D(&tmp4d, &tmpaka->Node) == 0)
          break;

      if (!tmpaka) {
        snprintf(cfgerr, errlen, "Unconfigured AKA \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastCNode->Aka = tmpaka;

      /* Packer */

      if (!(jbstrcpy(buf2, cfgbuf, 10, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      tmppacker = NULL;

      if (buf2[0] != 0 || stricmp(buf2, "NONE")) {
        for (tmppacker = (struct Packer *) cfg->PackerList.First; tmppacker; tmppacker = tmppacker->Next)
          if (stricmp(buf2, tmppacker->Name) == 0)
            break;

        if (!tmppacker) {
          snprintf(cfgerr, errlen, "Unknown packer \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }

      LastCNode->Packer = tmppacker;

      if (!(jbstrcpy(LastCNode->PacketPW, cfgbuf, 9, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastCNode->EchomailPri = PKTS_NORMAL;

      while (jbstrcpy(buf2, cfgbuf, 200, &jbcpos)) {
        if (stricmp(buf2, "NOTIFY") == 0)
          LastCNode->Flags |= NODE_NOTIFY;

        else if (stricmp(buf2, "PASSIVE") == 0)
          LastCNode->Flags |= NODE_PASSIVE;

        else if (stricmp(buf2, "NOSEENBY") == 0)
          LastCNode->Flags |= NODE_NOSEENBY;

        else if (stricmp(buf2, "TINYSEENBY") == 0)
          LastCNode->Flags |= NODE_TINYSEENBY;

        else if (stricmp(buf2, "FORWARDREQ") == 0)
          LastCNode->Flags |= NODE_FORWARDREQ;

        else if (stricmp(buf2, "SENDAREAFIX") == 0)
          LastCNode->Flags |= NODE_SENDAREAFIX;

        else if (stricmp(buf2, "SENDTEXT") == 0)
          LastCNode->Flags |= NODE_SENDTEXT;

        else if (stricmp(buf2, "AUTOADD") == 0)
          LastCNode->Flags |= NODE_AUTOADD;

        else if (stricmp(buf2, "CRASH") == 0)
          LastCNode->EchomailPri = PKTS_CRASH;

        else if (stricmp(buf2, "DIRECT") == 0)
          LastCNode->EchomailPri = PKTS_DIRECT;

        else if (stricmp(buf2, "HOLD") == 0)
          LastCNode->EchomailPri = PKTS_HOLD;

        else {
          snprintf(cfgerr, errlen, "Unknown switch \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }
    } else if (stricmp(cfgword, "AREAFIXINFO") == 0) {
      if (!LastCNode) {
        mystrncpy(cfgerr, "No previous NODE line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastCNode->AreafixPW, cfgbuf, 40, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (jbstrcpy(LastCNode->Groups, cfgbuf, 70, &jbcpos)) {
        if (!CorrectFlags(LastCNode->Groups)) {
          snprintf(cfgerr, errlen, "Invalid groups \"%s\"", LastCNode->Groups);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }

      if (jbstrcpy(LastCNode->ReadOnlyGroups, cfgbuf, 70, &jbcpos)) {
        if (!CorrectFlags(LastCNode->ReadOnlyGroups)) {
          snprintf(cfgerr, errlen, "Invalid groups \"%s\"", LastCNode->ReadOnlyGroups);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }

      if (jbstrcpy(LastCNode->AddGroups, cfgbuf, 70, &jbcpos)) {
        if (!CorrectFlags(LastCNode->AddGroups)) {
          snprintf(cfgerr, errlen, "Invalid groups \"%s\"", LastCNode->AddGroups);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }
    } else if (stricmp(cfgword, "DEFAULTGROUP") == 0) {
      if (!LastCNode) {
        mystrncpy(cfgerr, "No previous NODE line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 2, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!CorrectFlags(buf2)) {
        snprintf(cfgerr, errlen, "Invalid group \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastCNode->DefaultGroup = buf2[0];
    } else if (stricmp(cfgword, "AREAFIXNAME") == 0) {
      if (!(tmpareafixname = (struct AreaFixName *) calloc(1, sizeof(struct AreaFixName)))) {
        *seconderr = READCONFIG_NO_MEM;
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      jbAddNode(&cfg->AreaFixList, (struct jbNode *) tmpareafixname);

      if (!(jbstrcpy(tmpareafixname->Name, cfgbuf, 36, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "AFTERUNPACK") == 0) {
      if (!(jbstrcpy(cfg->cfg_AfterUnpack, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "BEFOREPACK") == 0) {
      if (!(jbstrcpy(cfg->cfg_BeforePack, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "INBOUND") == 0) {
      if (!(jbstrcpy(cfg->cfg_Inbound, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "PROTINBOUND") == 0) {
      if (!(jbstrcpy(cfg->cfg_ProtInbound, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "TEMPINBOUND") == 0) {
      if (!(jbstrcpy(cfg->cfg_TempInbound, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "LOCALINBOUND") == 0) {
      if (!(jbstrcpy(cfg->cfg_LocalInbound, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "OUTBOUND") == 0) {
      if (!(jbstrcpy(cfg->cfg_Outbound, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "SYSOP") == 0) {
      if (!(jbstrcpy(cfg->cfg_Sysop, cfgbuf, 35, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }
    } else if (stricmp(cfgword, "AREAFIXHELP") == 0) {
      if (!(jbstrcpy(cfg->cfg_AreaFixHelp, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "LOGFILE") == 0) {
      if (!(jbstrcpy(cfg->cfg_LogFile, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "LOGLEVEL") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (atoi(buf2) < 1 || atoi(buf2) > 6) {
        mystrncpy(cfgerr, "Loglevel out of range", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_LogLevel = atoi(buf2);
    } else if (stricmp(cfgword, "STATSFILE") == 0) {
      if (!(jbstrcpy(cfg->cfg_StatsFile, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }
    } else if (stricmp(cfgword, "DUPEFILE") == 0) {
      if (!(jbstrcpy(cfg->cfg_DupeFile, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_DupeSize = atoi(buf2) * 1024;
    } else if (stricmp(cfgword, "DUPEMODE") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (stricmp(buf2, "IGNORE") == 0)
        cfg->cfg_DupeMode = DUPE_IGNORE;

      else if (stricmp(buf2, "KILL") == 0)
        cfg->cfg_DupeMode = DUPE_KILL;

      else if (stricmp(buf2, "BAD") == 0)
        cfg->cfg_DupeMode = DUPE_BAD;

      else {
        snprintf(cfgerr, errlen, "Unknown dupemode \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }
    } else if (stricmp(cfgword, "CREATEPKTDIR") == 0) {
      if (!(jbstrcpy(cfg->cfg_PacketCreate, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "PACKETDIR") == 0) {
      if (!(jbstrcpy(cfg->cfg_PacketDir, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "PACKER") == 0) {
      if (!(tmppacker = (struct Packer *) calloc(1, sizeof(struct Packer)))) {
        *seconderr = READCONFIG_NO_MEM;
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      jbAddNode(&cfg->PackerList, (struct jbNode *) tmppacker);
      LastPacker = tmppacker;

      if (!(jbstrcpy(LastPacker->Name, cfgbuf, 10, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!stricmp(LastPacker->Name, "NONE")) {
        mystrncpy(cfgerr, "\"NONE\" packer is reserved", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastPacker->Packer, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastPacker->Unpacker, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastPacker->Recog, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }
    } else if (stricmp(cfgword, "CARBON") == 0) {
      if (!(tmpcarbon = (struct Carbon *) calloc(1, sizeof(struct Carbon)))) {
        *seconderr = READCONFIG_NO_MEM;
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      jbAddNode(&cfg->CarbonList, (struct jbNode *) tmpcarbon);

      if (!(jbstrcpy(tmpcarbon->FromArea, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!CheckPattern(tmpcarbon->FromArea)) {
        snprintf(cfgerr, errlen, "Invalid area pattern \"%s\"", tmpcarbon->FromArea);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (stricmp(buf2, "TO") == 0)
        tmpcarbon->CCType = CC_TO;
      else if (stricmp(buf2, "FROM") == 0)
        tmpcarbon->CCType = CC_FROM;
      else if (stricmp(buf2, "SUBJ") == 0)
        tmpcarbon->CCType = CC_SUBJ;
      else {
        mystrncpy(cfgerr, "Invalid carbon type", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(tmpcarbon->Pattern, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!CheckPattern(tmpcarbon->Pattern)) {
        snprintf(cfgerr, errlen, "Invalid pattern \"%s\"", tmpcarbon->Pattern);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(tmpcarbon->ToArea, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing areatag", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "AREAFILE") == 0) {
      if (!(jbstrcpy(cfg->areafile, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }
    } else if (stricmp(cfgword, "AREALIST") == 0) {
      if (!(tmparealist = (struct Arealist *) calloc(1, sizeof(struct Arealist)))) {
        *seconderr = READCONFIG_NO_MEM;
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      jbAddNode(&cfg->ArealistList, (struct jbNode *) tmparealist);

      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(Parse4D(buf2, &tmp4d))) {
        snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      for (tmpnode = (struct ConfigNode *) cfg->CNodeList.First; tmpnode; tmpnode = tmpnode->Next)
        if (Compare4D(&tmp4d, &tmpnode->Node) == 0)
          break;

      if (!tmpnode) {
        snprintf(cfgerr, errlen, "Unconfigured node \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      tmparealist->Node = tmpnode;

      if (!(jbstrcpy(tmparealist->AreaFile, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      while (jbstrcpy(buf2, cfgbuf, 200, &jbcpos)) {
        if (stricmp(buf2, "FORWARD") == 0) {
          tmparealist->Flags |= AREALIST_FORWARD;
        } else if (stricmp(buf2, "DESC") == 0) {
          tmparealist->Flags |= AREALIST_DESC;
        } else if (stricmp(buf2, "GROUP") == 0) {
          if (!jbstrcpy(buf2, cfgbuf, 200, &jbcpos)) {
            mystrncpy(cfgerr, "Missing argument", errlen);
            fclose(cfgfh);
            free(cfgbuf);
            return (FALSE);
          }

          if (!CorrectFlags(buf2)) {
            snprintf(cfgerr, errlen, "Invalid group \"%s\"", buf2);
            fclose(cfgfh);
            free(cfgbuf);
            return (FALSE);
          }

          tmparealist->Group = buf2[0];
        } else {
          snprintf(cfgerr, errlen, "Unknown switch \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }
    } else if (stricmp(cfgword, "REMOTEAF") == 0) {
      if (!LastCNode) {
        mystrncpy(cfgerr, "No previous NODE line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastCNode->RemoteAFName, cfgbuf, 36, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastCNode->RemoteAFPw, cfgbuf, 72, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "REMOTESYSOP") == 0) {
      if (!LastCNode) {
        mystrncpy(cfgerr, "No previous NODE line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastCNode->SysopName, cfgbuf, 36, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "STRIPRE") == 0) {
      cfg->cfg_Flags |= CFG_STRIPRE;
    } else if(stricmp(cfgword,"FORCEINTL")==0) {
      cfg->cfg_Flags|=CFG_FORCEINTL;
    } else if (stricmp(cfgword, "REMOVEWHENFEED") == 0) {
      cfg->cfg_Flags |= CFG_REMOVEWHENFEED;
    } else if (stricmp(cfgword, "INCLUDEFORWARD") == 0) {
      cfg->cfg_Flags |= CFG_INCLUDEFORWARD;
    } else if (stricmp(cfgword, "NOMAXOUTBOUNDZONE") == 0) {
      cfg->cfg_Flags |= CFG_NOMAXOUTBOUNDZONE;
    } else if (stricmp(cfgword, "USEASO") == 0) {
      cfg->cfg_Flags |= CFG_USEASO;
    } else if (stricmp(cfgword, "ALLOWKILLSENT") == 0) {
      cfg->cfg_Flags |= CFG_ALLOWKILLSENT;
    } else if (stricmp(cfgword, "CHECKSEENBY") == 0) {
      cfg->cfg_Flags |= CFG_CHECKSEENBY;
    } else if (stricmp(cfgword, "CHECKPKTDEST") == 0) {
      cfg->cfg_Flags |= CFG_CHECKPKTDEST;
    } else if (stricmp(cfgword, "ALLOWNULLPASSWORDS") == 0) {
      cfg->cfg_Flags |= CFG_ALLOWNULLPASSWORDS;
    } else if (stricmp(cfgword, "KEEPAREAFIX") == 0) {
      cfg->cfg_Flags |= CFG_KEEPAREAFIX;
    } else if (stricmp(cfgword, "KEEPRECEIPT") == 0) {
      cfg->cfg_Flags |= CFG_KEEPRECEIPT;
    } else if (stricmp(cfgword, "AREAFIXREMOVE") == 0) {
      cfg->cfg_Flags |= CFG_AREAFIXREMOVE;
    } else if (stricmp(cfgword, "WEEKDAYNAMING") == 0) {
      cfg->cfg_Flags |= CFG_WEEKDAYNAMING;
    } else if (stricmp(cfgword, "ADDTID") == 0) {
      cfg->cfg_Flags |= CFG_ADDTID;
    } else if (stricmp(cfgword, "ALLOWRESCAN") == 0) {
      cfg->cfg_Flags |= CFG_ALLOWRESCAN;
    } else if (stricmp(cfgword, "FORWARDPASSTHRU") == 0) {
      cfg->cfg_Flags |= CFG_FORWARDPASSTHRU;
    } else if (stricmp(cfgword, "LOCKBYLINK") == 0) {
      cfg->cfg_Flags |= CFG_LOCKBYLINK;
    } else if (stricmp(cfgword, "CCONSCAN") == 0) {
      cfg->cfg_Flags |= CFG_CCONSCAN;
    } else if (stricmp(cfgword, "FORWARDTIMEOUT") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_FwdTimeout = atoi(buf2);
    } else if (stricmp(cfgword, "REMOVETIMEOUT") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_RemoveTimeout = atoi(buf2);
    } else if (stricmp(cfgword, "MSGBASEUMASK") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (sscanf(buf2, "%o", &cfg->cfg_MsgbaseUmask) != 1) {
        mystrncpy(cfgerr, "Invalid umask value", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }
    } else if (stricmp(cfgword, "MAXPKTSIZE") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_MaxPktSize = atoi(buf2) * 1024;
    } else if (stricmp(cfgword, "MAXBUNDLESIZE") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_MaxBundleSize = atoi(buf2) * 1024;
    } else if (stricmp(cfgword, "DEFAULTZONE") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_DefaultZone = atoi(buf2);
    } else if (stricmp(cfgword, "AREAFIXMAXLINES") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_AreaFixMaxLines = atoi(buf2);
    }

    /*************************** MSG ******************************/
    else if (stricmp(cfgword, "MSG_HIGHWATER") == 0) {
      cfg->cfg_msg_Flags |= CFG_MSG_HIGHWATER;
/*    } else if (stricmp(cfgword, "MSG_WRITEBACK") == 0) {*/
/*      cfg->cfg_msg_Flags |= CFG_MSG_WRITEBACK;*/
    }
    /*************************** JAM ******************************/
#ifdef MSGBASE_JAM
    else if (stricmp(cfgword, "JAM_FLAGSDIR") == 0) {
      if (!(jbstrcpy(cfg->cfg_jam_FlagsDir, cfgbuf, 100, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "JAM_HIGHWATER") == 0) {
      cfg->cfg_jam_Flags |= CFG_JAM_HIGHWATER;
    } else if (stricmp(cfgword, "JAM_HARDDELETE") == 0) {
      cfg->cfg_jam_Flags |= CFG_JAM_HARDDELETE;
    } else if (stricmp(cfgword, "JAM_QUICKLINK") == 0) {
      /* Do nothing - checked in CrashMaint */
    } else if (stricmp(cfgword, "JAM_MAXOPEN") == 0) {
      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->cfg_jam_MaxOpen = atoi(buf2);
    }
#endif
    /*************************** Unknown *****************************/
    else {
      snprintf(cfgerr, errlen, "Unknown keyword \"%s\"", cfgword);
      fclose(cfgfh);
      free(cfgbuf);
      return (FALSE);
    }
  }

  fclose(cfgfh);
  free(cfgbuf);

  return (TRUE);
}

bool ReadAreas(uchar * filename, struct Config * cfg, short *seconderr, ulong * cfgline, uchar * cfgerr, size_t errlen)
{
  uchar buf2[200], cfgword[30];
  ulong c, jbcpos;
  FILE *cfgfh;

  struct Aka *tmpaka;
  struct ConfigNode *tmpnode;
  struct Area *tmparea, *LastArea = NULL;

  struct TossNode *tmptnode;
  struct BannedNode *tmpbnode;
  struct Node4D tmp4d;

  ushort flags;

  *cfgline = 0;

  if (!(cfgbuf = malloc(4000))) {
    *seconderr = READCONFIG_NO_MEM;
    return (FALSE);
  }

  if (!(cfgfh = fopen(filename, "rt"))) {
    *seconderr = READCONFIG_NOT_FOUND;
    free(cfgbuf);
    return (FALSE);
  }

  *seconderr = READCONFIG_INVALID;

  while (fgets(cfgbuf, 4000, cfgfh)) {
    jbcpos = 0;
    (*cfgline)++;

    jbstrcpy(cfgword, cfgbuf, 30, &jbcpos);

    if (cfgword[0] == 0 || strchr(";#", cfgword[0])) 
      continue;

    if (stricmp(cfgword, "NETMAILDIR") == 0) {
      strcpy(cfg->Netmail.Tagname, "NETMAIL");
      strcpy(cfg->Netmail.Description, "Primary netmail area");

      cfg->Netmail.Aka = (struct Aka *) cfg->AkaList.First;

      for (c = 0; AvailMessagebases[c].name; c++)
        if (stricmp(AvailMessagebases[c].name, "MSG") == 0)
          break;

      if (AvailMessagebases[c].name) {
        cfg->Netmail.Messagebase = &AvailMessagebases[c];
      } else {
        snprintf(cfgerr, errlen, "*.MSG format not supported");
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(cfg->Netmail.Path, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      cfg->Netmail.AreaType = AREATYPE_NETMAIL;

    } else if (stricmp(cfgword, "AREA") == 0 ||
        stricmp(cfgword, "NETMAIL") == 0 ||
        stricmp(cfgword, "LOCALAREA") == 0) {
      if (!(tmparea = (struct Area *) calloc(1, sizeof(struct Area)))) {
        *seconderr = READCONFIG_NO_MEM;
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

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

      jbAddNode(&cfg->AreaList, (struct jbNode *) tmparea);
      LastArea = tmparea;

      if (!(jbstrcpy(LastArea->Tagname, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!stricmp(LastArea->Tagname, "NETMAIL")) {
        mystrncpy(cfgerr, "NETMAIL areatag reserved for NETMAILDIR", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 200, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(Parse4D(buf2, &tmp4d))) {
        snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      for (tmpaka = (struct Aka *) cfg->AkaList.First; tmpaka;
          tmpaka = tmpaka->Next)
        if (Compare4D(&tmp4d, &tmpaka->Node) == 0)
          break;

      if (!tmpaka) {
        snprintf(cfgerr, errlen, "Unknown AKA \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Aka = tmpaka;

      if (jbstrcpy(buf2, cfgbuf, 200, &jbcpos)) {
        int c;

        for (c = 0; AvailMessagebases[c].name; c++)
          if (stricmp(buf2, AvailMessagebases[c].name) == 0)
            break;

        if (AvailMessagebases[c].name) {
          LastArea->Messagebase = &AvailMessagebases[c];
        } else {
          snprintf(cfgerr, errlen, "Unknown messagebase format \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        if (!(jbstrcpy(LastArea->Path, cfgbuf, 200, &jbcpos))) {
          mystrncpy(cfgerr, "Missing argument", errlen);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }
      }

      if (stricmp(cfgword, "NETMAIL") == 0) {
        if (!LastArea->Messagebase) {
          snprintf(cfgerr, errlen, "Netmail area cannot be pass-through");
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        LastArea->AreaType = AREATYPE_NETMAIL;
      } else if (stricmp(cfgword, "LOCALAREA") == 0) {
        if (!LastArea->Messagebase) {
          snprintf(cfgerr, errlen, "Local area cannot be pass-through");
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        LastArea->AreaType = AREATYPE_LOCAL;
      } else if (stricmp(LastArea->Tagname, "BAD") == 0) {
        if (LastArea->Path[0] == 0) {
          snprintf(cfgerr, errlen, "BAD area cannot be pass-through");
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        LastArea->AreaType = AREATYPE_BAD;
      } else if (stricmp(LastArea->Tagname, "DEFAULT") == 0 ||
          strnicmp(LastArea->Tagname, "DEFAULT_", 8) == 0) {
        LastArea->AreaType = AREATYPE_DEFAULT;
      } else {
        LastArea->AreaType = AREATYPE_ECHOMAIL;
      }
    } else if (stricmp(cfgword, "UNCONFIRMED") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Flags |= AREA_UNCONFIRMED;
    } else if (stricmp(cfgword, "MANDATORY") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Flags |= AREA_MANDATORY;
    } else if (stricmp(cfgword, "DEFREADONLY") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Flags |= AREA_DEFREADONLY;
    } else if (stricmp(cfgword, "IGNOREDUPES") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Flags |= AREA_IGNOREDUPES;
    } else if (stricmp(cfgword, "IGNORESEENBY") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Flags |= AREA_IGNORESEENBY;
    } else if (stricmp(cfgword, "IMPORTSEENBY") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Flags |= AREA_IMPORTSEENBY;
    } else if (stricmp(cfgword, "EXPORT") == 0) {
      struct Node4D tpl4d;

      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (LastArea->AreaType != AREATYPE_ECHOMAIL &&
          LastArea->AreaType != AREATYPE_DEFAULT) {
        mystrncpy(cfgerr, "Not an echomail or default area", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

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

      while (jbstrcpy(buf2, cfgbuf, 100, &jbcpos)) {
        flags = 0;

        if (buf2[0] == '!') {
          flags = TOSSNODE_READONLY;
          strcpy(buf2, &buf2[1]);
        }

        if (buf2[0] == '@') {
          flags = TOSSNODE_WRITEONLY;
          strcpy(buf2, &buf2[1]);
        }

        if (buf2[0] == '%') {
          flags = TOSSNODE_FEED;
          strcpy(buf2, &buf2[1]);
        }

        if (!(Parse4DTemplate(buf2, &tmp4d, &tpl4d))) {
          snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        Copy4D(&tpl4d, &tmp4d);
        tpl4d.Point = 0;

        for (tmpnode = (struct ConfigNode *) cfg->CNodeList.First; tmpnode;
            tmpnode = tmpnode->Next)
          if (Compare4D(&tmp4d, &tmpnode->Node) == 0)
            break;

        if (!tmpnode) {
          snprintf(cfgerr, errlen, "Unconfigured node \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        if (!(tmptnode = (struct TossNode *) calloc(1, sizeof(struct TossNode)))) {
          *seconderr = READCONFIG_NO_MEM;
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        jbAddNode(&LastArea->TossNodes, (struct jbNode *) tmptnode);
        tmptnode->ConfigNode = tmpnode;
        tmptnode->Flags = flags;
      }
    } else if (stricmp(cfgword, "BANNED") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      while (jbstrcpy(buf2, cfgbuf, 100, &jbcpos)) {
        if (!(Parse4D(buf2, &tmp4d))) {
          snprintf(cfgerr, errlen, "Invalid node number \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        for (tmpnode = (struct ConfigNode *) cfg->CNodeList.First; tmpnode;
            tmpnode = tmpnode->Next)
          if (Compare4D(&tmp4d, &tmpnode->Node) == 0)
            break;

        if (!tmpnode) {
          snprintf(cfgerr, errlen, "Unconfigured node \"%s\"", buf2);
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        if (!(tmpbnode = (struct BannedNode *) calloc(1, sizeof(struct BannedNode)))) {
          *seconderr = READCONFIG_NO_MEM;
          fclose(cfgfh);
          free(cfgbuf);
          return (FALSE);
        }

        jbAddNode(&LastArea->BannedNodes, (struct jbNode *) tmpbnode);
        tmpbnode->ConfigNode = tmpnode;
      }
    } else if (stricmp(cfgword, "DESCRIPTION") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(LastArea->Description, cfgbuf, 80, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

    } else if (stricmp(cfgword, "GROUP") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 30, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!CorrectFlags(buf2)) {
        snprintf(cfgerr, errlen, "Invalid group \"%s\"", buf2);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Group = buf2[0];
    } else if (stricmp(cfgword, "KEEPNUM") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 30, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->KeepNum = atol(buf2);
    } else if (stricmp(cfgword, "KEEPDAYS") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 30, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->KeepDays = atol(buf2);
    } else if (stricmp(cfgword, "EXPIRES") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 30, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Expires = atol(buf2);
    } else if (stricmp(cfgword, "REMOVED") == 0) {
      if (!LastArea) {
        mystrncpy(cfgerr, "No previous AREA line", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      if (!(jbstrcpy(buf2, cfgbuf, 30, &jbcpos))) {
        mystrncpy(cfgerr, "Missing argument", errlen);
        fclose(cfgfh);
        free(cfgbuf);
        return (FALSE);
      }

      LastArea->Removed = atol(buf2);
      LastArea->AreaType = AREATYPE_DELETED;
    } else {
      snprintf(cfgerr, errlen, "Unknown keyword \"%s\"", cfgword);
      fclose(cfgfh);
      free(cfgbuf);
      return (FALSE);
    }
  }

  fclose(cfgfh);
  free(cfgbuf);

  return (TRUE);
}

bool CheckConfig(struct Config * cfg, uchar * cfgerr, size_t errlen)
{
  struct Area *a1, *a2;

  if (cfg->cfg_ProtInbound[0] == 0) {
    snprintf(cfgerr, errlen, "Protected inbound directory not defined");
    return (FALSE);
  } else if (cfg->cfg_Inbound[0] == 0) {
    snprintf(cfgerr, errlen, "Unprotected inbound directory not defined");
    return (FALSE);
  } else if (cfg->cfg_LocalInbound[0] == 0) {
    snprintf(cfgerr, errlen, "Local inbound directory not defined");
    return (FALSE);
  } else if (cfg->cfg_TempInbound[0] == 0) {
    snprintf(cfgerr, errlen, "Temporary inbound directory not defined");
    return (FALSE);
  } else if (cfg->cfg_LogFile[0] == 0) {
    snprintf(cfgerr, errlen, "No logfile defined");
    return (FALSE);
  }

  if (cfg->cfg_PacketCreate[0] == 0)
    strcpy(cfg->cfg_PacketCreate, cfg->cfg_Outbound);
  if (cfg->cfg_PacketDir[0] == 0)
    strcpy(cfg->cfg_PacketDir, cfg->cfg_Outbound);

  if (!cfg->AkaList.First) {
    snprintf(cfgerr, errlen, "No AKAs configured");
    return (FALSE);
  }

  if (cfg->Netmail.Path[0] == 0) {
    snprintf(cfgerr, errlen, "Primary netmail area not defined");
    return (FALSE);
  }

  if (cfg->cfg_DefaultZone == 0) {
    snprintf(cfgerr, errlen, "No default zone configured");
    return (FALSE);
  }

  /* Check for areas with same path */
  for (a1 = (struct Area *) cfg->AreaList.First; a1; a1 = a1->Next) {
    if (a1->AreaType != AREATYPE_DEFAULT) {
      for (a2 = a1->Next; a2; a2 = a2->Next)
        if (stricmp(a1->Path, a2->Path) == 0 && a1->Messagebase && a2->Messagebase) {
          snprintf(cfgerr, errlen, "The areas %s and %s both have the same path", a1->Tagname, a2->Tagname);
          return (FALSE);
        }
    }
  }

  return (TRUE);
}

void InitConfig(struct Config *cfg)
{
  memset(cfg, 0, sizeof(struct Config));

  strcpy(cfg->cfg_Sysop, "Sysop");

  cfg->cfg_LogLevel = 3;
  cfg->cfg_DupeSize = 200;

  cfg->Netmail.Path[0] = 0;

  cfg->cfg_MsgbaseUmask = umask(0777);
  umask(cfg->cfg_MsgbaseUmask);

  jbNewList(&cfg->AkaList);
  jbNewList(&cfg->AreaList);
  jbNewList(&cfg->CNodeList);
  jbNewList(&cfg->PackerList);
  jbNewList(&cfg->CarbonList);
  jbNewList(&cfg->AreaFixList);
  jbNewList(&cfg->ArealistList);
}

void WriteSafely(FILE *fh, uchar * str)
{
  uchar buf[300];
  ushort c, d;

  d = 0;

  for (c = 0; str[c]; c++) {
    if (str[c] == '"' || str[c] == '\\')
      buf[d++] = '\\';

    buf[d++] = str[c];
  }

  buf[d] = 0;

  fprintf(fh, "\"%s\"", buf);
}

void WriteNode4D(FILE *fh, struct Node4D *n4d)
{
  fprintf(fh, "%u:%u/%u.%u", n4d->Zone, n4d->Net, n4d->Node, n4d->Point);
}

uchar *nodekeywords[] = { "DEFAULTGROUP", "REMOTESYSOP", "REMOTEAF",
  "AREAFIXINFO", "NODE", NULL
};

uchar *areakeywords[] = { "IGNORESEENBY", "IMPORTSEENBY", "IGNOREDUPES",
  "DEFREADONLY", "MANDATORY", "UNCONFIRMED", "KEEPNUM", "KEEPDAYS",
  "GROUP", "DESCRIPTION", "BANNED", "EXPORT", "IMPORT", "EXPIRES",
  "REMOVED", "AREA", "NETMAIL", NULL
};

void WriteNode(struct ConfigNode *tmpnode, FILE *osfh)
{
  fprintf(osfh, "NODE ");
  WriteNode4D(osfh, &tmpnode->Node);
  fprintf(osfh, " ");
  WriteNode4D(osfh, &tmpnode->Aka->Node);
  fprintf(osfh, " ");

  if (tmpnode->Packer)
    WriteSafely(osfh, tmpnode->Packer->Name);
  else
    WriteSafely(osfh, "NONE");

  fprintf(osfh, " ");
  WriteSafely(osfh, tmpnode->PacketPW);

  if (tmpnode->Flags & NODE_PASSIVE)
    fprintf(osfh, " PASSIVE");

  if (tmpnode->Flags & NODE_NOTIFY)
    fprintf(osfh, " NOTIFY");

  if (tmpnode->Flags & NODE_NOSEENBY)
    fprintf(osfh, " NOSEENBY");

  if (tmpnode->Flags & NODE_TINYSEENBY)
    fprintf(osfh, " TINYSEENBY");

  if (tmpnode->Flags & NODE_FORWARDREQ)
    fprintf(osfh, " FORWARDREQ");

  if (tmpnode->Flags & NODE_SENDAREAFIX)
    fprintf(osfh, " SENDAREAFIX");

  if (tmpnode->Flags & NODE_SENDTEXT)
    fprintf(osfh, " SENDTEXT");

  if (tmpnode->Flags & NODE_AUTOADD)
    fprintf(osfh, " AUTOADD");

  if (tmpnode->EchomailPri == PKTS_CRASH)
    fprintf(osfh, " CRASH");

  if (tmpnode->EchomailPri == PKTS_DIRECT)
    fprintf(osfh, " DIRECT");

  if (tmpnode->EchomailPri == PKTS_HOLD)
    fprintf(osfh, " HOLD");

  fprintf(osfh, "\n");

  if (tmpnode->AreafixPW[0] || tmpnode->Groups[0] || tmpnode->ReadOnlyGroups[0] || tmpnode->AddGroups[0]) {
    fprintf(osfh, "AREAFIXINFO ");
    WriteSafely(osfh, tmpnode->AreafixPW);
    fprintf(osfh, " ");

    WriteSafely(osfh, tmpnode->Groups);
    fprintf(osfh, " ");

    WriteSafely(osfh, tmpnode->ReadOnlyGroups);
    fprintf(osfh, " ");

    WriteSafely(osfh, tmpnode->AddGroups);
    fprintf(osfh, " ");

    fprintf(osfh, "\n");
  }

  if (tmpnode->RemoteAFName[0]) {
    fprintf(osfh, "REMOTEAF ");
    WriteSafely(osfh, tmpnode->RemoteAFName);
    fprintf(osfh, " ");
    WriteSafely(osfh, tmpnode->RemoteAFPw);
    fprintf(osfh, "\n");
  }

  if (tmpnode->SysopName[0]) {
    fprintf(osfh, "REMOTESYSOP ");
    WriteSafely(osfh, tmpnode->SysopName);
    fprintf(osfh, "\n");
  }

  if (tmpnode->DefaultGroup)
    fprintf(osfh, "DEFAULTGROUP %c\n", tmpnode->DefaultGroup);
}

void WriteArea(struct Area *tmparea, FILE *osfh)
{
  struct ImportNode *tmpinode;
  struct TossNode *tmptnode;
  struct BannedNode *tmpbnode;
  ulong c;

  if (tmparea->AreaType == AREATYPE_NETMAIL)
    fprintf(osfh, "NETMAIL ");

  if (tmparea->AreaType == AREATYPE_LOCAL)
    fprintf(osfh, "LOCALAREA ");

  else
    fprintf(osfh, "AREA ");

  WriteSafely(osfh, tmparea->Tagname);
  fprintf(osfh, " ");
  WriteNode4D(osfh, &tmparea->Aka->Node);

  if (tmparea->Messagebase) {
    fprintf(osfh, " %s ", tmparea->Messagebase->name);
    WriteSafely(osfh, tmparea->Path);
    fprintf(osfh, "\n");
  } else {
    fprintf(osfh, "\n");
  }

  if (tmparea->AreaType == AREATYPE_NETMAIL) {
    c = 0;

    for (tmpinode = (struct ImportNode *) tmparea->TossNodes.First; tmpinode; tmpinode = tmpinode->Next) {
      if (c % 10 == 0) {
        if (c != 0)
          fprintf(osfh, "\n");
        fprintf(osfh, "IMPORT");
      }

      fprintf(osfh, " ");
      WriteNode4D(osfh, &tmpinode->Node);
      c++;
    }
    if (c != 0)
      fprintf(osfh, "\n");
  } else if (tmparea->AreaType == AREATYPE_ECHOMAIL || tmparea->AreaType == AREATYPE_DEFAULT) {
    c = 0;

    for (tmptnode = (struct TossNode *) tmparea->TossNodes.First; tmptnode; tmptnode = tmptnode->Next) {
      if (c % 10 == 0) {
        if (c != 0)
          fprintf(osfh, "\n");
        fprintf(osfh, "EXPORT");
      }

      fprintf(osfh, " ");

      if (tmptnode->Flags & TOSSNODE_READONLY)
        fprintf(osfh, "!");

      if (tmptnode->Flags & TOSSNODE_WRITEONLY)
        fprintf(osfh, "@");

      if (tmptnode->Flags & TOSSNODE_FEED)
        fprintf(osfh, "%%");

      WriteNode4D(osfh, &tmptnode->ConfigNode->Node);
      c++;
    }
    if (c != 0)
      fprintf(osfh, "\n");
  }

  c = 0;
  for (tmpbnode = (struct BannedNode *) tmparea->BannedNodes.First; tmpbnode; tmpbnode = tmpbnode->Next) {
    if (c % 10 == 0) {
      if (c != 0)
        fprintf(osfh, "\n");
      fprintf(osfh, "BANNED");
    }

    fprintf(osfh, " ");
    WriteNode4D(osfh, &tmpbnode->ConfigNode->Node);
    c++;
  }
  if (c != 0)
    fprintf(osfh, "\n");

  if (tmparea->Description[0] != 0) {
    fprintf(osfh, "DESCRIPTION ");
    WriteSafely(osfh, tmparea->Description);
    fprintf(osfh, "\n");
  }

  if (tmparea->Group)
    fprintf(osfh, "GROUP %c\n", tmparea->Group);

  if (tmparea->KeepNum)
    fprintf(osfh, "KEEPNUM %lu\n", tmparea->KeepNum);

  if (tmparea->KeepDays)
    fprintf(osfh, "KEEPDAYS %lu\n", tmparea->KeepDays);

  if (tmparea->Expires)
    fprintf(osfh, "EXPIRES %lu\n", tmparea->Expires);

  if (tmparea->Removed)
    fprintf(osfh, "REMOVED %lu\n", tmparea->Removed);

  if (tmparea->Flags & AREA_UNCONFIRMED)
    fprintf(osfh, "UNCONFIRMED\n");

  if (tmparea->Flags & AREA_MANDATORY)
    fprintf(osfh, "MANDATORY\n");

  if (tmparea->Flags & AREA_DEFREADONLY)
    fprintf(osfh, "DEFREADONLY\n");

  if (tmparea->Flags & AREA_IGNOREDUPES)
    fprintf(osfh, "IGNOREDUPES\n");

  if (tmparea->Flags & AREA_IGNORESEENBY)
    fprintf(osfh, "IGNORESEENBY\n");

  if (tmparea->Flags & AREA_IMPORTSEENBY)
    fprintf(osfh, "IMPORTSEENBY\n");
}

bool UpdateConfig(struct Config *cfg, uchar * cfgerr, size_t errlen)
{
  uchar cfgtemp[110], cfgbak[110];
  uchar cfgword[30], buf[100];
  FILE *oldfh, *newfh;
  bool skipnode, dontwrite, copyres;
  struct ConfigNode *cnode;
  struct Node4D n4d;
  ulong jbcpos, c;
  struct stat cfgstat;

  snprintf(cfgtemp, 110, "%s.tmp", cfg->filename);
  snprintf(cfgbak, 110, "%s.bak", cfg->filename);

  if (stat(cfg->filename, &cfgstat) != 0) {
    snprintf(cfgerr, errlen, "Can't stat file %s (error: %s)", cfg->filename, strerror(errno));
    return (FALSE);
  }

  if (!(cfgbuf = malloc(4000))) {
    snprintf(cfgerr, errlen, "Out of memory");
    return (FALSE);
  }

  if (!(oldfh = fopen(cfg->filename, "rt"))) {
    snprintf(cfgerr, errlen, "Unable to read file %s (error: %s)", cfg->filename, strerror(errno));
    free(cfgbuf);
    return (FALSE);
  }

  if (!(newfh = fopen(cfgtemp, "wt"))) {
    snprintf(cfgerr, errlen, "Unable to write to config file %s (error: %s)", cfgtemp, strerror(errno));
    fclose(oldfh);
    free(cfgbuf);
    return (FALSE);
  }

  skipnode = FALSE;

  while (fgets(cfgbuf, 4000, oldfh)) {
    jbcpos = 0;
    copyres = jbstrcpy(cfgword, cfgbuf, 30, &jbcpos);

    if (stricmp(cfgword, "NODE") == 0) {
      skipnode = FALSE;

      if (jbstrcpy(buf, cfgbuf, 100, &jbcpos)) {
        if (Parse4D(buf, &n4d)) {
          for (cnode = (struct ConfigNode *) cfg->CNodeList.First; cnode; cnode = cnode->Next)
            if (Compare4D(&n4d, &cnode->Node) == 0)
              break;

          if (cnode && cnode->changed) {
            skipnode = TRUE;
            WriteNode(cnode, newfh);
            fprintf(newfh, "\n");
            cnode->changed = FALSE;
          }
        }
      }
    }

    dontwrite = FALSE;

    if (skipnode) {
      for (c = 0; nodekeywords[c]; c++)
        if (stricmp(cfgword, nodekeywords[c]) == 0)
          dontwrite = TRUE;

      if (!copyres)
        dontwrite = TRUE;
    }

    if (!dontwrite)
      fputs(cfgbuf, newfh);
  }

  fclose(oldfh);
  fclose(newfh);
  free(cfgbuf);

  unlink(cfgbak);
  rename(cfg->filename, cfgbak);
  rename(cfgtemp, cfg->filename);

  /* Restore attributes */
/*  chown(cfg->filename, cfgstat.st_uid, cfgstat.st_gid);*/
  chmod(cfg->filename, cfgstat.st_mode);

  cfg->changed = FALSE;

  return (TRUE);
}

bool UpdateAreas(struct Config *cfg, uchar * cfgerr, size_t errlen)
{
  uchar cfgtemp[110], cfgbak[110];
  uchar cfgword[30], buf[100];
  FILE *oldfh, *newfh;
  bool skiparea, dontwrite, copyres;
  struct Area *area;
  ulong jbcpos, c;
  struct stat cfgstat;

  snprintf(cfgtemp, 110, "%s.tmp", cfg->areafile);
  snprintf(cfgbak, 110, "%s.bak", cfg->areafile);

  if (stat(cfg->areafile, &cfgstat) != 0) {
    snprintf(cfgerr, errlen, "Can't stat file %s (error: %s)", cfg->areafile, strerror(errno));
    return (FALSE);
  }

  if (!(cfgbuf = malloc(4000))) {
    snprintf(cfgerr, errlen, "Out of memory");
    return (FALSE);
  }

  if (!(oldfh = fopen(cfg->areafile, "rt"))) {
    snprintf(cfgerr, errlen, "Unable to read file %s (error: %s)", cfg->areafile, strerror(errno));
    free(cfgbuf);
    return (FALSE);
  }

  if (!(newfh = fopen(cfgtemp, "wt"))) {
    snprintf(cfgerr, errlen, "Unable to write to config file %s (error: %s)", cfgtemp, strerror(errno));
    fclose(oldfh);
    free(cfgbuf);
    return (FALSE);
  }

  skiparea = FALSE;

  while (fgets(cfgbuf, 4000, oldfh)) {
    jbcpos = 0;
    copyres = jbstrcpy(cfgword, cfgbuf, 30, &jbcpos);

    if (stricmp(cfgword, "AREA") == 0 || stricmp(cfgword, "NETMAIL") == 0) {
      skiparea = FALSE;

      if (jbstrcpy(buf, cfgbuf, 100, &jbcpos)) {
        for (area = (struct Area *) cfg->AreaList.First; area; area = area->Next)
          if (stricmp(buf, area->Tagname) == 0)
            break;

        /* Area has been changed */
        if (area && area->changed) {
          skiparea = TRUE;
          WriteArea(area, newfh);
          fprintf(newfh, "\n");
          area->changed = FALSE;
        }

        /* Area has been removed */
        if (!area) {
          skiparea = TRUE;
        }
      }
    }

    dontwrite = FALSE;

    if (skiparea) {
      for (c = 0; areakeywords[c]; c++)
        if (stricmp(cfgword, areakeywords[c]) == 0)
          dontwrite = TRUE;

      if (!copyres)
        dontwrite = TRUE;
    }

    if (!dontwrite)
      fputs(cfgbuf, newfh);
  }

  for (area = (struct Area *) cfg->AreaList.First; area; area = area->Next)
    if (area->changed) {
      fprintf(newfh, "\n");
      WriteArea(area, newfh);
      area->changed = FALSE;
    }

  fclose(oldfh);
  fclose(newfh);
  free(cfgbuf);

  unlink(cfgbak);
  rename(cfg->areafile, cfgbak);
  rename(cfgtemp, cfg->areafile);

  /* Restore attributes */
/*  chown(cfg->areafile, cfgstat.st_uid, cfgstat.st_gid);*/
  chmod(cfg->areafile, cfgstat.st_mode);

  cfg->changed = FALSE;

  return (TRUE);
}

void FreeConfig(struct Config *cfg)
{
  struct Area *area;
  struct Aka *aka;
  struct ConfigNode *cnode;

  /* Config */
  for (area = (struct Area *) cfg->AreaList.First; area; area = area->Next) {
    jbFreeList(&area->TossNodes);
    jbFreeList(&area->BannedNodes);
  }

  for (aka = (struct Aka *) cfg->AkaList.First; aka; aka = aka->Next) {
    jbFreeList(&aka->AddList);
    jbFreeList(&aka->RemList);
  }

  for (cnode = (struct ConfigNode *) cfg->CNodeList.First; cnode;
      cnode = cnode->Next)
    jbFreeList(&cnode->RemoteAFList);

  jbFreeList(&cfg->AkaList);
  jbFreeList(&cfg->AreaList);
  jbFreeList(&cfg->CNodeList);
  jbFreeList(&cfg->CarbonList);
  jbFreeList(&cfg->PackerList);
  jbFreeList(&cfg->AreaFixList);
  jbFreeList(&cfg->ArealistList);
}


syntax highlighted by Code2HTML, v. 0.9.1