/********************************************************
 * File: crashecho.c
 * Created at Sun Jan 28 22:10:29 MSK 2001 by raorn // raorn@binec.ru
 * The CrashEcho itself
 * $Id: crashecho.c,v 1.26 2002/10/27 22:47:52 raorn Exp $
 *******************************************************/
#include "crashecho.h"
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#else
# include <shared/getopt.h>
#endif

/* Version string to use when writing config */
uchar verstr[] = "CrashEcho/" PLATFORM_NAME " " VERSION;

/*********************************** Global *******************************/

struct jbList PktList;

bool nomem = FALSE;
bool ioerror = FALSE;
bool exitprg = FALSE;

ulong ioerrornum = 0;

ulong toss_total = 0;
ulong toss_bad = 0;
ulong toss_import = 0;
ulong toss_written = 0;
ulong toss_dupes = 0;
ulong toss_cc = 0;

ulong scan_total = 0;
ulong rescan_total = 0;

int nowdoing = DOING_NONE;

struct ConfigNode *RescanNode;

ulong DayStatsWritten;          /* The area statistics are updated until
                                 * this day */

struct Config config;

bool ctrlc;

uchar *prinames[] = { "Normal", "Hold", "Normal", "Direct", "Crash" };

/**************************** Local for this file ****************************/

#define SHOWVER   128

bool init_openlog;
bool init_dupebuf;

void Free(void)
{
  if (init_dupebuf) {
    FreeDupebuf();
    init_dupebuf = 0;
  }

  if (init_openlog) {
    CloseLogfile();
    init_openlog = FALSE;
  }

  jbFreeList(&PktList);
}

bool Init(void)
{
  struct Area *area;

  if (!OpenLogfile(config.cfg_LogFile, config.cfg_LogLevel)) {
    Free();
    return (FALSE);
  }

  init_openlog = TRUE;

  if (config.cfg_DupeMode != DUPE_IGNORE) {
    if (!AllocDupebuf()) {
      fprintf(stderr, "No memory for dupe-check buffer\n");
      Free();
      return (FALSE);
    }

    init_dupebuf = TRUE;

    if (!ReadDupes(config.cfg_DupeFile)) {
      Free();
      return (FALSE);
    }
  }

  if (!ReadStats(config.cfg_StatsFile))
    return (FALSE);

  for (area = (struct Area *) config.AreaList.First; area; area = area->Next)
    if (area->Messagebase)
      area->Messagebase->active = TRUE;

  return (TRUE);
}

void AfterScanToss(bool success)
{
  struct Area *area;
  struct ConfigNode *cnode;
  uchar errbuf[200];
  ulong day, d, e, i;

  LogWrite(6, SYSTEMINFO, "In AfterScanToss(%s)", success ? "TRUE" : "FALSE");

  ClosePackets();

  if (success) {
    if (config.cfg_BeforePack[0]) {
      int res, rc;

      LogWrite(2, TOSSINGINFO, "Executing BEFOREPACK script %s", config.cfg_BeforePack);
      res = Execute(config.cfg_BeforePack, &rc);
      if (!res)
        LogWrite(3, TOSSINGINFO, "BEFOREPACK script exited with rc = %d", rc);
      else
        LogWrite(3, TOSSINGINFO, "BEFOREPACK script failed");
    }

    ArchiveOutbound();
  }

  for (i = 0; AvailMessagebases[i].name; i++)
    if (AvailMessagebases[i].active && AvailMessagebases[i].afterfunc)
      (*AvailMessagebases[i].afterfunc) (success);

  if (success) {
    /* Rotate last8 if needed */
    if (DayStatsWritten == 0)   /* First time we use this statsfile */
      DayStatsWritten = time(NULL) / (24 * 60 * 60);

    day = time(NULL) / (24 * 60 * 60);

    for (area = (struct Area *) config.AreaList.First; area; area = area->Next)
      if (day > DayStatsWritten) {
        for (d = DayStatsWritten; d < day; d++) {
          for (e = 0; e < 7; e++)
            area->Last8Days[7 - e] = area->Last8Days[7 - e - 1];

          area->Last8Days[0] = 0;
        }
      }

    DayStatsWritten = day;

    /* Areas */
    for (area = (struct Area *) config.AreaList.First; area; area = area->Next)
      if (area->NewTexts || area->NewDupes) {
        area->Texts += area->NewTexts;
        area->Last8Days[0] += area->NewTexts;
        area->Dupes += area->NewDupes;

        if (area->NewTexts)
          area->LastTime = time(NULL);

        if (area->NewTexts && area->FirstTime == 0)
          area->FirstTime = time(NULL);
      }

    for (cnode = (struct ConfigNode *) config.CNodeList.First; cnode; cnode = cnode->Next) {
      if (cnode->FirstTime == 0 && (cnode->GotEchomails != 0 || cnode->GotNetmails != 0 || cnode->SentEchomails != 0 || cnode->SentNetmails) != 0)
        cnode->FirstTime = time(NULL);
    }

    WriteStats(config.cfg_StatsFile);

    if (config.cfg_DupeMode != DUPE_IGNORE)
      WriteDupes(config.cfg_DupeFile);

    if (config.changed) {
      LogWrite(2, SYSTEMINFO, "Updating configuration file \"%s\"", config.filename);

      if (!UpdateConfig(&config, errbuf, 200))
        LogWrite(1, SYSTEMERR, errbuf);

      LogWrite(2, SYSTEMINFO, "Updating areas file \"%s\"", config.areafile);

      if (!UpdateAreas(&config, errbuf, 200))
        LogWrite(1, SYSTEMERR, errbuf);
    }
  }

  jbFreeList(&PktList);
}

