Contributor: SEBASTIAN FIORENTINI

{
Hi..
Well, this is the "Repaired" Packets.Pas Unit. The Unit uses the Mark
May's MKSM106.zip unit plus the Duncan Murdoch Streams unit.
I've placed the URL and a Ftp where the readers can obtain the latest version of
those units.

{PACKETS.PAS : Sort of Objects for Reading-Writing Fidonet PKT type
2+
               files and standard QWK and REP files.
 + Donated to public domain by Best Software Mar del Plata
 + You can contact me via e-mail to sebastianf@usa.net or
                                    bsmdp@usa.net
 + You can compile this unit under BP 6.0 or 7.0.
 + Runs perfectly under Protected Mode
}
UNIT PACKETS;
INTERFACE
USES OBJECTS,DOS, {PASCAL UNITS}
{These units are part of Mark May's Msgbase Objects. You can download
the file from www.dnaco.net/~mmay or you can found the Zip file in
ftp.pcmicro.com or elsewhere.
They are used to write the Pkt or Rep file directly from the Msgbase
}
     MKGLOBT,
     MKMSGABS,
     MKSTRING,
{This unit is the Duncan Murdoch's Streams Units. Used for simple and 
fast
buffering in XMS-EMS. You can found the unit in ftp.garbo.uwasa.fi}
     STREAMS;
CONST
{STATUS CONSTANTS}
 ERROK       = 0;
 ERRFINPKT   =-1;
 ERRFINQWK   =ERRFINPKT;
 ERRNOPKT2   =-2;
 ERRNOMEMORY =-3;
 ERRBADQWKMSG=-4;
 ERRNOTOSS   =-5;
 ERRMAXAREAS =-6;
{PREDEFINDED STRINGS}
AREABADMAIL     ='BADMAIL';
AREAKLUDGE      ='AREA:';
MSGIDKLUDGE     =#1'MSGID:';
INTLKLUDGE      =#1'INTL';
FMPTKLUDGE      =#1'FMPT';
TOPTKLUDGE      =#1'TOPT';
REPLYKLUDGE     =#1'REPLY:';
BAUDIOSPKT      =0;
TIPOPKT         =2;
PRODUCTCODE     =$FF;
VERSION         =$0200;
VALORCAPWORD    =1;
VALORCAPWORDCOPY=256;
COSTOMENSAJE    =0;
ANCHOMENSAJE    :BYTE=80;
ENDOFMSG        :CHAR=#0;
ENDOFPACKET     :STRING[2]=#0#0;
{ATRIBUTOS}
ATRPRIVADO           =    1;
ATRCRASH             =    2;
ATRRECIBIDO          =    4;
ATRENVIADO           =    8;
ATRFILEATTACH        =   16;
ATRENTRANSITO        =   32;
ATRORPHAN            =   64;
ATRBORRARENVIADO     =  128;
ATRLOCAL             =  256;
ATRHOLD              =  512;
ATRDIRECTO           = 1024;
ATRFILEREQUEST       = 2048;
ATRPEDIRRECIBIDO     = 4096;
ATRRETORNARRECIBIDO  = 8192;
ATREXAMINARPEDIDO    =16384;
ATRACTUALIZARPEDIDO  =32768;

TYPE
ORIGPKTHDR=RECORD {THIS IS THE ORIGINAL PKT HEADER}
 ONODE,DNODE,
 ANO,MES,DIA,
 HORA,MINUTO,SEGUNDO,
 BAUDIOS,
 TIPOPAQUETE:WORD;
 ONET,DNET:INTEGER;
 CODPH,REVH:BYTE;
 PASSWORD:ARRAY[1..8] OF CHAR;
 OZONE1,DZONE1,AUXNET,CWORD:WORD;
 CODPL,REVL:BYTE;
 CWORDCOPY,OZONE2,DZONE2,OPOINT,DPOINT:WORD;
 SPECDATA:ARRAY[1..4] OF CHAR;
END;
{MODIFIED PKT HEADER. IT'S FOR PACKETS.PAS ONLY.
 Used for easy access of PKT Headers}
MODPKTHDR=RECORD
 ODIR,DDIR:ADDRTYPE;
 CLAVE:STRING[8];
END;
FECHATYPE=RECORD
 DIA,
 MES,
 ANO:WORD;
END;
HORATYPE=RECORD
 HORA,
 MINUTO,
 SEGUNDO:WORD;
END;
{Modified Message Header
 Used for writing Pkt messages headers in a very easy way}
MODMSGHDR=RECORD
 ATRIBUTOS:WORD;
 DIRORIG,DIRDEST:ADDRTYPE;
 DE,PARA:STRING[36];
 FECHA:FECHATYPE;
 HORA:HORATYPE;
 SOBRE:STRING[72];
 AREA:STRING[40];
END;
{Pkt Message Header. Original}
ORIGMSGHDR=RECORD
 MSGID:WORD;
 ONODE,DNODE,ONET,DNET,
 ATR,
 COSTO:WORD;
 FECHA:ARRAY[1..20] OF CHAR;
END;
{Qwk Header}
QWKHEADER=RECORD
 STATUS:CHAR;
 MSGNUM:ARRAY[1..7] OF CHAR;
 FECHA: ARRAY[1..8] OF CHAR;
 HORA:  ARRAY[1..5] OF CHAR;
 PARA:  ARRAY[1..25] OF CHAR;
 DE:    ARRAY[1..25] OF CHAR;
 SOBRE: ARRAY[1..25] OF CHAR;
 PASSWORD:ARRAY[1..12] OF CHAR;
 REFER: ARRAY[1..8] OF CHAR;
 NUMBLOCKS:ARRAY[1..6] OF CHAR;
 ACTIVO:CHAR;
 AREANUM:WORD;
 FILL:ARRAY[1..2] OF CHAR;
 HASTAG:CHAR;
END;
{}
{This Object is for Read the Pkt named PKTFILE.
 The Message is retrieved from the file to the Pased Text Stream. If 
the
 Stream is nil, it's created. The Header Variable is filled with 
apropiate
 values, and the ReadPktMessage retruns one of Status Constants
}
 PPACKETREADPROCESS=^TPACKETREADPROCESS;
 TPACKETREADPROCESS=OBJECT(TOBJECT)
  PKTHEADER:ORIGPKTHDR;
  PKTSTREAM:PBUFSTREAM;
  CONSTRUCTOR INIT(PKTFILE:STRING);
  PROCEDURE GETPKTORIGADDRESS(VAR DIR:ADDRTYPE);
  PROCEDURE GETPKTDESTADDRESS(VAR DIR:ADDRTYPE);
  FUNCTION  READPKTMESSAGE(VAR HEADER:MODMSGHDR;VAR 
TEXT:PSTREAM):INTEGER;
  DESTRUCTOR DONE;VIRTUAL;
  PRIVATE
   FUNCTION GETSTRINGTONULL:STRING;
   FUNCTION GETMSGHEADER(VAR HDR:MODMSGHDR):INTEGER;
 END;
{This Object Write the Pkt file PKTFILE. Fill's the main Header with
HDRDATA and if the PKTFILE already exists the OVERWRITEHDR boolean 
decides
if the object must replace the Header with HDRDATA or must preserve 
the old
header.
The Pkt Message is writed from the Current Message in MSGBASE and 
fill's the
area name with AREA.
}
 PPACKETWRITEPROCESS=^TPACKETWRITEPROCESS;
 TPACKETWRITEPROCESS=OBJECT(TOBJECT)
  PKTSTREAM:PBUFSTREAM;
  CONSTRUCTOR 
INIT(PKTFILE:STRING;HDRDATA:MODPKTHDR;OVERWRITEHDR:BOOLEAN);
  FUNCTION   WRITEPKTMESSAGE(AREA:STRING;MSGBASE:ABSMSGPTR):INTEGER;
  FUNCTION   WRITEPKTFROMBUFFER(HDR:MODMSGHDR;TEXT:PSTREAM):INTEGER;
  DESTRUCTOR DONE;VIRTUAL;
 END;
{This Object read the Qwkfiles located in the path PATHTOQWKFILES
 The READQWKMESSAGE read the current qwk message into TEXT stream, 
and fill
 the Header with Apropiate values.
}
 PQWKREADPROCESS=^TQWKREADPROCESS;
 TQWKREADPROCESS=OBJECT(TOBJECT)
  BBSID,NOMBREBBS,NOMBRESYSOP,NOMBREUSUARIO:STRING;
  AREALIST:PSTRINGCOLLECTION;
  QWKSTREAM:PBUFSTREAM;
  CONSTRUCTOR INIT(PATHTOQWKFILES:STRING);
  FUNCTION READQWKMESSAGE(VAR HEADER:MODMSGHDR;VAR 
TEXT:PSTREAM):INTEGER;
  DESTRUCTOR DONE;VIRTUAL;
 END;
{This Object writes the Messages.dat file, that conforms the Rep 
file.
 The Write procedure writes the current MSGBASE message to the File 
and
 you must supply the Area Number of the message in the AreaNumb 
field}
 PQWKWRITEPROCESS=^TQWKWRITEPROCESS;
 TQWKWRITEPROCESS=OBJECT(TOBJECT)
  QWKSTREAM:PBUFSTREAM;
  CONSTRUCTOR INIT(PATHTOQWKFILES:STRING;BBSID:STRING);
  FUNCTION WRITEQWKMESSAGE(MSGBASE:ABSMSGPTR;AREANUMB:WORD):INTEGER;
  DESTRUCTOR DONE;VIRTUAL;
 END;
{}
PROCEDURE GETORIGADDRESS(PKT:STRING;VAR ADR:ADDRTYPE);{Que direccion 
origen tiene el paquete PKT}
PROCEDURE GETDESTADDRESS(PKT:STRING;VAR ADR:ADDRTYPE);{Que direccion 
destino tiene el paquete PKT}
IMPLEMENTATION
{}
FUNCTION SCANBUFFER(VAR BLOCK; SIZE: WORD; STR: STRING): 
WORD;ASSEMBLER;
ASM
        PUSH    DS
        LES     DI,BLOCK
        LDS     SI,STR
        MOV     CX,SIZE
        JCXZ    @@3
        CLD
        LODSB
        CMP     AL,1
        JB      @@5
        JA      @@1
        LODSB
        REPNE   SCASB
        JNE     @@3
        JMP     @@5
@@1:    XOR     AH,AH
        MOV     BX,AX
        DEC     BX
        MOV     DX,CX
        SUB     DX,AX
        JB      @@3
        LODSB
        INC     DX
        INC     DX
@@2:    DEC     DX
        MOV     CX,DX
        REPNE   SCASB
        JNE     @@3
        MOV     DX,CX
        MOV     CX,BX
        REP     CMPSB
        JE      @@4
        SUB     CX,BX
        ADD     SI,CX
        ADD     DI,CX
        INC     DI
        OR      DX,DX
        JNE     @@2
@@3:    XOR     AX,AX
        JMP     @@6
@@4:    SUB     DI,BX
@@5:    MOV     AX,DI
        SUB     AX,WORD PTR BLOCK
@@6:    DEC     AX
        POP     DS
END; { SCAN }
PROCEDURE INSERTAAD(CAR:CHAR;VAR S:STRING;TAM:WORD);
VAR
LN:WORD;
FILL:STRING;
ST:STRING;
BEGIN
 IF LENGTH(S)>=TAM THEN EXIT;
 FILL[0]:=CHR(TAM-LENGTH(S));
 FILLCHAR(FILL[1],TAM-LENGTH(S),CAR);
 INSERT(FILL,S,1);
END;
FUNCTION MAYUSMINUS(MN:BOOLEAN;DESTINO:STRING):STRING;
 FUNCTION LOSTR(CONST S:STRING):STRING; ASSEMBLER;
  ASM
    PUSH DS
    LDS  SI,S
    LES  DI,@RESULT
    LODSB            { LOAD AND STORE LENGTH OF STRING }
    STOSB
    XOR  CH,CH
    MOV  CL,AL
    JCXZ @EMPTY      { FIX FOR NULL STRING }
  @LOWERLOOP:
    LODSB
    CMP  AL,'A'
    JB   @CONT
    CMP  AL,'Z'
    JA   @CONT
    ADD  AL,' '
  @CONT:
    STOSB
    LOOP @LOWERLOOP
  @EMPTY:
    POP  DS
  END;  { LOSTR }
  FUNCTION UPSTR(CONST S:STRING):STRING; ASSEMBLER;
  ASM
    PUSH DS
    LDS  SI,S
    LES  DI,@RESULT
    LODSB            { LOAD AND STORE LENGTH OF STRING }
    STOSB
    XOR  CH,CH
    MOV  CL,AL
    JCXZ @EMPTY      { FIX FOR NULL LENGTH STRING }
  @UPPERLOOP:
    LODSB
    CMP  AL,'a'
    JB   @CONT
    CMP  AL,'z'
    JA   @CONT
    SUB  AL,' '
  @CONT:
    STOSB
    LOOP @UPPERLOOP
  @EMPTY:
    POP  DS
  END;  { UPSTR }
BEGIN
 IF MN THEN MAYUSMINUS:=UPSTR(DESTINO) ELSE
            MAYUSMINUS:=LOSTR(DESTINO);
END;
PROCEDURE ACTUALIZAR(VAR M:STRING;L:BYTE);
VAR
FILL:STRING;
BEGIN
 IF LENGTH(M)>L THEN M[0]:=CHAR(L) ELSE
 IF LENGTH(M)=P2) THEN BEGIN
   SUBCAD:='';
   EXIT;
  END;
  ST1:='';
  FOR M:=P1+1 TO P2-1 DO
  ST1:=ST1+STIN[M];
  SUBCAD:=ST1;
