home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / mksmvp10.zip / MKMSGJAM.PAS < prev    next >
Pascal/Delphi Source File  |  1997-09-24  |  48KB  |  1,816 lines

  1. Unit MKMsgJam;       {JAM Msg Object Unit}
  2.  
  3. {
  4.      Thanks to the following for creating the JAM message base format
  5. }
  6.  
  7.  
  8. {
  9.      JAM(mbp) - Copyright 1993 Joaquim Homrighausen, Andrew Milner,
  10.                                Mats Birch, Mats Wallin.
  11.                                ALL RIGHTS RESERVED.
  12. }
  13.  
  14. {
  15.      The JAM developers have requested that you include the above
  16.      copyright notice in any JAM products you develop
  17. }
  18.  
  19. {
  20.      MKMsgJam - Copyright 1993 by Mark May - MK Software
  21.      You are free to use this code in your programs, however
  22.      it may not be included in Source/TPU function libraries
  23.      without my permission.
  24.  
  25.      Mythical Kingom Tech BBS (513)237-7737 HST/v32
  26.      FidoNet: 1:110/290
  27.      You may also reach me at maym@dmapub.dma.org
  28.  
  29.      This implementation handles unlimited text size (well actually
  30.      limited by available disk space and a maximum number of characters
  31.      of 2,147,483,647) by using a temporary buffer file for larger msgs.
  32.      Smaller msgs (under 4k) are still handled in memory for maximum
  33.      speed.
  34.  
  35.      Mark May
  36.      MK Software
  37. }
  38.  
  39. {$I MKB.Def}
  40.  
  41. Interface
  42.  
  43. Uses MKGlobT, MKMsgAbs, Use32,
  44. {$IFDEF WINDOWS}
  45.   Strings, WinDos;
  46. {$ELSE}
  47.   Dos;
  48. {$ENDIF}
  49.  
  50.  
  51. Type JamHdrType = Record
  52.   Signature: Array[1..4] of Char;
  53.   Created: LongInt;
  54.   ModCounter: LongInt;
  55.   ActiveMsgs: LongInt;
  56.   PwdCRC: LongInt;
  57.   BaseMsgNum: LongInt;
  58.   Extra: Array[1..1000] of Char;
  59.   End;
  60.  
  61.  
  62. Type JamMsgHdrType = Record
  63.   Signature: Array[1..4] of Char;
  64.   Rev: SmallWord;
  65.   Resvd: SmallWord;
  66.   SubFieldLen: LongInt;
  67.   TimesRead: LongInt;
  68.   MsgIdCrc: LongInt;
  69.   ReplyCrc: LongInt;
  70.   ReplyTo: LongInt;
  71.   ReplyFirst: LongInt;
  72.   ReplyNext: LongInt;
  73.   DateWritten: LongInt;
  74.   DateRcvd: LongInt;
  75.   DateArrived: LongInt;
  76.   MsgNum: LongInt;
  77.   Attr1: LongInt;
  78.   Attr2: LongInt;
  79.   TextOfs: LongInt;
  80.   TextLen: LongInt;
  81.   PwdCrc: LongInt;
  82.   Cost: LongInt;
  83.   End;
  84.  
  85.  
  86. Type JamIdxType = Record
  87.   MsgToCrc: LongInt;
  88.   HdrLoc: LongInt;
  89.   End;
  90.  
  91.  
  92. Type JamLastType = Record
  93.   NameCrc: LongInt;
  94.   UserNum: LongInt;
  95.   LastRead: LongInt;
  96.   HighRead: LongInt;
  97.   End;
  98.  
  99.  
  100. Const JamIdxBufSize = 500;
  101.  
  102.  
  103. Type JamIdxArrayType = Array[0..JamIdxBufSize] of JamIdxType;
  104.  
  105.  
  106. Const JamSubBufSize = 8192;
  107.  
  108.  
  109. Type JamSubBuffer = Array[1..JamSubBufSize] of Char;
  110.  
  111.  
  112. Const JamTxtBufSize = 8192;
  113.  
  114. Const TxtSubBufSize = 4096;            {Note actual size is one greater}
  115.  
  116.  
  117. Type JamTxtBufType = Array[0..JamTxtBufSize] Of Char;
  118.  
  119. Type HdrType = Record
  120.   JamHdr: JamMsgHdrType;
  121.   SubBuf: JamSubBuffer;
  122.   End;
  123.  
  124.  
  125. Type JamMsgType = Record
  126.   HdrFile: File;
  127.   TxtFile: File;
  128.   IdxFile: File;
  129.   MsgPath: String[128];
  130.   BaseHdr: JamHdrType;
  131.   Dest: AddrType;
  132.   Orig: AddrType;
  133.   MsgFrom: String[65];
  134.   MsgTo: String[65];
  135.   MsgSubj: String[100];
  136.   MsgDate: String[8];
  137.   MsgTime: String[5];
  138.   CurrMsgNum: LongInt;
  139.   YourName: String[35];
  140.   YourHdl: String[35];
  141.   NameCrc: LongInt;
  142.   HdlCrc: LongInt;
  143.   TxtPos: LongInt; {TxtPos < 0 means get from sub text}
  144.   TxtEnd: LongInt;
  145.   TxtBufStart: LongInt;
  146.   TxtRead: Word;
  147.   MailType: MsgMailType;
  148.   BufFile: File;
  149.   LockCount: LongInt;
  150.   IdxStart: LongInt;
  151.   IdxRead: Word;
  152.   TxtSubBuf: Array[0..TxtSubBufSize] of Char; {temp storage for text on subfields}
  153.   TxtSubChars: Integer;
  154.   End;
  155.  
  156.  
  157. Type JamMsgObj = Object (AbsMsgObj)
  158.   JM: ^JamMsgType;
  159.   MsgHdr: ^HdrType;
  160.   JamIdx: ^JamIdxArrayType;
  161.   TxtBuf: ^JamTxtBufType;
  162.   Error: Word;
  163.   Constructor Init;                      {Initialize}
  164.   Destructor Done; Virtual; {Done}
  165.   Procedure SetMsgPath(St: String); Virtual; {Set netmail path}
  166.   Function  GetHighMsgNum: LongInt; Virtual; {Get highest netmail msg number in area}
  167.   Function  LockMsgBase: Boolean; Virtual; {Lock the message base}
  168.   Function  UnLockMsgBase: Boolean; Virtual; {Unlock the message base}
  169.   Procedure SetDest(Var Addr: AddrType); Virtual; {Set Zone/Net/Node/Point for Dest}
  170.   Procedure SetOrig(Var Addr: AddrType); Virtual; {Set Zone/Net/Node/Point for Orig}
  171.   Procedure SetFrom(Name: String); Virtual; {Set message from}
  172.   Procedure SetTo(Name: String); Virtual; {Set message to}
  173.   Procedure SetSubj(Str: String); Virtual; {Set message subject}
  174.   Procedure SetCost(SCost: Word); Virtual; {Set message cost}
  175.   Procedure SetRefer(SRefer: LongInt); Virtual; {Set message reference}
  176.   Procedure SetSeeAlso(SAlso: LongInt); Virtual; {Set message see also}
  177.   Function  GetNextSeeAlso: LongInt; Virtual;
  178.   Procedure SetNextSeeAlso(SAlso: LongInt); Virtual;
  179.   Procedure SetDate(SDate: String); Virtual; {Set message date}
  180.   Procedure SetTime(STime: String); Virtual; {Set message time}
  181.   Procedure SetLocal(LS: Boolean); Virtual; {Set local status}
  182.   Procedure SetRcvd(RS: Boolean); Virtual; {Set received status}
  183.   Procedure SetPriv(PS: Boolean); Virtual; {Set priveledge vs public status}
  184.   Procedure SetCrash(SS: Boolean); Virtual; {Set crash netmail status}
  185.   Procedure SetKillSent(SS: Boolean); Virtual; {Set kill/sent netmail status}
  186.   Procedure SetSent(SS: Boolean); Virtual; {Set sent netmail status}
  187.   Procedure SetFAttach(SS: Boolean); Virtual; {Set file attach status}
  188.   Procedure SetReqRct(SS: Boolean); Virtual; {Set request receipt status}
  189.   Procedure SetReqAud(SS: Boolean); Virtual; {Set request audit status}
  190.   Procedure SetRetRct(SS: Boolean); Virtual; {Set return receipt status}
  191.   Procedure SetFileReq(SS: Boolean); Virtual; {Set file request status}
  192.   Procedure DoString(Str: String); Virtual; {Add string to message text}
  193.   Procedure DoChar(Ch: Char); Virtual; {Add character to message text}
  194.   Procedure DoStringLn(Str: String); Virtual; {Add string and newline to msg text}
  195.   Procedure DoKludgeLn(Str: String); Virtual; {Add ^AKludge line to msg}
  196.   Function  WriteMsg: Word; Virtual;
  197.   Function  GetChar: Char; Virtual;
  198.   Procedure AddTxtSub(St: String);
  199.   Procedure MsgStartUp; Virtual; {set up msg for reading}
  200.   Function  EOM: Boolean; Virtual; {No more msg text}
  201.   Function  GetString(MaxLen: Word): String; Virtual; {Get wordwrapped string}
  202.   Function  WasWrap: Boolean; Virtual; {Last line was soft wrapped no CR}
  203.   Procedure SeekFirst(MsgNum: LongInt); Virtual; {Seek msg number}
  204.   Procedure SeekNext; Virtual; {Find next matching msg}
  205.   Procedure SeekPrior; Virtual; {Seek prior matching msg}
  206.   Function  GetFrom: String; Virtual; {Get from name on current msg}
  207.   Function  GetTo: String; Virtual; {Get to name on current msg}
  208.   Function  GetSubj: String; Virtual; {Get subject on current msg}
  209.   Function  GetCost: Word; Virtual; {Get cost of current msg}
  210.   Function  GetDate: String; Virtual; {Get date of current msg}
  211.   Function  GetTime: String; Virtual; {Get time of current msg}
  212.   Function  GetRefer: LongInt; Virtual; {Get reply to of current msg}
  213.   Function  GetSeeAlso: LongInt; Virtual; {Get see also of current msg}
  214.   Function  GetMsgNum: LongInt; Virtual; {Get message number}
  215.   Procedure GetOrig(Var Addr: AddrType); Virtual; {Get origin address}
  216.   Procedure GetDest(Var Addr: AddrType); Virtual; {Get destination address}
  217.   Function  IsLocal: Boolean; Virtual; {Is current msg local}
  218.   Function  IsCrash: Boolean; Virtual; {Is current msg crash}
  219.   Function  IsKillSent: Boolean; Virtual; {Is current msg kill sent}
  220.   Function  IsSent: Boolean; Virtual; {Is current msg sent}
  221.   Function  IsFAttach: Boolean; Virtual; {Is current msg file attach}
  222.   Function  IsReqRct: Boolean; Virtual; {Is current msg request receipt}
  223.   Function  IsReqAud: Boolean; Virtual; {Is current msg request audit}
  224.   Function  IsRetRct: Boolean; Virtual; {Is current msg a return receipt}
  225.   Function  IsFileReq: Boolean; Virtual; {Is current msg a file request}
  226.   Function  IsRcvd: Boolean; Virtual; {Is current msg received}
  227.   Function  IsPriv: Boolean; Virtual; {Is current msg priviledged/private}
  228.   Function  IsDeleted: Boolean; Virtual; {Is current msg deleted}
  229.   Function  IsEchoed: Boolean; Virtual; {Msg should be echoed}
  230.   Function  GetMsgLoc: LongInt; Virtual; {Msg location}
  231.   Procedure SetMsgLoc(ML: LongInt); Virtual; {Msg location}
  232.   Procedure YoursFirst(Name: String; Handle: String); Virtual; {Seek your mail}
  233.   Procedure YoursNext; Virtual; {Seek next your mail}
  234.   Function  YoursFound: Boolean; Virtual; {Message found}
  235.   Procedure StartNewMsg; Virtual;
  236.   Function  OpenMsgBase: Word; Virtual;
  237.   Function  CloseMsgBase: Word; Virtual;
  238.   Function  MsgBaseExists: Boolean; Virtual; {Does msg base exist}
  239.   Function  CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word; Virtual;
  240.   Function  SeekFound: Boolean; Virtual;
  241.   Procedure SetMailType(MT: MsgMailType); Virtual; {Set message base type}
  242.   Function  GetSubArea: Word; Virtual; {Get sub area number}
  243.   Procedure ReWriteHdr; Virtual; {Rewrite msg header after changes}
  244.   Procedure DeleteMsg; Virtual; {Delete current message}
  245.   Function  NumberOfMsgs: LongInt; Virtual; {Number of messages}
  246.   Function  GetLastRead(UNum: LongInt): LongInt; Virtual; {Get last read for user num}
  247.   Procedure SetLastRead(UNum: LongInt; LR: LongInt); Virtual; {Set last read}
  248.   Procedure MsgTxtStartUp; Virtual; {Do message text start up tasks}
  249.   Function  GetTxtPos: LongInt; Virtual; {Get indicator of msg text position}
  250.   Procedure SetTxtPos(TP: LongInt); Virtual; {Set text position}
  251.   Procedure SetAttr1(Mask: LongInt; St: Boolean); {Set attribute 1}
  252.   Function  ReadIdx: Word;
  253.   Function  WriteIdx: Word;
  254.   Procedure AddSubField(id: Word; Data: String);
  255.   Function  FindLastRead(Var LastFile: File; UNum: LongInt): LongInt;
  256.   Function  ReReadIdx(Var IdxLoc : LongInt) : Word;
  257.   End;
  258.  
  259.  
  260. Type JamMsgPtr = ^JamMsgObj;
  261.  
  262.  
  263. Function JamStrCrc(St: String): LongInt;
  264.  
  265.  
  266. Implementation
  267.  
  268. Uses MKFile, MKString, MKDos, Crc32, MKMisc;
  269.  
  270. Const
  271.   Jam_Local =        $00000001;
  272.   Jam_InTransit =    $00000002;
  273.   Jam_Priv =         $00000004;
  274.   Jam_Rcvd =         $00000008;
  275.   Jam_Sent =         $00000010;
  276.   Jam_KillSent =     $00000020;
  277.   Jam_AchvSent =     $00000040;
  278.   Jam_Hold =         $00000080;
  279.   Jam_Crash =        $00000100;
  280.   Jam_Imm =          $00000200;
  281.   Jam_Direct =       $00000400;
  282.   Jam_Gate =         $00000800;
  283.   Jam_Freq =         $00001000;
  284.   Jam_FAttch =       $00002000;
  285.   Jam_TruncFile =    $00004000;
  286.   Jam_KillFile =     $00008000;
  287.   Jam_RcptReq =      $00010000;
  288.   Jam_ConfmReq =     $00020000;
  289.   Jam_Orphan =       $00040000;
  290.   Jam_Encrypt =      $00080000;
  291.   Jam_Compress =     $00100000;
  292.   Jam_Escaped =      $00200000;
  293.   Jam_FPU =          $00400000;
  294.   Jam_TypeLocal =    $00800000;
  295.   Jam_TypeEcho =     $01000000;
  296.   Jam_TypeNet =      $02000000;
  297.   Jam_NoDisp =       $20000000;
  298.   Jam_Locked =       $40000000;
  299.   Jam_Deleted =      $80000000;
  300.  
  301.  
  302. Type SubFieldType = Record
  303.   LoId: SmallWord;
  304.   HiId: SmallWord;
  305.   DataLen: LongInt;
  306.   Data: Array[1..1000] of Char;
  307.   End;
  308.  
  309.  
  310. Constructor JamMsgObj.Init;
  311.   Begin
  312.   New(JM);
  313.   New(JamIdx);
  314.   New(MsgHdr);
  315.   New(TxtBuf);
  316.   If ((JM = Nil) Or (JamIdx = Nil) or (MsgHdr = Nil) or (TxtBuf = Nil)) Then
  317.     Begin
  318.     If JM <> Nil Then
  319.       Dispose(JM);
  320.     If JamIdx <> Nil Then
  321.       Dispose(JamIdx);
  322.     If MsgHdr <> Nil Then
  323.       Dispose(MsgHdr);
  324.     If TxtBuf <> Nil Then
  325.       Dispose(TxtBuf);
  326.     Fail;
  327.     Exit;
  328.     End
  329.   Else
  330.     Begin;
  331.     FillChar(JM^, SizeOf(JM^), #0);
  332.     JM^.MsgPath := '';
  333.     JM^.IdxStart := -30;
  334.     JM^.IdxRead := 0;
  335.     Error := 0;
  336.     End;
  337.   End;
  338.  
  339.  
  340. Destructor JamMsgObj.Done;
  341.   Begin
  342.   If JM <> Nil Then
  343.     Dispose(JM);
  344.   If JamIdx <> Nil Then
  345.     Dispose(JamIdx);
  346.   If MsgHdr <> Nil Then
  347.     Dispose(MsgHdr);
  348.   If TxtBuf <> Nil Then
  349.     Dispose(TxtBuf);
  350.   End;
  351.  
  352.  
  353. Function JamStrCrc(St: String): LongInt;
  354.   Var
  355.     i: Word;
  356.     crc: LongInt;
  357.  
  358.   Begin
  359.   Crc := -1;
  360.   For i := 1 to Length(St) Do
  361.     Crc := Updc32(Ord(LoCase(St[i])), Crc);
  362.   JamStrCrc := Crc;
  363.   End;
  364.  
  365.  
  366. Procedure JamMsgObj.SetMsgPath(St: String);
  367.   Begin
  368.   JM^.MsgPath := Copy(St, 1, 124);
  369.   End;
  370.  
  371.  
  372. Function JamMsgObj.GetHighMsgNum: LongInt;
  373.   Begin
  374.   GetHighMsgNum := JM^.BaseHdr.BaseMsgNum + FileSize(JM^.IdxFile) - 1;
  375.   End;
  376.  
  377.  
  378. Procedure JamMsgObj.SetDest(Var Addr: AddrType);
  379.   Begin
  380.   JM^.Dest := Addr;
  381.   End;
  382.  
  383.  
  384. Procedure JamMsgObj.SetOrig(Var Addr: AddrType);
  385.   Begin
  386.   JM^.Orig := Addr;
  387.   End;
  388.  
  389.  
  390. Procedure JamMsgObj.SetFrom(Name: String);
  391.   Begin
  392.   JM^.MsgFrom := Name;
  393.   End;
  394.  
  395.  
  396. Procedure JamMsgObj.SetTo(Name: String);
  397.   Begin
  398.   JM^.MsgTo := Name;
  399.   End;
  400.  
  401.  
  402. Procedure JamMsgObj.SetSubj(Str: String);
  403.   Begin
  404.   JM^.MsgSubj := Str;
  405.   End;
  406.  
  407.  
  408. Procedure JamMsgObj.SetCost(SCost: Word);
  409.   Begin
  410.   MsgHdr^.JamHdr.Cost := SCost;
  411.   End;
  412.  
  413.  
  414. Procedure JamMsgObj.SetRefer(SRefer: LongInt);
  415.   Begin
  416.   MsgHdr^.JamHdr.ReplyTo := SRefer;
  417.   End;
  418.  
  419.  
  420. Procedure JamMsgObj.SetSeeAlso(SAlso: LongInt);
  421.   Begin
  422.   MsgHdr^.JamHdr.ReplyFirst := SAlso;
  423.   End;
  424.  
  425.  
  426. Procedure JamMsgObj.SetDate(SDate: String);
  427.   Begin
  428.   JM^.MsgDate := SDate;
  429.   End;
  430.  
  431.  
  432. Procedure JamMsgObj.SetTime(STime: String);
  433.   Begin
  434.   JM^.MsgTime := STime;
  435.   End;
  436.  
  437.  
  438. Procedure JamMsgObj.SetAttr1(Mask: LongInt; St: Boolean);
  439.   Begin
  440.   If St Then
  441.     MsgHdr^.JamHdr.Attr1 := MsgHdr^.JamHdr.Attr1 Or Mask
  442.   Else
  443.     MsgHdr^.JamHdr.Attr1 := MsgHdr^.JamHdr.Attr1 And (Not Mask);
  444.   End;
  445.  
  446.  
  447.  
  448. Procedure JamMsgObj.SetLocal(LS: Boolean);
  449.   Begin
  450.   SetAttr1(Jam_Local, LS);
  451.   End;
  452.  
  453.  
  454. Procedure JamMsgObj.SetRcvd(RS: Boolean);
  455.   Begin
  456.   SetAttr1(Jam_Rcvd, RS);
  457.   End;
  458.  
  459.  
  460. Procedure JamMsgObj.SetPriv(PS: Boolean);
  461.   Begin
  462.   SetAttr1(Jam_Priv, PS);
  463.   End;
  464.  
  465.  
  466. Procedure JamMsgObj.SetCrash(SS: Boolean);
  467.   Begin
  468.   SetAttr1(Jam_Crash, SS);
  469.   End;
  470.  
  471.  
  472. Procedure JamMsgObj.SetKillSent(SS: Boolean);
  473.   Begin
  474.   SetAttr1(Jam_KillSent, SS);
  475.   End;
  476.  
  477.  
  478. Procedure JamMsgObj.SetSent(SS: Boolean);
  479.   Begin
  480.   SetAttr1(Jam_Sent, SS);
  481.   End;
  482.  
  483.  
  484. Procedure JamMsgObj.SetFAttach(SS: Boolean);
  485.   Begin
  486.   SetAttr1(Jam_FAttch, SS);
  487.   End;
  488.  
  489.  
  490. Procedure JamMsgObj.SetReqRct(SS: Boolean);
  491.   Begin
  492.   SetAttr1(Jam_RcptReq, SS);
  493.   End;
  494.  
  495.  
  496. Procedure JamMsgObj.SetReqAud(SS: Boolean);
  497.   Begin
  498.   SetAttr1(Jam_ConfmReq, SS);
  499.   End;
  500.  
  501.  
  502. Procedure JamMsgObj.SetRetRct(SS: Boolean);
  503.   Begin
  504.   End;
  505.  
  506.  
  507. Procedure JamMsgObj.SetFileReq(SS: Boolean);
  508.   Begin
  509.   SetAttr1(Jam_Freq, SS);
  510.   End;
  511.  
  512.  
  513. Procedure JamMsgObj.DoString(Str: String);
  514.   Var
  515.     i: Word;
  516.  
  517.   Begin
  518.   i := 1;
  519.   While i <= Length(Str) Do
  520.     Begin
  521.     DoChar(Str[i]);
  522.     Inc(i);
  523.     End;
  524.   End;
  525.  
  526.  
  527. Procedure JamMsgObj.DoChar(Ch: Char);
  528.   Var
  529.     TmpStr: String;
  530.     NumWrite: Word;
  531.  
  532.   Begin
  533.   Case ch of
  534.     #13: LastSoft := False;
  535.     #10:;
  536.     Else
  537.       LastSoft := True;
  538.     End;
  539.   If (JM^.TxtPos - JM^.TxtBufStart) >= JamTxtBufSize Then
  540.     Begin
  541.     If JM^.TxtBufStart = 0 Then
  542.       Begin
  543.       GetDir(0, TmpStr);
  544.       TmpStr := GetTempName(TmpStr);
  545.       Assign(JM^.BufFile, TmpStr);
  546.       FileMode := fmReadWrite + fmDenyNone;
  547.       ReWrite(JM^.BufFile, 1);
  548.       End;
  549.     NumWrite := JM^.TxtPos - JM^.TxtBufStart;
  550.     BlockWrite(JM^.BufFile, TxtBuf^, NumWrite);
  551.     Error := IoResult;
  552.     JM^.TxtBufStart := FileSize(JM^.BufFile);
  553.     End;
  554.   TxtBuf^[JM^.TxtPos - JM^.TxtBufStart] := Ch;
  555.   Inc(JM^.TxtPos);
  556.   End;
  557.  
  558.  
  559. Procedure JamMsgObj.DoStringLn(Str: String);
  560.   Begin
  561.   DoString(Str);
  562.   DoChar(#13);
  563.   End;
  564.  
  565.  
  566. Procedure JamMsgObj.DoKludgeLn(Str: String);
  567.   Var
  568.     TmpStr: String;
  569.  
  570.   Begin
  571.   If Str[1] = #1 Then
  572.     Str := Copy(Str,2,255);
  573.   If Copy(Str,1,3) = 'PID' Then
  574.     Begin
  575.     TmpStr := StripLead(Copy(Str,4,255),':');
  576.     TmpStr := Copy(StripBoth(TmpStr, ' '),1,40);
  577.     AddSubField(7, TmpStr);
  578.     End
  579.   Else If Copy(Str,1,5) = 'MSGID' Then
  580.     Begin
  581.     TmpStr := StripLead(Copy(Str,6,255),':');
  582.     TmpStr := Copy(StripBoth(TmpStr,' '),1,100);
  583.     AddSubField(4, TmpStr);
  584.     MsgHdr^.JamHdr.MsgIdCrc := JamStrCrc(TmpStr);
  585.     End
  586.   Else If Copy(Str,1,4) = 'INTL' Then  {ignore}
  587.     Begin
  588.     End
  589.   Else If Copy(Str,1,4) = 'TOPT' Then  {ignore}
  590.     Begin
  591.     End
  592.   Else If Copy(Str,1,4) = 'FMPT' Then  {ignore}
  593.     Begin
  594.     End
  595.   Else If Copy(Str,1,5) = 'REPLY' Then
  596.     Begin
  597.     TmpStr := StripLead(Copy(Str,8,255),':');
  598.     TmpStr := Copy(StripBoth(TmpStr,' '),1,100);
  599.     AddSubField(5, TmpStr);
  600.     MsgHdr^.JamHdr.ReplyCrc := JamStrCrc(TmpStr);
  601.     End
  602.   Else If Copy(Str,1,4) = 'PATH' Then
  603.     Begin
  604.     TmpStr := StripLead(Copy(Str,5,255),':');
  605.     TmpStr := StripBoth(TmpStr,' ');
  606.     AddSubField(2002, TmpStr);
  607.     End
  608.   Else
  609.     Begin
  610.     AddSubField(2000, StripBoth(Str,' '));
  611.     End;
  612.   End;
  613.  
  614.  
  615. Procedure JamMsgObj.AddSubField(id: Word; Data: String);
  616.   Type SubFieldType = Record
  617.     LoId: SmallWord;
  618.     HiId: SmallWord;
  619.     DataLen: LongInt;
  620.     Data: Array[1..256] of Char;
  621.     End;
  622.  
  623.   Var
  624.     SubField: ^SubFieldType;
  625.  
  626.   Begin
  627.   SubField := @MsgHdr^.SubBuf[MsgHdr^.JamHdr.SubFieldLen + 1];
  628.   If (MsgHdr^.JamHdr.SubFieldLen + 8 + Length(Data) < JamSubBufSize) Then
  629.     Begin
  630.     Inc(MsgHdr^.JamHdr.SubFieldLen, 8 + Length(Data));
  631.     SubField^.LoId := Id;
  632.     SubField^.HiId := 0;
  633.     SubField^.DataLen := Length(Data);
  634.     Move(Data[1], SubField^.Data[1], Length(Data));
  635.     End;
  636.   End;
  637.  
  638.  
  639. Function  JamMsgObj.WriteMsg: Word;
  640.   Var
  641.     {$IFDEF WINDOWS}
  642.     DT: TDateTime;
  643.     {$ELSE}
  644.     DT: DateTime;
  645.     {$ENDIF}
  646.     WriteError: Word;
  647.     i: Word;
  648.     TmpIdx: JamIdxType;
  649.  
  650.   Begin
  651.   WriteError := 0;
  652.   If LastSoft Then
  653.     Begin
  654.     DoChar(#13);
  655.     DoChar(#10);
  656.     End;
  657.   If WriteError = 0 Then
  658.     Begin
  659.     MsgHdr^.JamHdr.Signature[1] := 'J';{Set signature}
  660.     MsgHdr^.JamHdr.Signature[2] := 'A';
  661.     MsgHdr^.JamHdr.Signature[3] := 'M';
  662.     MsgHdr^.JamHdr.Signature[4] := #0;
  663.     Case JM^.MailType of
  664.       mmtNormal: SetAttr1(Jam_TypeLocal, True);
  665.       mmtEchoMail: SetAttr1(Jam_TypeEcho, True);
  666.       mmtNetMail: SetAttr1(Jam_TypeNet, True);
  667.       End;
  668.     MsgHdr^.JamHdr.Rev := 1;
  669.     MsgHdr^.JamHdr.DateArrived := ToUnixDate(GetDosDate); {Get date processed}
  670.     DT.Year := Str2Long(Copy(JM^.MsgDate, 7, 2)); {Convert date written}
  671.     DT.Month := Str2Long(Copy(JM^.MsgDate, 1, 2));
  672.     DT.Day := Str2Long(Copy(JM^.MsgDate, 4, 2));
  673.     If DT.Year < 80 Then
  674.       Inc(DT.Year, 2000)
  675.     Else
  676.       Inc(DT.Year, 1900);
  677.     DT.Sec := 0;
  678.     DT.Hour := Str2Long(Copy(JM^.MsgTime, 1, 2));
  679.     DT.Min := Str2Long(Copy(JM^.MsgTime, 4, 2));
  680.     MsgHdr^.JamHdr.DateWritten := DTToUnixDate(DT);
  681.     End;
  682.   If WriteError = 0 Then
  683.     Begin                              {Lock message base for update}
  684.     If Not LockMsgBase Then
  685.       WriteError := 5;
  686.     End;
  687.   If WriteError = 0 Then
  688.     Begin                              {Handle message text}
  689.     MsgHdr^.JamHdr.TextOfs := FileSize(JM^.TxtFile);
  690.     MsgHdr^.JamHdr.MsgNum := GetHighMsgNum + 1;
  691.     MsgHdr^.Jamhdr.TextLen := JM^.TxtPos;
  692.     If JM^.TxtBufStart > 0 Then
  693.       Begin                            {Write text using buffer file}
  694.       i := JM^.TxtPos - JM^.TxtBufStart;
  695.       BlockWrite(JM^.BufFile, TxtBuf^, i); {write buffer to file}
  696.       WriteError := IoResult;
  697.       If WriteError = 0 Then           {seek start of buffer file}
  698.         Begin
  699.         Seek(JM^.BufFile, 0);
  700.         WriteError := IoResult;
  701.         End;
  702.       If WriteError = 0 Then           {seek end of text file}
  703.         Begin
  704.         Seek(JM^.TxtFile, FileSize(JM^.TxtFile));
  705.         WriteError := IoResult;
  706.         End;
  707.       While ((Not Eof(JM^.BufFile)) and (WriteError = 0)) Do
  708.         Begin                          {copy buffer file to text file}
  709.         BlockRead(JM^.BufFile, TxtBuf^, SizeOf(TxtBuf^), i);
  710.         WriteError := IoResult;
  711.         If WriteError = 0 Then
  712.           Begin
  713.           JM^.TxtBufStart := FilePos(JM^.TxtFile);
  714.           JM^.TxtRead := i;
  715.           BlockWrite(JM^.TxtFile, TxtBuf^, i);
  716.           Error := IoResult;
  717.           End;
  718.         End;
  719.       Close(JM^.BufFile);
  720.       Error := IoResult;
  721.       Erase(JM^.BufFile);
  722.       Error := IoResult;
  723.       End
  724.     Else
  725.       Begin                            {Write text using TxtBuf only}
  726.       Seek(JM^.Txtfile, FileSize(JM^.TxtFile));
  727.       WriteError := IoResult;
  728.       If WriteError = 0 Then
  729.         Begin
  730.         BlockWrite(JM^.TxtFile, TxtBuf^, JM^.TxtPos);
  731.         WriteError := IoResult;
  732.         JM^.TxtRead := JM^.TxtPos;
  733.         End;
  734.       End;
  735.     If WriteError = 0 Then             {Add index record}
  736.       Begin
  737.       TmpIdx.HdrLoc := FileSize(JM^.HdrFile);
  738.       TmpIdx.MsgToCrc := JamStrCrc(JM^.MsgTo);
  739.       Seek(JM^.IdxFile, FileSize(JM^.IdxFile));
  740.       WriteError := IoResult;
  741.       End;
  742.     If WriteError = 0 Then             {write index record}
  743.       Begin
  744.       BlockWrite(JM^.IdxFile, TmpIdx, 1);
  745.       WriteError := IoResult;
  746.       End;
  747.     If WriteError = 0 Then
  748.       Begin                            {Add subfields as needed}
  749.       If Length(JM^.MsgTo) > 0 Then
  750.         AddSubField(3, JM^.MsgTo);
  751.       If Length(JM^.MsgFrom) > 0 Then
  752.         AddSubField(2, JM^.MsgFrom);
  753.       If Length(JM^.MsgSubj) > 0 Then
  754.         Begin
  755.         If IsFileReq Then
  756.           AddSubField(11, JM^.MsgSubj)
  757.         Else
  758.           AddSubField(6, JM^.MsgSubj);
  759.         End;
  760.       If ((JM^.Dest.Zone <> 0) or (JM^.Dest.Net <> 0) or
  761.         (JM^.Dest.Node <> 0) or (JM^.Dest.Point <> 0)) Then
  762.         AddSubField(1, AddrStr(JM^.Dest));
  763.       If ((JM^.Orig.Zone <> 0) or (JM^.Orig.Net <> 0) or
  764.         (JM^.Orig.Node <> 0) or (JM^.Orig.Point <> 0)) Then
  765.         AddSubField(0, AddrStr(JM^.Orig));
  766.       Seek(JM^.HdrFile, FileSize(JM^.HdrFile)); {Seek to end of .jhr file}
  767.       WriteError := IoResult;
  768.       End;
  769.     If WriteError = 0 Then
  770.       Begin                            {write msg header}
  771.       BlockWrite(JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^.JamHdr) +
  772.         MsgHdr^.JamHdr.SubFieldLen);
  773.       WriteError := IoResult;
  774.       End;
  775.     If WriteError = 0 Then
  776.       Begin                            {update msg base header}
  777.       Inc(JM^.BaseHdr.ActiveMsgs);
  778.       Inc(JM^.BaseHdr.ModCounter);
  779.       End;
  780.     If UnLockMsgBase Then;             {unlock msg base}
  781.     End;
  782.   WriteMsg := WriteError;              {return result of writing msg}
  783.   End;
  784.  
  785.  
  786. Function JamMsgObj.GetChar: Char;
  787.   Begin
  788.   If JM^.TxtPos < 0 Then
  789.     Begin
  790.     GetChar := JM^.TxtSubBuf[JM^.TxtSubChars + JM^.TxtPos];
  791.     Inc(JM^.TxtPos);
  792.     If JM^.TxtPos >= 0 Then
  793.       JM^.TxtPos := MsgHdr^.JamHdr.TextOfs;
  794.     End
  795.   Else
  796.     Begin
  797.     If ((JM^.TxtPos < JM^.TxtBufStart) Or
  798.     (JM^.TxtPos >= JM^.TxtBufStart + JM^.TxtRead)) Then
  799.       Begin
  800.       JM^.TxtBufStart := JM^.TxtPos - 80;
  801.       If JM^.TxtBufStart < 0 Then
  802.         JM^.TxtBufStart := 0;
  803.       Seek(JM^.TxtFile, JM^.TxtBufStart);
  804.       Error := IoResult;
  805.       If Error = 0 Then
  806.         Begin
  807.         BlockRead(JM^.TxtFile, TxtBuf^, SizeOf(TxtBuf^), JM^.TxtRead);
  808.         Error := IoResult;
  809.         End;
  810.       End;
  811.     GetChar := TxtBuf^[JM^.TxtPos - JM^.TxtBufStart];
  812.     Inc(JM^.TxtPos);
  813.     End;
  814.   End;
  815.  
  816.  
  817.  
  818. Procedure JamMsgObj.AddTxtSub(St: String);
  819.   Var
  820.     i: Word;
  821.  
  822.   Begin
  823.   For i := 1 to Length(St) Do
  824.     Begin
  825.     If JM^.TxtSubChars <= TxtSubBufSize Then
  826.       Begin
  827.       JM^.TxtSubBuf[JM^.TxtSubChars] := St[i];
  828.       Inc(JM^.TxtSubChars);
  829.       End;
  830.     End;
  831.   If JM^.TxtSubChars <= TxtSubBufSize Then
  832.     Begin
  833.     JM^.TxtSubBuf[JM^.TxtSubChars] := #13;
  834.     Inc(JM^.TxtSubChars);
  835.     End;
  836.   End;
  837.  
  838.  
  839. Procedure JamMsgObj.MsgStartUp;
  840.   Var
  841.     SubCtr: LongInt;
  842.     SubPtr: ^SubFieldType;
  843.     NumRead: Word;
  844.     {$IFDEF WINDOWS}
  845.     DT: TDateTime;
  846.     {$ELSE}
  847.     DT: DateTime;
  848.     {$ENDIF}
  849.     IdxLoc: LongInt;
  850.     TmpStr: String;
  851.     TmpAddr: AddrType;
  852.  
  853.   Begin
  854.   Error := 0;
  855.   LastSoft := False;
  856.   JM^.MsgFrom := '';
  857.   JM^.MsgTo := '';
  858.   JM^.MsgSubj := '';
  859.   JM^.TxtSubChars := 0;
  860.   If SeekFound Then
  861.     Begin
  862.     Error := ReReadIdx(IdxLoc);
  863.     If Error = 0 Then
  864.       Begin
  865.       Seek(JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
  866.       Error := IoResult;
  867.       End;
  868.     If Error = 0 Then
  869.       Begin
  870.       BlockRead(JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^), NumRead);
  871.       Error := IoResult;
  872.       End;
  873.     If Error = 0 Then
  874.       Begin
  875.       UnixToDt(MsgHdr^.JamHdr.DateWritten, DT);
  876.       JM^.MsgDate := FormattedDate(Dt, 'MM-DD-YY');
  877.       JM^.MsgTime := FormattedDate(Dt, 'HH:II');
  878.       SubCtr := 1;
  879.       While ((SubCtr <= MsgHdr^.JamHdr.SubFieldLen) and
  880.       (SubCtr < JamSubBufSize)) Do
  881.         Begin
  882.         SubPtr := @MsgHdr^.SubBuf[SubCtr];
  883.         Inc(SubCtr, SubPtr^.DataLen + 8);
  884.         Case(SubPtr^.LoId) Of
  885.           0: Begin {Orig}
  886.              FillChar(TmpAddr, SizeOf(TmpAddr), #0);
  887.              FillChar(JM^.Orig, SizeOf(JM^.Orig), #0);
  888.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  889.              If Ord(TmpStr[0]) > 128 Then
  890.                TmpStr[0] := #128;
  891.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  892.              If ParseAddr(TmpStr, TmpAddr, JM^.Orig) Then;
  893.              End;
  894.           1: Begin {Dest}
  895.              FillChar(TmpAddr, SizeOf(TmpAddr), #0);
  896.              FillChar(JM^.Dest, SizeOf(JM^.Dest), #0);
  897.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  898.              If Ord(TmpStr[0]) > 128 Then
  899.                TmpStr[0] := #128;
  900.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  901.              If ParseAddr(TmpStr, TmpAddr, JM^.Dest) Then;
  902.              End;
  903.           2: Begin {MsgFrom}
  904.              JM^.MsgFrom[0] := Chr(SubPtr^.DataLen and $ff);
  905.              If Ord(JM^.MsgFrom[0]) > 65 Then
  906.                JM^.MsgFrom[0] := #65;
  907.              Move(SubPtr^.Data, JM^.MsgFrom[1], Ord(JM^.MsgFrom[0]));
  908.              End;
  909.           3: Begin {MsgTo}
  910.              JM^.MsgTo[0] := Chr(SubPtr^.DataLen and $ff);
  911.              If Ord(JM^.MsgTo[0]) > 65 Then
  912.                JM^.MsgTo[0] := #65;
  913.              Move(SubPtr^.Data, JM^.MsgTo[1], Ord(JM^.MsgTo[0]));
  914.              End;
  915.           4: Begin {MsgId}
  916.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  917.              If Ord(TmpStr[0]) > 240 Then
  918.                TmpSTr[0] := #240;
  919.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  920.              AddTxtSub(#1'MSGID: ' + TmpStr);
  921.              End;
  922.           5: Begin {Reply}
  923.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  924.              If Ord(TmpStr[0]) > 240 Then
  925.                TmpSTr[0] := #240;
  926.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  927.              AddTxtSub(#1'REPLY: ' + TmpStr);
  928.              End;
  929.           6: Begin {MsgSubj}
  930.              JM^.MsgSubj[0] := Chr(SubPtr^.DataLen and $ff);
  931.              If Ord(JM^.MsgSubj[0]) > 100 Then
  932.                JM^.MsgSubj[0] := #100;
  933.              Move(SubPtr^.Data, JM^.MsgSubj[1], Ord(JM^.MsgSubj[0]));
  934.              End;
  935.           7: Begin {PID}
  936.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  937.              If Ord(TmpStr[0]) > 240 Then
  938.                TmpSTr[0] := #240;
  939.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  940.              AddTxtSub(#1'PID: ' + TmpStr);
  941.              End;
  942.           8: Begin {VIA}
  943.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  944.              If Ord(TmpStr[0]) > 240 Then
  945.                TmpSTr[0] := #240;
  946.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  947.              AddTxtSub(#1'Via ' + TmpStr);
  948.              End;
  949.           9: Begin {File attached}
  950.              If IsFAttach Then
  951.                Begin
  952.                JM^.MsgSubj[0] := Chr(SubPtr^.DataLen and $ff);
  953.                If Ord(JM^.MsgSubj[0]) > 100 Then
  954.                  JM^.MsgSubj[0] := #100;
  955.                Move(SubPtr^.Data, JM^.MsgSubj[1], Ord(JM^.MsgSubj[0]));
  956.                End
  957.              End;
  958.           11: Begin {File request}
  959.              If IsFileReq Then
  960.                Begin
  961.                JM^.MsgSubj[0] := Chr(SubPtr^.DataLen and $ff);
  962.                If Ord(JM^.MsgSubj[0]) > 100 Then
  963.                  JM^.MsgSubj[0] := #100;
  964.                Move(SubPtr^.Data, JM^.MsgSubj[1], Ord(JM^.MsgSubj[0]));
  965.                End
  966.              End;
  967.           2000: Begin {Unknown kludge}
  968.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  969.              If Ord(TmpStr[0]) > 240 Then
  970.                TmpSTr[0] := #240;
  971.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  972.              AddTxtSub(#1 + TmpStr);
  973.              End;
  974.           2001: Begin {SEEN-BY}
  975.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  976.              If Ord(TmpStr[0]) > 240 Then
  977.                TmpSTr[0] := #240;
  978.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  979.              AddTxtSub(#1'SEEN-BY: ' + TmpStr);
  980.              End;
  981.           2002: Begin {PATH}
  982.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  983.              If Ord(TmpStr[0]) > 240 Then
  984.                TmpSTr[0] := #240;
  985.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  986.              AddTxtSub(#1'PATH: ' + TmpStr);
  987.              End;
  988.           2003: Begin {FLAGS}
  989.              TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
  990.              If Ord(TmpStr[0]) > 240 Then
  991.                TmpSTr[0] := #240;
  992.              Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
  993.              AddTxtSub(#1'FLAGS: ' + TmpStr);
  994.              End;
  995.           End;
  996.         End;
  997.       End;
  998.     End;
  999.   End;
  1000.  
  1001.  
  1002. Procedure JamMsgObj.MsgTxtStartUp;
  1003.   Begin
  1004.   LastSoft := False;
  1005.   JM^.TxtEnd := MsgHdr^.JamHdr.TextOfs + MsgHdr^.JamHdr.TextLen - 1;
  1006.   If JM^.TxtSubChars > 0 Then
  1007.     JM^.TxtPos := - JM^.TxtSubChars
  1008.   Else
  1009.     JM^.TxtPos := MsgHdr^.JamHdr.TextOfs;
  1010.   End;
  1011.  
  1012.  
  1013. Function JamMsgObj.GetString(MaxLen: Word): String;
  1014.   Var
  1015.     WPos: LongInt;
  1016.     WLen: Byte;
  1017.     StrDone: Boolean;
  1018.     TxtOver: Boolean;
  1019.     StartSoft: Boolean;
  1020.     CurrLen: Word;
  1021.     PPos: LongInt;
  1022.     TmpCh: Char;
  1023.  
  1024.   Begin
  1025.   StrDone := False;
  1026.   CurrLen := 0;
  1027.   PPos := JM^.TxtPos;
  1028.   WPos := 0;
  1029.   WLen := 0;
  1030.   StartSoft := LastSoft;
  1031.   LastSoft := True;
  1032.   TmpCh := GetChar;
  1033.   While ((Not StrDone) And (CurrLen < MaxLen) And (Not EOM)) Do
  1034.     Begin
  1035.     Case TmpCh of
  1036.       #$00:;
  1037.       #$0d: Begin
  1038.             StrDone := True;
  1039.             LastSoft := False;
  1040.             End;
  1041.       #$8d:;
  1042.       #$0a:;
  1043.       #$20: Begin
  1044.             If ((CurrLen <> 0) or (Not StartSoft)) Then
  1045.               Begin
  1046.               Inc(CurrLen);
  1047.               WLen := CurrLen;
  1048.               GetString[CurrLen] := TmpCh;
  1049.               WPos := JM^.TxtPos;
  1050.               End
  1051.             Else
  1052.               StartSoft := False;
  1053.             End;
  1054.       Else
  1055.         Begin
  1056.         Inc(CurrLen);
  1057.         GetString[CurrLen] := TmpCh;
  1058.         End;
  1059.       End;
  1060.     If Not StrDone Then
  1061.       TmpCh := GetChar;
  1062.     End;
  1063.   If StrDone Then
  1064.     Begin
  1065.     GetString[0] := Chr(CurrLen);
  1066.     End
  1067.   Else
  1068.     If EOM Then
  1069.       Begin
  1070.       GetString[0] := Chr(CurrLen);
  1071.       End
  1072.     Else
  1073.       Begin
  1074.       If WLen = 0 Then
  1075.         Begin
  1076.         GetString[0] := Chr(CurrLen);
  1077.         Dec(JM^.TxtPos);
  1078.         End
  1079.       Else
  1080.         Begin
  1081.         GetString[0] := Chr(WLen);
  1082.         JM^.TxtPos := WPos;
  1083.         End;
  1084.       End;
  1085.   End;
  1086.  
  1087.  
  1088. Function JamMsgObj.EOM: Boolean;
  1089.   Begin
  1090.   EOM := (((JM^.TxtPos < MsgHdr^.JamHdr.TextOfs) Or
  1091.     (JM^.TxtPos > JM^.TxtEnd)) And (JM^.TxtPos >= 0));
  1092.   End;
  1093.  
  1094.  
  1095. Function JamMsgObj.WasWrap: Boolean;
  1096.   Begin
  1097.   WasWrap := LastSoft;
  1098.   End;
  1099.  
  1100.  
  1101. Function JamMsgObj.GetFrom: String; {Get from name on current msg}
  1102.   Begin
  1103.   GetFrom := JM^.MsgFrom;
  1104.   End;
  1105.  
  1106.  
  1107. Function JamMsgObj.GetTo: String; {Get to name on current msg}
  1108.   Begin
  1109.   GetTo := JM^.MsgTo;
  1110.   End;
  1111.  
  1112.  
  1113. Function JamMsgObj.GetSubj: String; {Get subject on current msg}
  1114.   Begin
  1115.   GetSubj := JM^.MsgSubj;
  1116.   End;
  1117.  
  1118.  
  1119. Function JamMsgObj.GetCost: Word; {Get cost of current msg}
  1120.   Begin
  1121.   GetCost := MsgHdr^.JamHdr.Cost;
  1122.   End;
  1123.  
  1124.  
  1125. Function JamMsgObj.GetDate: String; {Get date of current msg}
  1126.   Begin
  1127.   GetDate := JM^.MsgDate;
  1128.   End;
  1129.  
  1130.  
  1131. Function JamMsgObj.GetTime: String; {Get time of current msg}
  1132.   Begin
  1133.   GetTime := JM^.MsgTime;
  1134.   End;
  1135.  
  1136.  
  1137. Function JamMsgObj.GetRefer: LongInt; {Get reply to of current msg}
  1138.   Begin
  1139.   GetRefer := MsgHdr^.JamHdr.ReplyTo;
  1140.   End;
  1141.  
  1142.  
  1143. Function JamMsgObj.GetSeeAlso: LongInt; {Get see also of current msg}
  1144.   Begin
  1145.   GetSeeAlso := MsgHdr^.JamHdr.ReplyFirst;
  1146.   End;
  1147.  
  1148.  
  1149. Function JamMsgObj.GetMsgNum: LongInt; {Get message number}
  1150.   Begin
  1151.   GetMsgNum := MsgHdr^.JamHdr.MsgNum;
  1152.   End;
  1153.  
  1154.  
  1155. Procedure JamMsgObj.GetOrig(Var Addr: AddrType); {Get origin address}
  1156.   Begin
  1157.   Addr := JM^.Orig;
  1158.   End;
  1159.  
  1160.  
  1161. Procedure JamMsgObj.GetDest(Var Addr: AddrType); {Get destination address}
  1162.   Begin
  1163.   Addr := JM^.Dest;
  1164.   End;
  1165.  
  1166.  
  1167. Function JamMsgObj.IsLocal: Boolean; {Is current msg local}
  1168.   Begin
  1169.   IsLocal := (MsgHdr^.JamHdr.Attr1 and Jam_Local) <> 0;
  1170.   End;
  1171.  
  1172.  
  1173. Function JamMsgObj.IsCrash: Boolean; {Is current msg crash}
  1174.   Begin
  1175.   IsCrash := (MsgHdr^.JamHdr.Attr1 and Jam_Crash) <> 0;
  1176.   End;
  1177.  
  1178.  
  1179. Function JamMsgObj.IsKillSent: Boolean; {Is current msg kill sent}
  1180.   Begin
  1181.   IsKillSent := (MsgHdr^.JamHdr.Attr1 and Jam_KillSent) <> 0;
  1182.   End;
  1183.  
  1184.  
  1185. Function JamMsgObj.IsSent: Boolean; {Is current msg sent}
  1186.   Begin
  1187.   IsSent := (MsgHdr^.JamHdr.Attr1 and Jam_Sent) <> 0;
  1188.   End;
  1189.  
  1190.  
  1191. Function JamMsgObj.IsFAttach: Boolean; {Is current msg file attach}
  1192.   Begin
  1193.   IsFAttach := (MsgHdr^.JamHdr.Attr1 and Jam_FAttch) <> 0;
  1194.   End;
  1195.  
  1196.  
  1197. Function JamMsgObj.IsReqRct: Boolean; {Is current msg request receipt}
  1198.   Begin
  1199.   IsReqRct := (MsgHdr^.JamHdr.Attr1 and Jam_RcptReq) <> 0;
  1200.   End;
  1201.  
  1202.  
  1203. Function JamMsgObj.IsReqAud: Boolean; {Is current msg request audit}
  1204.   Begin
  1205.   IsReqAud := (MsgHdr^.JamHdr.Attr1 and Jam_ConfmReq) <> 0;
  1206.   End;
  1207.  
  1208.  
  1209. Function JamMsgObj.IsRetRct: Boolean; {Is current msg a return receipt}
  1210.   Begin
  1211.   IsRetRct := False;
  1212.   End;
  1213.  
  1214.  
  1215. Function JamMsgObj.IsFileReq: Boolean; {Is current msg a file request}
  1216.   Begin
  1217.   IsFileReq := (MsgHdr^.JamHdr.Attr1 and Jam_Freq) <> 0;
  1218.   End;
  1219.  
  1220.  
  1221. Function JamMsgObj.IsRcvd: Boolean; {Is current msg received}
  1222.   Begin
  1223.   IsRcvd := (MsgHdr^.JamHdr.Attr1 and Jam_Rcvd) <> 0;
  1224.   End;
  1225.  
  1226.  
  1227. Function JamMsgObj.IsPriv: Boolean; {Is current msg priviledged/private}
  1228.   Begin
  1229.   IsPriv := (MsgHdr^.JamHdr.Attr1 and Jam_Priv) <> 0;
  1230.   End;
  1231.  
  1232.  
  1233. Function JamMsgObj.IsDeleted: Boolean; {Is current msg deleted}
  1234.   Begin
  1235.   IsDeleted := (MsgHdr^.JamHdr.Attr1 and Jam_Deleted) <> 0;
  1236.   End;
  1237.  
  1238.  
  1239. Function JamMsgObj.IsEchoed: Boolean; {Is current msg echoed}
  1240.   Begin
  1241.   IsEchoed := True;
  1242.   End;
  1243.  
  1244.  
  1245. Procedure JamMsgObj.SeekFirst(MsgNum: LongInt); {Start msg seek}
  1246.   Begin
  1247.   JM^.CurrMsgNum := MsgNum - 1;
  1248.   If JM^.CurrMsgNum < (JM^.BaseHdr.BaseMsgNum - 1) Then
  1249.     JM^.CurrMsgNum := JM^.BaseHdr.BaseMsgNum - 1;
  1250.   SeekNext;
  1251.   End;
  1252.  
  1253.  
  1254. Procedure JamMsgObj.SeekNext; {Find next matching msg}
  1255.   Var
  1256.     IdxLoc: LongInt;
  1257.  
  1258.   Begin
  1259.   If JM^.CurrMsgNum <= GetHighMsgNum Then
  1260.     Inc(JM^.CurrMsgNum);
  1261.   Error := ReReadIdx(IdxLoc);
  1262.   While (((JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc < 0) or
  1263.   (JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = -1)) And
  1264.   (JM^.CurrMsgNum <= GetHighMsgNum)) Do
  1265.     Begin
  1266.     Inc(JM^.CurrMsgNum);
  1267.     Error := ReReadIdx(IdxLoc);
  1268.     End;
  1269.   End;
  1270.  
  1271.  
  1272. Procedure JamMsgObj.SeekPrior;
  1273.   Var
  1274.     IdxLoc: LongInt;
  1275.  
  1276.   Begin
  1277.   If JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum Then
  1278.     Dec(JM^.CurrMsgNum);
  1279.   Error := ReReadIdx(IdxLoc);
  1280.   If JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum Then
  1281.     Begin
  1282.     While (((JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc < 0) or
  1283.     (JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = -1)) And
  1284.     (JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum)) Do
  1285.       Begin
  1286.       Dec(JM^.CurrMsgNum);
  1287.       Error := ReReadIdx(IdxLoc);
  1288.       End;
  1289.     End;
  1290.   End;
  1291.  
  1292.  
  1293. Function JamMsgObj.SeekFound: Boolean;
  1294.   Begin
  1295.   SeekFound := ((JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum) and
  1296.     (JM^.CurrMsgNum <= GetHighMsgNum));
  1297.   End;
  1298.  
  1299.  
  1300. Function JamMsgObj.GetMsgLoc: LongInt; {Msg location}
  1301.   Begin
  1302.   GetMsgLoc := GetMsgNum;
  1303.   End;
  1304.  
  1305.  
  1306. Procedure JamMsgObj.SetMsgLoc(ML: LongInt); {Msg location}
  1307.   Begin
  1308.   JM^.CurrMsgNum := ML;
  1309.   End;
  1310.  
  1311.  
  1312. Procedure JamMsgObj.YoursFirst(Name: String; Handle: String);
  1313.   Begin
  1314.   JM^.YourName := Name;
  1315.   JM^.YourHdl := Handle;
  1316.   JM^.NameCrc := JamStrCrc(Name);
  1317.   JM^.HdlCrc := JamStrCrc(Handle);
  1318.   JM^.CurrMsgNum := JM^.BaseHdr.BaseMsgNum - 1;
  1319.   YoursNext;
  1320.   End;
  1321.  
  1322.  
  1323. Procedure JamMsgObj.YoursNext;
  1324.   Var
  1325.     Found: Boolean;
  1326.     IdxLoc: LongInt;
  1327.     NumRead: Word;
  1328.     SubCtr: LongInt;
  1329.     SubPtr: ^SubFieldType;
  1330.  
  1331.   Begin
  1332.   Error := 0;
  1333.   Found := False;
  1334.   Inc(JM^.CurrMsgNum);
  1335.   While ((Not Found) and (JM^.CurrMsgNum <= GetHighMsgNum) And
  1336.   (Error = 0)) Do
  1337.     Begin
  1338.     Error := ReReadIdx(IdxLoc);
  1339.     If Error = 0 Then
  1340.       Begin                            {Check CRC values}
  1341.       If ((JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = JM^.NameCrc) or
  1342.       (JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = JM^.HdlCrc)) Then
  1343.         Begin
  1344.         Seek(JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
  1345.         Error := IoResult;
  1346.         If Error = 0 Then              {Read message header}
  1347.           Begin
  1348.           BlockRead(JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^), NumRead);
  1349.           Error := IoResult;
  1350.           End;
  1351.         If ((Error = 0) and (Not IsRcvd)) Then
  1352.           Begin
  1353.           SubCtr := 1;
  1354.           While ((SubCtr <= MsgHdr^.JamHdr.SubFieldLen) and
  1355.           (SubCtr < JamSubBufSize)) Do
  1356.             Begin
  1357.             SubPtr := @MsgHdr^.SubBuf[SubCtr];
  1358.             Inc(SubCtr, SubPtr^.DataLen + 8);
  1359.             Case(SubPtr^.LoId) Of
  1360.               3: Begin {MsgTo}
  1361.                  JM^.MsgTo[0] := Chr(SubPtr^.DataLen and $ff);
  1362.                  If Ord(JM^.MsgTo[0]) > 65 Then
  1363.                    JM^.MsgTo[0] := #65;
  1364.                  Move(SubPtr^.Data, JM^.MsgTo[1], Ord(JM^.MsgTo[0]));
  1365.                  If ((Upper(JM^.MsgTo) = Upper(JM^.YourName)) Or
  1366.                  (Upper(JM^.MsgTo) = Upper(JM^.YourHdl))) Then
  1367.                    Found := True;
  1368.                  End;
  1369.               End;
  1370.             End;
  1371.           End;
  1372.         End;
  1373.       End;
  1374.     If (Not Found) Then
  1375.       Inc(JM^.CurrMsgNum);
  1376.     End;
  1377.   End;
  1378.  
  1379.  
  1380. Function JamMsgObj.YoursFound: Boolean;
  1381.   Begin
  1382.   YoursFound := ((JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum) and
  1383.     (JM^.CurrMsgNum <= GetHighMsgNum));
  1384.   End;
  1385.  
  1386.  
  1387. Procedure JamMsgObj.StartNewMsg;
  1388.   Begin
  1389.   JM^.TxtBufStart := 0;
  1390.   JM^.TxtPos := 0;
  1391.   FillChar(MsgHdr^, SizeOf(MsgHdr^), #0);
  1392.   MsgHdr^.JamHdr.SubFieldLen := 0;
  1393.   MsgHdr^.JamHdr.MsgIdCrc := -1;
  1394.   MsgHdr^.JamHdr.ReplyCrc := -1;
  1395.   MsgHdr^.JamHdr.PwdCrc := -1;
  1396.   JM^.MsgTo := '';
  1397.   JM^.MsgFrom := '';
  1398.   JM^.MsgSubj := '';
  1399.   FillChar(JM^.Orig, SizeOf(JM^.Orig), #0);
  1400.   FillChar(JM^.Dest, SizeOf(JM^.Dest), #0);
  1401.   JM^.MsgDate := DateStr(GetDosDate);
  1402.   JM^.MsgTime := TimeStr(GetDosDate);
  1403.   End;
  1404.  
  1405.  
  1406. Function JamMsgObj.MsgBaseExists: Boolean;
  1407.   Begin
  1408.   MsgBaseExists := (FileExist(JM^.MsgPath + '.JHR'));
  1409.   End;
  1410.  
  1411.  
  1412. Function JamMsgObj.ReadIdx: Word;
  1413.   Begin
  1414.   If JM^.IdxStart < 0 Then
  1415.     JM^.IdxStart := 0;
  1416.   Seek(JM^.IdxFile, JM^.IdxStart);
  1417.   BlockRead(JM^.IdxFile, JamIdx^, JamIdxBufSize, JM^.IdxRead);
  1418.   ReadIdx := IoResult;
  1419.   End;
  1420.  
  1421.  
  1422. Function JamMsgObj.WriteIdx: Word;
  1423.   Begin
  1424.   Seek(JM^.IdxFile, JM^.IdxStart);
  1425.   BlockWrite(JM^.IdxFile, JamIdx^, JM^.IdxRead);
  1426.   WriteIdx := IoResult;
  1427.   End;
  1428.  
  1429.  
  1430. Function JamMsgObj.OpenMsgBase: Word;
  1431.   Var
  1432.     OpenError: Word;
  1433.  
  1434.   Begin
  1435.   OpenError := 0;
  1436.   JM^.LockCount := 0;
  1437.   Assign(JM^.HdrFile, JM^.MsgPath + '.JHR');
  1438.   Assign(JM^.TxtFile, JM^.MsgPath + '.JDT');
  1439.   Assign(JM^.IdxFile, JM^.MsgPath + '.JDX');
  1440.   FileMode := fmReadWrite + fmDenyNone;
  1441.   Reset(JM^.HdrFile, 1);
  1442.   OpenError := IoResult;
  1443.   If OpenError = 0 Then
  1444.     Begin
  1445.     Seek(JM^.HdrFile, 1);
  1446.     BlockRead(JM^.HdrFile, JM^.BaseHdr.Signature[2] , SizeOf(JM^.BaseHdr) - 1);
  1447.     OpenError := IoResult;
  1448.     End;
  1449.   If OpenError = 0 Then
  1450.     Begin
  1451.     FileMode := fmReadWrite + fmDenyNone;
  1452.     Reset(JM^.TxtFile, 1);
  1453.     OpenError := IoResult;
  1454.     End;
  1455.   If OpenError = 0 Then
  1456.     Begin
  1457.     FileMode := fmReadWrite + fmDenyNone;
  1458.     Reset(JM^.IdxFile, SizeOf(JamIdxType));
  1459.     OpenError := IoResult;
  1460.     End;
  1461.   JM^.IdxStart := -10;
  1462.   JM^.IdxRead := 0;
  1463.   JM^.TxtBufStart := - 10;
  1464.   JM^.TxtRead := 0;
  1465.   OpenMsgBase := OpenError;
  1466.   End;
  1467.  
  1468.  
  1469. Function JamMsgObj.CloseMsgBase: Word;
  1470.   Var
  1471.     CloseError: Word;
  1472.  
  1473.   Begin
  1474.   Close(JM^.HdrFile);
  1475.   CloseError := IoResult;
  1476.   Close(JM^.TxtFile);
  1477.   If CloseError = 0 Then
  1478.     CloseError := IoResult;
  1479.   Close(JM^.IdxFile);
  1480.   If CloseError = 0 Then
  1481.     CloseError := IoResult;
  1482.   CloseMsgBase := CloseError;
  1483.   End;
  1484.  
  1485.  
  1486. Function JamMsgObj.CreateMsgBase(MaxMsg: Word; MaxDays: Word): Word;
  1487.   Var
  1488.     TmpHdr: ^JamHdrType;
  1489.     CreateError: Word;
  1490.     i: Word;
  1491.  
  1492.   Begin
  1493.   CreateError := 0;
  1494.   i := PosLastChar('\', JM^.MsgPath);
  1495.   If i > 0 Then
  1496.     Begin
  1497.     If Not MakePath(Copy(JM^.MsgPath, 1, i)) Then
  1498.       CreateError := 0;
  1499.     End;
  1500.   New(TmpHdr);
  1501.   If TmpHdr = Nil Then
  1502.     CreateError := 500
  1503.   Else
  1504.     Begin;
  1505.     FillChar(TmpHdr^, SizeOf(TmpHdr^), #0);
  1506.     TmpHdr^.Signature[1] :=  'J';
  1507.     TmpHdr^.Signature[2] :=  'A';
  1508.     TmpHdr^.Signature[3] :=  'M';
  1509.     TmpHdr^.BaseMsgNum := 1;
  1510.     TmpHdr^.Created := ToUnixDate(GetDosDate);
  1511.     TmpHdr^.PwdCrc := -1;
  1512.     CreateError := SaveFile(JM^.MsgPath + '.JHR', TmpHdr^, SizeOf(TmpHdr^));
  1513.     Dispose(TmpHdr);
  1514.     If CreateError = 0 Then
  1515.       CreateError := SaveFile(JM^.MsgPath + '.JLR', CreateError, 0);
  1516.     If CreateError = 0 Then
  1517.       CreateError := SaveFile(JM^.MsgPath + '.JDT', CreateError, 0);
  1518.     If CreateError = 0 Then
  1519.       CreateError := SaveFile(JM^.MsgPath + '.JDX', CreateError , 0);
  1520.     If IoResult <> 0 Then;
  1521.     End;
  1522.   CreateMsgBase := CreateError;
  1523.   End;
  1524.  
  1525.  
  1526. Procedure JamMsgObj.SetMailType(MT: MsgMailType);
  1527.   Begin
  1528.   JM^.MailType := MT;
  1529.   End;
  1530.  
  1531.  
  1532. Function JamMsgObj.GetSubArea: Word;
  1533.   Begin
  1534.   GetSubArea := 0;
  1535.   End;
  1536.  
  1537.  
  1538. Procedure JamMsgObj.ReWriteHdr;
  1539.   Var
  1540.     IdxLoc: LongInt;
  1541.  
  1542.   Begin
  1543.   If LockMsgBase Then
  1544.     Error := 0
  1545.   Else
  1546.     Error := 5;
  1547.   Error := ReReadIdx(IdxLoc);
  1548.   If Error = 0 Then
  1549.     Begin
  1550.     Seek(JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
  1551.     Error := IoResult;
  1552.     End;
  1553.   If Error = 0 Then
  1554.     Begin
  1555.     BlockWrite(JM^.HdrFile, MsgHdr^.JamHdr, SizeOf(MsgHdr^.JamHdr));
  1556.     Error := IoResult;
  1557.     End;
  1558.   If UnLockMsgBase Then;
  1559.   End;
  1560.  
  1561.  
  1562. Procedure JamMsgObj.DeleteMsg;
  1563.   Var
  1564.     DelError: Word;
  1565.     IdxLoc: LongInt;
  1566.  
  1567.   Begin
  1568.   If Not IsDeleted Then
  1569.     Begin
  1570.     If LockMsgBase Then
  1571.       DelError := 0
  1572.     Else
  1573.       DelError := 5;
  1574.     If DelError = 0 Then
  1575.       Begin
  1576.       SetAttr1(Jam_Deleted, True);
  1577.       Dec(JM^.BaseHdr.ActiveMsgs);
  1578.       DelError := ReReadIdx(IdxLoc);
  1579.       End;
  1580.     If DelError = 0 Then
  1581.       ReWriteHdr;
  1582.     If DelError = 0 Then
  1583.       Begin
  1584.       Inc(JM^.BaseHdr.ModCounter);
  1585.       JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc := -1;
  1586.       JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc := -1;
  1587.       If WriteIdx=0 Then;
  1588.       End;
  1589.     If UnLockMsgBase Then;
  1590.     End;
  1591.   End;
  1592.  
  1593.  
  1594. Function JamMsgObj.NumberOfMsgs: LongInt;
  1595.   Begin
  1596.   NumberOfMsgs := JM^.BaseHdr.ActiveMsgs;
  1597.   End;
  1598.  
  1599.  
  1600. Function JamMsgObj.FindLastRead(Var LastFile: File; UNum: LongInt): LongInt;
  1601.   Const
  1602.     LastSize = 100;
  1603.  
  1604.   Type LastArray = Array[1..LastSize] of JamLastType;
  1605.  
  1606.   Var
  1607.     LastBuf: ^LastArray;
  1608.     LastError: Word;
  1609.     NumRead: Word;
  1610.     Found: Boolean;
  1611.     i: Word;
  1612.     LastStart: LongInt;
  1613.  
  1614.   Begin
  1615.   FindLastRead := -1;
  1616.   Found := False;
  1617.   New(LastBuf);
  1618.   Seek(LastFile, 0);
  1619.   LastError := IoResult;
  1620.   While ((Not Eof(LastFile)) and (LastError = 0) And (Not Found)) Do
  1621.     Begin
  1622.     LastStart := FilePos(LastFile);
  1623.     BlockRead(LastFile, LastBuf^, LastSize, NumRead);
  1624.     LastError := IoResult;
  1625.     For i := 1 to NumRead Do
  1626.       Begin
  1627.       If LastBuf^[i].UserNum = UNum Then
  1628.         Begin
  1629.         Found := True;
  1630.         FindLastRead := LastStart + i - 1;
  1631.         End;
  1632.       End;
  1633.     End;
  1634.   Dispose(LastBuf);
  1635.   End;
  1636.  
  1637.  
  1638. Function JamMsgObj.GetLastRead(UNum: LongInt): LongInt;
  1639.   Var
  1640.     RecNum: LongInt;
  1641.     LastFile: File;
  1642.     TmpLast: JamLastType;
  1643.  
  1644.   Begin
  1645.   Assign(LastFile, JM^.MsgPath + '.JLR');
  1646.   FileMode := fmReadWrite + fmDenyNone;
  1647.   Reset(LastFile, SizeOf(JamLastType));
  1648.   Error := IoResult;
  1649.   RecNum := FindLastRead(LastFile, UNum);
  1650.   If RecNum >= 0 Then
  1651.     Begin
  1652.     Seek(LastFile, RecNum);
  1653.     If Error = 0 Then
  1654.       Begin
  1655.       BlockRead(LastFile, TmpLast, 1);
  1656.       Error := IoResult;
  1657.       GetLastRead := TmpLast.HighRead;
  1658.       End;
  1659.     End
  1660.   Else
  1661.     GetLastRead := 0;
  1662.   Close(LastFile);
  1663.   Error := IoResult;
  1664.   End;
  1665.  
  1666.  
  1667. Procedure JamMsgObj.SetLastRead(UNum: LongInt; LR: LongInt);
  1668.   Var
  1669.     RecNum: LongInt;
  1670.     LastFile: File;
  1671.     TmpLast: JamLastType;
  1672.  
  1673.   Begin
  1674.   Assign(LastFile, JM^.MsgPath + '.JLR');
  1675.   FileMode := fmReadWrite + fmDenyNone;
  1676.   Reset(LastFile, SizeOf(JamLastType));
  1677.   Error := IoResult;
  1678.   RecNum := FindLastRead(LastFile, UNum);
  1679.   If RecNum >= 0 Then
  1680.     Begin
  1681.     Seek(LastFile, RecNum);
  1682.     If Error = 0 Then
  1683.       Begin
  1684.       BlockRead(LastFile, TmpLast, 1);
  1685.       Error := IoResult;
  1686.       TmpLast.HighRead := LR;
  1687.       TmpLast.LastRead := LR;
  1688.       If Error = 0 Then
  1689.         Begin
  1690.         Seek(LastFile, RecNum);
  1691.         Error := IoResult;
  1692.         End;
  1693.       If Error = 0 Then
  1694.         Begin
  1695.         BlockWrite(LastFile, TmpLast, 1);
  1696.         Error := IoResult;
  1697.         End;
  1698.       End;
  1699.     End
  1700.   Else
  1701.     Begin
  1702.     TmpLast.UserNum := UNum;
  1703.     TmpLast.HighRead := Lr;
  1704.     TmpLast.NameCrc := UNum;
  1705.     TmpLast.LastRead := Lr;
  1706.     Seek(LastFile, FileSize(LastFile));
  1707.     Error := IoResult;
  1708.     If Error = 0 Then
  1709.       Begin
  1710.       BlockWrite(LastFile, TmpLast, 1);
  1711.       Error := IoResult;
  1712.       End;
  1713.     End;
  1714.   Close(LastFile);
  1715.   Error := IoResult;
  1716.   End;
  1717.  
  1718.  
  1719. Function JamMsgObj.GetTxtPos: LongInt;
  1720.   Begin
  1721.   GetTxtPos := JM^.TxtPos;
  1722.   End;
  1723.  
  1724.  
  1725. Procedure JamMsgObj.SetTxtPos(TP: LongInt);
  1726.   Begin
  1727.   JM^.TxtPos := TP;
  1728.   End;
  1729.  
  1730.  
  1731. Function JamMsgObj.LockMsgBase: Boolean;
  1732.   Var
  1733.     LockError: Word;
  1734.  
  1735.   Begin
  1736.   LockError := 0;
  1737.   If JM^.LockCount = 0 Then
  1738.     Begin
  1739.     If LockError = 0 Then
  1740.       Begin
  1741.       LockError := shLock(JM^.HdrFile, 0, 1);
  1742.       End;
  1743.     If LockError = 0 Then
  1744.       Begin
  1745.       Seek(JM^.HdrFile, 0);
  1746.       LockError := IoResult;
  1747.       End;
  1748.     If LockError = 0 Then
  1749.       Begin
  1750.       BlockRead(JM^.HdrFile, JM^.BaseHdr , SizeOf(JM^.BaseHdr));
  1751.       LockError := IoResult;
  1752.       End;
  1753.     End;
  1754.   Inc(JM^.LockCount);
  1755.   LockMsgBase := (LockError = 0);
  1756.   End;
  1757.  
  1758.  
  1759. Function JamMsgObj.UnLockMsgBase: Boolean;
  1760.   Var
  1761.     LockError: Word;
  1762.  
  1763.   Begin
  1764.   LockError := 0;
  1765.   If JM^.LockCount > 0 Then
  1766.     Dec(JM^.LockCount);
  1767.   If JM^.LockCount = 0 Then
  1768.     Begin
  1769.     If LockError = 0 Then
  1770.       Begin
  1771.       LockError := UnLockFile(JM^.HdrFile, 0, 1);
  1772.       End;
  1773.     If LockError = 0 Then
  1774.       Begin
  1775.       Seek(JM^.HdrFile, 0);
  1776.       LockError := IoResult;
  1777.       End;
  1778.     If LockError = 0 Then
  1779.       Begin
  1780.       BlockWrite(JM^.HdrFile, JM^.BaseHdr, SizeOf(JM^.BaseHdr));
  1781.       LockError := IoResult;
  1782.       End;
  1783.     End;
  1784.   UnLockMsgBase := (LockError = 0);
  1785.   End;
  1786.  
  1787. {SetSeeAlso/GetSeeAlso provided by 2:201/623@FidoNet Jonas@iis.bbs.bad.se}
  1788.  
  1789.  
  1790. Procedure JamMsgObj.SetNextSeeAlso(SAlso: LongInt);
  1791.   Begin
  1792.   MsgHdr^.JamHdr.ReplyNext := SAlso;
  1793.   End;
  1794.  
  1795. Function JamMsgObj.GetNextSeeAlso: LongInt; {Get next see also of current msg}
  1796.   Begin
  1797.   GetNextSeeAlso := MsgHdr^.JamHdr.ReplyNext;
  1798.   End;
  1799.  
  1800.  
  1801. Function JamMsgObj.ReReadIdx(Var IdxLoc : LongInt) : Word;
  1802.   Begin
  1803.   ReReadIdx := 0;
  1804.   IdxLoc := JM^.CurrMsgNum - JM^.BaseHdr.BaseMsgNum;
  1805.   If ((IdxLoc < JM^.IdxStart) OR
  1806.     (IdxLoc >= (JM^.IdxStart+JM^.IdxRead))) Then
  1807.     Begin
  1808.     JM^.IdxStart := IdxLoc - 30;
  1809.     If JM^.IdxStart < 0 Then JM^.IdxStart := 0;
  1810.     ReReadIdx := ReadIdx;
  1811.     End;
  1812.   End;
  1813.  
  1814.  
  1815. End.
  1816.