bool BeforeScanToss(void)
{
  struct Area *area;
  struct jbList NewPktFEList;
  struct FileEntry *fe;
  uchar buf[200];
  int i;

  toss_total = 0;
  toss_bad = 0;
  toss_import = 0;
  toss_dupes = 0;
  toss_written = 0;
  toss_cc = 0;

  scan_total = 0;

  LogWrite(6, SYSTEMINFO, "In BeforeScanToss()");

  for (area = (struct Area *) config.AreaList.First; area; area = area->Next) {
    area->NewDupes = 0;
    area->NewTexts = 0;
  }

  jbNewList(&PktList);          /* Created packets */

  /* Find orphan files */
  if (!ReadDir(config.cfg_PacketCreate, &NewPktFEList, IsNewPkt)) {
    LogWrite(1, SYSTEMERR, "Failed to read directory \"%s\"", config.cfg_PacketCreate);
    LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno));
    return (FALSE);
  }

  /* XXX */
  for (fe = (struct FileEntry *) NewPktFEList.First; fe; fe = fe->Next) {
    uchar buf2[100];
    uchar oldname[200];
    ulong jbcpos;
    struct Pkt *pkttmp;
    struct Node4D n4d;

    LogWrite(1, SYSTEMINFO, "Found orphan tempfile %s", fe->Name);
/*    MakeFullPath(config.cfg_PacketCreate, fe->Name, buf, 200);
    unlink(buf); */

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

    pkttmp->Type = PKTS_ECHOMAIL;

    mystrncpy(buf, fe->Name, 200);

    for (i = 0; buf[i]; i++)
      if (buf[i] == '_')
        buf[i] = ' ';

    jbcpos = 0;

    /* Get hexnum */
    jbstrcpy(buf2, buf, 100, &jbcpos);
    sscanf(buf2, "%lx", &pkttmp->hexnum);

    /* Get pktorig */
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Zone = atol(buf2);
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Net = atol(buf2);
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Node = atol(buf2);
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Point = atol(buf2);
    Copy4D(&pkttmp->Orig, &n4d);

    /* Get pktdest */
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Zone = atol(buf2);
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Net = atol(buf2);
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Node = atol(buf2);
    jbstrcpy(buf2, buf, 100, &jbcpos);
    n4d.Point = atol(buf2);
    Copy4D(&pkttmp->Dest, &n4d);

    snprintf(buf2, 100, "%08lx.newpkt", pkttmp->hexnum);

    MakeFullPath(config.cfg_PacketCreate, fe->Name, oldname, 200);
    MakeFullPath(config.cfg_PacketCreate, buf2, buf, 200);

    if (rename(oldname, buf)) {
      LogWrite(1, SYSTEMERR, "Failed to rename %s to %s", oldname, buf);
      LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno));
      free(pkttmp);
      return FALSE;
    }

    if (!(pkttmp->fh = fopen(buf, "r+b"))) {
      LogWrite(1, SYSTEMERR, "Unable to open packet %s", buf);
      LogWrite(1, SYSTEMERR, "Error: %s", strerror(errno));
      free(pkttmp);
      return FALSE;
    }

    fseek(pkttmp->fh, -2L, SEEK_END); /* Step 2 bytes backwards */
    pkttmp->Len = ftell(pkttmp->fh);

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

  jbFreeList(&NewPktFEList);

  for (i = 0; AvailMessagebases[i].name; i++)
    if (AvailMessagebases[i].active)
      if (AvailMessagebases[i].beforefunc)
        if (!(*AvailMessagebases[i].beforefunc) ())
          return (FALSE);

  return (TRUE);
}

