Unit MKMsgFid;       {Fido *.Msg Unit}

{$I-}
{$IFNDEF OS2}
{$V-,S-,F+,R-,O+}
{$ENDIF}

Interface

Uses
  MKGlobT,
  MKMsgAbs,
{$IFNDEF WIN32}
  DOS,
{$ELSE}
  SysUtils,
  OpCrt,
{$ENDIF}
  tMisc,
  MkFile,
  MkDOS,
  Objects,
  tGlob;

Const
  MaxFidMsgArray = 4000;
  MaxFidMsgNum = (MaxFidMsgArray * 8) - 1;

Type
  FMsgType = Record
    MsgFile: shFile;
    TextCtr: LongInt;
    MsgName: String[13];
    TmpName: String[130];
    TmpOpen: Boolean;
    MsgOpen: Boolean;
    Error: Word;
    NetMailPath: String[128];
    Dest: AddrType;
    Orig: AddrType;
    MsgStart: LongInt;
    MsgEnd: LongInt;
    MsgSize: LongInt;
    DefaultZone: Word;
    QDate: String[8];
    QTime: String[5];
    MsgDone: Boolean;
    CurrMsg: LongInt;
    SeekOver: Boolean;
    SR: {$IFNDEF WIN32} SearchRec; {$ELSE} TSearchRec; {$ENDIF}
    Name: String[35];
    Handle: String[35];
    MailType: MsgMailType;
    MsgPresent: Array [0..MaxFidMsgArray] of Byte;
  End;

  FidoMsgObj = Object (AbsMsgObj)
    FM                    : ^FMsgType;
    SeekOver              : Boolean;

    Constructor Init;                      {Initialize FidoMsgOut}
    Destructor Done; Virtual; {Done FidoMsgOut}
    Procedure RemoveTmp; {remove temporary file}
    Procedure PutLong (L: LongInt; Position: LongInt); {Put long into msg}
    Procedure PutWord (W: Word; Position: LongInt);  {Put word into msg}
    Procedure PutByte (B: Byte; Position: LongInt);  {Put byte into msg}
    Function  GetByte (Position: LongInt): Byte; {Get byte from msg}
    Function  GetWord (Position: LongInt): Word; {Get word from msg}
    Procedure PutNullStr(St: String; Position: LongInt);  {Put string & null into msg}
    Procedure SetMsgPath(St: String); Virtual; {Set netmail path}
    Procedure SetDest(Var Addr: AddrType); Virtual; {Set Zone/Net/Node/Point for Dest}
    Procedure SetOrig(Var Addr: AddrType); Virtual; {Set Zone/Net/Node/Point for Orig}
    Procedure SetFrom(Name: String); Virtual; {Set message from}
    Procedure SetTo(Name: String); Virtual; {Set message to}
    Procedure SetSubj(Str: String); Virtual; {Set message subject}
    Procedure SetCost(SCost: Word); Virtual; {Set message cost}
    Procedure SetRefer(SRefer: LongInt); Virtual; {Set message reference}
    Procedure SetSeeAlso(SAlso: LongInt); Virtual; {Set message see also}
    Procedure SetDate(SDate: String); Virtual; {Set message date}
    Procedure SetTime(STime: String); Virtual; {Set message time}
    Procedure SetLocal(LS: Boolean); Virtual; {Set local status}
    Procedure SetRcvd(RS: Boolean); Virtual; {Set received status}
    Procedure SetPriv(PS: Boolean); Virtual; {Set priveledge vs public status}
    Procedure SetCrash(SS: Boolean); Virtual; {Set crash netmail status}
    Procedure SetKillSent(SS: Boolean); Virtual; {Set kill/sent netmail status}
    Procedure SetSent (SS: Boolean); Virtual; {Set sent netmail status}
    Procedure SetFAttach (SS: Boolean); Virtual; {Set file attach status}
    Procedure SetReqRct (SS: Boolean); Virtual; {Set request receipt status}
    Procedure SetReqAud (SS: Boolean); Virtual; {Set request audit status}
    Procedure SetRetRct (SS: Boolean); Virtual; {Set return receipt status}
    Procedure SetFileReq (SS: Boolean); Virtual; {Set file request status}
    Procedure SetHold (SS: Boolean); Virtual; {Set file request status}
    Procedure DoString(Str: String); Virtual; {Add string to message text}
    Procedure DoChar(Ch: Char); Virtual; {Add character to message text}
    Procedure DoStringLn(Str: String); Virtual; {Add string and newline to msg text}
    Function  WriteMsg: Word; Virtual;
    Function  ReWriteMsg: Word; Virtual;
    Procedure SetDefaultZone(DZ: Word); Virtual; {Set default zone to use}
    Procedure LineStart; Virtual; {Internal use to skip LF, ^A}
    Function  GetChar: Char; Virtual;
    Procedure CheckZone(ZoneStr: String); Virtual;
    Procedure CheckPoint(PointStr: String); Virtual;
    Procedure CheckLine(TStr: String); Virtual;
    Function  CvtDate: Boolean; Virtual;
    Function  BufferWord(i: Word):Word; Virtual;
    Function  BufferByte(i: Word):Byte; Virtual;
    Function  BufferNullString(i: Word; Max: Word): String; Virtual;
    Procedure MsgStartUp; Virtual; {set up msg for reading}
    Function  EOM: Boolean; Virtual; {No more msg text}
    Function  GetString(MaxLen: Word): String; Virtual; {Get wordwrapped string}
    Function  WasWrap: Boolean; Virtual; {Last line was soft wrapped no CR}
    Procedure SeekFirst(MsgNum: LongInt); Virtual; {Seek msg number}
    Procedure SeekNext; Virtual; {Find next matching msg}
    Procedure SeekPrior; Virtual; {Seek prior matching msg}
    Function  GetFrom: String; Virtual; {Get from name on current msg}
    Function  GetTo: String; Virtual; {Get to name on current msg}
    Function  GetSubj: String; Virtual; {Get subject on current msg}
    Function  GetCost: Word; Virtual; {Get cost of current msg}
    Function  GetDate: String; Virtual; {Get date of current msg}
    Function  GetTime: String; Virtual; {Get time of current msg}
    Function  GetRefer: LongInt; Virtual; {Get reply to of current msg}
    Function  GetSeeAlso: LongInt; Virtual; {Get see also of current msg}
    Function  GetMsgNum: LongInt; Virtual; {Get message number}
    Procedure GetOrig(Var Addr: AddrType); Virtual; {Get origin address}
    Procedure GetDest(Var Addr: AddrType); Virtual; {Get destination address}
    Function  IsLocal: Boolean; Virtual; {Is current msg local}
    Function  IsCrash: Boolean; Virtual; {Is current msg crash}
    Function  IsKillSent: Boolean; Virtual; {Is current msg kill sent}
    Function  IsSent: Boolean; Virtual; {Is current msg sent}
    Function  IsFAttach: Boolean; Virtual; {Is current msg file attach}
    Function  IsReqRct: Boolean; Virtual; {Is current msg request receipt}
    Function  IsReqAud: Boolean; Virtual; {Is current msg request audit}
    Function  IsRetRct: Boolean; Virtual; {Is current msg a return receipt}
    Function  IsFileReq: Boolean; Virtual; {Is current msg a file request}
    Function  IsRcvd: Boolean; Virtual; {Is current msg received}
    Function  IsPriv: Boolean; Virtual; {Is current msg priviledged/private}
    Function  IsDeleted: Boolean; Virtual; {Is current msg deleted}
    Function  IsEchoed: Boolean; Virtual; {Msg should be echoed}
    Function  IsHold: Boolean; Virtual; {Is current msg hold}
    Function  GetMsgLoc: LongInt; Virtual; {Msg location}
    Procedure SetMsgLoc(ML: LongInt); Virtual; {Msg location}
    Procedure YoursFirst(Name: String; Handle: String); Virtual; {Seek your mail}
    Procedure YoursNext; Virtual; {Seek next your mail}
    Function  YoursFound: Boolean; Virtual; {Message found}
    Procedure StartNewMsg; Virtual;
    Function  OpenMsgBase: Word; Virtual;
    Function  CloseMsgBase: Word; Virtual;
    Function  CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word; Virtual;
    Function  SeekFound: Boolean; Virtual;
    Procedure SetMailType(MT: MsgMailType); Virtual; {Set message base type}
    Function  GetSubArea: Word; Virtual; {Get sub area number}
    Procedure ReWriteHdr; Virtual; {Rewrite msg header after changes}
    Procedure DeleteMsg (CarePos: Boolean); Virtual; {Delete current message}
    Function  GetLastRead(UNum: LongInt): LongInt; Virtual; {Get last read for user num}
    Procedure SetLastRead(UNum: LongInt; LR: LongInt); Virtual; {Set last read}
    Procedure MsgTxtStartUp; Virtual; {Do message text start up tasks}
    Function  GetTxtPos: LongInt; Virtual; {Get indicator of msg text position}
    Procedure SetTxtPos(TP: LongInt); Virtual; {Set text position}
    Function  MsgBaseExists: Boolean; Virtual;
    Procedure Rescan;
    Function  MsgExists(MsgNum: LongInt): Boolean;
    Procedure GetTotalMsgs; Virtual;
    Function  GetMsgNumRelative: LongInt; Virtual;
  End;

  FidoMsgPtr = ^FidoMsgObj;

