UNIT Renegade;

{ Renegade.Pas                                                          }
{                                                                       }
{ Code om simpel toegang te krijgen tot een renegade base               }
{ Simpeler kan het eigenlijk niet, dit message base formaat is zo       }
{ belachelijk simpel,..                                                 }
{                                                                       }
{ [MD] 13/06/94 Gewoon niets beters te doen, en dus maar wat aan het    }
{               knutselen.                                              }

INTERFACE

CONST rmsDeleted     = $01;
      rmsSent        = $02;
      rmsUnvalidated = $04;
      rmsPermanent   = $08;
      rmsAllowMCI    = $10;
      rmsNetmail     = $20;
      rmsPrvt        = $40;
      rmsForwarded   = $80;

TYPE ReneFromToInfo = RECORD
                            anon     : BYTE;
                            usernum  : WORD;
                            as       : STRING[36];
                            real     : STRING[36];
                            name     : STRING[36];
                            zone,
                            net,
                            node,
                            point    : WORD;
                     END;

     ReneNetattr = (Private,
                    Crash,
                    Recd,
                    NSent,
                    FileAttach,
                    Intransit,
                    Orphan,
                    KillSent,
                    Local,
                    Hold,
                    Unused,
                    FileRequest,
                    ReturnReceiptRequest,
                    IsReturnReceipt,
                    AuditRequest,
                    FileUpdateRequest);

     ReneMHeader = RECORD
                         from,
                         mto          : ReneFromtoinfo;        { message from/to info    }
                         pointer      : LONGINT;               { starting record of text }
                         textsize     : WORD;                  { size of text            }
                         replyto      : WORD;                  { ORIGINAL + REPLYTO = CURRENT }
                         date         : LONGINT;               { date/time packed string }
                         dayofweek    : BYTE;                  { message day of week     }
                         status       : BYTE;                  { message status flags    }
                         replies      : WORD;                  { times replied to        }
                         subject      : STRING[40];            { subject of message      }
                         origindate   : STRING[19];            { date of echo/group msgs }
                         fileattached : BYTE;                  { 0=No, 1=Yes&Del, 2=Yes&Save }
                         netattribute : ReneNetAttr;           { Netmail attributes }
                         res          : ARRAY[1..2] OF BYTE;   { reserved }
                   END;

     ReneBase = OBJECT
                      { Standaard info }
                      BaseName      : STRING;
                      BasePath      : STRING[79];
                      BaseOpen      : BOOLEAN;
                      SplitCurrent  : BYTE;
                      SplitParts    : BYTE;
                      HeaderFile    : FILE;
                      DataFile      : FILE;

                      MessageHeader : ReneMHeader;

                      CONSTRUCTOR InitBase;
                      DESTRUCTOR  ShutBase;

                      FUNCTION  OpenBase (AreaName,AreaPath : STRING) : BOOLEAN;
                      PROCEDURE CloseBase;
                      FUNCTION  CreateBase : BOOLEAN;

                      PROCEDURE ScanArea (AreaPath,AreaName : STRING);
                      PROCEDURE WriteMessage (AreaName,AreaPath : STRING);

                      PROCEDURE SetReneGadeHeader;
                      PROCEDURE CvtFidoTimeToRenegade;
                      PROCEDURE GuessSplitParts;
                END;

IMPLEMENTATION

USES Dos,
     Stats,
     Database,
     AreaBase,
     Userbase,
     FBuffer,
     Ramon,
     Globals,
     Msgs,
     Fido,
     Logs,
     UnixTime,
     Cfg;


{--------------------------------------------------------------------------}
{ InitBase                                                                 }
{                                                                          }
{ Constructor die zorgt voor de algemene initialisatie van de base         }
{                                                                          }
CONSTRUCTOR ReneBase.InitBase;
BEGIN
     BaseOpen:=FALSE;
END;


{ -------------------------------------------------------------------------}
{ ShutBase                                                                 }
{                                                                          }
{ Destructor die zorgt voor het afsluiten van de base                      }
{                                                                          }
DESTRUCTOR ReneBase.ShutBase;
BEGIN
     IF BaseOpen THEN
        CloseBase;
END;



{--------------------------------------------------------------------------}
{ CreateBase                                                               }
{                                                                          }
{ Probeer een base te creeren als die nog niet bestond.                    }
{                                                                          }
FUNCTION ReneBase.CreateBase : BOOLEAN;
BEGIN
     {$I-}
     Assign (HeaderFile,BasePath+'.HDR');
     Assign (DataFile,BasePath+'.DAT');
     ReWrite (HeaderFile,1);
     ReWrite (DataFile,1);
     {$I+}
     CreateBase:=(IOResult =0);