void Version(void)
{
  int i;

  fprintf(stderr, "%s, compiled %s at %s\n", verstr, __DATE__, __TIME__);
  fprintf(stderr, "\n");
  fprintf(stderr, "Available messagebase formats:\n");

  for (i = 0; AvailMessagebases[i].name; i++)
    fprintf(stderr, " %-10.10s %s\n", AvailMessagebases[i].name, AvailMessagebases[i].desc);
  fprintf(stderr, "\n");
  fprintf(stderr, "Features:\n");
#ifdef USE_SYSLOG
  fprintf(stderr, "  syslog\n");
#endif
}

bool Rescan(uchar * areaname, uchar * node, ulong max)
{
  struct Area *area;
  struct ConfigNode *cnode;
  struct Node4D n4d;
  bool success;

  if (!Parse4DTemplate(node, &n4d, &(((struct Aka *) (config.AkaList.First))->Node))) {
    LogWrite(1, USERERR, "Invalid node number %s", node);
    return (TRUE);
  }

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

  if (!cnode) {
    LogWrite(1, USERERR, "Unknown node %u:%u/%u.%u", n4d.Zone, n4d.Net, n4d.Node, n4d.Point);
    return (TRUE);
  }

  for (area = (struct Area *) config.AreaList.First; area; area = area->Next)
    if (area->AreaType == AREATYPE_ECHOMAIL)
      if (stricmp(areaname, area->Tagname) == 0)
        break;

  if (!area) {
    LogWrite(1, USERERR, "Unknown area %s", areaname);
    return (TRUE);
  }

  if (!area->Messagebase) {
    LogWrite(1, USERERR, "Can't rescan %s, area is pass-through", areaname);
    return (TRUE);
  }

  if (!area->Messagebase->scanfunc) {
    LogWrite(1, USERERR, "Can't rescan %s", areaname);
    return (TRUE);
  }

  RescanNode = cnode;
  rescan_total = 0;
  success = (*area->Messagebase->scanfunc) (area, FALSE, FALSE, max, NULL, AcceptAny, HandleRescan);
  RescanNode = NULL;

  if (success)
    LogWrite(4, TOSSINGINFO, "Rescanned %lu messages", rescan_total);

  return (success);
}

bool SendAFList(uchar * node, short type)
{
  struct Node4D n4d;
  struct ConfigNode *cnode;

  if (stricmp(node, "ALL") == 0) {
    for (cnode = (struct ConfigNode *) config.CNodeList.First; cnode; cnode = cnode->Next)
      if (cnode->Flags & NODE_NOTIFY)
        DoSendAFList(type, cnode);
  } else {
    if (!Parse4DTemplate(node, &n4d, &(((struct Aka *) (config.AkaList.First))->Node))) {
      LogWrite(1, USERERR, "Invalid node number \"%s\"", node);
      return (FALSE);
    }

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

    if (cnode) {
      DoSendAFList(type, cnode);
    } else {
      LogWrite(1, USERERR, "Unknown node %u:%u/%u.%u", n4d.Zone, n4d.Net, n4d.Node, n4d.Point);
      return (FALSE);
    }
  }

  if (nomem || exitprg)
    return (FALSE);

  return (TRUE);
}

bool RemoveArea(uchar * areaname)
{
  struct Area *area;

  for (area = (struct Area *) config.AreaList.First; area; area = area->Next)
    if (area->AreaType != AREATYPE_NETMAIL)
      if (stricmp(areaname, area->Tagname) == 0)
        break;

  if (!area) {
    LogWrite(1, USERERR, "Unknown area %s", areaname);
    return (TRUE);
  }

  LogWrite(1, AREAFIX, "AreaFix: Removing area %s", area->Tagname);

  SendRemoveMessages(area);
  area->AreaType=AREATYPE_DELETED;
  RemoveDeletedAreas();

  return (TRUE);
}