Function MonthNum (St: String): Word;

Implementation

Const
  PosArray: Array [0..7] Of Byte = (1, 2, 4, 8, 16, 32, 64, 128);
  fidPriv     = $0001;
  fidCrash    = $0002;
  fidRcvd     = $0004;
  fidSent     = $0008;
  fidFAttach  = $0010;
  fidKillSent = $0080;
  fidLocal    = $0100;
  fidHold     = $0200;
  fidFreq     = $0800;
  fidRRQ      = $1000;
  fidRRC      = $2000;
  fidARQ      = $4000;

Constructor FidoMsgObj.Init;
Begin
  New (FM);

  If FM = Nil Then
  Begin
    Fail;
    Exit;
  End;

  FM^. NetMailPath := '';
  FM^. TextCtr := 190;
  FM^. Dest.Zone := 0;
  FM^. Orig.Zone := 0;
  FM^. SeekOver := False;
  FM^. DefaultZone := 2;
  FM^. TmpOpen := False;
  FM^. MsgOpen := False;

  C := New (PCollection, Init (10, 10));
  CurMsg := 1;
End;

Destructor FidoMsgObj.Done;
Begin
  If C <> Nil Then
  Begin
    While C^. Count > 0 Do C^. AtDelete (0);
    Dispose (C, Done);
  End;

  If FM^. MsgOpen Then shClose (FM^. MsgFile);
  If FM^. TmpOpen Then RemoveTmp;
  Dispose (FM);
End;

Procedure FidoMsgObj. RemoveTmp;
Var
  TmpFile     : shFile;

Begin
  If FM^. MsgOpen Then shClose (FM^. MsgFile);
  shAssign (TmpFile, FM^. TmpName);
  tDeleteFile (FM^. TmpName);
  If shIOResult <> 0 Then;
  FM^. TmpOpen := False;
End;

Procedure FidoMsgObj. PutLong (L: LongInt; Position: LongInt);
Begin
  If shSeekFile (FM^. MsgFile, Position) Then
  shWrite (FM^. MsgFile, L, SizeOf (LongInt));
End;

Procedure FidoMsgObj. PutWord (W: Word; Position: LongInt);
Begin
  If shSeekFile (FM^. MsgFile, Position) Then
  shWrite (FM^. MsgFile, W, SizeOf (Word));
End;

Procedure FidoMsgObj. PutByte (B: Byte; Position: LongInt);
Begin
  If shSeekFile (FM^. MsgFile, Position) Then
  shWrite (FM^. MsgFile, B, SizeOf (Byte));
End;

Function FidoMsgObj. GetByte (Position: LongInt): Byte;
Var
  B             : Byte;
  NumRead       : SysInt;

Begin
  If shSeekFile (FM^. MsgFile, Position) Then
  shRead (FM^. MsgFile, B, SizeOf(Byte), NumRead);
  GetByte := B;
End;

Function FidoMsgObj. GetWord (Position: LongInt): Word;
Var
  B             : Word;
  NumRead       : SysInt;

Begin
  If shSeekFile (FM^. MsgFile, Position) Then
  shRead (FM^. MsgFile, B, SizeOf (Word), NumRead);
  GetWord := B;
End;

Procedure FidoMsgObj. PutNullStr (St: String; Position: LongInt);
Var
  i : Byte;

Begin
  i := 0;
  If shSeekFile (FM^. MsgFile, Position) Then
  Begin
    shWrite (FM^. MsgFile, St [1], Length (St));
    shWrite (FM^. MsgFile, i, 1);
  End;
End;

Procedure FidoMsgObj. GetTotalMsgs;
Var
  DirInfo       : {$IFNDEF WIN32} SearchRec {$ELSE} TSearchRec {$ENDIF};

