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