bool done_initconfig = FALSE;
bool done_welcomemsg = FALSE;
bool done_init = FALSE;
bool done_lockconfig = FALSE;

bool LockConfig(uchar * file)
{
  int i;
  bool rc = FALSE;

  for (i=0; i < 6 && !(rc=Lock(file, config.cfg_Flags & CFG_LOCKBYLINK, FALSE)); i++) {
    fprintf(stderr, "Configuration file %s is already in use, waiting 10 seconds...\n", file);

#ifdef __MINGW32__
    sleep(10000);
#else
    sleep(10);
#endif

    if (ctrlc)
      return (FALSE);
  }

  return rc;
}

void CleanUp(int err)
{
  if (done_welcomemsg)
    LogWrite(2, SYSTEMINFO, "CrashEcho end");

  if (done_lockconfig)
    Unlock(config.filename);

  if (done_init)
    Free();

  if (done_initconfig)
    FreeConfig(&config);

  exit(err);
}

RETSIGTYPE breakfunc(int x)
{
  ctrlc = TRUE;
}

bool CmdAreaFix(int argc, char **argv)
{
  bool rc, check = FALSE;
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {"check", 0, NULL, 'c'},
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+hc";
  static uchar helpstr[] =
"Usage: crashecho areafix\n"
"Scan primary netmail area for AreaFix requests\n"
"\n"
"  -h, --help            display this help and exit\n"
"  -c, --check           only check for expired requests\n"
"\n";
  int ch, lopt_index;

  optind = 0;
  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
      case 'h':
      case '?':
      default:
        fprintf(stderr, helpstr);
        return TRUE;
        /* Not reached */
      case 'c':
        check = TRUE;
        break;
    }
  }

  argc -= optind;
  argv += optind;

  if(argc){
    fprintf(stderr, helpstr);
    return FALSE;
  }

  if (!BeforeScanToss())
    return FALSE;

  nowdoing = DOING_AFIX;

  if (check)
    rc = CheckAreaFix();
  else
    rc = ScanAreaFix();

  AfterScanToss(rc);
  return rc;
}

bool CmdToss(int argc, char **argv)
{
  bool no_security = FALSE, tossbad = FALSE;
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {"bad", 0, NULL, 'b'},
    {"no-security", 0, NULL, 's'},
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+hbs";
  static uchar helpstr[] =
"Usage: crashecho toss [OPTION]...\n"
"Toss all .pkt files and bundles in inbound directories\n"
"\n"
"  -h, --help            display this help and exit\n"
"  -b, --bad             retoss BAD area\n"
"  -s, --no-security     process all packets without security checks\n"
"\n";
  int ch, lopt_index;

  optind = 0;
  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
      case 'h':
      case '?':
      default:
        fprintf(stderr, helpstr);
        return TRUE;
        /* Not reached */
      case 'b':
        tossbad = TRUE;
        break;
      case 's':
        no_security = TRUE;
        break;
    }
  }

  argc -= optind;
  argv += optind;

  if(argc){
    fprintf(stderr, helpstr);
    return FALSE;
  }

  if (no_security)
    LogWrite(2, TOSSINGINFO, "Packets will be tossed without security checks");

  if (!BeforeScanToss())
    return (FALSE);

  nowdoing = DOING_TOSS;

  if (tossbad) {
    struct Area *area;

    for (area = (struct Area *) config.AreaList.First; area; area = area->Next)
      if (area->Messagebase && area->AreaType == AREATYPE_BAD)
        if (area->Messagebase->scanfunc)
          break;

    if (area) {
      LogWrite(2, TOSSINGINFO, "Retossing BAD area...");

      nowdoing = DOING_TOSSBAD;

      if (!(*area->Messagebase->scanfunc) (area, FALSE, FALSE, 0, NULL, AcceptAny, HandleBad)) {
        AfterScanToss(FALSE);
        return (FALSE);
      }
    } else {
      LogWrite(2, TOSSINGINFO, "No BAD area configured");
    }
  } else {
    /* Unpack bundles from ProtInbound to TempInbound */
    if (!UnpackDir(config.cfg_ProtInbound, config.cfg_TempInbound)) {
      AfterScanToss(FALSE);
      return FALSE;
    }

    if (config.cfg_AfterUnpack[0]) {
      int res, rc;

      LogWrite(2, TOSSINGINFO, "Executing AFTERUNPACK script %s", config.cfg_AfterUnpack);
      res = Execute(config.cfg_AfterUnpack, &rc);
      if (!res)
        LogWrite(3, TOSSINGINFO, "AFTERUNPACK script exited with rc = %d", rc);
      else
        LogWrite(3, TOSSINGINFO, "AFTERUNPACK script failed");
    }

    /* Toss packets in LocalInbound without security */
    if (!TossDir(config.cfg_LocalInbound, TRUE, FALSE)) {
      AfterScanToss(FALSE);
      return FALSE;
    }

    /* Toss packets in ProtInbound */
    if (!TossDir(config.cfg_ProtInbound, no_security, FALSE)) {
      AfterScanToss(FALSE);
      return FALSE;
    }

    /* Toss packets in TempInbound */
    if (!TossDir(config.cfg_TempInbound, no_security, FALSE)) {
      AfterScanToss(FALSE);
      return FALSE;
    }

    /* Toss packets in Inbound */
    if (!TossDir(config.cfg_Inbound, no_security, TRUE)) {
      AfterScanToss(FALSE);
      return FALSE;
    }
  }

  LogTossResults();
  AfterScanToss(TRUE);

  return TRUE;
}

