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