home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / lora299s.zip / MSGDATA.CPP < prev    next >
C/C++ Source or Header  |  1998-05-12  |  35KB  |  1,350 lines

  1.  
  2. // LoraBBS Version 2.99 Free Edition
  3. // Copyright (C) 1987-98 Marco Maccaferri
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. #include "_ldefs.h"
  20. #include "lora_api.h"
  21.  
  22. TMsgData::TMsgData (void)
  23. {
  24.    fdDat = -1;
  25.    fdIdx = -1;
  26.    strcpy (DataFile, "msg.dat");
  27.    strcpy (IdxFile, "msg.idx");
  28.    LastKey[0] = '\0';
  29.    LastReaded = 0L;
  30. }
  31.  
  32. TMsgData::TMsgData (PSZ pszDataPath)
  33. {
  34.    strcpy (DataFile, pszDataPath);
  35.    strcpy (IdxFile, DataFile);
  36.  
  37.    fdDat = fdIdx = -1;
  38.    strcat (DataFile, "msg.dat");
  39.    strcat (IdxFile, "msg.idx");
  40.    LastKey[0] = '\0';
  41.    AdjustPath (strlwr (DataFile));
  42.    AdjustPath (strlwr (IdxFile));
  43.    LastReaded = 0L;
  44. }
  45.  
  46. TMsgData::~TMsgData (void)
  47. {
  48.    if (fdDat != -1)
  49.       close (fdDat);
  50.    if (fdIdx != -1)
  51.       close (fdIdx);
  52. }
  53.  
  54. USHORT TMsgData::Add (VOID)
  55. {
  56.    USHORT retVal = FALSE, DoClose = FALSE;
  57.    MESSAGE *Msg;
  58.    INDEX Idx;
  59.  
  60.    if (fdIdx == -1) {
  61.       fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  62.       DoClose = TRUE;
  63.    }
  64.    if (fdDat == -1) {
  65.       fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  66.       DoClose = TRUE;
  67.    }
  68.  
  69.    if (fdDat != -1 && fdIdx != -1) {
  70.       lseek (fdDat, 0L, SEEK_END);
  71.       lseek (fdIdx, 0L, SEEK_END);
  72.  
  73.       if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  74.          memset (Msg, 0, sizeof (MESSAGE));
  75.          Msg->Size = sizeof (MESSAGE);
  76.          Class2Struct (Msg);
  77.  
  78.          memset (&Idx, 0, sizeof (Idx));
  79.          strcpy (Idx.Key, Key);
  80.          Idx.Level = Level;
  81.          Idx.AccessFlags = AccessFlags;
  82.          Idx.DenyFlags = DenyFlags;
  83.          Idx.Position = tell (fdDat);
  84.  
  85.          write (fdDat, Msg, sizeof (MESSAGE));
  86.          write (fdIdx, &Idx, sizeof (Idx));
  87.          retVal = TRUE;
  88.  
  89.          free (Msg);
  90.       }
  91.    }
  92.  
  93.    if (DoClose == TRUE) {
  94.       if (fdDat != -1) {
  95.          close (fdDat);
  96.          fdDat = -1;
  97.       }
  98.       if (fdIdx != -1) {
  99.          close (fdIdx);
  100.          fdIdx = -1;
  101.       }
  102.    }
  103.  
  104.    return (retVal);
  105. }
  106.  
  107. VOID TMsgData::Class2Struct (MESSAGE *Msg)
  108. {
  109.    strcpy (Msg->Display, Display);
  110.    strcpy (Msg->Key, Key);
  111.    Msg->Level = Level;
  112.    Msg->AccessFlags = AccessFlags;
  113.    Msg->DenyFlags = DenyFlags;
  114.    Msg->WriteLevel = WriteLevel;
  115.    Msg->WriteFlags = WriteFlags;
  116.    Msg->DenyWriteFlags = DenyWriteFlags;
  117.    Msg->Age = Age;
  118.    Msg->Storage = Storage;
  119.    strcpy (Msg->Path, Path);
  120.    Msg->Board = Board;
  121.    Msg->Flags = Flags;
  122.    Msg->Group = Group;
  123.    Msg->EchoMail = EchoMail;
  124.    Msg->ShowGlobal = ShowGlobal;
  125.    Msg->UpdateNews = UpdateNews;
  126.    Msg->Offline = Offline;
  127.    strcpy (Msg->MenuName, MenuName);
  128.    strcpy (Msg->Moderator, Moderator);
  129.    Msg->Cost = Cost;
  130.    Msg->DaysOld = DaysOld;
  131.    Msg->RecvDaysOld = RecvDaysOld;
  132.    Msg->MaxMessages = MaxMessages;
  133.    Msg->ActiveMsgs = ActiveMsgs;
  134.    strcpy (Msg->NewsGroup, NewsGroup);
  135.    Msg->Highest = Highest;
  136.    Msg->HighWaterMark = HighWaterMark;
  137.    strcpy (Msg->EchoTag, EchoTag);
  138.    strcpy (Msg->Origin, Origin);
  139.    strcpy (Msg->Address, Address);
  140.    Msg->FirstMessage = FirstMessage;
  141.    Msg->LastMessage = LastMessage;
  142.    Msg->OriginIndex = OriginIndex;
  143.    Msg->NewsHWM = NewsHWM;
  144. }
  145.  
  146. VOID TMsgData::Delete (VOID)
  147. {
  148.    int fdNew;
  149.    ULONG Position;
  150.    MESSAGE *Msg;
  151.    INDEX Idx;
  152.  
  153.    if (fdIdx == -1)
  154.       fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  155.    if (fdDat == -1)
  156.       fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  157.  
  158.    fdNew = sopen ("Temp1.Dat", O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  159.  
  160.    if (fdDat != -1 && fdIdx != -1 && fdNew != -1) {
  161.       lseek (fdDat, 0L, SEEK_SET);
  162.  
  163.       if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  164.          while (read (fdDat, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  165.             if (strcmp (LastKey, Msg->Key))
  166.                write (fdNew, Msg, sizeof (MESSAGE));
  167.          }
  168.  
  169.          if ((Position = tell (fdIdx)) > 0L)
  170.             Position -= sizeof (Idx);
  171.  
  172.          close (fdDat);
  173.          close (fdIdx);
  174.          fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, SH_DENYNO, S_IREAD|S_IWRITE);
  175.          fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT|O_TRUNC, SH_DENYNO, S_IREAD|S_IWRITE);
  176.  
  177.          if (fdDat != -1 && fdIdx != -1) {
  178.             lseek (fdNew, 0L, SEEK_SET);
  179.  
  180.             while (read (fdNew, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  181.                memset (&Idx, 0, sizeof (Idx));
  182.                strcpy (Idx.Key, Msg->Key);
  183.                Idx.Level = Msg->Level;
  184.                Idx.AccessFlags = Msg->AccessFlags;
  185.                Idx.DenyFlags = Msg->DenyFlags;
  186.                Idx.Position = tell (fdDat);
  187.  
  188.                write (fdIdx, &Idx, sizeof (Idx));
  189.                write (fdDat, Msg, sizeof (MESSAGE));
  190.             }
  191.  
  192.             lseek (fdIdx, Position, SEEK_SET);
  193.             if (Next () == FALSE) {
  194.                if (Previous () == FALSE)
  195.                   New ();
  196.             }
  197.          }
  198.  
  199.          free (Msg);
  200.       }
  201.    }
  202.  
  203.    if (fdNew != -1) {
  204.       close (fdNew);
  205.       unlink ("Temp1.Dat");
  206.    }
  207. }
  208.  
  209. USHORT TMsgData::First (VOID)
  210. {
  211.    USHORT retVal = FALSE;
  212.  
  213.    if (fdIdx == -1)
  214.       fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  215.    if (fdDat == -1)
  216.       fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  217.  
  218.    if (fdDat != -1 && fdIdx != -1) {
  219.       lseek (fdIdx, 0L, SEEK_SET);
  220.       lseek (fdDat, 0L, SEEK_SET);
  221.       retVal = Next ();
  222.    }
  223.  
  224.    return (retVal);
  225. }
  226.  
  227. USHORT TMsgData::Insert (class TMsgData *Data)
  228. {
  229.    PSZ p;
  230.  
  231.    strcpy (Display, Data->Display);
  232.    strcpy (Key, Data->Key);
  233.    Level = Data->Level;
  234.    AccessFlags = Data->AccessFlags;
  235.    DenyFlags = Data->DenyFlags;
  236.    WriteLevel = Data->WriteLevel;
  237.    WriteFlags = Data->WriteFlags;
  238.    DenyWriteFlags = Data->DenyWriteFlags;
  239.    Age = Data->Age;
  240.    Storage = Data->Storage;
  241.  
  242.    strcpy (Path, Data->Path);
  243.    if (Data->Storage == ST_FIDO || Data->Storage == ST_HUDSON) {
  244.       if (Path[strlen (Path) - 1] != '\\' && Path[strlen (Path) - 1] != '/')
  245.          strcat (Path, "\\");
  246.    }
  247.    else {
  248.       if (Path[strlen (Path) - 1] == '\\' || Path[strlen (Path) - 1] == '/')
  249.          Path[strlen (Path) - 1] = '\0';
  250.    }
  251. #if defined(__LINUX__)
  252.    while ((p = strchr (Path, '\\')) != NULL)
  253.       *p = '/';
  254. #else
  255.    while ((p = strchr (Path, '/')) != NULL)
  256.       *p = '\\';
  257. #endif
  258.  
  259.    Board = Data->Board;
  260.    Flags = Data->Flags;
  261.    Group = Data->Group;
  262.    EchoMail = Data->EchoMail;
  263.    ShowGlobal = Data->ShowGlobal;
  264.    UpdateNews = Data->UpdateNews;
  265.    Offline = Data->Offline;
  266.    strcpy (MenuName, Data->MenuName);
  267.    strcpy (Moderator, Data->Moderator);
  268.    Cost = Data->Cost;
  269.    DaysOld = Data->DaysOld;
  270.    RecvDaysOld = Data->RecvDaysOld;
  271.    MaxMessages = Data->MaxMessages;
  272.    ActiveMsgs = Data->ActiveMsgs;
  273.    strcpy (NewsGroup, Data->NewsGroup);
  274.    Highest = Data->Highest;
  275.    HighWaterMark = Data->HighWaterMark;
  276.    strcpy (EchoTag, Data->EchoTag);
  277.    strcpy (Origin, Data->Origin);
  278.    strcpy (Address, Data->Address);
  279.    FirstMessage = Data->FirstMessage;
  280.    LastMessage = Data->LastMessage;
  281.    OriginIndex = Data->OriginIndex;
  282.    NewsHWM = Data->NewsHWM;
  283.  
  284.    return (Insert ());
  285. }
  286.  
  287. USHORT TMsgData::Insert (VOID)
  288. {
  289.    int fdNew;
  290.    USHORT retVal = FALSE;
  291.    ULONG Position;
  292.    MESSAGE *Msg;
  293.    INDEX Idx;
  294.  
  295.    if (fdIdx == -1)
  296.       fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  297.    if (fdDat == -1)
  298.       fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  299.  
  300.    fdNew = sopen ("Temp1.Dat", O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  301.  
  302.    if (fdDat != -1 && fdIdx != -1 && fdNew != -1) {
  303.       lseek (fdDat, 0L, SEEK_SET);
  304.  
  305.       if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  306.          while (read (fdDat, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  307.             write (fdNew, Msg, sizeof (MESSAGE));
  308.             if (!strcmp (LastKey, Msg->Key)) {
  309.                memset (Msg, 0, sizeof (MESSAGE));
  310.                Msg->Size = sizeof (MESSAGE);
  311.                Class2Struct (Msg);
  312.                write (fdNew, Msg, sizeof (MESSAGE));
  313.             }
  314.          }
  315.  
  316.          lseek (fdDat, 0L, SEEK_SET);
  317.          lseek (fdNew, 0L, SEEK_SET);
  318.  
  319.          Position = tell (fdIdx);
  320.          lseek (fdIdx, 0L, SEEK_SET);
  321.  
  322.          while (read (fdNew, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  323.             memset (&Idx, 0, sizeof (Idx));
  324.             strcpy (Idx.Key, Msg->Key);
  325.             Idx.Level = Msg->Level;
  326.             Idx.AccessFlags = Msg->AccessFlags;
  327.             Idx.DenyFlags = Msg->DenyFlags;
  328.             Idx.Position = tell (fdDat);
  329.  
  330.             write (fdIdx, &Idx, sizeof (Idx));
  331.             write (fdDat, Msg, sizeof (MESSAGE));
  332.          }
  333.  
  334.          lseek (fdIdx, Position, SEEK_SET);
  335.          Next ();
  336.  
  337.          retVal = TRUE;
  338.          free (Msg);
  339.       }
  340.    }
  341.  
  342.    if (fdNew != -1) {
  343.       close (fdNew);
  344.       unlink ("Temp1.Dat");
  345.    }
  346.  
  347.    return (retVal);
  348. }
  349.  
  350. USHORT TMsgData::Last (VOID)
  351. {
  352.    USHORT retVal = FALSE, IsValid;
  353.    MESSAGE *Msg;
  354.    INDEX Idx;
  355.  
  356.    IsValid = FALSE;
  357.  
  358.    if (fdDat != -1 && fdIdx != -1) {
  359.       lseek (fdIdx, 0L, SEEK_END);
  360.       if (tell (fdIdx) >= sizeof (INDEX)) {
  361.          lseek (fdIdx, tell (fdIdx) - sizeof (INDEX), SEEK_SET);
  362.          read (fdIdx, &Idx, sizeof (Idx));
  363.          if (!(Idx.Flags & IDX_DELETED)) {
  364.             lseek (fdDat, Idx.Position, SEEK_SET);
  365.             if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  366.                if (read (fdDat, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  367.                   retVal = TRUE;
  368.                   New ();
  369.                   Struct2Class (Msg);
  370.                   IsValid = TRUE;
  371.                }
  372.  
  373.                free (Msg);
  374.             }
  375.          }
  376.       }
  377.  
  378.       while (IsValid == FALSE && tell (fdIdx) >= sizeof (INDEX) * 2) {
  379.          lseek (fdIdx, tell (fdIdx) - sizeof (INDEX) * 2, SEEK_SET);
  380.          read (fdIdx, &Idx, sizeof (Idx));
  381.          if (!(Idx.Flags & IDX_DELETED)) {
  382.             lseek (fdDat, Idx.Position, SEEK_SET);
  383.             if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  384.                if (read (fdDat, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  385.                   retVal = TRUE;
  386.                   New ();
  387.                   Struct2Class (Msg);
  388.                   IsValid = TRUE;
  389.                }
  390.  
  391.                free (Msg);
  392.             }
  393.          }
  394.       }
  395.    }
  396.  
  397.    return (retVal);
  398. }
  399.  
  400. VOID TMsgData::New (VOID)
  401. {
  402.    memset (Display, 0, sizeof (Display));
  403.    memset (Key, 0, sizeof (Key));
  404.    Level = 0;
  405.    AccessFlags = 0L;
  406.    DenyFlags = 0L;
  407.    WriteLevel = 0;
  408.    WriteFlags = 0L;
  409.    DenyWriteFlags = 0L;
  410.    Age = 0;
  411.    Storage = 0;
  412.    memset (Path, 0, sizeof (Path));
  413.    Board = 0;
  414.    Flags = 0;
  415.    Group = 0;
  416.    EchoMail = FALSE;
  417.    ShowGlobal = TRUE;
  418.    UpdateNews = FALSE;
  419.    Offline = TRUE;
  420.    memset (MenuName, 0, sizeof (MenuName));
  421.    memset (Moderator, 0, sizeof (Moderator));
  422.    Cost = 0;
  423.    DaysOld = 0;
  424.    RecvDaysOld = 0;
  425.    MaxMessages = 0;
  426.    FirstMessage = LastMessage = ActiveMsgs = 0L;
  427.    memset (NewsGroup, 0, sizeof (NewsGroup));
  428.    Highest = 0L;
  429.    memset (EchoTag, 0, sizeof (EchoTag));
  430.    memset (Origin, 0, sizeof (Origin));
  431.    HighWaterMark = 0L;
  432.    memset (Address, 0, sizeof (Address));
  433.    NewsHWM = 0L;
  434. }
  435.  
  436. USHORT TMsgData::Next (VOID)
  437. {
  438.    USHORT retVal = FALSE, IsValid;
  439.    MESSAGE *Msg;
  440.    INDEX Idx;
  441.  
  442.    IsValid = FALSE;
  443.  
  444.    if (fdDat != -1 && fdIdx != -1) {
  445.       while (IsValid == FALSE && read (fdIdx, &Idx, sizeof (Idx)) == sizeof (Idx)) {
  446.          if (!(Idx.Flags & IDX_DELETED)) {
  447.             if (tell (fdDat) != Idx.Position)
  448.                lseek (fdDat, Idx.Position, SEEK_SET);
  449.             if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  450.                if (read (fdDat, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  451.                   retVal = TRUE;
  452.                   New ();
  453.                   Struct2Class (Msg);
  454.                   IsValid = TRUE;
  455.                }
  456.  
  457.                free (Msg);
  458.             }
  459.          }
  460.       }
  461.    }
  462.  
  463.    return (retVal);
  464. }
  465.  
  466. VOID TMsgData::Pack (VOID)
  467. {
  468.    int fdNewIdx, fdNewDat;
  469.    INDEX Idx;
  470.    MESSAGE *Msg;
  471.  
  472.    if (fdIdx == -1)
  473.       fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  474.    if (fdDat == -1)
  475.       fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  476.  
  477.    if (fdIdx != -1 && fdDat != -1) {
  478.       lseek (fdIdx, 0L, SEEK_SET);
  479.       if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  480.          fdNewIdx = open ("Msg-New.Idx", O_RDWR|O_BINARY|O_CREAT, S_IREAD|S_IWRITE);
  481.          fdNewDat = open ("Msg-New.Dat", O_RDWR|O_BINARY|O_CREAT, S_IREAD|S_IWRITE);
  482.  
  483.          if (fdNewIdx != -1 && fdNewDat != -1) {
  484.             while (read (fdIdx, &Idx, sizeof (Idx)) == sizeof (Idx)) {
  485.                if (!(Idx.Flags & IDX_DELETED)) {
  486.                   lseek (fdDat, Idx.Position, SEEK_SET);
  487.                   read (fdDat, Msg, sizeof (MESSAGE));
  488.                   Idx.Position = tell (fdNewDat);
  489.                   write (fdNewDat, Msg, sizeof (MESSAGE));
  490.                   write (fdNewIdx, &Idx, sizeof (Idx));
  491.                }
  492.             }
  493.  
  494.             close (fdNewIdx);
  495.             close (fdNewDat);
  496.  
  497.             unlink (DataFile);
  498.             rename ("Msg-New.Dat", DataFile);
  499.             unlink (IdxFile);
  500.             rename ("Msg-New.Idx", IdxFile);
  501.          }
  502.  
  503.          unlink ("Msg-New.Dat");
  504.          unlink ("Msg-New.Idx");
  505.          free (Msg);
  506.       }
  507.  
  508.       close (fdIdx);
  509.       close (fdDat);
  510.       fdIdx = fdDat = -1;
  511.    }
  512. }
  513.  
  514. USHORT TMsgData::Previous (VOID)
  515. {
  516.    USHORT retVal = FALSE, IsValid;
  517.    MESSAGE *Msg;
  518.    INDEX Idx;
  519.  
  520.    IsValid = FALSE;
  521.  
  522.    if (fdDat != -1 && fdIdx != -1) {
  523.       while (IsValid == FALSE && tell (fdIdx) >= sizeof (INDEX) * 2) {
  524.          lseek (fdIdx, tell (fdIdx) - sizeof (INDEX) * 2, SEEK_SET);
  525.          read (fdIdx, &Idx, sizeof (Idx));
  526.          if (!(Idx.Flags & IDX_DELETED)) {
  527.             lseek (fdDat, Idx.Position, SEEK_SET);
  528.             if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  529.                if (read (fdDat, Msg, sizeof (MESSAGE)) == sizeof (MESSAGE)) {
  530.                   retVal = TRUE;
  531.                   New ();
  532.                   Struct2Class (Msg);
  533.                   IsValid = TRUE;
  534.                }
  535.  
  536.                free (Msg);
  537.             }
  538.          }
  539.       }
  540.    }
  541.  
  542.    return (retVal);
  543. }
  544.  
  545. USHORT TMsgData::Read (PSZ pszName, USHORT fCloseFile)
  546. {
  547.    USHORT retVal = FALSE;
  548.    MESSAGE *Msg;
  549.    INDEX Idx;
  550.  
  551.    New ();
  552.  
  553.    if (fdIdx == -1)
  554.       fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  555.    if (fdDat == -1)
  556.       fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  557.  
  558.    if (fdDat != -1 && fdIdx != -1) {
  559.       lseek (fdIdx, 0L, SEEK_SET);
  560.       lseek (fdDat, 0L, SEEK_SET);
  561.  
  562.       while (retVal == FALSE && read (fdIdx, &Idx, sizeof (Idx)) == sizeof (Idx)) {
  563.          if (!(Idx.Flags & IDX_DELETED) && !stricmp (pszName, Idx.Key)) {
  564.             if ((Msg = (MESSAGE *)malloc (sizeof (MESSAGE))) != NULL) {
  565.                lseek (fdDat, Idx.Position, SEEK_SET);
  566.                read (fdDat, Msg, sizeof (MESSAGE));
  567.  
  568.                retVal = TRUE;
  569.                Struct2Class (Msg);
  570.  
  571.                free (Msg);
  572.             }
  573.          }
  574.       }
  575.    }
  576.  
  577.    if (fCloseFile == TRUE) {
  578.       if (fdDat != -1) {
  579.          close (fdDat);
  580.          fdDat = -1;
  581.       }
  582.       if (fdIdx != -1) {
  583.          close (fdIdx);
  584.          fdIdx = -1;
  585.       }
  586.    }
  587.  
  588.    return (retVal);
  589. }
  590.  
  591. USHORT TMsgData::ReadEcho (PSZ pszEchoTag)
  592. {
  593.    FILE *fp;
  594.    USHORT retVal = FALSE;
  595.    MESSAGE *Msg;
  596.  
  597.    New ();
  598.  
  599.    fp = fopen (DataFile, "r+b");
  600.    Msg = (MESSAGE *)malloc (sizeof (MESSAGE));
  601.  
  602.    if (fp != NULL && Msg != NULL) {
  603.       setvbuf (fp, NULL, _IOFBF, 2048);
  604.       while (fread (Msg, sizeof (MESSAGE), 1, fp) == 1) {
  605.          if (!stricmp (pszEchoTag, Msg->EchoTag)) {
  606.             retVal = TRUE;
  607.             Struct2Class (Msg);
  608.             break;
  609.          }
  610.       }
  611.    }
  612.  
  613.    if (Msg != NULL)
  614.       free (Msg);
  615.    if (fp != NULL)
  616.       fclose (fp);
  617.  
  618.    return (retVal);
  619. }
  620.  
  621. USHORT TMsgData::ReRead (VOID)
  622. {
  623.    USHORT retVal = FALSE;
  624.  
  625.    if (fdDat != -1 && fdIdx != -1) {
  626.       if (tell (fdIdx) >= sizeof (INDEX)) {
  627.          lseek (fdIdx, tell (fdIdx) - sizeof (INDEX), SEEK_SET);
  628.          retVal = Next ();
  629.       }
  630.    }
  631.  
  632.    return (retVal);
  633. }
  634.  
  635. VOID TMsgData::Struct2Class (MESSAGE *Msg)
  636. {
  637.    PSZ p;
  638.  
  639.    strcpy (Display, Msg->Display);
  640.    strcpy (Key, Msg->Key);
  641.    Level = Msg->Level;
  642.    AccessFlags = Msg->AccessFlags;
  643.    DenyFlags = Msg->DenyFlags;
  644.    WriteLevel = Msg->WriteLevel;
  645.    WriteFlags = Msg->WriteFlags;
  646.    DenyWriteFlags = Msg->DenyWriteFlags;
  647.    Age = Msg->Age;
  648.    Storage = Msg->Storage;
  649.  
  650.    strcpy (Path, Msg->Path);
  651.    if (Msg->Storage == ST_FIDO || Msg->Storage == ST_HUDSON) {
  652.       if (Path[strlen (Path) - 1] != '\\' && Path[strlen (Path) - 1] != '/')
  653.          strcat (Path, "\\");
  654.    }
  655.    else {
  656.       if (Path[strlen (Path) - 1] == '\\' || Path[strlen (Path) - 1] == '/')
  657.          Path[strlen (Path) - 1] = '\0';
  658.    }
  659. #if defined(__LINUX__)
  660.    while ((p = strchr (Path, '\\')) != NULL)
  661.       *p = '/';
  662. #else
  663.    while ((p = strchr (Path, '/')) != NULL)
  664.       *p = '\\';
  665. #endif
  666.  
  667.    Board = Msg->Board;
  668.    Flags = Msg->Flags;
  669.    Group = Msg->Group;
  670.    EchoMail = Msg->EchoMail;
  671.    ShowGlobal = Msg->ShowGlobal;
  672.    UpdateNews = Msg->UpdateNews;
  673.    Offline = Msg->Offline;
  674.    strcpy (MenuName, Msg->MenuName);
  675.    strcpy (Moderator, Msg->Moderator);
  676.    Cost = Msg->Cost;
  677.    DaysOld = Msg->DaysOld;
  678.    RecvDaysOld = Msg->RecvDaysOld;
  679.    MaxMessages = Msg->MaxMessages;
  680.    ActiveMsgs = Msg->ActiveMsgs;
  681.    strcpy (NewsGroup, Msg->NewsGroup);
  682.    Highest = Msg->Highest;
  683.    HighWaterMark = Msg->HighWaterMark;
  684.    strcpy (EchoTag, Msg->EchoTag);
  685.    strcpy (Origin, Msg->Origin);
  686.    strcpy (Address, Msg->Address);
  687.    FirstMessage = Msg->FirstMessage;
  688.    LastMessage = Msg->LastMessage;
  689.    OriginIndex = Msg->OriginIndex;
  690.    NewsHWM = Msg->NewsHWM;
  691.  
  692.    strcpy (LastKey, Msg->Key);
  693. }
  694.  
  695. USHORT TMsgData::Update (PSZ pszNewKey)
  696. {
  697.    USHORT retVal = FALSE, Size, DoClose = FALSE;
  698.    MESSAGE *Msg;
  699.    INDEX Idx;
  700.  
  701.    if (fdIdx == -1) {
  702.       fdIdx = sopen (IdxFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  703.       DoClose = TRUE;
  704.    }
  705.    if (fdDat == -1) {
  706.       fdDat = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE);
  707.       DoClose = TRUE;
  708.    }
  709.  
  710.    if (fdDat != -1 && fdIdx != -1) {
  711.       if (DoClose == TRUE) {
  712.          lseek (fdIdx, 0L, SEEK_SET);
  713.          while (read (fdIdx, &Idx, sizeof (Idx)) == sizeof (Idx)) {
  714.             if (!(Idx.Flags & IDX_DELETED) && !stricmp (Key, Idx.Key)) {
  715.                retVal = TRUE;
  716.                break;
  717.             }
  718.          }
  719.       }
  720.       else {
  721.          lseek (fdIdx, tell (fdIdx) - sizeof (INDEX), SEEK_SET);
  722.          read (fdIdx, &Idx, sizeof (Idx));
  723.  
  724.          if (strcmp (Idx.Key, Key)) {
  725.             lseek (fdIdx, 0L, SEEK_SET);
  726.             while (read (fdIdx, &Idx, sizeof (Idx)) == sizeof (Idx)) {
  727.                if (!(Idx.Flags & IDX_DELETED) && !stricmp (Key, Idx.Key)) {
  728.                   retVal = TRUE;
  729.                   break;
  730.                }
  731.             }
  732.          }
  733.          else
  734.             retVal = TRUE;
  735.       }
  736.  
  737.       if (retVal == TRUE && tell (fdIdx) >= sizeof (INDEX)) {
  738.          retVal = FALSE;
  739.          Size = sizeof (MESSAGE);
  740.  
  741.          if ((Msg = (MESSAGE *)malloc (Size)) != NULL) {
  742.             memset (Msg, 0, Size);
  743.             Msg->Size = Size;
  744.             if (pszNewKey != NULL)
  745.                strcpy (Key, pszNewKey);
  746.  
  747.             Class2Struct (Msg);
  748.  
  749.             strcpy (Idx.Key, Key);
  750.             lseek (fdIdx, tell (fdIdx) - sizeof (INDEX), SEEK_SET);
  751.             write (fdIdx, &Idx, sizeof (Idx));
  752.  
  753.             lseek (fdDat, Idx.Position, SEEK_SET);
  754.             write (fdDat, Msg, sizeof (MESSAGE));
  755.             free (Msg);
  756.  
  757.             retVal = TRUE;
  758.          }
  759.       }
  760.    }
  761.  
  762.    if (DoClose == TRUE) {
  763.       if (fdDat != -1) {
  764.          close (fdDat);
  765.          fdDat = -1;
  766.       }
  767.       if (fdIdx != -1) {
  768.          close (fdIdx);
  769.          fdIdx = -1;
  770.       }
  771.    }
  772.  
  773.    return (retVal);
  774. }
  775.  
  776. // --------------------------------------------------------------------------
  777.  
  778. #define ECHOLINK_INDEX     32
  779.  
  780. TEchoLink::TEchoLink (void)
  781. {
  782.    Data.Clear ();
  783.    strcpy (DataFile, "echolink.dat");
  784.    Skip4D = FALSE;
  785. }
  786.  
  787. TEchoLink::TEchoLink (PSZ pszDataPath)
  788. {
  789.    Data.Clear ();
  790.  
  791.    strcpy (DataFile, pszDataPath);
  792.    if (DataFile[0] != '\0') {
  793. #if defined(__LINUX__)
  794.       if (DataFile[strlen (DataFile) - 1] != '/')
  795.          strcat (DataFile, "/");
  796. #else
  797.       if (DataFile[strlen (DataFile) - 1] != '\\')
  798.          strcat (DataFile, "\\");
  799. #endif
  800.    }
  801.    strcpy (DataFile, DataFile);
  802.    strcat (DataFile, "echolink.dat");
  803.    Skip4D = FALSE;
  804. }
  805.  
  806. TEchoLink::~TEchoLink (void)
  807. {
  808.    Data.Clear ();
  809. }
  810.  
  811. USHORT TEchoLink::Add (VOID)
  812. {
  813.    USHORT Insert = FALSE;
  814.    ECHOLINK Buffer, *Current;
  815.  
  816.    memset (&Buffer, 0, sizeof (ECHOLINK));
  817.  
  818.    Buffer.Free = FALSE;
  819.    Buffer.EchoTag = EchoTag;
  820.    Buffer.Zone = Zone;
  821.    Buffer.Net = Net;
  822.    Buffer.Node = Node;
  823.    Buffer.Point = Point;
  824.    strcpy (Buffer.Domain, Domain);
  825.    Buffer.SendOnly = SendOnly;
  826.    Buffer.ReceiveOnly = ReceiveOnly;
  827.    Buffer.PersonalOnly = PersonalOnly;
  828.    Buffer.Passive = Passive;
  829.    Buffer.Skip = Skip;
  830.  
  831.    if ((Current = (ECHOLINK *)Data.First ()) != NULL) {
  832.       if (Current->Zone > Zone)
  833.          Insert = TRUE;
  834.       else if (Current->Zone == Zone && Current->Net > Net)
  835.          Insert = TRUE;
  836.       else if (Current->Zone == Zone && Current->Net == Net && Current->Node > Node)
  837.          Insert = TRUE;
  838.       else if (Current->Zone == Zone && Current->Net == Net && Current->Node == Node && Current->Point > Point)
  839.          Insert = TRUE;
  840.  
  841.       if (Insert == TRUE) {
  842.          Data.Insert (&Buffer, sizeof (ECHOLINK));
  843.          Data.Insert (Current, sizeof (ECHOLINK));
  844.          Data.First ();
  845.          Data.Remove ();
  846.          Data.First ();
  847.       }
  848.       else {
  849.          while ((Current = (ECHOLINK *)Data.Next ()) != NULL) {
  850.             if (Current->Zone > Zone)
  851.                Insert = TRUE;
  852.             else if (Current->Zone == Zone && Current->Net > Net)
  853.                Insert = TRUE;
  854.             else if (Current->Zone == Zone && Current->Net == Net && Current->Node > Node)
  855.                Insert = TRUE;
  856.             else if (Current->Zone == Zone && Current->Net == Net && Current->Node == Node && Current->Point > Point)
  857.                Insert = TRUE;
  858.  
  859.             if (Insert == TRUE) {
  860.                Data.Previous ();
  861.                Data.Insert (&Buffer, sizeof (ECHOLINK));
  862.                break;
  863.             }
  864.          }
  865.          if (Insert == FALSE) {
  866.             Data.Add (&Buffer, sizeof (ECHOLINK));
  867.             Insert = TRUE;
  868.          }
  869.       }
  870.    }
  871.    else {
  872.       if (Insert == FALSE) {
  873.          Data.Add (&Buffer, sizeof (ECHOLINK));
  874.          Insert = TRUE;
  875.       }
  876.    }
  877.  
  878.    return (Insert);
  879. }
  880.  
  881. USHORT TEchoLink::AddString (PSZ pszString)
  882. {
  883.    USHORT RetVal = FALSE;
  884.    CHAR Temp[128], *p;
  885.    class TAddress Address;
  886.  
  887.    strcpy (Temp, pszString);
  888.    if ((p = strtok (Temp, " ")) != NULL)
  889.       do {
  890.          Skip = FALSE;
  891.          ReceiveOnly = SendOnly = PersonalOnly = FALSE;
  892.          if (Check (p) == FALSE) {
  893.             while (isdigit (*p) == 0 && *p != '.') {
  894.                if (*p == '>')
  895.                   ReceiveOnly = TRUE;
  896.                if (*p == '<')
  897.                   SendOnly = TRUE;
  898.                if (*p == '!')
  899.                   PersonalOnly = TRUE;
  900.                p++;
  901.             }
  902.             Address.Parse (p);
  903.             if (Check (Address.String) == FALSE) {
  904.                if (Address.Zone != 0)
  905.                   Zone = Address.Zone;
  906.                if (Address.Net != 0)
  907.                   Net = Address.Net;
  908.                Node = Address.Node;
  909.                Point = Address.Point;
  910.                strcpy (Domain, Address.Domain);
  911.                RetVal = Add ();
  912.             }
  913.          }
  914.       } while ((p = strtok (NULL, " ")) != NULL);
  915.  
  916.    return (RetVal);
  917. }
  918.  
  919. VOID TEchoLink::Change (PSZ pszFrom, PSZ pszTo)
  920. {
  921.    int fd, i, Count, Changed;
  922.    ULONG CrcFrom, CrcTo, Position;
  923.    ECHOLINK *Buffer;
  924.  
  925.    CrcFrom = StringCrc32 (pszFrom, 0xFFFFFFFFL);
  926.    CrcTo = StringCrc32 (pszTo, 0xFFFFFFFFL);
  927.  
  928.    if ((fd = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE)) != -1) {
  929.       if ((Buffer = (ECHOLINK *)malloc (sizeof (ECHOLINK) * ECHOLINK_INDEX)) != NULL) {
  930.          do {
  931.             Changed = FALSE;
  932.  
  933.             Position = tell (fd);
  934.             Count = read (fd, Buffer, sizeof (ECHOLINK) * ECHOLINK_INDEX) / sizeof (ECHOLINK);
  935.             for (i = 0; i < Count; i++) {
  936.                if (Buffer[i].EchoTag == CrcFrom) {
  937.                   Buffer[i].EchoTag = CrcTo;
  938.                   Changed = TRUE;
  939.                }
  940.             }
  941.  
  942.             if (Changed == TRUE) {
  943.                lseek (fd, Position, SEEK_SET);
  944.                write (fd, Buffer, sizeof (ECHOLINK) * Count);
  945.             }
  946.          } while (Count == ECHOLINK_INDEX);
  947.          free (Buffer);
  948.       }
  949.  
  950.       close (fd);
  951.    }
  952. }
  953.  
  954. USHORT TEchoLink::Check (PSZ pszAddress)
  955. {
  956.    USHORT RetVal = FALSE;
  957.    ECHOLINK *El;
  958.    class TAddress Addr;
  959.  
  960.    while (isdigit (*pszAddress) == 0 && *pszAddress != '.')
  961.       pszAddress++;
  962.    Addr.Parse (pszAddress);
  963.  
  964.    if ((El = (ECHOLINK *)Data.First ()) != NULL)
  965.       do {
  966.          if (El->Zone == Addr.Zone && El->Net == Addr.Net && El->Node == Addr.Node && El->Point == Addr.Point) {
  967.             EchoTag = El->EchoTag;
  968.             Zone = El->Zone;
  969.             Net = El->Net;
  970.             Node = El->Node;
  971.             Point = El->Point;
  972.             strcpy (Domain, El->Domain);
  973.             SendOnly = El->SendOnly;
  974.             ReceiveOnly = El->ReceiveOnly;
  975.             PersonalOnly = El->PersonalOnly;
  976.             Passive = El->Passive;
  977.             Skip = El->Skip;
  978.             if (Point == 0)
  979.                sprintf (Address, "%u:%u/%u", Zone, Net, Node);
  980.             else
  981.                sprintf (Address, "%u:%u/%u.%u", Zone, Net, Node, Point);
  982.             if (Domain[0] != '\0') {
  983.                strcat (Address, "@");
  984.                strcat (Address, Domain);
  985.             }
  986.             RetVal = TRUE;
  987.             break;
  988.          }
  989.       } while ((El = (ECHOLINK *)Data.Next ()) != NULL);
  990.  
  991.    return (RetVal);
  992. }
  993.  
  994. VOID TEchoLink::Clear (VOID)
  995. {
  996.    Data.Clear ();
  997.    New ();
  998. }
  999.  
  1000. VOID TEchoLink::Delete (VOID)
  1001. {
  1002.    if (Data.Value () != NULL)
  1003.       Data.Remove ();
  1004. }
  1005.  
  1006. USHORT TEchoLink::First (VOID)
  1007. {
  1008.    USHORT RetVal = FALSE;
  1009.    ECHOLINK *El;
  1010.  
  1011.    if ((El = (ECHOLINK *)Data.First ()) != NULL) {
  1012.       EchoTag = El->EchoTag;
  1013.       Zone = El->Zone;
  1014.       Net = El->Net;
  1015.       Node = El->Node;
  1016.       Point = El->Point;
  1017.       strcpy (Domain, El->Domain);
  1018.       SendOnly = El->SendOnly;
  1019.       ReceiveOnly = El->ReceiveOnly;
  1020.       PersonalOnly = El->PersonalOnly;
  1021.       Passive = El->Passive;
  1022.       Skip = El->Skip;
  1023.       if (Point == 0)
  1024.          sprintf (Address, "%u:%u/%u", Zone, Net, Node);
  1025.       else
  1026.          sprintf (Address, "%u:%u/%u.%u", Zone, Net, Node, Point);
  1027.       if (Domain[0] != '\0') {
  1028.          strcat (Address, "@");
  1029.          strcat (Address, Domain);
  1030.       }
  1031.       if (Skip4D == TRUE && Point != 0)
  1032.          ShortAddress[0] = '\0';
  1033.       else
  1034.          strcpy (ShortAddress, Address);
  1035.       RetVal = TRUE;
  1036.    }
  1037.  
  1038.    return (RetVal);
  1039. }
  1040.  
  1041. VOID TEchoLink::Load (PSZ pszEchoTag)
  1042. {
  1043.    int fd, i, Count;
  1044.    CHAR Temp[64];
  1045.    ULONG Crc;
  1046.    ECHOLINK *Buffer;
  1047.  
  1048.    Data.Clear ();
  1049.    strcpy (Temp, pszEchoTag);
  1050.    strupr (Temp);
  1051.    Crc = StringCrc32 (Temp, 0xFFFFFFFFL);
  1052.    EchoTag = Crc;
  1053.  
  1054.    if ((fd = sopen (DataFile, O_RDONLY|O_BINARY, SH_DENYNO, S_IREAD|S_IWRITE)) != -1) {
  1055.       if ((Buffer = (ECHOLINK *)malloc (sizeof (ECHOLINK) * ECHOLINK_INDEX)) != NULL) {
  1056.          do {
  1057.             Count = read (fd, Buffer, sizeof (ECHOLINK) * ECHOLINK_INDEX) / sizeof (ECHOLINK);
  1058.             for (i = 0; i < Count; i++) {
  1059.                if (Buffer[i].Free == FALSE && Buffer[i].EchoTag == Crc)
  1060.                   Data.Add (&Buffer[i], sizeof (ECHOLINK));
  1061.             }
  1062.          } while (Count == ECHOLINK_INDEX);
  1063.          free (Buffer);
  1064.       }
  1065.       close (fd);
  1066.    }
  1067.  
  1068.    First ();
  1069. }
  1070.  
  1071. VOID TEchoLink::New (VOID)
  1072. {
  1073.    Zone = 0;
  1074.    Net = 0;
  1075.    Node = 0;
  1076.    Point = 0;
  1077.    Address[0] = Domain[0] = '\0';
  1078.    SendOnly = FALSE;
  1079.    ReceiveOnly = FALSE;
  1080.    PersonalOnly = FALSE;
  1081.    Passive = FALSE;
  1082.    Skip = FALSE;
  1083. }
  1084.  
  1085. USHORT TEchoLink::Next (VOID)
  1086. {
  1087.    USHORT RetVal = FALSE;
  1088.    ECHOLINK *El;
  1089.  
  1090.    if ((El = (ECHOLINK *)Data.Next ()) != NULL) {
  1091.       if (Skip4D == FALSE || El->Point == 0) {
  1092.          if (Zone != El->Zone) {
  1093.             if (El->Point == 0)
  1094.                sprintf (ShortAddress, "%u:%u/%u", El->Zone, El->Net, El->Node);
  1095.             else
  1096.                sprintf (ShortAddress, "%u:%u/%u.%u", El->Zone, El->Net, El->Node, El->Point);
  1097.          }
  1098.          else if (Net != El->Net) {
  1099.             if (El->Point == 0)
  1100.                sprintf (ShortAddress, "%u/%u", El->Net, El->Node);
  1101.             else
  1102.                sprintf (ShortAddress, "%u/%u.%u", El->Net, El->Node, El->Point);
  1103.          }
  1104.          else if (Node != El->Node) {
  1105.             if (El->Point == 0)
  1106.                sprintf (ShortAddress, "%u", El->Node);
  1107.             else
  1108.                sprintf (ShortAddress, "%u.%u", El->Node, El->Point);
  1109.          }
  1110.          else
  1111.             sprintf (ShortAddress, ".%u", El->Point);
  1112.       }
  1113.       else
  1114.          ShortAddress[0] = '\0';
  1115.  
  1116.       EchoTag = El->EchoTag;
  1117.       Zone = El->Zone;
  1118.       Net = El->Net;
  1119.       Node = El->Node;
  1120.       Point = El->Point;
  1121.       strcpy (Domain, El->Domain);
  1122.       SendOnly = El->SendOnly;
  1123.       ReceiveOnly = El->ReceiveOnly;
  1124.       PersonalOnly = El->PersonalOnly;
  1125.       Passive = El->Passive;
  1126.       Skip = El->Skip;
  1127.       if (Point == 0)
  1128.          sprintf (Address, "%u:%u/%u", Zone, Net, Node);
  1129.       else
  1130.          sprintf (Address, "%u:%u/%u.%u", Zone, Net, Node, Point);
  1131.       if (Domain[0] != '\0') {
  1132.          strcat (Address, "@");
  1133.          strcat (Address, Domain);
  1134.       }
  1135.       RetVal = TRUE;
  1136.    }
  1137.  
  1138.    return (RetVal);
  1139. }
  1140.  
  1141. USHORT TEchoLink::Previous (VOID)
  1142. {
  1143.    USHORT RetVal = FALSE;
  1144.    ECHOLINK *El;
  1145.  
  1146.    if ((El = (ECHOLINK *)Data.Previous ()) != NULL) {
  1147.       EchoTag = El->EchoTag;
  1148.       Zone = El->Zone;
  1149.       Net = El->Net;
  1150.       Node = El->Node;
  1151.       Point = El->Point;
  1152.       strcpy (Domain, El->Domain);
  1153.       SendOnly = El->SendOnly;
  1154.       ReceiveOnly = El->ReceiveOnly;
  1155.       PersonalOnly = El->PersonalOnly;
  1156.       Passive = El->Passive;
  1157.       Skip = El->Skip;
  1158.       if (Point == 0)
  1159.          sprintf (Address, "%u:%u/%u", Zone, Net, Node);
  1160.       else
  1161.          sprintf (Address, "%u:%u/%u.%u", Zone, Net, Node, Point);
  1162.       if (Domain[0] != '\0') {
  1163.          strcat (Address, "@");
  1164.          strcat (Address, Domain);
  1165.       }
  1166.       RetVal = TRUE;
  1167.    }
  1168.  
  1169.    return (RetVal);
  1170. }
  1171.  
  1172. VOID TEchoLink::Save (VOID)
  1173. {
  1174.    int fd, i, Count, Changed;
  1175.    ULONG Position;
  1176.    ECHOLINK *Buffer, *Record;
  1177.  
  1178.    if ((fd = sopen (DataFile, O_RDWR|O_BINARY|O_CREAT, SH_DENYNO, S_IREAD|S_IWRITE)) != -1) {
  1179.       Record = (ECHOLINK *)Data.First ();
  1180.  
  1181.       if ((Buffer = (ECHOLINK *)malloc (sizeof (ECHOLINK) * ECHOLINK_INDEX)) != NULL) {
  1182.          do {
  1183.             Changed = FALSE;
  1184.  
  1185.             Position = tell (fd);
  1186.             Count = read (fd, Buffer, sizeof (ECHOLINK) * ECHOLINK_INDEX) / sizeof (ECHOLINK);
  1187.             for (i = 0; i < Count && Record != NULL; i++) {
  1188.                if (Buffer[i].EchoTag == Record->EchoTag || Buffer[i].Free == TRUE) {
  1189.                   memcpy (&Buffer[i], Record, sizeof (ECHOLINK));
  1190.                   Record = (ECHOLINK *)Data.Next ();
  1191.                   Changed = TRUE;
  1192.                }
  1193.             }
  1194.  
  1195.             for (; i < Count; i++) {
  1196.                if (Buffer[i].EchoTag == EchoTag) {
  1197.                   memset (&Buffer[i], 0, sizeof (ECHOLINK));
  1198.                   Buffer[i].Free = TRUE;
  1199.                   Changed = TRUE;
  1200.                }
  1201.             }
  1202.  
  1203.             if (Changed == TRUE) {
  1204.                lseek (fd, Position, SEEK_SET);
  1205.                write (fd, Buffer, sizeof (ECHOLINK) * Count);
  1206.             }
  1207.          } while (Count == ECHOLINK_INDEX);
  1208.          free (Buffer);
  1209.       }
  1210.  
  1211.       if (Record != NULL) {
  1212.          do {
  1213.             write (fd, Record, sizeof (ECHOLINK));
  1214.          } while ((Record = (ECHOLINK *)Data.Next ()) != NULL);
  1215.       }
  1216.  
  1217.       close (fd);
  1218.    }
  1219. }
  1220.  
  1221. VOID TEchoLink::Update (VOID)
  1222. {
  1223.    ECHOLINK *Buffer;
  1224.  
  1225.    if ((Buffer = (ECHOLINK *)Data.Value ()) != NULL) {
  1226.       Buffer->EchoTag = EchoTag;
  1227.       Buffer->Zone = Zone;
  1228.       Buffer->Net = Net;
  1229.       Buffer->Node = Node;
  1230.       Buffer->Point = Point;
  1231.       strcpy (Buffer->Domain, Domain);
  1232.       Buffer->SendOnly = SendOnly;
  1233.       Buffer->ReceiveOnly = ReceiveOnly;
  1234.       Buffer->PersonalOnly = PersonalOnly;
  1235.       Buffer->Passive = Passive;
  1236.       Buffer->Skip = Skip;
  1237.       if (Point == 0)
  1238.          sprintf (Address, "%u:%u/%u", Zone, Net, Node);
  1239.       else
  1240.          sprintf (Address, "%u:%u/%u.%u", Zone, Net, Node, Point);
  1241.       if (Domain[0] != '\0') {
  1242.          strcat (Address, "@");
  1243.          strcat (Address, Domain);
  1244.       }
  1245.    }
  1246. }
  1247.  
  1248. // --------------------------------------------------------------------------
  1249.  
  1250. TEchotoss::TEchotoss (PSZ path)
  1251. {
  1252.    strcpy (DataFile, path);
  1253.    strcat (DataFile, "echotoss.log");
  1254.    AdjustPath (strlwr (DataFile));
  1255.  
  1256.    Data.Clear ();
  1257. }
  1258.  
  1259. TEchotoss::~TEchotoss (void)
  1260. {
  1261.    Data.Clear ();
  1262. }
  1263.  
  1264. VOID TEchotoss::Add (PSZ tag)
  1265. {
  1266.    USHORT Found = FALSE;
  1267.    PSZ p;
  1268.  
  1269.    if ((p = (PSZ)Data.First ()) != NULL)
  1270.       do {
  1271.          if (!stricmp (p, tag)) {
  1272.             Found = TRUE;
  1273.             break;
  1274.          }
  1275.       } while ((p = (PSZ)Data.Next ()) != NULL);
  1276.  
  1277.    if (Found == FALSE)
  1278.       Data.Add (tag);
  1279. }
  1280.  
  1281. VOID TEchotoss::Clear (VOID)
  1282. {
  1283.    Data.Clear ();
  1284. }
  1285.  
  1286. VOID TEchotoss::Delete (VOID)
  1287. {
  1288.    unlink (DataFile);
  1289. }
  1290.  
  1291. USHORT TEchotoss::First (VOID)
  1292. {
  1293.    USHORT RetVal = FALSE;
  1294.    PSZ p;
  1295.  
  1296.    if ((p = (PSZ)Data.First ()) != NULL) {
  1297.       strcpy (Tag, p);
  1298.       RetVal = TRUE;
  1299.    }
  1300.  
  1301.    return (RetVal);
  1302. }
  1303.  
  1304. USHORT TEchotoss::Load (VOID)
  1305. {
  1306.    FILE *fp;
  1307.    USHORT RetVal = FALSE;
  1308.    CHAR Temp[128], *p;
  1309.  
  1310.    if ((fp = fopen (DataFile, "rt")) != NULL) {
  1311.       RetVal = TRUE;
  1312.       while (fgets (Temp, sizeof (Temp) - 1, fp) != NULL) {
  1313.          if ((p = strtok (Temp, " \r\n")) != NULL)
  1314.             Add (p);
  1315.       }
  1316.       fclose (fp);
  1317.    }
  1318.  
  1319.    return (RetVal);
  1320. }
  1321.  
  1322. USHORT TEchotoss::Next (VOID)
  1323. {
  1324.    USHORT RetVal = FALSE;
  1325.    PSZ p;
  1326.  
  1327.    if ((p = (PSZ)Data.Next ()) != NULL) {
  1328.       strcpy (Tag, p);
  1329.       RetVal = TRUE;
  1330.    }
  1331.  
  1332.    return (RetVal);
  1333. }
  1334.  
  1335. VOID TEchotoss::Save (VOID)
  1336. {
  1337.    FILE *fp;
  1338.    PSZ p;
  1339.  
  1340.    if ((fp = fopen (DataFile, "wt")) != NULL) {
  1341.       if ((p = (PSZ)Data.First ()) != NULL)
  1342.          do {
  1343.             fprintf (fp, "%s\n", p);
  1344.          } while ((p = (PSZ)Data.Next ()) != NULL);
  1345.       fclose (fp);
  1346.    }
  1347. }
  1348.  
  1349.  
  1350.