bool CmdScan(int argc, char **argv)
{
  uchar *area = NULL, *list = NULL;
  bool rc;
  bool ignoreflags = FALSE;
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {"area", required_argument, NULL, 'a'},
    {"list", required_argument, NULL, 'l'},
#ifdef MSGBASE_JAM
    {"ignore-flags", 0, NULL, 'i'},
#endif
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+"
                          "h"
                          "a:"
                          "l:"
#ifdef MSGBASE_JAM
                          "i"
#endif
                          ;
  static uchar helpstr[] =
"Usage: crashecho scan [OPTION]...\n"
"Scan areas for messages to export\n"
"\n"
"  -h, --help            display this help and exit\n"
"  -a, --area=NAME       scan only specified area\n"
"  -l, --list=FILE       scan only areas listed in the specified file\n"
#ifdef MSGBASE_JAM
"  -i, --ignore-flags    ignore netmail.jam/echomail.jam files\n"
#endif
"\n";
  int ch, lopt_index;

  optind = 0;
  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
      case 'h':
      case '?':
      default:
        fprintf(stderr, helpstr);
        return TRUE;
        /* Not reached */
      case 'a':
        area = optarg;
        break;
      case 'l':
        list = optarg;
        break;
#ifdef MSGBASE_JAM
      case 'i':
        ignoreflags = TRUE;
        break;
#endif
    }
  }

  argc -= optind;
  argv += optind;

  if(argc){
    fprintf(stderr, helpstr);
    return FALSE;
  }

  if (!BeforeScanToss())
    return (FALSE);

  nowdoing = DOING_SCAN;

  if (area)
    rc = ScanArea(area);
  else if (list)
    rc = ScanList(list);
  else
    rc = Scan(ignoreflags);

  if (rc)
    LogScanResults();

  AfterScanToss(rc);
  return rc;
}

bool CmdRescan(int argc, char **argv)
{
  ulong num = 0;
  bool rc;
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {"max", required_argument, NULL, 'm'},
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+hm:";
  static uchar helpstr[] =
"Usage: crashecho rescan [OPTION]... AREA NODE\n"
"Rescans the specified area for the specied node\n"
"\n"
"  -h, --help            display this help and exit\n"
"  -m, --max=NUMBER      maximum number of messages to rescan\n"
"\n";
  int ch, lopt_index;

  optind = 0;
  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
      case 'h':
      case '?':
      default:
        fprintf(stderr, helpstr);
        return TRUE;
        /* Not reached */
      case 'm':
        num = atoi(optarg);
        break;
    }
  }

  argc -= optind;
  argv += optind;

  if(argc != 2){
    fprintf(stderr, helpstr);
    return FALSE;
  }

  if (!BeforeScanToss())
    return (FALSE);

  nowdoing = DOING_RESCAN;

  rc = Rescan(argv[0], argv[1], num);

  AfterScanToss(rc);
  return rc;
}