END;


{--------------------------------------------------------------------------}
{ CloseBase                                                                }
{                                                                          }
{ Sluit een open Renegade message base                                     }
{                                                                          }
PROCEDURE ReneBase.CloseBase;
BEGIN
     IF BaseOpen THEN
     BEGIN
          {$I-}
          Close (HeaderFile);
          Close (DataFile);
          {$I+}
          BaseOpen:=FALSE;
     END;
END;


{--------------------------------------------------------------------------}
{ CvtFidoTimeToRenegade                                                    }
{                                                                          }
{ Converteerd een fido tijd string naar een packed time string zoals die   }
{ binnen Renegade gebruikt wordt.                                          }
{                                                                          }
PROCEDURE ReneBase.CvtFidoTimeToRenegade;

VAR DatumTijd  : DateTime;
    PackedTime : LONGINT;
    Error      : INTEGER;

BEGIN
     WITH DatumTijd DO
     BEGIN
          Val (Copy (Msg.Date_F,1,2),Day,Error);
          Val (Copy (Msg.Date_F,8,2),Year,Error);
          Val (Copy (Msg.Date_F,12,2),Hour,Error);
          Val (Copy (Msg.Date_F,15,2),Min,Error);
          Val (Copy (Msg.Date_F,18,2),Sec,Error);
     END; { with }

     FOR DatumTijd.Month:=1 TO 12 DO
         IF (Month[DatumTijd.Month] = Copy (Msg.Date_F,4,3)) THEN
            Break;

     IF (DatumTijd.Year < 70) THEN
        DatumTijd.Year:=DatumTijd.Year+2000
     ELSE
         DatumTijd.Year:=DatumTijd.Year+1900;

     DosToUnix (DatumTijd,MessageHeader.Date);

     { Berekenen van de dag van de week, haalt uit het aantal seconden na }
     { 1970 het aantal dagen, en daaruit het aantal verstreken weken, en  }
     { dus de dag van vandaag.                                            }
     { Corrigeer voor de datum van 1 januari 1970                         }
     MessageHeader.DayOfWeek:=((MessageHeader.Date DIV (60*60*24))+5) MOD 7;
END;


{--------------------------------------------------------------------------}
{ SetReneGadeHeader                                                        }
{                                                                          }
{ Vult de header van een bericht met zinnige informatie :)                 }
{                                                                          }
PROCEDURE ReneBase.SetReneGadeHeader;
BEGIN
     WITH MessageHeader DO
     BEGIN
          WITH From DO                {FromUserInfo}
          BEGIN
               anon:=0;
               usernum:=0;            { Onbekende gebruiker ? }
               as:=Msg.FromUser_F;
               real:=Msg.FromUser_F;
               name:=Msg.FromUser_F;
               zone:=Msg.FromAddr_F.Zone;
               net:=Msg.FromAddr_F.Net;
               node:=Msg.FromAddr_F.Node;
               point:=Msg.FromAddr_F.point;
          END; { with }

          WITH Mto DO
          BEGIN
               anon:=0;
               usernum:=0;
               as:=Msg.ToUser_F;
               real:=Msg.ToUser_F;
               name:=Msg.ToUser_F;
               zone:=Msg.ToAddr_F.Zone;
               net:=Msg.ToAddr_F.Net;
               node:=Msg.ToAddr_F.Node;
               point:=Msg.ToAddr_F.Point;
          END; { with }

          TextSize   := 0;                                     { Nog onbekend }
          ReplyTo    := 0;

          CvtFidoTimeToReneGade;

          Status:=rmsSent;
          replies:=0;
          subject:=Msg.Subj_F;
          origindate:=Msg.Date_F;
          fileattached:=0;
          netattribute:=ReneNetAttr (Msg.Attr_F);
     END; { with }

END;


{--------------------------------------------------------------------------}
{ GuessSplitParts                                                          }
{                                                                          }
{ Probeer het aantal delen te bepalen waarin het bericht gesplitst op      }
{ disk bewaard zal worden.                                                 }

PROCEDURE ReneBase.GuessSplitParts;
VAR FidoHeaderlen,
    FidoBodyLen,
    FidoFooterLen  : Longint;
    SplitParts_R   : Real;