Begin
{$IFDEF WIN32}
  DOSerror :=
{$ENDIF}
  FindFirst (AddBackSlash (FM^. NetMailPath) + '*.msg', AnyFile-Directory{$IFNDEF OS2}-VolumeID{$ENDIF}, DirInfo);

  While DOSerror = 0 Do
  Begin
    C^. Insert (Pointer (Str2Long (ExtractWord (1, DirInfo. Name, ['.']))));
{$IFDEF WIN32}
    DOSerror :=
{$ENDIF}
    FindNext (DirInfo);
  End;

{$IFNDEF MSDOS}
  FindClose (DirInfo);
{$ENDIF}
End;

Procedure FidoMsgObj. SetMsgPath (St: String);
Begin
  FM^. NetMailPath := AddBackSlash (Copy (St, 1, 110));
End;
{
Function FidoMsgObj. GetHighMsgNum: LongInt;
Var
  Highest, Cnt  : LongInt;

Begin
  Cnt := MaxFidMsgArray;

  While (Cnt > 0) and (FM^. MsgPresent [Cnt] = 0) Do Dec (Cnt);
  If Cnt < 0 Then Highest := 0 Else
  Begin
    Highest := Cnt * 8;
    If (FM^.MsgPresent[Cnt] and $80) <> 0 Then Inc (Highest, 7)
    Else If (FM^.MsgPresent[Cnt] and $40) <> 0 Then Inc (Highest, 6)
    Else If (FM^.MsgPresent[Cnt] and $20) <> 0 Then Inc (Highest, 5)
    Else If (FM^.MsgPresent[Cnt] and $10) <> 0 Then Inc (Highest, 4)
    Else If (FM^.MsgPresent[Cnt] and $08) <> 0 Then Inc (Highest, 3)
    Else If (FM^.MsgPresent[Cnt] and $04) <> 0 Then Inc (Highest, 2)
    Else If (FM^.MsgPresent[Cnt] and $02) <> 0 Then Inc (Highest, 1)
  End;

  GetHighMsgNum := Highest;
End;
}