bool CmdRemove(int argc, char **argv)
{
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+h";
  static uchar helpstr[] =
"Usage: crashecho remove AREA [AREA]...\n"
"Remove area(s) and unsubscribe\n"
"\n"
"  -h, --help            display this help and exit\n"
"\n";
  int ch, lopt_index;

  optind = 0;
  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
      case 'h':
      case '?':
      default:
        fprintf(stderr, helpstr);
        return TRUE;
        /* Not reached */
    }
  }

  argc -= optind;
  argv += optind;

  if(argc == 0){
    fprintf(stderr, helpstr);
    return FALSE;
  }

  if (!BeforeScanToss())
    return (FALSE);

  for (; argc > 0; argc--, argv++)
    RemoveArea(argv[0]);

  AfterScanToss(TRUE);
  return TRUE;
}

bool CmdNotify(int argc, char **argv)
{
  int list = SENDLIST_INFO;
  bool rc = FALSE;
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {"HELP", 0, NULL, 'H'},
    {"INFO", 0, NULL, 'I'},
    {"LIST", 0, NULL, 'L'},
    {"AVAIL", 0, NULL, 'A'},
    {"QUERY", 0, NULL, 'Q'},
    {"UNLINKED", 0, NULL, 'U'},
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+hHILAQU";
  static uchar helpstr[] =
"Usage: crashecho notify [OPTION] NODE\n"
"Send AreaFix response to node\n"
"\n"
"  -h, --help            display this help and exit\n"
"  -H, --HELP            send response to %%HELP command\n"
"  -I, --INFO            send response to %%INFO command (default)\n"
"  -L, --LIST            send response to %%LIST command\n"
"  -A, --AVAIL           send response to %%AVAIL command\n"
"  -Q, --QUERY           send response to %%QUERY command\n"
"  -U, --UNLINKED        send response to %%UNLINKED command\n"
"\n";
  int ch, lopt_index;

  optind = 0;

  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
    case 'h':
    case '?':
    default:
      fprintf(stderr, helpstr);
      return TRUE;
      /* Not reached */
    case 'H':
      list = SENDLIST_HELP;
      break;
    case 'I':
      list = SENDLIST_INFO;
      break;
    case 'L':
      list = SENDLIST_FULL;
      break;
    case 'A':
      list = SENDLIST_AVAIL;
      break;
    case 'Q':
      list = SENDLIST_QUERY;
      break;
    case 'U':
      list = SENDLIST_UNLINKED;
      break;
    }
  }

  argc -= optind;
  argv += optind;

  if(argc != 1){
    fprintf(stderr, helpstr);
    return FALSE;
  }

  if (!BeforeScanToss())
    return FALSE;

  rc = SendAFList(argv[0], list);

  AfterScanToss(rc);
  return rc;
}

static struct cmd {
  char *name;
  bool (*func)(int, char **);
} cmds[] = {
  {"areafix", CmdAreaFix},
  {"toss", CmdToss},
  {"scan", CmdScan},
  {"rescan", CmdRescan},
  {"remove", CmdRemove},
  {"notify", CmdNotify},
  {0, 0}
};