BEGIN

  { Kijkt of we de limiet gaan overschrijven }
  IF Msg.HeaderTop_F <> NIL THEN
    FidoHeaderLen:= Msg.HeaderTop_F^.TotalRegelLength
  ELSE
    FidoBodyLen  := 0;

  IF Msg.BodyTop <> NIL THEN
   FidoBodyLen  := Msg.BodyTop^.TotalRegelLength
  ELSE
   FidoBodyLen  := 0;

  IF Msg.FooterTop_F <> NIL THEN
   FidoFooterLen:= Msg.FooterTop_F^.TotalRegelLength
  ELSE
   FidoFooterLen:= 0;

  { bereken het aantal delen waarin het bericht gesplitst gaat worden }
  SplitCurrent:=0;
  SplitParts  :=0;

  IF Config.MaxSquishMsgLen > 0 THEN
   BEGIN
   SplitParts_R:=FidoBodyLen / (Config.MaxSquishMsgLen-FidoFooterLen);
   SplitParts:=Trunc (SplitParts_R);
   IF (SplitParts < SplitParts_R) THEN
      Inc (SplitParts);
   END;

END;




{ ------------------------------------------------------------------------ }
{ OpenBase                                                                 }
{                                                                          }
{ Opent een base, kijk of er toevallig nog een base met dezelfde naam open }
{ is, dat scheelt weer diskaccess tijd.                                    }

FUNCTION ReneBase.OpenBase( AreaName, AreaPath : String ) : Boolean;
BEGIN

 OpenBase := True;

 IF BaseOpen THEN
  IF BasePath = AreaPath THEN
   Exit
  ELSE
   CloseBase;

  BasePath := AreaPath;
  BaseName := AreaName;

  {$I-}
  Assign( HeaderFile , AreaPath  + '.HDR' );
  Assign( DataFile   , AreaPath  + '.DAT' );
  Reset( HeaderFile, 1 );
  PeekFiles;
  Reset( DataFile  , 1 );
  PeekFiles;
  {$I+}

  IF IoResult > 0 THEN
   IF NOT CreateBase THEN
    BEGIN
    OpenBase := False;
    Exit;
    END;

  BaseOpen := True;
END;

{ ------------------------------------------------------------------------ }
{ WriteMessage                                                             }
{                                                                          }
{ Schrijft een berichtje naar een Renegade message base.                   }

PROCEDURE ReneBase.WriteMessage( AreaName , AreaPath : STRING );
VAR HeaderLine,
    FooterLine,
    MessageLine : EenRegelRecordPtr;
    MessageSize : Longint;
BEGIN

 {Open de base}
 IF NOT OpenBase( AreaName , AreaPath ) THEN
  BEGIN
   LogMessage( 'Renegade! Unable to open/create base for ' + AreaName );
   Exit;
  END;

 {Vul de header met zinnige informatie}
 SetReneGadeHeader;

 {Schrijf het bericht naar disk}
 GuessSplitParts;
 MessageLine := Msg.BodyTop^.FirstRegelRecordPtr;

 REPEAT

  { Pas de header aan de situatie aan }
  IF (SplitParts > 1) THEN
   BEGIN
   Inc (SplitCurrent);
   MessageHeader.Subject := '('+Byte2String (SplitCurrent)+
                            '/'+Byte2String (SplitParts)+
                            ') '+Msg.Subj_F;
   END;

  { Voeg toe aan het einde     }
  Seek      ( DataFile   , FileSize( DataFile   ));

  MessageHeader.Pointer  := FileSize( DataFile ) + 1;
  MessageSize := 0;

  { Schrijf de Fido header naar disk }
  HeaderLine := Msg.HeaderTop_F^.FirstRegelRecordPtr;
  IF HeaderLine <> NIL THEN
   HeaderLine := HeaderLine^.NextRegelRecordPtr; {Skip AREA:}

  WHILE (HeaderLine <> NIL) DO
   BEGIN
   {$I-}
   BlockWrite( DataFile , HeaderLine^.RegelPtr^ , Length( HeaderLine^.RegelPtr^) + 1 );
   {$I+}
   MessageSize := MessageSize + Length( HeaderLine^.RegelPtr^ ) + 1;
   HeaderLine := HeaderLine^.NextRegelRecordPtr;
   END;

  { Schrijf de message body naar disk}
  WHILE (MessageLine <> NIL) AND (MessageSize<Config.MaxSquishMsgLen) DO
   BEGIN
    {$I-}
    BlockWrite( DataFile , MessageLine^.RegelPtr^ , Length( MessageLine^.RegelPtr^) + 1 );
    {$I+}
    IF IOResult>0 THEN
     Exit;
    MessageSize := MessageSize + Length( MessageLine^.RegelPtr^ ) + 1;
    MessageLine := MessageLine^.NextRegelRecordPtr;
   END;

  { Schrijf de footer naar disk}
  FooterLine := Msg.FooterTop_F^.FirstRegelRecordPtr;
  WHILE (FooterLine <> NIL) DO
   BEGIN
   {$I-}
   BlockWrite( DataFile , FooterLine^.RegelPtr^ , Length( FooterLine^.RegelPtr^) + 1 );
   {$I+}
   MessageSize := MessageSize + Length( FooterLine^.RegelPtr^ ) + 1;
   FooterLine := FooterLine^.NextRegelRecordPtr;
   END;

   { Schrijf de header naar disk}
   { Voeg toe aan het einde     }

   MessageHeader.TextSize := MessageSize;

   Seek      ( HeaderFile , FileSize( HeaderFile ));
   BlockWrite( HeaderFile , MessageHeader, Sizeof(MessageHeader));


 UNTIL MessageLine = NIL;