Procedure FidoMsgObj. SetDest (Var Addr: AddrType);
Begin
  FM^. Dest := Addr;
  PutWord (Addr. Net, 174);
  PutWord (Addr. Node, 166);

  If ((Addr.Point <> 0) and (FM^.MailType = mmtNetmail)) Then
  Begin
    If ((FM^. TextCtr <> 190) And (GetByte (FM^. TextCtr - 1) <> 13)) Then DoChar(#13);
    DoStringLn (#1 + 'TOPT ' + Long2Str (Addr. Point));
  End;

  If ((FM^.Orig.Zone <> 0) and (FM^.MailTYpe = mmtNetMail)) Then
  Begin
    If ((FM^. TextCtr <> 190) And (GetByte (FM^. TextCtr - 1) <> 13)) Then DoChar (#13);
    DoStringLn (#1'INTL ' + PointlessAddrStr (FM^. Dest) + ' ' + PointlessAddrStr (FM^. Orig));
  End;
End;

Procedure FidoMsgObj. SetOrig (Var Addr: AddrType);
Begin
  FM^. Orig := Addr;
  PutWord (Addr. Net, 172);
  PutWord (Addr. Node, 168);

  If ((Addr.Point <> 0) and (FM^.MailType = mmtNetmail)) Then
  Begin
    If ((FM^.TextCtr <> 190) And (GetByte (FM^.TextCtr - 1) <> 13)) Then DoChar (#13);
    DoStringLn (#1'FMPT ' + Long2Str (Addr. Point));
  End;

  If ((FM^.Dest.Zone <> 0) and (FM^.MailType = mmtNetmail)) Then
  Begin
    If ((FM^.TextCtr <> 190) And (GetByte(FM^.TextCtr - 1) <> 13)) Then DoChar (#13);
    DoStringLn (#1'INTL ' + PointlessAddrStr (FM^. Dest) + ' ' + PointlessAddrStr (FM^. Orig));
  End;
End;

Procedure FidoMsgObj. SetFrom (Name: String);
Begin
  PutNullStr (Copy (Name, 1, 35), 0);
End;

Procedure FidoMsgObj. SetTo (Name: String);
Begin
  PutNullStr (Copy (Name, 1, 35), 36);
End;

Procedure FidoMsgObj. SetSubj (Str: String);
Begin
  PutNullStr (Copy (Str, 1, 71), 72);
End;

Procedure FidoMsgObj. SetCost(SCost: Word);
Begin
  PutWord (SCost, 170);
End;

Procedure FidoMsgObj. SetRefer(SRefer: LongInt);
Begin
  PutWord (SRefer, 184);
End;

Procedure FidoMsgObj. SetSeeAlso(SAlso: LongInt);
Begin
  PutWord (SAlso, 188);
End;

Procedure FidoMsgObj. SetDate (SDate: String);
Var
  TempNum : Word;
  Code    : SysInt;
  TmpStr  : String [20];

Begin
  FM^. QDate := Copy (SDate, 1, 8);
  Val (Copy (SDate, 1, 2), TempNum, Code);
  TmpStr := Copy (SDate, 4, 2) + ' ' + MonthStr (TempNum) + ' ' + Copy (SDate, 7, 2) + '  ';
  For TempNum := 1 to 11 Do PutByte (Ord (TmpStr [TempNum]), TempNum + 143);
End;

Procedure FidoMsgObj. SetTime (STime: String);
Begin
  FM^. QTime := Copy (STime, 1, 5);
  PutNullStr (Copy (STime + ':00', 1, 8), 155);
End;

Procedure FidoMsgObj. SetLocal (LS: Boolean);
Begin
  If LS
  Then PutWord (GetWord (186) or fidLocal, 186)
  Else PutWord (GetWord (186) and (Not fidLocal), 186);
End;

Procedure FidoMsgObj. SetRcvd (RS: Boolean);
Begin
  If RS
  Then PutWord (GetWord (186) or fidRcvd, 186)
  Else PutWord (GetWord (186) and (not fidRcvd), 186);
End;

Procedure FidoMsgObj. SetPriv (PS: Boolean);
Begin
  If PS
  Then PutWord (GetWord (186) or fidPriv, 186)
  Else PutWord (GetWord (186) and (not fidPriv), 186);
End;

Procedure FidoMsgObj. SetCrash (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidCrash, 186)
  Else PutWord (GetWord (186) and (not fidCrash), 186);
End;

Procedure FidoMsgObj. SetKillSent (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidKillSent, 186)
  Else PutWord (GetWord (186) and (Not fidKillSent), 186);
End;

Procedure FidoMsgObj. SetSent (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidSent, 186)
  Else PutWord (GetWord (186) and (not fidSent), 186);
End;

Procedure FidoMsgObj. SetFAttach (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidFAttach, 186)
  Else PutWord (GetWord (186) and (not fidFAttach), 186);
End;

Procedure FidoMsgObj. SetReqRct (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidRRQ, 186)
  Else PutWord (GetWord (186) and (not fidRRQ), 186);
End;

Procedure FidoMsgObj. SetReqAud (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidARQ, 186)
  Else PutWord (GetWord (186) and (not fidARQ), 186);
End;

Procedure FidoMsgObj. SetRetRct (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidRRC, 186)
  Else PutWord (GetWord (186) and (not fidRRC), 186);
End;

Procedure FidoMsgObj. SetHold (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidHold, 186)
  Else PutWord (GetWord (186) and (not fidHold), 186);
End;

Procedure FidoMsgObj. SetFileReq (SS: Boolean);
Begin
  If SS
  Then PutWord (GetWord (186) or fidFreq, 186)
  Else PutWord (GetWord (186) and (not fidFreq), 186);
End;

Procedure FidoMsgObj. DoString (Str: String);
Var
  i     : Word;

Begin
  i := 1;
  While i <= Length (Str) Do
  Begin
    DoChar (Str [i]);
    Inc (i);
  End;
End;

Procedure FidoMsgObj. DoChar (Ch: Char);
Begin
  PutByte (Ord (Ch), FM^. TextCtr);
  Inc (FM^.TextCtr);
End;

Procedure FidoMsgObj. DoStringLn (Str: String);
Begin
  DoString (Str);
  DoChar (#13);
End;

Function FidoMsgObj. WriteMsg: Word;
Var
  NetNum        : Word;
  TmpDate, Code : LongInt;
  TmpDT         : DateTime;
  TmpFile       : File;
  S             : String;

Begin
  If aDir or aImm or aLock Then
  Begin
    S := '';
    If aDir Then S := S + 'DIR ';
    If aImm Then S := S + 'IMM ';
    If aLock Then S := S + 'LOK ';
    S := Trim (S);
    DoStringLn ('');
    DoKludgeLn (#1'FLAGS ' + S);
  End;

  DoChar (#0);
  PutLong (GetDosDate, 180);
  TmpDT. Year := Str2Long (Copy (FM^.QDate, 7, 2));
  If TmpDT. Year > 79 Then Inc (TmpDT.Year, 1900) Else Inc (TmpDT.Year, 2000);

  TmpDT. Month := Str2Long (Copy (FM^. QDate, 1, 2));
  TmpDT. Day   := Str2Long (Copy (FM^. QDate, 4, 2));
  TmpDt. Hour  := Str2Long (Copy (FM^. QTime, 1, 2));
  TmpDt. Min   := Str2Long (Copy (FM^. QTime, 4, 2));
  TmpDt. Sec   := 0;

  PackTime (TmpDT, TmpDate);
  PutLong (TmpDate, 176);
  NetNum := GetHighMsgNum + 1;

  With FM^ Do
  Begin
    While FileExists (NetMailPath + Long2Str (NetNum) + '.msg') Do
    Begin
      Rescan;
      NetNum := GetHighMsgNum + 1;
    End;

    Code := NetNum Shr 3; {div by 8 to get byte position}
    MsgPresent [Code] := MsgPresent [Code] Or PosArray [NetNum and 7];

    If TmpOpen Then
    Begin
      shClose (FM^. MsgFile);
      tRenameFile (TmpName, NetMailPath + Long2Str (NetNum) + '.msg');
      (*
      Assign (TmpFile, FM^. TmpName);
      ReName (TmpFile, FM^. NetMailPath + Long2Str (NetNum) + '.msg');
      *)
    End;

    WriteMsg := IOResult;
    CurrMsg := NetNum;
    SeekOver := False;
  End;

  C^. Insert (Pointer (NetNum));
End;

Function FidoMsgObj. ReWriteMsg: Word;
Var
  NetNum        : Word;
  TmpDate, Code : LongInt;
  TmpDT         : DateTime;
  TmpFile       : File;
  FName         : PathStr;
  S             : String;

Begin
  If aDir or aImm or aLock Then
  Begin
    S := '';
    If aDir Then S := S + 'DIR ';
    If aImm Then S := S + 'IMM ';
    If aLock Then S := S + 'LOK ';
    S := Trim (S);
    DoStringLn ('');
    DoKludgeLn (#1'FLAGS ' + S);
  End;

  DoChar (#0);
  PutLong (GetDosDate, 180);
  TmpDT. Year := Str2Long (Copy (FM^.QDate, 7, 2));
  If TmpDT. Year > 79 Then
    Inc (TmpDT.Year, 1900)
  Else
    Inc (TmpDT.Year, 2000);

  TmpDT. Month := Str2Long (Copy (FM^. QDate, 1, 2));
  TmpDT. Day := Str2Long (Copy (FM^. QDate, 4, 2));
  TmpDt. Hour := Str2Long (Copy (FM^. QTime, 1, 2));
  TmpDt. Min := Str2Long (Copy (FM^. QTime, 4, 2));
  TmpDt. Sec := 0;
  PackTime (TmpDT, TmpDate);
  PutLong (TmpDate, 176);

  FName := FM^. NetMailPath + Long2Str (Relative2Num (CurMsg)) + '.MSG';
  tDeleteFile (FName);
  NetNum := FM^. CurrMsg;

  Code := NetNum shr 3; {div by 8 to get byte position}
  FM^.MsgPresent [Code] := FM^.MsgPresent [Code] or PosArray [NetNum and 7];

  If FM^. TmpOpen Then
  Begin
    shClose (FM^. MsgFile);
    Assign (TmpFile, FM^. TmpName);
    Rename (TmpFile, FName)
  End;

  ReWriteMsg := IoResult;
  SeekOver := False;
End;

Procedure FidoMsgObj.SetDefaultZone(DZ: Word); {Set default zone to use}
Begin
  FM^.DefaultZone := DZ;
End;

Procedure FidoMsgObj.LineStart;
  Begin
  If GetByte(FM^.TextCtr) = 10 Then
    Inc(FM^.TextCtr);
  If GetByte(FM^.TextCtr) = 1 Then
    Inc(FM^.TextCtr);
  End;


Function FidoMsgObj.GetChar: Char;
  Begin
  If ((FM^.TextCtr >= FM^.MsgSize) Or (GetByte(FM^.TextCtr) = 0)) Then
    Begin
    GetChar := #0;
    FM^.MsgDone := True;
    End
  Else
    Begin
    GetChar := Chr(GetByte(FM^.TextCtr));
    Inc(FM^.TextCtr);
    End;
  End;


Procedure FidoMsgObj.CheckZone(ZoneStr: String);
  Var
    DestZoneStr: String;
    Code: SysInt;

  Begin
  If (UpString(Copy(ZoneStr,1,4)) = 'INTL') Then
    Begin
    DestZoneStr := ExtractWord (2, ZoneStr, [' ']);
    DestZoneStr := Trim (DestZoneStr);
    DestZoneStr := Copy (DestZoneStr, 1, Pos(':', DestZoneStr) - 1);
    Val (DestZoneStr, FM^.Dest.Zone, Code);
    DestZoneStr := ExtractWord (3, ZoneStr, [' ']);
    DestZoneStr := Trim (DestZoneStr);
    DestZoneStr := Copy(DestZoneStr, 1, Pos(':', DestZoneStr) - 1);
    Val (DestZoneStr, FM^.Orig.Zone, Code);
    End;
  End;


Procedure FidoMsgObj.CheckPoint(PointStr: String);
  Var
    DestPointStr: String;
    Code: SysInt;
    Temp: Word;

  Begin
  If (UpString(Copy(PointStr,1,4)) = 'TOPT') Then
    Begin
    DestPointStr := ExtractWord(2, PointStr, [' ']);
    DestPointStr := Trim (DestPointStr);
    Val(DestPointStr, Temp, Code);
    If Code = 0 Then
      FM^.Dest.Point := Temp;
    End;
  If (UpString(Copy(PointStr,1,4)) = 'FMPT') Then
    Begin
    DestPointStr := ExtractWord (2, PointStr, [' ']);
    DestPointStr := Trim (DestPointStr);
    Val(DestPointStr, Temp, Code);
    If Code = 0 Then
      FM^.Orig.Point := Temp;
    End;
  End;


Function MonthNum(St: String):Word;
  Begin
  ST := UpString(St);
  MonthNum := 0;
  If St = 'JAN' Then MonthNum := 01;
  If St = 'FEB' Then MonthNum := 02;
  If St = 'MAR' Then MonthNum := 03;
  If St = 'APR' Then MonthNum := 04;
  If St = 'MAY' Then MonthNum := 05;
  If St = 'JUN' Then MonthNum := 06;
  If St = 'JUL' Then MonthNum := 07;
  If St = 'AUG' Then MonthNum := 08;
  If St = 'SEP' Then MonthNum := 09;
  If St = 'OCT' Then MonthNum := 10;
  If St = 'NOV' Then MonthNum := 11;
  If St = 'DEC' Then MonthNum := 12;
  End;


Function FidoMsgObj.CvtDate: Boolean;
  Var
    TmpStr: String;
    i: Word;
    MsgDt: String[25];

  Begin
  MsgDt := BufferNullString(144, 20);
  MsgDt := PadCh(MsgDt,' ', 20);
  CvtDate := True;
  If MsgDt[3] = ' ' Then
    Begin {Fido or Opus}
    If MsgDt[11] = ' ' Then
      Begin {Fido DD MON YY  HH:MM:SSZ}
      FM^.QTime := Copy (MsgDT,12,5);
      TmpStr := Long2Str(MonthNum(Copy(MsgDt,4,3)));
      If Length(TmpStr) = 1 Then
        TmpStr := '0' + TmpStr;
      FM^.QDate := TmpStr + '-' + Copy(MsgDT,1,2) + '-' + Copy (MsgDt,8,2);
      End
    Else
      Begin {Opus DD MON YY HH:MM:SS}
      FM^.QTime := Copy(MsgDT,11,5);
      TmpStr := Long2Str(MonthNum(Copy(MsgDt,4,3)));
      If Length(TmpStr) = 1 Then
        TmpStr := '0' + TmpStr;
      FM^.QDate := TmpStr + '-' + Copy(MsgDT,1,2) + '-' + Copy (MsgDt,8,2);
      End;
    End
  Else
    Begin
    If MsgDT[4] = ' ' Then
      Begin {SeaDog format DOW DD MON YY HH:MM}
      FM^.QTime := Copy(MsgDT,15,5);
      TmpStr := Long2Str(MonthNum(Copy(MsgDT,8,3)));
      If Length(TmpStr) = 1 Then
        TmpStr := '0' + TmpStr;
      FM^.QDate := TmpStr + '-' + Copy(MsgDT,5,2) + '-' + Copy (MsgDt,12,2);
      End
    Else
      Begin
      If MsgDT[3] = '-' Then
        Begin {Wierd format DD-MM-YYYY HH:MM:SS}
        FM^.QTime := Copy(MsgDt,12,5);
        FM^.QDate := Copy(MsgDt,4,3) + Copy (MsgDt,1,3) + Copy (MsgDt,9,2);
        End
      Else
        Begin  {Bad Date}
        CvtDate := False;
        End;
      End;
    End;
  For i := 1 to 5 Do
    If FM^.QTime[i] = ' ' Then
      FM^.QTime[i] := '0';
  For i := 1 to 8 Do
    If FM^.QDate[i] = ' ' Then
      FM^.QDate[i] := '0';
  If Length(FM^.QDate) <> 8 Then
    CvtDate := False;
  If Length(FM^.QTime) <> 5 Then
    CvtDate := False;
  End;


Function FidoMsgObj.BufferWord(i: Word):Word;
  Begin
  BufferWord := BufferByte(i) + (BufferByte(i + 1) shl 8);
  End;


Function FidoMsgObj.BufferByte(i: Word):Byte;
  Begin
  BufferByte := GetByte(i);
  End;


Function FidoMsgObj.BufferNullString(i: Word; Max: Word): String;
Var
  Ctr, CurrPos  : Word;
  Tmp           : String;

Begin
  Tmp := '';
  Ctr := i;
  CurrPos := 0;
  While ((CurrPos < Max) and (GetByte(Ctr) <> 0)) Do
  Begin
    Inc (CurrPos);
    Tmp [CurrPos] := Chr (GetByte (Ctr));
    Inc (Ctr);
  End;
  SetLength (Tmp, CurrPos);
  BufferNullString := Tmp;
End;

Procedure FidoMsgObj. CheckLine (TStr: String);
Begin
  If TStr [1] = #10 Then TStr := Copy (TStr, 2, 255);
  If TStr [1] = #01 Then TStr := Copy (TStr, 2, 255);
  CheckZone (TStr);
  CheckPoint (TStr);
End;

Procedure FidoMsgObj. MsgStartUp;
Var
  TStr          : String;
  TmpChr        : Char;
  NumRead       : SysInt;

Begin
  If FM^. MsgOpen Then shClose (FM^. MsgFile);
  FM^. MsgOpen := False;
  If FM^. TmpOpen Then RemoveTmp;

  LastSoft := False;

  If FileExists (FM^. NetMailPath + Long2Str (Relative2Num (CurMsg)) + '.MSG')
  Then FM^. Error := 0
  Else FM^. Error := 200;

  If FM^.Error = 0 Then
  Begin
    shAssign (FM^.MsgFile, FM^. NetMailPath + Long2Str (Relative2Num (CurMsg)) + '.MSG');
    If Not shReSet (FM^. MsgFile, 1) Then FM^. Error := 1000;
  End;

  If FM^. Error = 0 Then
  Begin
    FM^. MsgOpen := True;
    FM^. MsgDone := False;
  End;

  If FM^. MsgOpen Then
  Begin
    FM^. MsgSize := shFileSize (FM^. MsgFile);
    FM^. MsgEnd := 0;
    FM^. MsgStart := 190;
    FM^. Dest. Zone := FM^.DefaultZone;
    FM^. Dest. Point := 0;
    FM^. Orig. Zone := FM^.DefaultZone;
    FM^. Orig. Point := 0;
    FM^. Orig. Net := BufferWord (172);
    FM^. Orig. Node := BufferWord (168);
    FM^. Dest. Net := BufferWord (174);
    FM^. Dest. Node := BufferWord (166);
    FM^. TextCtr := FM^. MsgStart;
  End Else
  Begin
    FillChar (FM^. Dest, SizeOf (FM^. Dest), #0);
    FillChar (FM^. Orig, SizeOf (FM^. Orig), #0);
    FM^. MsgSize := 0;
  End;

  If FM^.Error = 0 Then
  Begin
    If Not CvtDate Then
    Begin
      FM^. QDate := '09-06-89';
      FM^. QTime := '19:76';
    End;

    TStr := GetString (128);
    CheckLine (TStr);
    Dec (FM^. TextCtr);

    If shSeekFile (FM^. MsgFile, FM^. TextCtr) Then shRead (FM^. MsgFile, TmpChr, 1, NumRead);

    While ((FM^.MsgEnd = 0) and (FM^.TextCtr <= FM^.MsgSize)) Do
    Case TmpChr Of
      #0  : FM^.MsgEnd := FM^.TextCtr;
      #13 : Begin
              Inc (FM^. TextCtr);
              TStr := GetString (128);
              CheckLine (TStr);
              If Length (TStr) > 0 Then Dec (FM^.TextCtr);
            End;
    Else
      Inc (FM^.TextCtr);
      shRead (FM^. MsgFile, TmpChr, 1, NumRead);
    End;

    If FM^.MsgEnd = 0 Then FM^.MsgEnd := FM^.MsgSize;
    FM^.MsgSize := FM^.MsgEnd;
    FM^.MsgStart := 190;
    FM^.TextCtr := FM^.MsgStart;
    FM^.MsgDone := False;
    LastSoft := False;
  End;
End;

Procedure FidoMsgObj.MsgTxtStartUp;
Begin
  FM^. MsgStart := 190;
  FM^. TextCtr := FM^. MsgStart;
  FM^. MsgDone := False;
  LastSoft := False;
End;

Function FidoMsgObj.GetString(MaxLen: Word): String;
Var
  WPos, StrCtr, CurrLen       : LongInt;
  WLen                        : Byte;
  StrDone, StartSoft          : Boolean;
  TmpCh                       : Char;
  TmpStr, Tmp                 : String;
  NumRead                     : SysInt;

Begin
  If MaxLen > 254 Then MaxLen := 254;
  StrDone := False;
  CurrLen := 0;
  WPos := 0;
  WLen := 0;
  StartSoft := LastSoft;
  LastSoft := True;
  Tmp := Replicate (' ', 255);

  If (FM^.TextCtr >= FM^.MsgSize) Then
  Begin
    TmpStr := #0;
    TmpCh := #0;
    FM^.MsgDone := True;
  End Else
  Begin
    If shSeekFile (FM^. MsgFile, FM^.TextCtr) Then
    shRead (FM^. MsgFile, TmpStr [1], 255, NumRead);
    SetLength (TmpStr, NumRead);
    TmpCh := TmpStr[1];
  End;

  StrCtr := 1;
  { **1 TmpCh := GetChar; }
  While ((Not StrDone) And (CurrLen < MaxLen) And (Not FM^.MsgDone)) Do
  Begin
    Case TmpCh of
      #$00:;
      #$0d: Begin
            StrDone := True;
            LastSoft := False;
            End;
      #$8d:;
      #$0a:;
      #$20: If ((CurrLen <> 0) or (Not StartSoft)) Then
            Begin
              Inc(CurrLen);
              WLen := CurrLen;
              Tmp [CurrLen] := TmpCh;
              WPos := FM^.TextCtr + StrCtr;
            End Else
              StartSoft := False;
      Else
      Begin
        Inc(CurrLen);
        Tmp [CurrLen] := TmpCh;
      End;
    End;
    If Not StrDone Then
    Begin
      Inc(StrCtr);
      TmpCh := TmpStr[StrCtr];
      If StrCtr > Length(TmpStr) Then
      Begin
        TmpCh := #0;
        StrDone := True;
      End
    End;
  End;
  FM^.TextCtr := FM^.TextCtr + StrCtr;

  If StrDone Then SetLength (Tmp, CurrLen) Else
  If FM^.MsgDone Then SetLength (Tmp, CurrLen) Else
  Begin
    If WLen = 0 Then
    Begin
      SetLength (Tmp, CurrLen);
      Dec (FM^.TextCtr);
    End Else
    Begin
      SetLength (Tmp, WLen);
      FM^.TextCtr := WPos;
    End;
  End;

  If Copy (Tmp, 1, 9) = 'SEEN-BY: ' Then Tmp := #1 + Tmp;
  GetString := Tmp;
End;

Function FidoMsgObj.EOM: Boolean;
Begin
  EOM := Not FM^. MsgOpen Or FM^. MsgDone;
End;

Function FidoMsgObj.WasWrap: Boolean;
Begin
  WasWrap := LastSoft;
End;

Function FidoMsgObj.GetFrom: String; {Get from name on current msg}
Begin
  If FM^. MsgOpen Then GetFrom := BufferNullString (0, 35) Else GetFrom := '';
End;

Function FidoMsgObj.GetTo: String; {Get to name on current msg}
Begin
  If FM^. MsgOpen Then GetTo := BufferNullString (36, 35) Else GetTo := '';
End;

Function FidoMsgObj.GetSubj: String; {Get subject on current msg}
Begin
  If FM^. MsgOpen Then GetSubj := BufferNullString (72, 71) Else GetSubj := '';
End;

Function FidoMsgObj.GetCost: Word; {Get cost of current msg}
Begin
  If FM^. MsgOpen Then GetCost := BufferWord (170) Else GetCost := 0;
End;

Function FidoMsgObj.GetDate: String; {Get date of current msg}
Begin
  GetDate := FM^. QDate;
End;

Function FidoMsgObj.GetTime: String; {Get time of current msg}
Begin
  GetTime := FM^. QTime;
End;

Function FidoMsgObj.GetRefer: LongInt; {Get reply to of current msg}
Begin
  If FM^. MsgOpen Then GetRefer := BufferWord (184) Else GetRefer := 0;
End;

Function FidoMsgObj. GetSeeAlso: LongInt; {Get see also of current msg}
Begin
  If FM^. MsgOpen Then GetSeeAlso := BufferWord (188) Else GetSeeAlso := 0;
End;

Function FidoMsgObj. GetMsgNum: LongInt; {Get message number}
Begin
  GetMsgNum := FM^. CurrMsg;
End;

Function FidoMsgObj. GetMsgNumRelative: LongInt; {Get message number relative to start}
Begin
  GetMsgNumRelative := CurMsg;
End;

Procedure FidoMsgObj. GetOrig (Var Addr: AddrType); {Get origin address}
Begin
  Addr := FM^.Orig;
End;

Procedure FidoMsgObj.GetDest(Var Addr: AddrType); {Get destination address}
Begin
  Addr := FM^.Dest;
End;

Function FidoMsgObj. IsLocal: Boolean; {Is current msg local}
Begin
  IsLocal := FM^. MsgOpen And ((GetWord (186) and fidLocal) <> 0);
End;

Function FidoMsgObj. IsCrash: Boolean; {Is current msg crash}
Begin
  IsCrash := FM^. MsgOpen And ((GetWord (186) and fidCrash) <> 0);
End;

Function FidoMsgObj. IsKillSent: Boolean; {Is current msg kill sent}
Begin
  IsKillSent := FM^. MsgOpen And ((GetWord (186) and fidKillSent) <> 0);
End;

Function FidoMsgObj. IsSent: Boolean; {Is current msg sent}
Begin
  IsSent := FM^. MsgOpen And ((GetWord (186) and fidSent) <> 0);
End;

Function FidoMsgObj. IsFAttach: Boolean; {Is current msg file attach}
Begin
  IsFAttach := FM^. MsgOpen And ((GetWord (186) and fidFAttach) <> 0);
End;

Function FidoMsgObj. IsReqRct: Boolean; {Is current msg request receipt}
Begin
  IsReqRct := FM^. MsgOpen And ((GetWord (186) and fidRRQ) <> 0);
End;

Function FidoMsgObj. IsReqAud: Boolean; {Is current msg request audit}
Begin
  IsReqAud := FM^. MsgOpen And ((GetWord (186) and fidARQ) <> 0);
End;

Function FidoMsgObj. IsRetRct: Boolean; {Is current msg a return receipt}
Begin
  IsRetRct := FM^. MsgOpen And ((GetWord (186) and fidRRC) <> 0);
End;

Function FidoMsgObj. IsFileReq: Boolean; {Is current msg a file request}
Begin
  IsFileReq := FM^. MsgOpen And ((GetWord (186) and fidFreq) <> 0);
End;

Function FidoMsgObj. IsRcvd: Boolean; {Is current msg received}
Begin
  IsRcvd := FM^. MsgOpen And ((GetWord (186) and fidRcvd) <> 0);
End;

Function FidoMsgObj. IsPriv: Boolean; {Is current msg priviledged/private}
Begin
  IsPriv := FM^. MsgOpen And ((GetWord (186) and fidPriv) <> 0);
End;

Function FidoMsgObj. IsHold: Boolean;
Begin
  IsHold := FM^. MsgOpen And ((GetWord (186) and fidHold) <> 0);
End;

Function FidoMsgObj. IsDeleted: Boolean; {Is current msg deleted}
Begin
  IsDeleted := Not FileExists (FM^.NetMailPath + Long2Str (FM^. CurrMsg) + '.MSG');
End;

Function FidoMsgObj.IsEchoed: Boolean; {Is current msg echoed}
Begin
  IsEchoed := True;
End;

Procedure FidoMsgObj. SeekFirst (MsgNum: LongInt); {Start msg seek}
Var
  j : LongInt;

Begin
  SeekOver := False;
  j := Num2Relative (MsgNum);
  If j <> 0 Then
  Begin
    FM^. CurrMsg := MsgNum;
    CurMsg := j;
  End;
End;

Procedure FidoMsgObj. SeekNext; {Find next matching msg}
Begin
  SeekOver := CurMsg >= NumberOfMsgs;
  If Not SeekOver Then
  Begin
    Inc (CurMsg);
    FM^. CurrMsg := Relative2Num (CurMsg);
  End;
End;

Procedure FidoMsgObj. SeekPrior;
Begin
  SeekOver := CurMsg < 2;
  If Not SeekOver Then
  Begin
    Dec (CurMsg);
    FM^. CurrMsg := Relative2Num (CurMsg);
  End;
End;

Function FidoMsgObj.SeekFound: Boolean;
Begin
  SeekFound := (C^. Count > 0) And (FM^. CurrMsg > 0) And Not SeekOver;
End;

Function FidoMsgObj.GetMsgLoc: LongInt; {Msg location}
Begin
  GetMsgLoc := GetMsgNum;
End;


Procedure FidoMsgObj.SetMsgLoc(ML: LongInt); {Msg location}
Begin
  FM^.CurrMsg := ML;
End;

Procedure FidoMsgObj.YoursFirst(Name: String; Handle: String);
Begin
  FM^.Name := UpString(Name);
  FM^.Handle := UpString(Handle);
  FM^.CurrMsg := 0;
  YoursNext;
End;

Procedure FidoMsgObj.YoursNext;
Var
  FoundDone: Boolean;

  Begin
  FoundDone := False;
  SeekFirst(FM^.CurrMsg + 1);
  While ((FM^.CurrMsg <> 0) And (Not FoundDone)) Do
    Begin
    MsgStartUp;
    If ((UpString(GetTo) = FM^.Name) Or (UpString(GetTo) = FM^.Handle)) Then
      FoundDone := True;
    If IsRcvd Then FoundDone := False;
    If Not FoundDone Then
      SeekNext;
    If Not SeekFound Then
      FoundDone := True;
    End;
  End;

Function FidoMsgObj.YoursFound: Boolean;
Begin
  YoursFound := SeekFound;
End;

Procedure FidoMsgObj.StartNewMsg;
Var
  Tmp    : Array [0..189] Of Char;

Begin
  Inherited StartNewMsg;

  FM^. Error       := 0;
  FM^. TextCtr     := 190;
  FM^. Dest. Zone  := 0;
  FM^. Orig. Zone  := 0;
  FM^. Dest. Point := 0;
  FM^. Orig. Point := 0;

  If FM^.TmpOpen Then RemoveTmp Else
  If FM^.MsgOpen Then
  If shClose (FM^. MsgFile) Then FM^.MsgOpen := False;

  FM^.TmpName := GetTempName(FM^.NetMailPath);

  If Length (FM^. TmpName) > 0 Then
  Begin
    shAssign (FM^. MsgFile, FM^. TmpName);
    If shReSet (FM^. MsgFile, 1)
    Then FM^. TmpOpen := True Else FM^. Error := 1002;
  End Else
    FM^. Error := 1001;

  FillChar (Tmp, SizeOf (Tmp), #0);
  shSeekFile (FM^. MsgFile, 0);
  shWrite (FM^. MsgFile, Tmp, SizeOf (Tmp));
End;

Function FidoMsgObj.OpenMsgBase: Word;
Begin
  Rescan;
  If MsgBaseExists Then OpenMsgBase := 0 Else OpenMsgBase := 500;
End;

Function FidoMsgObj.CloseMsgBase: Word;
Begin
  CloseMsgBase := 0;
End;

Function FidoMsgObj.CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word;
Begin
  If MakePath (FM^. NetMailPath) Then CreateMsgBase := 0 Else CreateMsgBase := 1;
End;

Procedure FidoMsgObj.SetMailType(MT: MsgMailType);
Begin
  FM^.MailType := Mt;
End;

Function FidoMsgObj.GetSubArea: Word;
Begin
  GetSubArea := 0;
End;

Procedure FidoMsgObj.ReWriteHdr;
Begin
  { Not needed, rewrite is automatic when updates are done }
End;

Procedure FidoMsgObj. DeleteMsg (CarePos: Boolean);
Var
  TmpFile  : File;
  Code     : LongInt;
  IsBegin,
  IsEnd    : Boolean;

Begin
  IsBegin := (CurMsg = 1);
  IsEnd   := (CurMsg = C^. Count);

  If FM^. MsgOpen Then
  If shClose (FM^. MsgFile) Then FM^. MsgOpen := False;

  Assign (TmpFile, FM^. NetMailPath + Long2Str (FM^. CurrMsg) + '.MSG');
  Erase (TmpFile);
  Code := FM^. CurrMsg Shr 3; {div by 8 to get byte position}
  FM^. MsgPresent [Code] := FM^.MsgPresent [Code] And Not (PosArray[FM^.CurrMsg and 7]);
  If IOResult <> 0 Then;

  (*
  FreeMem (C^. At (CurMsg-1), 4);
  *)
  C^. AtDelete (CurMsg-1);

  If CarePos Then
  Begin
    If IsBegin Then SeekFirst (GetLowMsgNum) Else
    If IsEnd   Then SeekFirst (GetHighMsgNum) Else
    SeekFirst (Relative2Num (LongInt (C^. At (CurMsg-1))));
  End;
End;

Function FidoMsgObj.GetLastRead(UNum: LongInt): LongInt;
Var
  LRec: Word;

Begin
  If ((UNum + 1) * SizeOf(LRec)) > gFileSize (FM^.NetMailPath + 'LastRead') Then
    GetLastRead := 0
  Else
  If LoadFilePos (FM^. NetMailPath + 'LastRead', LRec, SizeOf (LRec),
                  UNum * SizeOf(LRec)) = 0
  Then GetLastRead := LRec
  Else GetLastRead := 0;
End;

Procedure FidoMsgObj.SetLastRead(UNum: LongInt; LR: LongInt);
Var
  LRec  : Word;

Begin
  If ((UNum + 1) * SizeOf(LRec)) > gFileSize (FM^.NetMailPath + 'LastRead')
  Then ExtendFile(FM^.NetMailPath + 'LastRead', (UNum + 1) * SizeOf(LRec));

  If LoadFilePos (FM^. NetMailPath + 'LastRead', LRec, SizeOf (LRec), UNum * SizeOf (LRec)) = 0 Then
  Begin
    LRec := LR;
    SaveFilePos (FM^. NetMailPath + 'LastRead', LRec,
    SizeOf (LRec), UNum * SizeOf(LRec));
  End;
End;

Function FidoMsgObj.GetTxtPos: LongInt;
Begin
  GetTxtPos := FM^.TextCtr;
End;

Procedure FidoMsgObj.SetTxtPos(TP: LongInt);
Begin
  FM^.TextCtr := TP;
End;

Function FidoMsgObj.MsgBaseExists: Boolean;
Begin
  MsgBaseExists := DirExists (FM^. NetMailPath);
End;

Procedure FidoMsgObj.Rescan;
Var
  SR       : {$IFNDEF WIN32} SearchRec; {$ELSE} TSearchRec; {$ENDIF}
  TmpName  : String[13];
  TmpNum   : Word;
  Code     : SysInt;

Begin
  FillChar (FM^. MsgPresent, SizeOf (FM^. MsgPresent), 0);
{$IFDEF WIN32}
  DOSerror :=
{$ENDIF}
  FindFirst (FM^.NetMailPath + '*.MSG', ReadOnly and Archive, SR);
  While DosError = 0 Do
  Begin
    TmpName := SR. Name;
    Val (Copy (TmpName, 1,  Pos ('.', TmpName) - 1), TmpNum, Code);
    If ((Code = 0) And (TmpNum > 0)) Then
    Begin
      If TmpNum <= MaxFidMsgNum Then
      Begin
        Code := TmpNum shr 3; {div by 8 to get byte position}
        FM^. MsgPresent [Code] := FM^. MsgPresent [Code] or PosArray [TmpNum and 7];
      End;
    End;
  {$IFDEF WIN32}
  DOSerror :=
  {$ENDIF}
    FindNext(SR);
  End;
  {$IFDEF OS2}
  FindClose (Sr);
  {$ENDIF}
End;

Function FidoMsgObj.MsgExists(MsgNum: LongInt): Boolean;
Var
  Code: LongInt;

Begin
  If ((MsgNum > 0) and (MsgNum <= MaxFidMsgNum)) Then
  Begin
    Code := MsgNum shr 3;
    MsgExists := (FM^. MsgPresent [Code] and PosArray [MsgNum and 7]) <> 0;
  End Else
    MsgExists := False;
  End;
End.