END;
FUNCTION RECORTAFINAL(IND:STRING):STRING;
VAR
P1:INTEGER;
RES:STRING;
BEGIN
 RES:=IND;
 IF RES<>'' THEN BEGIN
  FOR P1:=LENGTH(RES) DOWNTO 1 DO
  IF RES[P1]<>#32 THEN BREAK;
  IF P1>1 THEN SYSTEM.DELETE(RES,P1+1,LENGTH(RES));
 END;
 RECORTAFINAL:=RES;
END;
FUNCTION FULLPATH(INP:STRING):STRING;
VAR
RES:STRING;
BEGIN
 RES:=FEXPAND(INP);
 IF RES[LENGTH(RES)]<>'\' THEN RES:=RES+'\';
 FULLPATH:=RES;
END;
FUNCTION EXISTE(NOMBRE:STRING):BOOLEAN;
VAR
SR:SEARCHREC;
BEGIN
 FINDFIRST(NOMBRE,DOS.ARCHIVE,SR);
 EXISTE:=DOSERROR=0;
END;
{}
FUNCTION FILTER0A(INS:STRING):STRING;
 VAR
 CONT:WORD;
 RES:STRING;
 BEGIN
  RES:='';
  FILTER0A:='';
  IF LENGTH(INS)=0 THEN EXIT;
  FOR CONT:=1 TO LENGTH(INS) DO
   IF INS[CONT]<>#$A THEN RES:=RES+INS[CONT];
  FILTER0A:=RES;
 END;