END;

{ ---------------------------------------------------------------------- }
{ ScanArea                                                               }
{                                                                        }
{ Scant een Renegade area op zoek naar uitgaande berichten               }

PROCEDURE ReneBase.ScanArea( AreaPath , AreaName : String );

VAR BodyFile      : FILE;
    LastRegel,
    Regel         : STRING;
    DatumTijd     : DateTime;
    BytesToRead,
    CurrentOffset : LONGINT;
    IORes         : BYTE;

BEGIN
     {Open de base}
     UserDataRecNr:=NILRecordNr;

     IF (NOT OpenBase (AreaName,AreaPath)) THEN
     BEGIN
          LogMessage ('[Renegade] Unable to open/create base for '+AreaName);
          Exit;
     END;

     {$I-}
     Assign (BodyFile,AreaPath+'.DAT');
     Reset (BodyFile,1);
     {$I+}

     IF (IOResult > 0) THEN
     BEGIN
          LogMessage ('[Renegade] Unable to open message body for '+AreaName);
          Exit;
     END;

     IF (FileSize (BodyFile) <> 0) THEN
        WHILE (NOT Eof (HeaderFile)) DO
        BEGIN
             { Lees de bericht header , en subfields in het geheugen }
             {$I-}
             CurrentOffset:=FilePos (HeaderFile);
             BlockRead (HeaderFile,MessageHeader,Sizeof (MessageHeader));
             {$I+}

             IF (IOResult > 0) THEN
             BEGIN
                  LogMessage ('[Renegade] Error reading message header for '+AreaName);
                  { Einde area }
                  Close (BodyFile);
                  CloseBase;
                  Exit;
             END;

             { Controleer of het bericht a) de local vlag heeft , en b) niet de sent vlag }
             { een c) deleted vlag heeft.                                                 }
             { Zonee, ga dan verder met het volgende bericht                              }

             IF {(NOT (local IN MessageHeader.NetAttribute)) OR}
                ((MessageHeader.Status AND rmsSent) > 0) OR
                ((MessageHeader.Status AND rmsDeleted) > 0)
             THEN
                 Continue;

             { Ok, fine ... lees het bericht maar in }

             {$I-} Seek (BodyFile,MessageHeader.Pointer-1); {$I+}
             IF (IOResult <> 0) THEN
             BEGIN
                  LogMessage ('[Renegade] Error seeking position '+LongInt2String (MessageHeader.Pointer)+' in '+AreaName);
                  Close (BodyFile);
                  CloseBase;
                  Exit;
             END;

             { --- Init boolean variabelen }
             Found_SeenBy := False;
             Found_Path   := False;
             Found_Origin := False;
             Found_Tear   := False;
             PrevKludgeID := None;

             MsgsEmpty;

             IF (AreaData.AreaType = Area_Netmail) THEN
                Msg.Ready_F:=Local_Netmail
             ELSE BEGIN
                  { Stop de AREA: kludge in de eerste regel van het bericht }
                  Msg.Ready_F:=Local_Echomail;
                  Msg.Area_F:=AreaData.AreaName_F;
                  MsgsAddLineTo (Header_F,'AREA:'+Msg.Area_F);
             END;

             { Lees het bericht regel voor regel in }
             BytesToRead:=MessageHeader.TextSize;

             WHILE (BytesToRead > 0) DO
             BEGIN
                  {$I-}
                  BlockRead (BodyFile,Regel[0],1);
                  BlockRead (BodyFile,Regel[1],Byte (Regel[0]));
                  {$I+}

                  IF (IOResult <> 0) THEN
                     Break;

                     FidoAddLineToMessage (TRUE{WeSend},Regel,LastRegel);
                     Dec (BytesToRead,Length (Regel)+1);
              END;

              { -- Converteer wat algemene informatie }

              Msg.FromUser_F:=MessageHeader.From.Name;
              Msg.ToUser_F:=MessageHeader.Mto.Name;
              Msg.Subj_F:=MessageHeader.Subject;
              Msg.Attr_F:=Word(MessageHeader.NetAttribute);
              Msg.Cost_F:=0; { Not Supported by Renegade }
              Msg.FromAddr_F:=Config.NodeNrs[AreaData.OriginAka];
              Msg.ToAddr_F:=Config.NodeNrs[AreaData.OriginAka];

              { Converteer de JAM Header naar de fido stijl header }
              UnixToDos (MessageHeader.Date,DatumTijd);

              { Stel de datum in }
              WITH DatumTijd DO
                   Msg.Date_F:=FidoTime2Str (Day,Month,Year,Hour,Min,Sec);

              { Fix het bericht door ontbrekende delen bij te vullen }
              IF (NOT Found_Tear) THEN
                 MsgsAddLineTo (Footer_F,FidoTear);

              { Zorg dat er een tearline wordt toegevoegd , een origin line }
              { zodat we een 'echt' fido bericht krijgen.                   }

              IF AreaData.AreaType = Area_Echo THEN
              BEGIN
                   { Werk echo statistieken bij }
                   {Inc (FidoProcessStatus.MsgCount);}

                   { Als er geen origin lijn gevonden kon worden }
                   IF (NOT Found_Origin) THEN
                      IF (AreaData.OriginNr = 0) THEN
                         MsgsAddLineTo (Footer_F,FidoBuildOrigin (Config.Origins[AreaData.OriginNr],Msg.FromAddr_F))
                      ELSE
                          MsgsAddLineTo (Footer_F,FidoBuildOrigin (AreaData.Origin,Msg.FromAddr_F));

                   { Als er geen seenby lines waren }
                   IF (NOT Found_SeenBy) THEN
                      FidoAddSeenBy (TRUE,AreaData,
                                     GetAreaBaseRecordNrByAreaName_F (AreaData.AreaName_F),
                                     'SEEN-BY:');

                   { Als er geen Path gevonden kon worden }
                   IF (NOT Found_Path) THEN
                      FidoAddPath (#1'PATH:',AreaData);

                   UpdateAreaStats (GetAreaBaseRecordNrByAreaName_F (AreaData.AreaName_F),Msg.MsgSize);
              END ELSE
              BEGIN
                   { Local_Netmail }
                   { Voeg INTL, TOPT en FMPT regels toe }

                   {Inc (FidoProcessStatus.NetCount);}
                   MsgsAddlineTo (Header_F,#1'INTL '+Fido23DStr (Msg.ToAddr_F)+' '+Fido23DStr (Msg.FromAddr_F));

                   IF (Msg.ToAddr_F.Point > 0) THEN
                      MsgsAddLineTo (Header_F,#1'TOPT '+Word2String (Msg.ToAddr_F.Point));

                   IF (Msg.FromAddr_F.Point > 0) THEN
                      MsgsAddLineTo (Header_F,#1'FMPT '+Word2String (Msg.FromAddr_F.Point));
              END;

              { Als dit een netmail bericht is , en we draaien in frontdoor mode }
              { mag het bericht alleen verstuurd worden als het a) voor ons      }
              { bestemd is, en b) voor postmaster is !                           }

              IF (NOT FidoCheckNetmail (FALSE)) THEN
                 Continue;

              { -- Exporteer het bericht    }

              {Inc( FidoProcessStatus.BytesCount , Msg.MsgSize );
              FidoProcessStatusShow;}
              MsgsExport;

              { Update de header met een SENT vlag }
              IF NOT ((Config.FidoSystem = stFrontdoor) AND (Msg.Ready_F = Local_Netmail )) THEN
              BEGIN
                   MessageHeader.Status:=MessageHeader.Status OR rmsSent;

                   {
                      IF (Jam_MSG_Header.Attribute AND JMSG_KILLSENT)>0 THEN
                       Jam_MSG_Header.Attribute := Jam_MSG_Header.Attribute OR JMSG_DELETED;
                   }

                   { Schrijf de header naar disk }
                   {$I-}
                   Seek (HeaderFile,CurrentOffset );
                   BlockWrite (HeaderFile,MessageHeader,SizeOf (MessageHeader));
                   {$I+}
                   IORes:=IOResult;
              END;
        END; { while }

     { Einde area }
     CloseBase;
     Close (BodyFile);
END;



END.