int main(int argc, char **argv)
{
  uchar *cfg = CONFIG_NAME;
  ulong cfgline;
  short seconderr = 0;
  uchar errorbuf[500];
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {"config", required_argument, NULL, 'c'},
    {"version", 0, NULL, SHOWVER},
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+hc:";
  static uchar helpstr[] =
"Usage: crashecho [OPTION]... COMMAND [OPTION]...\n"
"\n"
"  -h, --help            display this help and exit\n"
"      --version         print version number and  information about supported\n"
"                        messagebases and features\n"
"  -c, --config=FILE     use this configuration file instead of the default\n"
"\n"
"Valid commands are:\n"
"\n"
"  areafix     scan primary netmail area for AreaFix requests\n"
"  toss        toss all .pkt files and bundles in inbound directory\n"
"  scan        scan all areas for messages to export\n"
"  rescan      rescans the specied area for the specied node\n"
"  remove      remove specied area and unsubscribe\n"
"  notify      send AreaFix response to a node\n"
"  lock        lock CrashEcho's configuration file and exit\n"
"  unlock      remove lock on CrashEcho's configuration file and exit\n"
"\n"
"(Specify the --help option for a list of other help options)\n"
"\n";
  static uchar helplock[] =
"Usage: crashecho lock\n"
"Lock CrashEcho's configuration file\n"
"\n"
"  -h, --help            display this help and exit\n"
"\n";
  static uchar helpunlock[] =
"Usage: crashecho unlock\n"
"Remove lock on CrashEcho's configuration file\n"
"\n"
"  -h, --help            display this help and exit\n"
"\n";
  int ch, lopt_index;

  signal(SIGINT, breakfunc);

  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
      case 'h':
      case '?':
      default:
        fprintf(stderr, helpstr);
        CleanUp(EXIT_OK);
        /* Not reached */
      case SHOWVER:
        Version();
        CleanUp(EXIT_OK);
        /* Not reached */
      case 'c':
        cfg = optarg;
        break;
    }
  }

  argc -= optind;
  argv += optind;

  if (argc < 1) {
    fprintf(stderr, helpstr);
    CleanUp(EXIT_ERROR);
  }

  if (!stricmp(argv[0], "lock")) {
    if(argc != 1){
      fprintf(stderr, helplock);
      if(!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))
        CleanUp(EXIT_OK);
      else
        CleanUp(EXIT_ERROR);
    }

    if (!LockConfig(cfg)) {
      fprintf(stderr, "Failed to lock configuration file %s\n", cfg);
      CleanUp(EXIT_ERROR);
    }

    fprintf(stderr, "CrashEcho is now locked, use `crashecho unlock' to unlock\n");
    CleanUp(EXIT_OK);
  }

  if (!stricmp(argv[0], "unlock")) {
    if(argc != 1){
      fprintf(stderr, helpunlock);
      if(!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))
        CleanUp(EXIT_OK);
      else
        CleanUp(EXIT_ERROR);
    }

    Unlock(cfg);
    CleanUp(EXIT_OK);
  }

  InitConfig(&config);

  done_initconfig = TRUE;

  if (!(done_lockconfig = LockConfig(cfg))) {
    fprintf(stderr, "Failed to lock configuration file %s\n", cfg);
    CleanUp(EXIT_ERROR);
  }

  if (!ReadConfig(cfg, &config, &seconderr, &cfgline, errorbuf, 500)) {
    if (seconderr == READCONFIG_INVALID)
      fprintf(stderr, "Configuration error in %s on line %ld:\n%s\n", cfg, cfgline, errorbuf);

    else if (seconderr == READCONFIG_NO_MEM)
      fprintf(stderr, "Out of memory\n");

    else
      fprintf(stderr, "Failed to read configuration file %s\n", cfg);

    CleanUp(EXIT_ERROR);
  }

  if (config.areafile[0] == 0) {
    fprintf(stderr, "AREAFILE not defined in %s\n", cfg);
    CleanUp(EXIT_ERROR);
  }

  if (!ReadAreas(config.areafile, &config, &seconderr, &cfgline, errorbuf, 500)) {
    if (seconderr == READCONFIG_INVALID)
      fprintf(stderr, "Configuration error in %s on line %ld:\n%s\n", config.areafile, cfgline, errorbuf);

    else if (seconderr == READCONFIG_NO_MEM)
      fprintf(stderr, "Out of memory\n");

    else
      fprintf(stderr, "Failed to read configuration file %s\n", config.areafile);

    CleanUp(EXIT_ERROR);
  }

  if (!CheckConfig(&config, errorbuf, 500)) {
    fprintf(stderr, "Configuration error:\n%s\n", errorbuf);
    CleanUp(EXIT_ERROR);
  }

  if (!Init())
    CleanUp(EXIT_ERROR);

  done_init = TRUE;

  LogWrite(2, SYSTEMINFO, "%s started successfully!", verstr);

  done_welcomemsg = TRUE;

  for (ch = 0; cmds[ch].name; ch++)
    if (!stricmp(argv[0], cmds[ch].name))
      break;

  if (!cmds[ch].name) {
    fprintf(stderr, helpstr);
    CleanUp(EXIT_ERROR);
  }

  if (!cmds[ch].func) {
    fprintf(stderr, "Command %s is not supported yet\n", cmds[ch].name);
    CleanUp(EXIT_ERROR);
  }

  (*cmds[ch].func)(argc, argv);

  if (nomem)
    LogWrite(1, SYSTEMERR, "Out of memory");

  if (ioerror)
    LogWrite(1, SYSTEMERR, "I/O error: %s", strerror(ioerrornum));

  if (ctrlc)
    LogWrite(1, SYSTEMERR, "*** User break ***");

  CleanUp(EXIT_OK);

  /* The next line is never actually executed.
   * It is just there to stop gcc from giving a warning. */

  return (0);
}


syntax highlighted by Code2HTML, v. 0.9.1