FUNCTION CONVERTPITOCRLF(INS:STRING):STRING;
 VAR
 CONT:WORD;
 RES:STRING;
 BEGIN
  RES:='';
  CONVERTPITOCRLF:='';
  IF LENGTH(INS)=0 THEN EXIT;
  FOR CONT:=1 TO LENGTH(INS) DO
   IF INS[CONT]<>#227 THEN RES:=RES+INS[CONT] ELSE
                           RES:=RES+#13#10;
  CONVERTPITOCRLF:=RES;
 END;
FUNCTION CONVERTCRLFTOPI(INS:STRING):STRING;
 VAR
 P1:WORD;
 RES:STRING;
 BEGIN
  RES:=INS;
  WHILE POS(#13,RES)<>0 DO
  BEGIN
   P1:=POS(#13,RES);
   DELETE(RES,P1,2);
   INSERT(#227,RES,P1);
  END;
  CONVERTCRLFTOPI:=RES;
 END;
{}
CONSTRUCTOR TQWKREADPROCESS.INIT(PATHTOQWKFILES:STRING);
VAR
CONT,CANTAREAS:WORD;
PATH,CNUM,CNAM,TMPST:STRING;
TXT:TEXT;
BEGIN
 INHERITED INIT;
 PATH:=FULLPATH(PATHTOQWKFILES);
 ASSIGN(TXT,PATH+'CONTROL.DAT');
 {$I-}RESET(TXT);{$I+}
 IF IORESULT<>0 THEN FAIL;
 READLN(TXT,NOMBREBBS);
 READLN(TXT,TMPST);READLN(TXT,TMPST);
 READLN(TXT,NOMBRESYSOP);
 READLN(TXT,BBSID);READLN(TXT,TMPST);
 BBSID:=COPY(BBSID,POS(',',BBSID)+1,LENGTH(BBSID)-POS(',',BBSID));
 READLN(TXT,NOMBREUSUARIO);
 READLN(TXT,TMPST);READLN(TXT,TMPST);READLN(TXT,TMPST);
 READLN(TXT,TMPST);CANTAREAS:=STR2LONG(TMPST);
 IF CANTAREAS=0 THEN BEGIN CLOSE(TXT);FAIL;END;
 AREALIST:=NEW(PSTRINGCOLLECTION,INIT(1,1));
 FOR CONT:=0 TO CANTAREAS-1 DO
 BEGIN
  READLN(TXT,CNUM);
  READLN(TXT,CNAM);
  AREALIST^.INSERT(NEWSTR(#1+CNUM+#2#3+CNAM+#4));
 END;
 CLOSE(TXT);
 QWKSTREAM:=NEW(PBUFSTREAM,INIT(PATH+'MESSAGES.DAT',STOPEN,1024));
 IF QWKSTREAM^.STATUS<>STOK THEN BEGIN
  DISPOSE(QWKSTREAM,DONE);
  DISPOSE(AREALIST,DONE);
  FAIL;
 END;
 QWKSTREAM^.SEEK(128);{SALTEA EL COPYRIGHT DEL QWK}
END;
FUNCTION TQWKREADPROCESS.READQWKMESSAGE(VAR HEADER:MODMSGHDR;VAR 
TEXT:PSTREAM):INTEGER;
VAR
QWKHDR:QWKHEADER;
TEMPST:STRING;
NUMRECS,CONT:WORD;
BEGIN
 QWKSTREAM^.READ(QWKHDR,SIZEOF(QWKHDR));
 IF QWKSTREAM^.STATUS<>STOK THEN BEGIN
  READQWKMESSAGE:=ERRFINPKT;
  EXIT;
 END;
 TEMPST[0]:=#6;
 MOVE(QWKHDR.NUMBLOCKS,TEMPST[1],6);
 TEMPST:=RECORTAFINAL(TEMPST);
 NUMRECS:=STR2LONG(TEMPST);
 IF NUMRECS<2 THEN BEGIN
  READQWKMESSAGE:=ERRBADQWKMSG;
  EXIT;
 END;
 WITH HEADER DO
 BEGIN
  ATRIBUTOS:=ATRLOCAL;
  
DE[0]:=#25;MOVE(QWKHDR.DE,DE[1],25);SYSTEM.DELETE(DE,POS(#0,DE),LENGTH(DE)-POS(#0,DE)+1);
  
PARA[0]:=#25;MOVE(QWKHDR.PARA,PARA[1],25);SYSTEM.DELETE(PARA,POS(#0,PARA),LENGTH(PARA)-POS(#0,PARA)+1);
  
SOBRE[0]:=#25;MOVE(QWKHDR.SOBRE,SOBRE[1],25);SYSTEM.DELETE(SOBRE,POS(#0,SOBRE),LENGTH(SOBRE)-POS(#0,SOBRE)+1);
  FECHA.DIA:=STR2LONG(COPY(QWKHDR.FECHA,4,2));
  FECHA.MES:=STR2LONG(COPY(QWKHDR.FECHA,1,2));
  FECHA.ANO:=STR2LONG(COPY(QWKHDR.FECHA,7,2));
  HORA.HORA:=STR2LONG(COPY(QWKHDR.HORA,1,2));
  HORA.MINUTO:=STR2LONG(COPY(QWKHDR.HORA,4,2));
  HORA.SEGUNDO:=00;
  AREA:=AREABADMAIL;
  FOR CONT:=0 TO AREALIST^.COUNT-1 DO
  BEGIN
   TEMPST:=PSTRING(AREALIST^.AT(CONT))^;
   IF SUBCAD(TEMPST,#1,#2)=LONG2STR(QWKHDR.AREANUM) THEN BEGIN
    AREA:=SUBCAD(TEMPST,#3,#4);
    BREAK;
   END;
  END;
 END;
 IF TEXT=NIL THEN BEGIN
  TEXT:=NEW(PWORKSTREAM,INIT(TEMPSTREAM,1024,16384,FORSIZEINMEM));
  IF TEXT^.STATUS<>STOK THEN BEGIN
   DISPOSE(TEXT,DONE);
   READQWKMESSAGE:=ERRNOMEMORY;
   EXIT;
  END;
 END;
 FOR CONT:=1 TO NUMRECS-1 DO
 BEGIN
  TEMPST[0]:=#128;
  QWKSTREAM^.READ(TEMPST[1],128);
  TEMPST:=CONVERTPITOCRLF(TEMPST);
  IF CONT=(NUMRECS-1) THEN TEMPST:=RECORTAFINAL(TEMPST);
  TEXT^.WRITE(TEMPST[1],LENGTH(TEMPST));
 END;
 {POR SER QWK,VA TODO EN 0}
 FILLCHAR(HEADER.DIRORIG,SIZEOF(HEADER.DIRORIG),0);
 FILLCHAR(HEADER.DIRDEST,SIZEOF(HEADER.DIRDEST),0);
 {}
 READQWKMESSAGE:=ERROK;
END;
DESTRUCTOR TQWKREADPROCESS.DONE;
BEGIN
 DISPOSE(AREALIST,DONE);
 DISPOSE(QWKSTREAM,DONE);
 INHERITED DONE;
END;
{}
CONSTRUCTOR 
TQWKWRITEPROCESS.INIT(PATHTOQWKFILES:STRING;BBSID:STRING);
VAR
PATH:STRING;
MODO:WORD;
BEGIN
 PATH:=FULLPATH(PATHTOQWKFILES);
 IF EXISTE(PATH+BBSID+'.MSG') THEN MODO:=STOPEN ELSE MODO:=STCREATE;
 QWKSTREAM:=NEW(PBUFSTREAM,INIT(PATH+BBSID+'.MSG',MODO,1024));
 IF MODO=STOPEN THEN QWKSTREAM^.SEEK(QWKSTREAM^.GETSIZE) ELSE
  BEGIN
   ACTUALIZAR(BBSID,128);
   QWKSTREAM^.WRITE(BBSID[1],128);
  END;
END;
FUNCTION 
TQWKWRITEPROCESS.WRITEQWKMESSAGE(MSGBASE:ABSMSGPTR;AREANUMB:WORD):INTEGER;
VAR
NUMBLOCKS:WORD;
LASTPOS,POSHEADER:LONGINT;
QWKHDR:QWKHEADER;
TEMPST:STRING;
BEGIN
 MSGBASE^.MSGSTARTUP;
 WITH QWKHDR DO
 BEGIN
  IF MSGBASE^.ISPRIV THEN STATUS:='+' ELSE STATUS:=' ';
  
TEMPST:=LONG2STR(AREANUMB);ACTUALIZAR(TEMPST,7);MOVE(TEMPST[1],MSGNUM,7);
  
TEMPST:=MSGBASE^.GETDATE;ACTUALIZAR(TEMPST,8);MOVE(TEMPST[1],FECHA,8);
  
TEMPST:=MSGBASE^.GETTIME;ACTUALIZAR(TEMPST,5);MOVE(TEMPST[1],HORA,5);
  TEMPST:=MSGBASE^.GETTO;IF LENGTH(TEMPST)>25 THEN 
ACTUALIZAR(TEMPST,25);TEMPST:=MAYUSMINUS(TRUE,TEMPST);
  FILLCHAR(PARA,25,0);MOVE(TEMPST[1],PARA,LENGTH(TEMPST));
  TEMPST:=MSGBASE^.GETFROM;IF LENGTH(TEMPST)>25 THEN 
ACTUALIZAR(TEMPST,25);TEMPST:=MAYUSMINUS(TRUE,TEMPST);
  FILLCHAR(DE,25,0);MOVE(TEMPST[1],DE,LENGTH(TEMPST));
  TEMPST:=MSGBASE^.GETSUBJ;IF LENGTH(TEMPST)>25 THEN 
ACTUALIZAR(TEMPST,25);
  FILLCHAR(SOBRE,25,0);MOVE(TEMPST[1],SOBRE,LENGTH(TEMPST));
  FILLCHAR(PASSWORD,12,0);
  FILLCHAR(REFER,8,#32);REFER[1]:='0';
  POSHEADER:=QWKSTREAM^.GETPOS;
  ACTIVO:=#225;
  AREANUM:=AREANUMB;
  FILLCHAR(FILL,2,0);
  HASTAG:=' ';
 END;
 QWKSTREAM^.WRITE(QWKHDR,SIZEOF(QWKHDR));
 NUMBLOCKS:=SIZEOF(QWKHDR);
 MSGBASE^.MSGTXTSTARTUP;
 TEMPST:=MSGBASE^.GETSTRING(ANCHOMENSAJE);
 WHILE NOT MSGBASE^.EOM DO
 BEGIN
  TEMPST:=FILTER0A(TEMPST)+#$D;
  TEMPST:=CONVERTCRLFTOPI(TEMPST);
  QWKSTREAM^.WRITE(TEMPST[1],LENGTH(TEMPST));
  INC(NUMBLOCKS,LENGTH(TEMPST));
  TEMPST:=MSGBASE^.GETSTRING(ANCHOMENSAJE);
 END;
 IF ((NUMBLOCKS DIV 128)+1)<2 THEN BEGIN
  QWKSTREAM^.SEEK(POSHEADER);
  QWKSTREAM^.TRUNCATE;
 END ELSE
 BEGIN
  IF (NUMBLOCKS MOD 128)>0 THEN BEGIN
   ACTUALIZAR(TEMPST,128-(NUMBLOCKS MOD 128));
   QWKSTREAM^.WRITE(TEMPST[1],LENGTH(TEMPST));
  END;
  TEMPST:=LONG2STR((NUMBLOCKS DIV 128)+1);ACTUALIZAR(TEMPST,6);
  MOVE(TEMPST[1],QWKHDR.NUMBLOCKS,6);
  LASTPOS:=QWKSTREAM^.GETPOS;
  QWKSTREAM^.SEEK(POSHEADER);
  QWKSTREAM^.WRITE(QWKHDR,SIZEOF(QWKHDR));
  QWKSTREAM^.SEEK(LASTPOS);
 END;
 WRITEQWKMESSAGE:=ERROK;
END;
DESTRUCTOR TQWKWRITEPROCESS.DONE;
BEGIN
 DISPOSE(QWKSTREAM,DONE);
 INHERITED DONE;
END;
{}
CONSTRUCTOR 
TPACKETWRITEPROCESS.INIT(PKTFILE:STRING;HDRDATA:MODPKTHDR;OVERWRITEHDR:BOOLEAN);
VAR
H,MI,S,C,D,M,A,DW,MODO:WORD;
PKTHDR:ORIGPKTHDR;
BEGIN
 INHERITED INIT;
 IF NOT EXISTE(PKTFILE) THEN MODO:=STCREATE ELSE MODO:=STOPEN;
 PKTSTREAM:=NEW(PBUFSTREAM,INIT(PKTFILE,MODO,1024));
 GETDATE(A,M,D,DW);
 GETTIME(H,MI,S,C);
 WITH PKTHDR DO
 BEGIN
  ONODE:=HDRDATA.ODIR.NODE;DNODE:=HDRDATA.DDIR.NODE;
  ANO:=A;MES:=M;DIA:=D;
  HORA:=H;MINUTO:=MI;SEGUNDO:=S;
  BAUDIOS:=BAUDIOSPKT;
  TIPOPAQUETE:=TIPOPKT;
  ONET:=HDRDATA.ODIR.NET;DNET:=HDRDATA.DDIR.NET;
  CODPH:=HI(PRODUCTCODE);REVH:=HI(VERSION);
  FILLCHAR(PASSWORD,SIZEOF(PASSWORD),0);
  MOVE(HDRDATA.CLAVE[1],PASSWORD,LENGTH(HDRDATA.CLAVE));
  OZONE1:=HDRDATA.ODIR.ZONE;DZONE1:=HDRDATA.DDIR.ZONE;
  AUXNET:=0;
  CWORD:=VALORCAPWORD;
  CODPL:=LO(PRODUCTCODE);REVL:=LO(VERSION);
  CWORDCOPY:=VALORCAPWORDCOPY;
  OZONE2:=OZONE1;DZONE2:=DZONE1;
  OPOINT:=HDRDATA.ODIR.POINT;DPOINT:=HDRDATA.DDIR.POINT;
  FILLCHAR(SPECDATA,SIZEOF(SPECDATA),0);
 END;
 IF ((MODO=STOPEN) AND (OVERWRITEHDR)) OR
    (MODO=STCREATE) THEN PKTSTREAM^.WRITE(PKTHDR,SIZEOF(PKTHDR));
 IF (MODO=STOPEN) THEN PKTSTREAM^.SEEK(PKTSTREAM^.GETSIZE-2);
END;
FUNCTION 
TPACKETWRITEPROCESS.WRITEPKTMESSAGE(AREA:STRING;MSGBASE:ABSMSGPTR):INTEGER;
VAR
MSGHDR:ORIGMSGHDR;
DIR:ADDRTYPE;
AREASTR,TEMPSTR,MES,RESFECHA,FECHASTR:STRING;
BEGIN
 WITH MSGHDR DO
 BEGIN
  MSGBASE^.MSGSTARTUP;
  MSGID:=TIPOPKT;
  MSGBASE^.GETORIG(DIR);
  ONODE:=DIR.NODE;ONET:=DIR.NET;
  MSGBASE^.GETDEST(DIR);
  DNODE:=DIR.NODE;DNET:=DIR.NET;
  ATR:=0;
  IF MSGBASE^.ISLOCAL THEN ATR:=ATR OR ATRLOCAL;
  IF MSGBASE^.ISCRASH THEN ATR:=ATR OR ATRCRASH;
  IF MSGBASE^.ISKILLSENT THEN ATR:=ATR OR ATRBORRARENVIADO;
  IF MSGBASE^.ISSENT THEN ATR:=ATR OR ATRENVIADO;
  IF MSGBASE^.ISFATTACH THEN ATR:=ATR OR ATRFILEATTACH;
  IF MSGBASE^.ISREQRCT THEN ATR:=ATR OR ATRPEDIRRECIBIDO;
  IF MSGBASE^.ISREQAUD THEN ATR:=ATR OR ATREXAMINARPEDIDO;
  IF MSGBASE^.ISRETRCT THEN ATR:=ATR OR ATRRETORNARRECIBIDO;
  IF MSGBASE^.ISFILEREQ THEN ATR:=ATR OR ATRFILEREQUEST;
  IF MSGBASE^.ISRCVD THEN ATR:=ATR OR ATRRECIBIDO;
  IF MSGBASE^.ISPRIV THEN ATR:=ATR OR ATRPRIVADO;
  COSTO:=COSTOMENSAJE;
  FECHASTR:=MSGBASE^.GETDATE;
  RESFECHA:=COPY(FECHASTR,4,2);
  MES:=COPY(FECHASTR,1,2);
  IF MES='01' THEN RESFECHA:=RESFECHA+' Jan';
  IF MES='02' THEN RESFECHA:=RESFECHA+' Feb';
  IF MES='03' THEN RESFECHA:=RESFECHA+' Mar';
  IF MES='04' THEN RESFECHA:=RESFECHA+' Apr';
  IF MES='05' THEN RESFECHA:=RESFECHA+' May';
  IF MES='06' THEN RESFECHA:=RESFECHA+' Jun';
  IF MES='07' THEN RESFECHA:=RESFECHA+' Jul';
  IF MES='08' THEN RESFECHA:=RESFECHA+' Aug';
  IF MES='09' THEN RESFECHA:=RESFECHA+' Sep';
  IF MES='10' THEN RESFECHA:=RESFECHA+' Oct';
  IF MES='11' THEN RESFECHA:=RESFECHA+' Nov';
  IF MES='12' THEN RESFECHA:=RESFECHA+' Dec';
  RESFECHA:=RESFECHA+' '+COPY(FECHASTR,7,2)+'  '+MSGBASE^.GETTIME;
  FILLCHAR(FECHA,SIZEOF(FECHA),0);
  MOVE(RESFECHA[1],FECHA,LENGTH(RESFECHA));
 END;
 PKTSTREAM^.WRITE(MSGHDR,SIZEOF(MSGHDR));
 
TEMPSTR:=MSGBASE^.GETTO+#0;PKTSTREAM^.WRITE(TEMPSTR[1],LENGTH(TEMPSTR));
 
TEMPSTR:=MSGBASE^.GETFROM+#0;PKTSTREAM^.WRITE(TEMPSTR[1],LENGTH(TEMPSTR));
 
TEMPSTR:=MSGBASE^.GETSUBJ+#0;PKTSTREAM^.WRITE(TEMPSTR[1],LENGTH(TEMPSTR));
 IF AREA<>'' THEN BEGIN
  AREASTR:=AREAKLUDGE+AREA+#13;
  PKTSTREAM^.WRITE(AREASTR[1],LENGTH(AREASTR));
 END;
 MSGBASE^.MSGTXTSTARTUP;
 TEMPSTR:=MSGBASE^.GETSTRING(ANCHOMENSAJE);
 WHILE NOT MSGBASE^.EOM DO
 BEGIN
  TEMPSTR:=FILTER0A(TEMPSTR)+#$D;
  PKTSTREAM^.WRITE(TEMPSTR[1],LENGTH(TEMPSTR));
  TEMPSTR:=MSGBASE^.GETSTRING(ANCHOMENSAJE);
 END;
 PKTSTREAM^.WRITE(ENDOFMSG,LENGTH(ENDOFMSG));
END;
FUNCTION 
TPACKETWRITEPROCESS.WRITEPKTFROMBUFFER(HDR:MODMSGHDR;TEXT:PSTREAM):INTEGER;
VAR
MSGHDR:ORIGMSGHDR;
DIR:ADDRTYPE;
AREASTR,D,M,A,S,H,TEMPSTR,MES,RESFECHA,FECHASTR:STRING;
BEGIN
 WITH MSGHDR DO
 BEGIN
  MSGID:=TIPOPKT;
  ONODE:=HDR.DIRORIG.NODE;ONET:=HDR.DIRORIG.NET;
  DNODE:=HDR.DIRDEST.NODE;DNET:=HDR.DIRDEST.NET;
  ATR:=HDR.ATRIBUTOS;
  COSTO:=COSTOMENSAJE;
  
D:=LONG2STR(HDR.FECHA.DIA);M:=LONG2STR(HDR.FECHA.MES);A:=LONG2STR(HDR.FECHA.ANO);
  INSERTAAD('0',D,2);INSERTAAD('0',M,2);
  FECHASTR:=M+'/'+D+'/'+A;
  RESFECHA:=COPY(FECHASTR,4,2);
  MES:=COPY(FECHASTR,1,2);
  IF MES='01' THEN RESFECHA:=RESFECHA+' Jan';
  IF MES='02' THEN RESFECHA:=RESFECHA+' Feb';
  IF MES='03' THEN RESFECHA:=RESFECHA+' Mar';
  IF MES='04' THEN RESFECHA:=RESFECHA+' Apr';
  IF MES='05' THEN RESFECHA:=RESFECHA+' May';
  IF MES='06' THEN RESFECHA:=RESFECHA+' Jun';
  IF MES='07' THEN RESFECHA:=RESFECHA+' Jul';
  IF MES='08' THEN RESFECHA:=RESFECHA+' Aug';
  IF MES='09' THEN RESFECHA:=RESFECHA+' Sep';
  IF MES='10' THEN RESFECHA:=RESFECHA+' Oct';
  IF MES='11' THEN RESFECHA:=RESFECHA+' Nov';
  IF MES='12' THEN RESFECHA:=RESFECHA+' Dec';
  
H:=LONG2STR(HDR.HORA.HORA);M:=LONG2STR(HDR.HORA.MINUTO);S:=LONG2STR(HDR.HORA.SEGUNDO);
  INSERTAAD('0',H,2);INSERTAAD('0',M,2);INSERTAAD('0',S,2);
  RESFECHA:=RESFECHA+' '+COPY(FECHASTR,7,2)+'  '+H+':'+M+':'+S;
  FILLCHAR(FECHA,SIZEOF(FECHA),0);
  MOVE(RESFECHA[1],FECHA,LENGTH(RESFECHA));
 END;
 PKTSTREAM^.WRITE(MSGHDR,SIZEOF(MSGHDR));
 TEMPSTR:=HDR.PARA+#0;PKTSTREAM^.WRITE(TEMPSTR[1],LENGTH(TEMPSTR));
 TEMPSTR:=HDR.DE+#0;PKTSTREAM^.WRITE(TEMPSTR[1],LENGTH(TEMPSTR));
 TEMPSTR:=HDR.SOBRE+#0;PKTSTREAM^.WRITE(TEMPSTR[1],LENGTH(TEMPSTR));
 IF HDR.AREA<>'' THEN BEGIN
  AREASTR:=AREAKLUDGE+HDR.AREA+#13;
  PKTSTREAM^.WRITE(AREASTR[1],LENGTH(AREASTR));
 END;
 PKTSTREAM^.COPYFROM(TEXT^,TEXT^.GETSIZE-TEXT^.GETPOS);
 PKTSTREAM^.WRITE(ENDOFMSG,LENGTH(ENDOFMSG));
END;
DESTRUCTOR TPACKETWRITEPROCESS.DONE;
BEGIN
 PKTSTREAM^.SEEK(PKTSTREAM^.GETSIZE);
 PKTSTREAM^.WRITE(ENDOFPACKET,2);
 DISPOSE(PKTSTREAM,DONE);
 INHERITED DONE;
END;
{}
CONSTRUCTOR TPACKETREADPROCESS.INIT(PKTFILE:STRING);
BEGIN
 INHERITED INIT;
 PKTSTREAM:=NEW(PBUFSTREAM,INIT(PKTFILE,STOPEN,1024));
 IF PKTSTREAM^.STATUS<>STOK THEN BEGIN
  DISPOSE(PKTSTREAM,DONE);
  FAIL;
 END;
 PKTSTREAM^.READ(PKTHEADER,SIZEOF(PKTHEADER));
 PKTHEADER.CWORDCOPY:=(LO(PKTHEADER.CWORDCOPY) SHL 8) OR 
HI(PKTHEADER.CWORDCOPY);
END;
PROCEDURE TPACKETREADPROCESS.GETPKTORIGADDRESS(VAR DIR:ADDRTYPE);
BEGIN
 FILLCHAR(DIR,SIZEOF(DIR),0);
 WITH PKTHEADER DO
 BEGIN
  IF TIPOPAQUETE<>2 THEN EXIT;
  IF (CWORD=CWORDCOPY) AND (CWORD<>0) AND (CWORD=256) THEN BEGIN
    IF (OPOINT<>0) AND (ONET=-1) THEN ONET:=AUXNET;
    DIR.ZONE:=OZONE1;
    DIR.NET:=ONET;
    DIR.NODE:=ONODE;
    DIR.POINT:=OPOINT;
  END ELSE BEGIN
    DIR.ZONE:=0;
    DIR.NET:=ONET;
    DIR.NODE:=ONODE;
    DIR.POINT:=OPOINT;
  END;
 END;
END;
PROCEDURE TPACKETREADPROCESS.GETPKTDESTADDRESS(VAR DIR:ADDRTYPE);
BEGIN
 FILLCHAR(DIR,SIZEOF(DIR),0);
 WITH PKTHEADER DO
 BEGIN
  IF TIPOPAQUETE<>2 THEN EXIT;
  IF (CWORD=CWORDCOPY) AND (CWORD<>0) AND (CWORD=256) THEN BEGIN
    IF (DPOINT<>0) AND (DNET=-1) THEN DNET:=AUXNET;
    DIR.ZONE:=DZONE1;
    DIR.NET:=DNET;
    DIR.NODE:=DNODE;
    DIR.POINT:=DPOINT;
  END ELSE BEGIN
    DIR.ZONE:=0;
    DIR.NET:=DNET;
    DIR.NODE:=DNODE;
    DIR.POINT:=DPOINT;
  END;
 END;
END;
FUNCTION TPACKETREADPROCESS.READPKTMESSAGE(VAR HEADER:MODMSGHDR;VAR 
TEXT:PSTREAM):INTEGER;
CONST
TAMBUF=1023;
TYPE
 TBUFTYPE=ARRAY[0..TAMBUF] OF CHAR;
 PBUFTYPE=^TBUFTYPE;
VAR
BUFSIZE:WORD;
TEXTSTREAM:PSTREAM;
POSCR,POSBUSQ,READSIZE:WORD;
FINDMSGID,FIRSTBLOCK,FINTEXTO:BOOLEAN;
TEMPBUF:POINTER;
RESTSIZE,RESTPOS:LONGINT;
BEGIN
 FILLCHAR(HEADER,SIZEOF(HEADER),0);
 CASE GETMSGHEADER(HEADER) OF
  ERRFINPKT:BEGIN READPKTMESSAGE:=ERRFINPKT;EXIT;END;
 END;
 IF TEXT=NIL THEN BEGIN
  TEXT:=NEW(PWORKSTREAM,INIT(TEMPSTREAM,1024,16384,FORSPEED));
   IF TEXT^.STATUS<>STOK THEN BEGIN
    DISPOSE(TEXT,DONE);
    READPKTMESSAGE:=ERRNOMEMORY;
    EXIT;
   END;
 END;
 GETMEM(TEMPBUF,TAMBUF);
 IF TEMPBUF=NIL THEN
 BEGIN
  READPKTMESSAGE:=ERRNOMEMORY;
  EXIT;
 END;
 FINTEXTO:=FALSE;
 FIRSTBLOCK:=TRUE;
 RESTSIZE:=PKTSTREAM^.GETSIZE-PKTSTREAM^.GETPOS;
 WHILE NOT FINTEXTO DO
 BEGIN
  IF TAMBUF>RESTSIZE THEN BUFSIZE:=RESTSIZE ELSE
                          BUFSIZE:=TAMBUF;
  PKTSTREAM^.READ(TEMPBUF^,BUFSIZE);
  DEC(RESTSIZE,BUFSIZE);
  IF FIRSTBLOCK THEN BEGIN
   FIRSTBLOCK:=FALSE;
   POSBUSQ:=SCANBUFFER(TEMPBUF^,BUFSIZE,AREAKLUDGE);
   IF POSBUSQ<$FFFF THEN BEGIN
    INC(POSBUSQ,LENGTH(AREAKLUDGE));
    POSCR:=SCANBUFFER(TEMPBUF^,BUFSIZE,#13);
    MOVE(PBUFTYPE(TEMPBUF)^[POSBUSQ],HEADER.AREA[1],POSCR-POSBUSQ);
    HEADER.AREA[0]:=CHR(POSCR-POSBUSQ);
    {}
     DEC(POSBUSQ,LENGTH(AREAKLUDGE));
     DEC(BUFSIZE,(POSCR-POSBUSQ)+1);
     MOVE(PBUFTYPE(TEMPBUF)^[POSCR+POSBUSQ+1],TEMPBUF^,BUFSIZE);
    {}
   END;
  END;
  POSBUSQ:=SCANBUFFER(TEMPBUF^,BUFSIZE,#0);
  IF POSBUSQ<$FFFF THEN
     BEGIN
      IF POSBUSQ>0 THEN TEXT^.WRITE(TEMPBUF^,POSBUSQ-1);
      TEXT^.TRUNCATE;
      RESTPOS:=BUFSIZE-POSBUSQ-1;
      PKTSTREAM^.SEEK(PKTSTREAM^.GETPOS-RESTPOS);
      FINTEXTO:=TRUE;
     END ELSE TEXT^.WRITE(TEMPBUF^,BUFSIZE);
 END;
 FREEMEM(TEMPBUF,TAMBUF);
 HEADER.AREA:=MAYUSMINUS(TRUE,HEADER.AREA);
 READPKTMESSAGE:=ERROK;
END;
FUNCTION TPACKETREADPROCESS.GETSTRINGTONULL:STRING;
VAR
RES:STRING;
C:CHAR;
BEGIN
 PKTSTREAM^.READ(C,1);
 RES:='';
 WHILE C<>#0 DO
 BEGIN
 RES:=RES+C;
  PKTSTREAM^.READ(C,1);
 END;
 GETSTRINGTONULL:=RES;
END;
FUNCTION TPACKETREADPROCESS.GETMSGHEADER(VAR HDR:MODMSGHDR):INTEGER;
VAR
TEMPFECHA:STRING;
SD,SM,SA,SFECHA,SHORA:STRING;
OHDR:ORIGMSGHDR;
BEGIN
 PKTSTREAM^.READ(OHDR,SIZEOF(OHDR));
 IF PKTSTREAM^.STATUS<>STOK THEN BEGIN 
GETMSGHEADER:=ERRFINPKT;EXIT;END;
 FILLCHAR(HDR,SIZEOF(HDR),0);
 HDR.DIRORIG.NODE:=OHDR.ONODE;
 HDR.DIRORIG.NET:=OHDR.ONET;
 HDR.DIRDEST.NODE:=OHDR.DNODE;
 HDR.DIRDEST.NET:=OHDR.DNET;
 HDR.ATRIBUTOS:=OHDR.ATR;
 TEMPFECHA[0]:=#20;
 MOVE(OHDR.FECHA[1],TEMPFECHA[1],20);
 TEMPFECHA:=RECORTAFINAL(TEMPFECHA);
 SFECHA:=COPY(TEMPFECHA,1,9);
 SHORA:=COPY(TEMPFECHA,12,8);
 SD:=COPY(SFECHA,1,2);HDR.FECHA.DIA:=STR2LONG(SD);
 SM:=COPY(SFECHA,4,3);SM:=MAYUSMINUS(TRUE,SM);
 IF SM='JAN' THEN HDR.FECHA.MES:=1;
 IF SM='FEB' THEN HDR.FECHA.MES:=2;
 IF SM='MAR' THEN HDR.FECHA.MES:=3;
 IF SM='APR' THEN HDR.FECHA.MES:=4;
 IF SM='MAY' THEN HDR.FECHA.MES:=5;
 IF SM='JUN' THEN HDR.FECHA.MES:=6;
 IF SM='JUL' THEN HDR.FECHA.MES:=7;
 IF SM='AUG' THEN HDR.FECHA.MES:=8;
 IF SM='SEP' THEN HDR.FECHA.MES:=9;
 IF SM='OCT' THEN HDR.FECHA.MES:=10;
 IF SM='NOV' THEN HDR.FECHA.MES:=11;
 IF SM='DEC' THEN HDR.FECHA.MES:=12;
 SA:=COPY(SFECHA,8,2);HDR.FECHA.ANO:=STR2LONG(SA);
 HDR.HORA.HORA:=STR2LONG(COPY(SHORA,1,2));
 HDR.HORA.MINUTO:=STR2LONG(COPY(SHORA,4,2));
 HDR.HORA.SEGUNDO:=STR2LONG(COPY(SHORA,7,2));
 HDR.PARA:=GETSTRINGTONULL;
 HDR.DE:=GETSTRINGTONULL;
 HDR.SOBRE:=GETSTRINGTONULL;
 GETMSGHEADER:=ERROK;
END;
DESTRUCTOR TPACKETREADPROCESS.DONE;
BEGIN
 DISPOSE(PKTSTREAM,DONE);
 INHERITED DONE;
END;
{}
PROCEDURE GETORIGADDRESS(PKT:STRING;VAR ADR:ADDRTYPE);
VAR
PKTR:PPACKETREADPROCESS;
BEGIN
 FILLCHAR(ADR,SIZEOF(ADR),0);
 PKTR:=NEW(PPACKETREADPROCESS,INIT(PKT));
 IF PKTR=NIL THEN EXIT;
 PKTR^.GETPKTORIGADDRESS(ADR);
 DISPOSE(PKTR,DONE);
END;
PROCEDURE GETDESTADDRESS(PKT:STRING;VAR ADR:ADDRTYPE);
VAR
PKTR:PPACKETREADPROCESS;
BEGIN
 FILLCHAR(ADR,SIZEOF(ADR),0);
 PKTR:=NEW(PPACKETREADPROCESS,INIT(PKT));
 IF PKTR=NIL THEN EXIT;
 PKTR^.GETPKTDESTADDRESS(ADR);
 DISPOSE(PKTR,DONE);
END;
END.