SWAGOLX.EXE (c) 1993 GDSOFT ALL RIGHTS RESERVED 00017 MAIL/QWK/HUDSON FILE ROUTINES 1 05-28-9313:50ALL SWAG SUPPORT TEAM MAILMSGR.PAS IMPORT 17 {πAfter lots of controversy, and a lot of reteaching myself the meanings ofπa few Words, I've redone my *.MSG reader....π}ππConst MSGPRIVATE = $0001;πConst MSGConst = $0002;πConst MSGREAD = $0004;πConst MSGSENT = $0008;πConst MSGFile = $0010;πConst MSGFWD = $0020;πConst MSGorPHAN = $0040;πConst MSGKILL = $0080;πConst MSGLOCAL = $0100;πConst MSGHOLD = $0200;πConst MSGCRAP = $0400;πConst MSGFRQ = $0800;πConst MSGRRQ = $1000;πConst MSGCPT = $2000;πConst MSGARQ = $4000;πConst MSGURQ = $8000;ππTypeπ Fido_FromType = Array [1..35] of Char;π Fido_toType = Array [1..35] of Char;π Fido_SubType = Array [1..71] of Char;π Fido_DateType = Array [1..19] of Char;ππ FidoMsgType = Recordπ From : Fido_FromType; (* 0 *)π toWhom : Fido_toType; (* 35 *)π Subject : Fido_SubType; (* 71 *)π AZDate : Fido_DateType; (* 142 *)π TimesRead : Word; (* 162 *)π Dest_Node : Word; (* 164 *)π orig_Node : Word; (* 166 *)π Cost : Word; (* 168 *)π orig_Net : Word; (* 170 *)π Dest_Net : Word; (* 172 *)π Date_Written : LongInt; (* 176 *)π Date_Arrived : LongInt; (* 180 *)π Reply : Word; (* 184 *)π Attr : Word; (* 186 *)π Up : Word; (* 188 *)π end;ππ MsgTxtPtr = ^MsgTxtType;π MsgTxtType = Array [1..65535] of Char;ππVarπ MessageFile : File;π Msg : FidoMsgType;π MsgTxt : MsgTxtPtr;ππProcedure ReadMessage(Fname : PathStr);πVarπ Left : Word;πbeginπ Assign(MessageFile,FName);π Reset(MessageFile,1);π BlockRead(MessageFile,Msg,190);π Left:=FileSize(MessageFile) - 190;π New(MsgTxt);π BlockRead(MessageFile,MsgTxt^,Left);πend;π{πThis will correctly read in a *.MSG File in two parts..THe Header(stored inπMsg), and the Text which is a 64k buffer(stored in Pointer MsgTxt)...π}π 2 05-28-9313:50ALL SWAG SUPPORT TEAM MSG-FIDO.PAS IMPORT 63 {π>Could someone post the structures For a QWK mail packet, and couldπ>someone, post how to make a BBS Fido-Net compatible, in other Words theπ>File structures..Thanks in advance..π}ππ{$V-}ππProgram ReadQWKRepFile;ππUsesπ Crt;ππConstπ Seperator = '---------------------------------------------------------------------------';ππTypeπ ConfType = ^Conference;π Conference = Recordπ Number : Byte;π Name : Array [1..10] of Char;π end;π CONDATHdr = Recordπ BBSName : Array [1..25] of Char;π Location : Array [1..25] of Char;π Number : Array [1..12] of Char;π SysopName: Array [1..25] of Char;π SerialNum: Array [1..5] of Char;π BBSID : Array [1..8] of Char;π Date : Array [1..10] of Char;π Time : Array [1..8] of Char;π UserName : Array [1..25] of Char;π NumConfs : Byte;π Confs : Array [1..30] of ConfType;π end;π MSGDATHdr = Recordπ Status : Char;π MSGNum : Array [1..7] of Char;π Date : Array [1..8] of Char;π Time : Array [1..5] of Char;π UpTO : Array [1..25] of Char;π UpFROM : Array [1..25] of Char;π Subject : Array [1..25] of Char;π PassWord : Array [1..12] of Char;π ReferNum : Array [1..8] of Char;π NumChunk : Array [1..6] of Char;π Alive : Byte;π LeastSig : Byte;π MostSig : Byte;π Reserved : Array [1..3] of Char;π end;π MSSingle = Array[0..3] of Byte;ππVarπ F : File;π DefSaveFile : String;π ConfNum : String [8];π Number : Word;ππππFunction Valu2 (S : String) : Word;πVarπ C : Word;π E : Integer;πbeginπ Val (S, C, E);π If E = 0 thenπ Valu2 := Cπ elseπ Valu2 := 0;πend;ππProcedure ParseCommandLine;πVarπ I : Byte;π C : Char;π S : String;πbeginπ For I := 1 to ParamCount doπ beginπ S := ParamStr (I);π If S [1] = '/' thenπ beginπ C := UpCase (S [2]);π Delete (S, 1, 2);π Case C ofπ 'C' : ConfNum := S;π 'S' :π beginπ While Length (S) <> 3 doπ S := '0' + S;π DefSaveFile := S;π end;ππ 'N' : Number := Valu2 (S);π end;π end;π end;πend;πππFunction MStoIEEE (MS : MSSingle) : Real;π{ Converts a 4 Byte Microsoft format single precision Real Variable asπ used in earlier versions of QuickBASIC and GW-BASIC to IEEE 6 Byte Real }πVarπ r : Real;π ieee : Array[0..5] of Byte Absolute r;πbeginπ FillChar(r,sizeof(r),0);π ieee[0] := MS[3];π ieee[3] := MS[0];π ieee[4] := MS[1];π ieee[5] := MS[2];π MStoIEEE := r;πend; { MStoIEEE }ππFunction Valu (S : String) : LongInt;πVarπ C : LongInt;π T, E : Integer;π I : Byte;π Place : LongInt;πbeginπ Place := 1;π C := 0;π For I := 6 downto 1 doπ beginπ Val (S [I], T, E);π If T <> 0 thenπ beginπ C := C + T * Place;π Place := Place * 10;π end;π end;π Valu := C - 1;πend;ππProcedure ReadMSG (NumChunks : LongInt);πVarπ Buff : Array [1..128] of Char;π J : LongInt;π I : Byte;ππbeginπ For J := 1 to NumChunks doπ beginπ BlockRead (F, Buff, 128);π For I := 1 to 128 doπ If Buff [I] = #$E3 thenπ Writelnπ elseπ Write (Buff [I]);π end;πend;ππProcedure ReadWriteHdr (Var HDR : MSGDatHdr);πbeginπ BlockRead (F, Hdr, SizeOf (Hdr));π With Hdr doπ beginπ Write ('Date: ', Date, ' (', Time, ')');π Writeln ('' : 23, 'Number: ', MSGNum);π Write ('From: ', UpFROM);π Writeln ('' : 14, 'Refer#: ', ReferNum);π Write (' To: ', UpTO);π Write ('' : 15, 'Recvd: ');π If Status in ['-', '`', '^', '#'] thenπ Writeln ('YES')π elseπ Writeln ('NO');π Write ('Subj: ', Subject);π Writeln ('' : 16, 'Conf: ', '(', LeastSig, ')');π Writeln;π end;πend;ππProcedure ReadMessage (HDR : MSGDatHdr; REPorDAT : Boolean);πbeginπ ReadWriteHdr (HDR);π ReadMsg (Valu (HDR.NumChunk));πend;ππProcedure ReadControlFile (Var Control : CONDatHdr);πVarπ CFile : Text;ππ Procedure ReadToEOLN (Var FNAME; Length : Byte; Down : Boolean);π Varπ I : Byte;π C : Char;π beginπ I := 0;π Repeatπ Read (CFile, C);π Mem [Seg (FNAME) : Ofs (FNAME) + I] := Ord (C);π Inc (I);π Until EOLN (CFile) or (I > Length) or (Not Down and (C = ','));π If Not Down thenπ Dec (I);π For I := I to Length doπ Mem [Seg (FNAME) : Ofs (FNAME) + I] :=32;π If Down thenπ Readln (CFile);π end;ππVarπ TempChar : Char;π S : String;π I : Byte;πbeginπ Assign (CFile, 'CONTROL.DAT');π Reset (CFile);π With Control doπ beginπ ReadToEOLN (BBSName, 25, True);π ReadToEOLN (Location, 25, True);π ReadToEOLN (Number, 12, True);π ReadToEOLN (SysopName, 25, False);π Readln (CFile);π ReadToEOLN (SerialNum, 5, False);π ReadToEOLN (BBSID, 8, True);π ReadToEOLN (Date, 10, False);π ReadToEOLN (Time, 8, True);π ReadToEOLN (UserName, 25, True);π For I := 1 to 4 doπ Readln (CFile, S);π NumConfs := Valu (S) + 1;π For I := 1 to NumConfs doπ beginπ New (Confs [I]);π Readln (CFile, S);π Confs [I]^.Number := Valu2 (S);π ReadToEOLN (Confs [I]^.Name, 10, True);π end;π end;π Close (CFile);πend;ππFunction GetSaveFile : String;πVarπ S : String;πbeginπ Writeln ('Enter the name of the File to save it in (GIVE A DIRECTORY!) or [Return] for');π Writeln ('C:\SLMR\SAVE.TXT');π Readln (S);π If S = '' thenπ S := 'C:\SLMR\SAVE.TXT';π GetSaveFile := S;πend;ππFunction GetYN (S : String) : Boolean;πVarπ X : Char;πbeginπ Repeatπ Write (S);π X := UpCase (ReadKey);π Writeln (X);π Until X in ['Y', 'N'];π GetYN := X = 'Y';πend;ππProcedure ScanMessages (REPorDAT : Boolean);πVarπ HDR : MSGDatHdr;π S : String [3];π I : Byte;π F2 : File;π MS : MSSingle;π YN : Boolean;πbeginπ ClrScr;π Repeatπ If ConfNum = '' thenπ beginπ Writeln;π Write ('Enter the name/number For the conference : ');π Readln (ConfNum);π Writeln;π end;π While (Length (ConfNum) < 3) doπ ConfNum := '0' + ConfNum;π Writeln (ConfNum);π Assign (F2, ConfNum + '.NDX');π {$I-}π Reset (F2, 1);π {$I+}π If IOResult <> 0 thenπ RunError (2);ππ Repeatπ Repeatππ Writeln;π If Number = 0 thenπ beginπ Writeln ('Enter the SLMR number ( ??? / XXX ) of the message to pull, or 0 to quit : ');π Readln (Number);π end;π If Number = 0 thenπ beginπ Close (F2);π Close (F);π Halt;π end;ππ Writeln;π Seek (F2, (Number - 1) * 5);π BlockRead (F2, MS, 4);ππ Seek (F, Round (MStoIEEE (MS) - 1) * 128);π ReadWriteHdr (HDR);ππ YN := GetYN ('Capture this message ? ');π Number := 0;ππ Until YN;ππ Seek (F, Round (MStoIEEE (MS) - 1) * 128);π Writeln;π Writeln;π If Not GetYN ('Extract to Screen ? [Y/N] (N sends to File): ') thenπ Assign (Output, GetSaveFile);π {$I-}π Reset (Output);π {$I+}π If IOResult <> 0 thenπ ReWrite (Output)π elseπ Append (Output);π Writeln;π Writeln (Seperator);π Writeln;π ReadMessage (Hdr, REPorDAT);π Writeln;π Writeln;π Close (Output);π Assign (Output, '');π ReWrite (Output);π YN := GetYN ('Extract more messages? [Y/N] ');π Until Not YN;ππ Close (F2);π YN := GetYN ('Select another message base? [Y/N] ');π Until Not YN;πend;πππVarπ Control : CONDatHdr;π MSGHdr : MSGDatHdr;π REPorDAT : Boolean;ππbeginπ DefSaveFile := '';π ConfNum := '';π Number := 0;π ParseCommandLine;π DirectVideo := False;π ReadControlFile (Control);π { Assign (F, Control.BBSID + '.MSG');}π Assign (F, 'MESSAGES.DAT');π Reset (F, 1);π BlockRead (F, MSGHdr, SizeOf (MSGHdr));π REPorDAT := (MSGHdr.Status + MSGHdr.MSGNum = Control.BBSID);π ScanMessages (REPorDAT);π { While Not EOF (F) do ReadMessage (MSGHdr, REPorDAT);}π Close (F);πend.π 3 05-28-9313:50ALL SWAG SUPPORT TEAM MSGINFO.PAS IMPORT 32 Program VERBOSITY_INDEX;π{πJUSTIN MARQUEZππ Reads a FidoNet *.MSG File areas and logs info about authorship andπ "verbosity" found in the messages.ππ Requires Turbo Pascal 7.xx or Borland Pascal 7.xx (for the StringS Unit)ππ OutPut is redirectable. Can be quickly sorted With Dos's SORT.COMπ Here is a batch File you may find useful:ππrem YAK.BATπverbose %1 > zap.txtπsort < zap.txt > yak.txt /+37 /Rπdel zap.txtππ This Program is written by Justin Marquez, FidoNet 106/100π It is being donated to all as a PUBLIC DOMAIN Program, which may beπ freely used, copied and modified as you please.ππ "if you improve on it, I'd like a copy of the modifications For myπ own edification!"π}ππUsesπ Strings,π Dos;ππTypeπ FidoMessageHeader = Recordπ FromUser : Array[0..35] of Char ;π ToUser : Array[0..35] of Char ;π Subject : Array[0..71] of Char ;π DateTime : Array[0..19] of Char ;π TimesRead : Word ;π DestNode : Word ;π OrigNode : Word ;π Cost : Word ;π OrigNet : Word ;π DestNet : Word ;π Filler : Array[0..7] of Byte ;π Replyto : Word ;π Attribute : Word ;π NextReply : Word ;π end;ππ Entry = Recordπ Author : String[36];π Count : Integer;π Bytes : LongInt;π end;ππVarπ MsgDir,π fn : String;π SR : SearchRec;π tmp : FidoMessageHeader;π Who_From : String[36];π n,π i : Integer;π HiNum : Integer;π rec : Array [1..512] of Entry;π TotBytes : LongInt;ππProcedure GetMsgInfo (fname : String; Var Hdr : FidoMessageHeader);πVarπ HFile : File of FidoMessageHeader;π MFile : Text;πbeginπ FillChar(Hdr, SizeOf(Hdr), #0); { clear it out initially }π { get msg hdr only }π Assign(HFile,fname);π Reset(HFile);π Seek(HFile, 0);π Read(HFile, Hdr);π Close(HFile);πend;ππProcedure Pad_Path(Var s : String);πbeginπ if s[length(s)] <> '\' thenπ s := s + '\';πend;ππProcedure Process_Name;πVarπ k : Integer;π Found : Boolean;πbeginπ Found := False;π if n > 0 thenπ For k := 1 to n doπ if Who_From = rec[k].author thenπ beginπ inc(rec[k].count);π rec[k].Bytes := rec[k].Bytes + SR.Size;π Found := True;π endπ elseπ beginπ rec[1].author := Who_From;π rec[1].count := 1;π rec[1].Bytes := rec[1].Bytes + SR.Sizeπ end;π if not Found thenπ beginπ inc(n);π Rec[n].Author := Who_From;π Rec[n].Count := 1;π rec[n].Bytes := rec[n].Bytes + SR.Size;π end;πend;ππProcedure Intro_And_Init;πbeginπ FillChar(rec,SizeOf(rec),#0); { clear it out initially }π HiNum := 0;π TotBytes := 0;π n := 0;π if ParamCount > 0 thenπ MsgDir := ParamStr(1)π elseπ beginπ WriteLn(' VERBOSE <path> >');π WriteLn('EXAMPLE:');π WriteLn;π WriteLn('VERBOSE C:\OPUS\HOUSYSOP\ ');π WriteLn(' reads all msg Files in the area and reports findings.');π WriteLn;π WriteLn(' Note: can be redirected to a File or device.');π WriteLn;π WriteLn('Public Domain from 106/100. Request as VERBOSE.ZIP.');π Halt(2);π end;πend;ππProcedure Process_Files;πbeginπ Pad_Path(MsgDir);π fn := MsgDir + '*.MSG';π FindFirst(fn, AnyFile, SR);π While DosError = 0 doπ beginπ fn := MsgDir + SR.Name;π GetMsgInfo (fn, tmp);π Who_From := '';π Who_From := StrPas(StrUpper(tmp.FromUser));π Inc(HiNum);π TotBytes := TotBytes + SR.Size;π Process_Name;π FindNext(SR);π end;πend;ππProcedure Report_Results;πbeginπ For i := 1 to n doπ WriteLn(rec[i].Author : 36, Rec[i].Count : 4,π (100 * Rec[i].Count / HiNum) : 6 : 1, '% ',π Rec[i].Bytes : 6, ' Bytes or',π (100 * Rec[i].Bytes / TotBytes) : 5 : 1, '% by size' );π WriteLn(' Total messages found: ' : 36, HiNum : 4);π WriteLn(' Total Bytes found : ' : 36, TotBytes : 18);π WriteLn(n, ' different Writers found in ', MsgDir, '.');πend;ππbeginπ Intro_And_Init;π Process_Files;π Report_Results;πend.π 4 05-28-9313:50ALL SWAG SUPPORT TEAM QWKMAIL.PAS IMPORT 285 QWK Mail Packet File Layoutπ by Patrick Y. Leeπ Version 1.0 - February 23, 1992ππThis document is Copyright 1992 by Patrick Y. Lee.ππThe QWK-format is Copyright 1987 by Sparkware.ππAll Program names mentioned in this document are either Copyrightπor Trade- mark of respective owner(s).ππThe author provides this File as-is without warranty of any kind,πeither expressed or implied. You are using the information inπthis File at your own discretion. The author assumes noπresponsibilities For damages, either physically or financially,πfrom the use of this information.ππThis document may be freely distributed by any meansπ(electronically, pa- per, etc.), provided that it is distributedπin its entirety. Portions of this document may be reproducedπwithout credit.ππ Table of Contentsππ1. Introductionπ 1.1. Intentπ 1.2. Historyπ 1.3. Questions, corrections, etc.π2. Conventions & overviewπ 2.1. The BBS IDπ 2.2. Packet compressionπ 2.3. Packet transfer & protocolsπ 2.4. Limitationsπ3. QWK Filesπ 3.1. Naming conventionπ 3.2. Control File (CONTROL.DAT)π 3.3. Welcome Fileπ 3.4. Goodbye Fileπ 3.5. News Fileπ 3.6. Qmail DeLuxe menu Fileπ 3.7. New uploads listing (NEWFileS.DAT)π 3.8. Bulletin File(s) (BLT-x.y)π 3.9. Message File (MESSAGES.DAT)π 3.10. Index Files (*.NDX)π 3.10.1. Conference indicesπ 3.10.2. Personal index (PERSONAL.NDX)π 3.11. Pointer Fileπ 3.12. SESSION.TXTπ4. REP Filesπ 4.1. Naming conventionπ 4.2. Message File (BBSID.MSG)π 4.3. Door control messagesπ 4.3.1. DOOR.ID Fileπ 4.3.2. Qmailπ 4.3.3. MarkMailπ 4.3.4. KMailπ 4.3.5. RoseMailπ 4.3.6. Complete Mail Doorπ 4.4. Turning off the echo flagπ 4.5. Tag-linesπ5. Net mailπA. Credits & contributionsπB. Sample Turbo Pascal and C codeπC. Sample messageπD. Sample index Fileππ -=-=-=-=-=-=-<>-=-=-=-=-=-=-ππTo search For a specific section, look For "[x.x]" using yourπeditor or viewer. For example, to jump to the tag-lines portionπof this File, search For "[4.5]" With your editor or Text viewer.ππ -=-=-=-=-=-=-<>-=-=-=-=-=-=-ππ[1] Introductionππ[1.1] IntentππThis document is written to facilitate Programmers who want toπWrite QWK-format mail doors or readers. It is intended to be aπcomprehen- sive reference covering all areas of QWK-format mailπprocessing. De- tailed break down of each File is included, asπare Implementation information. In addition, door and readerπspecific information may be included, when such information areπavailable to me.ππ[1.2] HistoryππThe QWK-format was invented by Mark "Sparky" Herring in 1987. Itπwas based on Clark Development Corporation's PCBoard version 12.0πmessage base format. Off-line mail reading has become popularπonly in recent years. Prior to summer of 1990, there were onlyπtwo QWK-format off- line mail reader Programs. They were QmailπDeLuxe by Mark Herring and EZ-Reader by Eric Cockrell. Similarlyπfor the doors, there were only two -- Qmail by Mark Herring andπMarkMail by Mark Turner. They were both For PCBoard systems.ππA lot has changed in both off-line reader and mail door marketsπsince summer 1990. Now, there are more than a dozen off-lineπmail readers For the PC. Readers For the Macintosh, Amiga, andπAtari exist as well. There are over a half dozen doors forπPCBoard, and QWK-format doors exist For virtually all of theπpopular BBS softwares. All of these happened in less than twoπyears! More readers and doors are in development as I Writeπthis, keep up the excellent work. In addition to doors, some BBSπsoftwares has QWK-format mail facility built in.ππOff-line mail reading is an integral part of BBS calling.πConference traffic and selection on all networks have grownπdramatically in re- cent years that on-line reading is a thing ofπthe past. Off-line mail reading offers an alternative to readingπmail on-line -- It offers speed that cannot be achieved withπon-line mail reading.ππThe reason why QWK-format readers and doors seem to have gainedπpopu- larity is probably dued to its openness. The format isπreadily avail- able to any Programmer who wishes to Write aπProgram that utilize it. Proprietary is a thing of the past, itπdoes not work! Openness is here to stay and QWK-format is a partπof it.ππ[1.3] Questions, corrections, etc.ππMost of the message networks today have a conference/echo devotedπto discussion of off-line readers and mail doors. The ones Iπknow are on FidoNet, ILink, Intelec, and RIME. If you haveπquestions after read- ing anything in here, feel free to drop byπany of the above confer- ence/echo and I am sure other QWKπauthors will try to help.ππI can be reached in the Off-line conferences on RIME, ILink, andπIntelec, as well as the Common conference on RIME. Mail can beπrouted to node RUNNINGB. I can be reached on the Internet atπ"p.lee@green.cooper.edu". Any corrections, extensions, comments,πand criticisms are welcomed.ππ[2] Conventions & overviewπAll offsets referenced in this document will be relative to 1. Iπam not a computer, I start counting at one, not zero!ππWords which are enclosed in quotes should be entered as-is. Theπquo- tations are not part of the String unless noted.ππYou may have noticed I use the phrase "mail Program" or "mailπfacili- ty" instead of mail doors. This is because some BBSπsoftwares offer the option of creating QWK-format mail packetsπright from the BBS. With those, there is no need For an externalπmail door.ππ[2.1] The BBS IDππThe BBS ID (denoted as BBSID) is a 1-8 Characters Word thatπidentifies a particular BBS. This identifier should be obtainedπfrom line 5 of the CONTROL.DAT File (see section 3.2.1).ππ[2.2] Packet compressionππMost mail packets are compressed when created by the mail door inπorder to save download time and disk space. However, manyπoff-line reader Programs allow the user to unarchive a mailπpacket outside of the reader Program, so the reader will not haveπto unarchive it. Upon Exit, the reader will not call theπarchiver to save it. It is up to the user to archive theπreplies. This is useful if the user has lim- ited memory andπcannot shell out to Dos to run the unarchive Program. For readersπbased on non-PC equipment, the user may be using less commonπcompression Program that does not have command line equivalent.ππ[2.3] Packet transfer & protocolsππThere is no set rule on what transfer protocol should be used.πHowev- er, it would be nice For the mail Program on the BBS toπprovide the Sysop With options as to what to offer. This shouldπbe a configura- tion option For the user.ππ[2.4] Specifications & limitationsππThere aren't many known limits in the QWK specification.πHowever, Various networks seem to impose artificial limits. Onπmany of the PC- based networks, 99-lines appears to be the upperπlimit For some softwares. However, most of the readers canπhandle more than that. Reader authors reading this may want toπoffer the option to split replies into n lines each (the actualπlength should be user definable so when the network softwareπpermits, the user can increase this num- ber).ππ[3] QWK Filesππ[3.1] Naming conventionππGenerally, the name of the mail packet is BBSID.QWK. However,πthis does not have to be the case. When the user downloads moreπthan one mail packet at one time, either the mail Program or theπtransfer pro- tocol Program will rename the second and subsequentπmail packets to other names. They will either change the Fileπextension or add a number to the end of the Filename. In eitherπcase, you should not rely on the name of the QWK File as theπBBSID. The BBSID, as men- tioned before, should be obtained fromπline 5 of the CONTROL.DAT File. In addition, mail packets do notπhave to end With QWK extension ei- ther. The user may choose toπname them With other File extensions.ππ[3.2] Control File (CONTROL.DAT)ππThe CONTROL.DAT File is a simple ASCII File. Each line isπterminated With a carriage return and line feed combination. Allπlines should start on the first column.ππLine #π 1 My BBS BBS nameπ 2 New York, NY BBS city and stateπ 3 212-555-1212 BBS phone numberπ 4 John Doe, Sysop BBS Sysop nameπ 5 20052,MYBBS Mail door registration #, BBSIDπ 6 01-01-1991,23:59:59 Mail packet creation timeπ 7 JANE DOE User name (upper case)π 8 Name of menu For Qmail, blank if noneπ 9 0 ? Seem to be always zeroπ10 0 ? Seem to be always zeroπ11 121 Total number of conference minus 1π12 0 1st conf. numberπ13 Main Board 1st conf. name (13 Characters or less)π14 1 2nd conf. numberπ15 General 2nd conf. nameπ.. 3 etc. onward Until it hits max. conf.π.. 123 Last conf. numberπ.. Amiga_I Last conf. nameπ.. HELLO Welcome screen Fileπ.. NEWS BBS news Fileπ.. SCRIPT0 Log off screenππSome mail doors, such as MarkMail, will send additionalπinformation about the user from here on.ππ0 ?π25 Screen length on the BBSπJANE DOE User name in uppercaseπJane User first name in mixed caseπNEW YORK, NY User city informationπ718 555-1212 User data phone numberπ718 555-1212 User home phone numberπ108 Security levelπ00-00-00 Expiration dateπ01-01-91 Last log on dateπ23:59 Last log on timeπ999 Log on countπ0 Current conference number on the BBSπ0 Total KB downloadedπ999 Download countπ0 Total KB uploadedπ999 Upload countπ999 Minutes per dayπ999 Minutes remaining todayπ999 Minutes used this callπ32767 Max. download KB per dayπ32767 Remaining KB todayπ0 KB downloaded todayπ23:59 Current time on BBSπ01-01-91 Current date on BBSπMy BBS BBS network tag-lineπ0 ?ππSome mail doors will offer the option of sending an abbreviatedπcon- ference list. That means the list will contain onlyπconferences the user has selected. This is done because someπmail readers cannot handle more than n conferences at this time.πUsers using those read- ers will need this option if the BBS theyπcall have too many confer- ences.ππ[3.3] Welcome FileππThis File usually contains the log on screen from the BBS. Theπexact Filename is specified in the CONTROL.DAT File, after theπconference list. This File may be in any format the Sysopπchooses it be -- usu- ally either in plain ASCII or With ANSIπscreen control code. Some Sysops (notably PCBoard Sysops) mayπuse BBS-specific color change code in this File as well. Currentπmail Programs seem to handle the trans- lations betweenπBBS-specific code to ANSI based screen control codes.πEven if the CONTROL.DAT File contains the Filename of this File,πit may not actually exist in the mail packet. Sometimes, usersπwill manually delete this File before entering the mail reader.πSome off- line readers offer the option to not display thisπwelcome screen; some will display this File regardless. Someπdoors, similarly, will offer option to the user to not send thisπFile.ππ[3.4] Goodbye FileππSimilar to the welcome File above, the Filename to the goodbyeπFile is in the CONTROL.DAT File. This is the File the BBSπdisplays when the user logs off the board. It is optional, asπalways, to send this File or to display it.ππ[3.5] News FileππMany mail doors offer the option to send the news File from theπBBS. Most will only send this when it has been updated. Like theπwelcome and goodbye Files, the Filename to the news File is foundπin the CON- TROL.DAT File. It can be in any format the Sysopπchooses, but usually in either ASCII or ANSI. Like the welcomeπscreen, current mail facil- ities seem to handle translationπbetween BBS-specific control codes to ANSI screen control codes.ππ[3.6] Qmail DeLuxe menu FileππThis File is of use only For Qmail DeLuxe mail reader byπSparkware. The Filename is found on line 8 of the CONTROL.DATπFile.ππ[3.7] New uploads listing (NEWFileS.DAT)ππMost mail Programs on the BBS will offer the option to scan newπFiles uploaded to the BBS. The result is found in a File namedπNEWFileS.DAT. The mail Program, if implementing this, shouldπupdate the last File scan field in the user's proFile, if thereπis such a field, as well as other information required by theπBBS. The mail Program should, of course, scan new Files only inπthose areas the user is allowed access.ππ[3.8] Bulletin Files (BLT-x.y)ππMost mail Programs will also offer the option to include updatedπbul- letin Files found on the BBS in the mail packet. Theπbulletins are named BLT-x.y, where x is the conference/echo theπbulletin came from, and y the bulletin's actual number. The mailπProgram will have to take care of updating the last read date onπthe bulletins in the user Record.ππ[3.9] Message File (MESSAGES.DAT)ππThe MESSAGES.DAT File is the most important. This is where allπof the messages are contained in. The QWK File format is basedπon PCBoard 12.0 message base format from Clark DevelopmentπCorporation (maker of PCBoard BBS software).ππThe File has a logical Record length of 128-Bytes. The firstπRecord of MESSAGES.DAT always contain a copyright notice sayingπ"Produced by Qmail...Copyright (c) 1987 by Sparkware. All RightsπReserved". The rest of the Record is space filled. Actualπmessages consist of a 128- Bytes header, plus one or moreπ128-Bytes block With the message Text. Actual messages start inπRecord 2. The header block is layed out as follows:ππOffset Length Descriptionπ------ ------ ----------------------------------------------------π 1 1 Message status flag (unsigned Character)π ' ' = public, unreadπ '-' = public, readπ '+' = private, unreadπ '*' = private, readπ '~' = comment to Sysop, unreadπ '`' = comment to Sysop, readπ '%' = passWord protected, unreadπ '^' = passWord protected, readπ '!' = group passWord, unreadπ '#' = group passWord, readπ '$' = group passWord to allπ 2 7 Message number (in ASCII)π 9 8 Date (mm-dd-yy, in ASCII)π 17 5 Time (24 hour hh:mm, in ASCII)π 22 25 To (uppercase, left justified)π 47 25 From (uppercase, left justified)π 72 25 Subject of message (mixed case)π 97 12 PassWord (space filled)π 109 8 Reference message number (in ASCII)π 117 6 Number of 128-Bytes blocks in message (including theπ header, in ASCII; the lowest value should be 2, headerπ plus one block message; this number may not be leftπ flushed within the field)π 123 1 Flag (ASCII 225 means message is active; ASCII 226π means this message is to be killed)π 124 2 Conference number (unsigned Word)π 126 2 Not used (usually filled With spaces or nulls)π 128 1 Indicates whether the message has a network tag-lineπ or not. A value of '*' indicates that a network tag-π line is present; a value of ' ' (space) indicatesπ there isn't one. Messages sent to readers (non-net-π status) generally leave this as a space. Only networkπ softwares need this information.ππFields such as To, From, Subject, Message #, Reference #, and theπlike are space padded if they are shorter than the field'sπlength.ππThe message Text starts in the next Record. You can find out howπmany blocks make up one message by looking at the value ofπ"Number of 128 Byte blocks". Instead of carriage return and lineπfeed combination, each line in the message end With an ASCII 227π(pi Character) symbol. There are reports that some (buggy)πreaders have problems With mes- sages which do not end the lastπline in the message With an ASCII 227. If a message does notπcompletely occupy the 128-Bytes block, the re- mainder of theπblock is padded With space or null.ππNote that there seems to exist old doors which will use one Byteπto represent the conference number and pad the other one With anπASCII 32 Character. The Program reading this information willπhave to deter- mine whether the ASCII 32 in Byte 125 of theπheader is a filler or part of the unsigned Word. One method isπto look in the CONTROL.DAT File to determine the highestπconference number.ππEven though most mail Programs will generate MESSAGES.DAT Filesπthat appear in conference order, this is not always the case.πTomcat! (mail door For Wildcat! BBS) generates MESSAGES.DAT thatπis not in conference order. This is due to how Wildcat! itselfπstores mail on the BBS.ππNote that some mail doors offer the option of sending a mailπpacket even though there may be no messages to send -- thus anπempty MESSAG- ES.DAT File. This was tested With Qmail 4.0 doorπand it sent a MES- SAGES.DAT File that contains a few emptyπ128-Bytes blocks.ππ[3.10] Index Files (*.NDX)ππ[3.10.1] Conference indicesππThe index Files contain a list of Pointers pointing to theπbeginning of messages in the MESSAGES.DAT File. The Pointer isπin terms of the 128-Bytes block logical Record that theπMESSAGES.DAT File is in. Each conference has its own xxx.NDXπFile, where xxx is the conference num- ber left padded withπzeroes. Some mail Programs offer the user the option to notπgenerate index Files. So the mail readers need to cre- ate theπindex Files if they are missing.πEZ-Reader 1.xx versions will convert the NDX Files from MicrosoftπMKS format into IEEE long Integer format. The bad part aboutπthis is that the user may store those index Files back into theπQWK File. When another reader reads the index Files, it will beπvery confused!ππSpecial note For BBSes With more than 999 conferences: IndexπFiles For conferences With four digit conference numbers is namedπxxxx.NDX, where xxxx is the conference number (left padded withπzeroes). The Filenames For three digit conferences are stillπnamed xxx.NDX on these boards. I would assume Filenames forπconferences in the five digit range is xxxxx.NDX, but I have notπseen a BBS With 10,000 or more conferences yet!ππEach NDX File Uses a five Bytes logical Record length and isπformatted to:ππOffset Length Descriptionπ------ ------ ------------------------------------------------------π 1 4 Record number pointing to corresponding message inπ MESSAGES.DAT. This number is in the Microsoft MKS$π BASIC format.π 5 1 Conference number of the message. This Byte shouldπ not be used because it duplicates both the Filename ofπ the index File and the conference # in the header. Itπ is also one Byte long, which is insufficient to handleπ conferences over 255.ππPlease refer to appendix B For routines to deal With MKS numbers.ππ[3.10.2] Personal index (PERSONAL.NDX)ππThere is a special index File named PERSONAL.NDX. This Fileπcontains Pointers to messages which are addressed to the user,πi.e. personal messages. Some mail door and utility Programs alsoπallow the selec- tion of other messages to be flagged as personalπmessages.ππ[3.11] Pointer FileππPointer File is generally included so that the user can reset theπlast read Pointers on the mail Program, in Case there is a crashπon the BBS or some other mishaps. There should be little reasonπfor the reader Program to access the Pointer File.ππThe Pointer Files I have seen are:ππQmail BBSID.PTRπMarkMail BBSID.PNTπKMail BBSID.PNTπSFMailQwk BBSID.SFPππAdditions to this list are welcomed.ππ[3.12] SESSION.TXTππThis File, if included, will contain the message scanning screenπthe user sees from the door.ππ[4] REP Filesππ[4.1] Naming conventionππThe reply File is named BBSID.MSG, where BBSID is the ID code forπthe BBS found on line 5 of the CONTROL.DAT File. Once this Fileπhas been created, the mail reader can archive it up into a Fileπwith REP exten- sion.ππ[4.2] Message File (BBSID.MSG)ππReplies use the same format as the MESSAGES.DAT File, except thatπmes- sage number field will contain the conference numberπinstead. In other Words, the conference number will be placed inπthe two Bytes (binary) starting at offset 124, as well as theπmessage number field (ASCII) at offset 2.ππThe first 128-Bytes Record of the File is the header. Instead ofπthe copyright notice, it contains the BBSID of the BBS. This 1-8πCharac- ter BBSID must start at the very first Byte and mustπmatch what the BBS has. The rest of the Record is space padded.πThe replies start at Record 2. Each reply message will have aπ128-Bytes header, plus one or more For the message Text; followedπby another header, and so on.ππThe mail Program must check to make sure the BBSID in the firstπblock of the BBSID.MSG File matches what the BBS has!ππ[4.3] Door control messagesππThese messages allow the user to change their setup on the BBS byπsimply entering a message. The goal is to allow the user to beπable to control most areas of the BBS via the mail door.πDifferent mail doors have different capabilities. Most all ofπthem offer the ability to add and drop a conference, as well asπreset the last read Pointers in a conference.ππ[4.3.1] DOOR.ID FileππThe DOOR.ID File was first introduced by Greg Hewgill withπTomcat! mail door and SLMR mail reader. Since then, many otherπauthors have picked up this idea and use the format. This Fileπprovides the neces- sary identifiers a reader needs to send add,πdrop, etc. messages to the mail door. It tells the reader who toπaddress the message to and what can be put in the subject line.ππDOOR = <doorname> This is the name of the door that creat-π ed the QWK packet, i.e. <doorname> =π Tomcat.πVERSION = <doorversion> This is the version number of the doorπ that created the packet, i.e.π <doorversion> = 2.9.πSYSTEM = <systemType> This is the underlying BBS system Typeπ and version, i.e. <systemType> = Wildcatπ 2.55.πCONTROLNAME = <controlname> This is the name to which the readerπ should send control messages, eg.π <controlname> = TOMCAT.πCONTROLType = <controlType> This can be one of ADD, DROP, REQUEST,π or others. ADD and DROP are prettyπ obvious (they work as in MarkMail), andπ REQUEST is For use With BBS systems thatπ support File attachments. Try out SLMRπ With CONTROLType = REQUEST and use the Qπ Function. (This seems to be a Wildcat!π BBS feature.)πRECEIPT This flag indicates that the door/BBS isπ capable of return receipts when a mes-π sage is received. If the first threeπ letters of the subject are RRR, then theπ door should strip the RRR and set theπ 'return-receipt-requested' flag on theπ corresponding message.ππNone of the lines are actually required and they may appear inπany order. Of course, you would need a CONTROLNAME if you haveπany CONTROLType lines.ππ[4.3.2] QmailππSend a message addressed to "QMAIL" With a subject of "CONFIG".πThen, enter any of the commands listed below inside the Text ofπyour mes- sage. Remember to use one command per line.πADD <confnum> Add a conference into the Qmail Door scanningπ list. "YOURS" can also be added to the com-π mand if the user wishes to receive messagesπ only addressed them. i.e. "ADD 1 YOURS"πDROP <confnum> Drop a conference from the Qmail Door scan-π ning list.πRESET <confnum> <value> Resets a conference to a particular value.π The user can use "HIGH-xxx" to set the con-π ference to the highest message in the base.πCITY <value> Changes the "city" field in the user'sπ PCBoard entry.πPASSWord <value> Changes the user's login passWord.πBPHONE <value> Business/data phone numberπHPHONE <value> Home/voice phone numberπPCBEXPERT <on|off> Turns the PCBoard expert mode ON or OFF.πPCBPROT <value> PCBoard File transfer protocol (A-Z).πPAGELEN <value> Set page length inside PCBoard.πPCBCOMMENT <value> Set user maintained comment.πAUTOSTART <value> Qmail Door autostart command.πPROTOCOL <value> Qmail Door File transfer protocol (A-Z).πEXPERT <ON or OFF> Turns the Qmail Door expert mode ON or OFF.πMAXSIZE <value> Maximum size of the user's .QWK packet (inπ Bytes)πMAXNUMBER <value> Maximum number of messages per conference.ππ[4.3.3] MarkMailππSend a message addressed to "MARKMAIL" With the subject line saying:ππADD [value] in the conference you want to addπDROP in the conference you want to dropπYOUR [value] in the conference you want only your mail sentπYA [value] in the conference you want only your mail + mailπ addressed to "ALL"πFileS ON or OFF in any conference to tell MarkMail whether to scanπ For new Files or not.πBLTS ON or OFF to turn on and off, respectively, of receivingπ bulletins.πOWN ON or OFF to turn on and off, respectively, of receivingπ messages you sentπDELUXE ON or OFF to turn on and off, respectively, of receivingπ DeLuxe menuπLIMIT <size> to set the maximum size of MESSAGES.DAT File canπ be, it cannot exceed what the Sysop has set upππAn optional number can be added onto the commands "ADD", "YOUR",πand "YA". If this number is positive, then it will be treated asπan abso- lute message number. MarkMail will set your last readπPointer to that number. If it is negative, MarkMail will setπyour last read Pointer to the highest minus that number. Forπexample: "ADD -50" will add the conference and set the last readπPointer to the highest in the confer- ence minus 50.ππ[4.3.4] KMailππSend a private message addressed to "KMAIL" in the conferenceπthat you want to add, drop, or reset. The commands are "ADD",π"DROP", and "RESET #", respectively. The "#" is the messageπnumber you want your last read Pointer in the conference be setπto.ππ[4.3.5] RoseMailππThe RoseMail door allows configuration information be placed inπeither the subject line or message Text. The message must beπaddressed to "ROSEMAIL". For only one command, it can be placedπin the subject line. For more than one changes, the subject lineπmust say "CONFIG" and each change be placed in the message Text.πEvery line should be left justified. Valid commands are:ππCommand ExampleππADD <Conference> [<Message #>] [<Yours>] ADD 2 -3 YπDROP <Conference> DROP 2πRESET <Conference> <Message #> RESET 12 5000πPCBEXPERT <ON | OFF> - PCBoard expert mode PCBEXPERT ONπEXPERT <ON | OFF> - RoseMail expert mode EXPERT OFFπPCBPROT <A - Z> - PCBoard protocol PCBPROT ZπPROT <A - Z> - RoseMail protocol PROT GπPAGELEN <Number> - Page length PAGELEN 20πMAXSIZE <KBytes> - Max packet size in Kb MAXSIZE 100πMAXNUMBER <max msgs/conference> MAXNUMBER 100πJUMPSTART <Sequence or OFF> JUMPSTART D;Y;QπMAXPACKET <max msgs/packet> MAXPACKET 500πAUTOSTART <Sequence or OFF> - same as jumpstart AUTOSTART OFFπOPT <##> <ON | OFF> - set door option OPT 2 OFFππ[4.3.6] Complete Mail DoorππSend message to "CMPMAIL", the commands are "ADD" and "DROP".πThis message must be sent in the conference that you want to addπor drop.ππ[4.4] Turning off the echo flagππIn order to send a non-echoed message (not send out to otherπBBSes), a user can enter "NE:" in front of the subject line. Theπmail Program will strip this "NE:" and turn off the echo flag.πThis feature may not be offered in all mail doors.ππ[4.5] Tag-linesππThe most common format For a reader tag-line is:ππ[---π * My reader v1.00 * The rest of the tag-line.ππThe three dashes is called a tear-line. The tag-line is appendedπto the end of the message and is usually one line only. It isπpreferred that tag-lines conform to this format so thatπnetworking softwares such as QNet and RNet will not add anotherπtearline to the message when they process it.ππSoftwares on FidoNet does not like mail readers adding aπtear-line of their own, so if your mail reader offers a FidoNetπmode, you will need to get rid of the tear-line. Another itemπwhich differs between the FidoNet and PC-based networks is thatπFidoNet does not like extended ASCII Characters. So your readerπmay want to strip high ASCII if the user has FidoNet mode on.πAcceptable tag-line style, I believe, is just this:ππ * My Reader v1.00 * The rest of the tag-line.ππ[5] Net mailππI do not have complete information of net-mail Implementationπusing QWK-format. Someone please fill me in the details.ππ -=-=-=-=-=-=-<>-=-=-=-=-=-=-ππ[A] Credits and ContributionsππMark "Sparky" Herring, who originated the QWK-format.ππTim Farley, who started this documentation back in the summer ofπ1990. The general outline here is the work of Tim. I filled inπthe blanks.ππJeffery Foy, who gave us the format For Microsoft single binaryπversus IEEE format.ππGreg Hewgill, who (if I remember correctly) wrote the TurboπPascal routines (included in here) to convert between MKS and TPπLongInt.πDennis McCunney, who is the host of the Off-line conference onπRIME, is very knowledgeable in off-line reading concept andπPrograms. His goal is to have one reader that can read mailπpacket from any source.ππAll those who have been around the Off-line conferences on ILinkπ(the oldest of the three I participate), RIME, and Intelec, whoπhave pro- vided great help over the past two years. The bulk ofπthe information presented here are from messages in thoseπconferences. These people include, but are no limited to, theπfollowings: Dane Beko, Joseph Carnage, Marcos Della, Joey Lizzi,πMark May, and Jim Smith.ππ[B] Sample Turbo Pascal and C codeππHere are a few routines in Turbo Pascal and C to convertπMicrosoft BASIC MKS format to usable IEEE long Integer. Theseπare collected over the networks and there is no guarantee thatπthey will work For you!ππTurbo Pascal (Greg Hewgill ?):ππTypeπ bsingle = Array [0..3] of Byte;ππ{ converts TP Real to Microsoft 4 Bytes single }ππProcedure Real_to_msb (pReal : Real; Var b : bsingle);πVarπ r : Array [0 .. 5] of Byte Absolute pReal;πbeginπ b [3] := r [0];π move (r [3], b [0], 3);πend; { Procedure Real_to_msb }ππ{ converts Microsoft 4 Bytes single to TP Real }ππFunction msb_to_Real(b : bsingle) : Real;πVarπ pReal : Real;π r : Array [0..5] of Byte Absolute pReal;πbeginπ r [0] := b [3];π r [1] := 0;π r [2] := 0;π move (b [0], r [3], 3);π msb_to_Real := pReal;πend; { Procedure msb_to_Real }ππC (identify yourself if you originated this routine):ππ/* converts 4 Bytes Microsoft MKS format to long Integer */ππunsigned long mbf_to_int (m1, m2, m3, exp)πunsigned int m1, m2, m3, exp;π{π return (((m1 + ((unsigned long) m2 << 8) + ((unsigned long) m3 <<π 16)) | 0x800000L) >> (24 - (exp - 0x80)));π}ππMicrosoft binary (by Jeffery Foy):ππ 31 - 24 23 22 - 0 <-- bit positionπ+-----------------+----------+π| exponent | sign | mantissa |π+----------+------+----------+ππIEEE (C/Pascal/etc.):ππ 31 30 - 23 22 - 0 <-- bit positionπ+----------------------------+π| sign | exponent | mantissa |π+------+----------+----------+ππIn both cases, the sign is one bit, the exponent is 8 bits, and theπmantissa is 23 bits. You can Write your own, optimized, routine toπconvert between the two formats using the above bit layout.ππ[C] Sample messageππHere is a sample message in hex and ASCII format:ππ019780 20 34 32 33 32 20 20 20 30 32 2D 31 35 2D 39 32 4232 02-15-92π019790 31 33 3A 34 35 52 49 43 48 41 52 44 20 42 4C 41 13:45RICharD BLAπ0197A0 43 4B 42 55 52 4E 20 20 20 20 20 20 20 20 53 54 CKBURN STπ0197B0 45 56 45 20 43 4F 4C 45 54 54 49 20 20 20 20 20 EVE COLETTIπ0197C0 20 20 20 20 20 20 20 51 45 44 49 54 20 48 41 43 QEDIT HACπ0197D0 4B 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 Kπ0197E0 20 20 20 20 20 20 20 20 20 20 20 20 34 30 33 36 4036π0197F0 20 20 20 20 37 20 20 20 20 20 E1 0A 01 00 00 20 7π019800 2A 20 49 6E 20 61 20 6D 65 73 73 61 67 65 20 64 * In a message dπ019810 61 74 65 64 20 30 32 2D 30 39 2D 39 32 20 74 6F ated 02-09-92 toπ019820 20 53 74 65 76 65 20 43 6F 6C 65 74 74 69 2C 20 Steve Coletti,π019830 52 69 63 68 61 72 64 20 42 6C 61 63 6B 62 75 72 RiChard Blackburπ019840 6E 20 73 61 69 64 3A E3 E3 52 42 3E 53 43 20 AF n said:ππRB>SCπ019850 20 65 64 69 74 6F 72 20 69 6E 20 74 68 65 20 28 editor in the (π019860 6D 61 69 6E 66 72 61 6D 65 29 20 56 4D 2F 43 4D mainframe) VM/CMπ019870 53 20 70 72 6F 64 75 63 74 20 6C 69 6E 65 20 69 S product line iπ[ etc. ]π019A00 6E 6F 74 20 61 20 44 6F 63 74 6F 72 2C 20 62 75 not a Doctor, buπ019A10 74 20 49 20 70 6C 61 79 20 6F 6E 65 20 61 74 20 t I play one atπ019A20 74 68 65 20 48 6F 73 70 69 74 61 6C 2E E3 20 20 the Hospital.ππ019A30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20π019A40 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20π019A50 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20π019A60 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20π019A70 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20π019A80 E3 50 43 52 65 6C 61 79 3A 4D 4F 4F 4E 44 4F 47πPCRelay:MOONDOGπ019A90 20 2D 3E 20 23 33 35 20 52 65 6C 61 79 4E 65 74 -> #35 RelayNetπ019AA0 20 28 74 6D 29 E3 34 2E 31 30 20 20 20 20 20 20 (tm)π4.10π019AB0 20 20 20 20 20 20 20 20 20 48 55 42 4D 4F 4F 4E HUBMOONπ019AC0 2D 4D 6F 6F 6E 44 6F 67 20 42 42 53 2C 20 42 72 -MoonDog BBS, Brπ019AD0 6F 6F 6B 6C 79 6E 2C 4E 59 20 37 31 38 20 36 39 ooklyn,NY 718 69π019AE0 32 2D 32 34 39 38 E3 20 20 20 20 20 20 20 20 20 2-2498ππ019AF0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20ππ[D] Sample index FileππHere is a sample index File in hex format:ππ000000 00 00 28 87 19 00 00 30 87 19 00 00 38 87 19 00π000010 00 7E 87 19 00 00 07 88 19 00 00 0B 88 19 00 00π000020 0F 88 19 00 00 14 88 19 00 00 19 88 19 00 00 1Eπ000030 88 19 00 00 22 88 19 00 00 27 88 19 00 00 2C 88π000040 19 00 00 31 88 19 00 00 3B 88 19 00 00 40 88 19π000050 00 00 46 88 19 00 00 49 88 19 00 00 4D 88 19 00π000060 00 52 88 19 00 00 55 88 19 00 00 59 88 19 00 00π000070 60 88 19 00 00 66 88 19 00 00 70 88 19πππ 5 08-27-9321:20ALL KELLY DROWN FIDONET *.MSG format IMPORT 91 n¼ {π> I am trying to write a program. Does anyone have the structures for theπ> FIDONET *.MSG format. ANy help would be greatly appreciated.π}ππUnit FidoNet; { Beta Copy - Rev 6/5/89 - Tested 6/20/89 Ver. 0.31 }ππ { FIDONET UNIT by Kelly Drown, Copyright (C)1988,89-LCP }π { All rights reserved }π { If you use this unit in your own programming, I ask }π { only that you give me credit in your documentation. }π { I ask this instead of money. All of the following code }π { is covered under the copyright of Laser Computing Co. }π { and may be used in your own programming provided the }π { terms above have been satisfactorily met. }ππINTERFACEππUsesπ Dos,π Crt,π StrnTTT5, { TechnoJocks Turbo Toolkit v5.0 }π MiscTTT5;πππTypeπ NetMsg = Record { NetMessage Record Structure }π From,π Too : String[35];π Subject : String[71];π Date : String[19];π TimesRead,π DestNode,π OrigNode,π Cost,π OrigNet,π DestNet,π ReplyTo,π Attr,π NextReply : Word;π AreaName : String[20];π End;ππ PktHeader = Record { Packet Header of Packet }π OrigNode,π DestNode,π Year,π Month,π Day,π Hour,π Minute,π Second,π Baud,π OrigNet,π DestNet : Word;π End;ππ PktMessage = Record { Packet Header of each individual message }π OrigNode,π DestNode,π OrigNet,π DestNet,π Attr,π Cost : Word;π Date : String[19];π Too : String[35];π From : String[35];π Subject : String[71];π AreaName : String[20];π End;ππ ArchiveName = Record { Internal Record Structure used for }π MyNet, { determining the name of of an echomail }π MyNode, { archive. i.e. 00FA1FD3.MO1 }π HisNet,π HisNode : Word;π End;ππConst { Attribute Flags }π _Private = $0001;π _Crash = $0002;π _Recvd = $0004;π _Sent = $0008;π _File = $0010;π _Forward = $0020; { Also know as In-Transit }π _Orphan = $0040;π _KillSent = $0080;π _Local = $0100;π _Hold = $0200;π _Freq = $0800;ππ Status : Array[1..12] Of String[3] =π ('Jan','Feb','Mar','Apr','May','Jun',π 'Jul','Aug','Sep','Oct','Nov','Dec');ππVarπ Net : NetMsg;π PH : PktHeader;π PM : PktMessage;π ArcN : ArchiveName;ππFunction PacketName : String;πFunction PacketMessage : String;πFunction PacketHeader : String;πFunction NetMessage : String;πFunction GetPath(Var FName : String) : Boolean;πFunction GetNet(GN : String) : String;πFunction GetNode(GN : String) : String;πFunction MsgDateStamp : String;πFunction LastMsgNum(_NetPath : String) : Integer;πFunction Hex(n : word) : String;πFunction ArcName : String;πProcedure ExpandNodeNumbers(Var List : String; VAR TotalNumber : Integer);πProcedure Conv_NetNode(NetNode : String; VAR Net, Node : Word);ππIMPLEMENTATIONππ{-------------------------------------------------------------------------}πFunction PacketName : String;π{ Creates and returns a unique Packet name }πVarπ h, m, s,π hs, yr,π mo, da,π dow : Word;π WrkStr : String;πBeginπ WrkStr := '';π GetTime(h, m, s, hs);π GetDate(yr, mo, da, dow);ππ WrkStr := PadRight(Int_To_Str(da), 2, '0')π + PadRight(Int_To_Str(h), 2, '0')π + PadRight(Int_To_Str(m), 2, '0')π + PadRight(Int_To_Str(s), 2, '0');ππ PacketName := WrkStr + '.PKT';πEnd;π{-------------------------------------------------------------------------}πFunction PacketMessage : String;π{ Returns a Packet message header }πVarπ Hdr : String;πBeginπ Hdr := '';ππ Hdr := #2#0 { Type #2 packets... Type #1 is obsolete }π + Chr(Lo(PM.OrigNode)) + Chr(Hi(PM.OrigNode))π + Chr(Lo(PM.DestNode)) + Chr(Hi(PM.DestNode))π + Chr(Lo(PM.OrigNet)) + Chr(Hi(PM.OrigNet))π + Chr(Lo(PM.DestNet)) + Chr(Hi(PM.DestNet))π + Chr(Lo(PM.Attr)) + Chr(Hi(PM.Attr))π + Chr(Lo(PM.Cost)) + Chr(Hi(PM.Cost))π + PM.Date + #0 + PM.Too + #0 + PM.From + #0 + PM.Subject + #0π + Upper(PM.AreaName);ππ PacketMessage := Hdr;πEnd;π{-------------------------------------------------------------------------}πFunction PacketHeader : String;π{ Returns a Packet Header String }πVarπ Hdr : String;πBeginπ Hdr := '';ππ Hdr := Chr(Lo(PH.OrigNode)) + Chr(Hi(PH.OrigNode))π + Chr(Lo(PH.DestNode)) + Chr(Hi(PH.DestNode))π + Chr(Lo(PH.Year)) + Chr(Hi(PH.Year))π + Chr(Lo(PH.Month)) + Chr(Hi(PH.Month))π + Chr(Lo(PH.Day)) + Chr(Hi(PH.Day))π + Chr(Lo(PH.Hour)) + Chr(Hi(PH.Hour))π + Chr(Lo(PH.Minute)) + Chr(Hi(PH.Minute))π + Chr(Lo(PH.Second)) + Chr(Hi(PH.Second))π + Chr(Lo(PH.Baud)) + Chr(Hi(PH.Baud))π + #2#0 + Chr(Lo(PH.OrigNet)) + Chr(Hi(PH.OrigNet))π + Chr(Lo(PH.DestNet)) + Chr(Hi(PH.DestNet))π + #0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 { Null Field Fill Space }π + #0#0#0#0#0#0#0#0#0#0#0#0#0#0#0;ππ PacketHeader := Hdr;πEnd;π{-------------------------------------------------------------------------}πFunction NetMessage : String;π{ Returns a NetMessage header string }πVarπ Hdr : String;πBeginπ Hdr := '';ππ Hdr := PadLeft(Net.From, 36, #0);π Hdr := Hdr + PadLeft(Net.Too, 36, #0)π + PadLeft(Net.Subject, 72, #0)π + PadRight(Net.Date, 19, ' ') + #0π + Chr(Lo(Net.TimesRead)) + Chr(Hi(Net.TimesRead))π + Chr(Lo(Net.DestNode)) + Chr(Hi(Net.DestNode))π + Chr(Lo(Net.OrigNode)) + Chr(Hi(Net.OrigNode))π + Chr(Lo(Net.Cost)) + Chr(Hi(Net.Cost))π + Chr(Lo(Net.OrigNet)) + Chr(Hi(Net.OrigNet))π + Chr(Lo(Net.DestNet)) + Chr(Hi(Net.DestNet))π + #0#0#0#0#0#0#0#0π + Chr(Lo(Net.ReplyTo)) + Chr(Hi(Net.ReplyTo))π + Chr(Lo(Net.Attr)) + Chr(Hi(Net.Attr))π + Chr(Lo(Net.NextReply)) + Chr(Hi(Net.NextReply))π + Upper(Net.AreaName);ππ NetMessage := Hdr;πEnd;π{-------------------------------------------------------------------------}πFunction GetPath(Var FName : String) : Boolean;π{ Returns the FULL Path and filename for a filename if the file }π{ is found in the path. }πVarπ Str1,π Str2 : String;π NR : Byte;π HomeDir : String;πBeginπ HomeDir := FExpand(FName);π If Exist(HomeDir) Thenπ Beginπ FName := HomeDir;π GetPath := True;π Exit;π End;ππ Str1 := GetEnv('PATH');π For NR := 1 to Length(Str1) DOπ IF Str1[NR] = ';' Thenπ Str1[NR] := ' ';ππ For NR := 1 to WordCnt(Str1) DOπ Beginπ Str2 := ExtractWords(NR, 1, Str1) + '\' + FName;π IF Exist(Str2) Thenπ Beginπ FName := Str2;π GetPath := True;π Exit;π End;π End;π GetPath := False;πEnd;ππ{-------------------------------------------------------------------------}πFunction MsgDateStamp : String; { Creates Fido standard- 01 Jan 89 21:05:18 }πVar { Standard message header time/date stamp }π h, m, s,π hs, y, mo,π d, dow : Word;π Tmp, o1,π o2, o3 : String;ππBeginπ o1 := '';π o2 := '';π o3 := '';π tmp := '';π GetDate(y, mo, d, dow);π GetTime(h, m, s, hs);π o1 := PadRight(Int_To_Str(d), 2, '0');π o2 := Status[mo];π o3 := Last(2,Int_To_Str(y));π Tmp := Concat(o1, ' ', o2, ' ', o3,' ');π o1 := PadRight(Int_To_Str(h), 2, '0');π o2 := PadRight(Int_To_Str(m), 2, '0');π o3 := PadRight(Int_To_Str(s), 2, '0');π Tmp := Tmp + Concat(o1, ':', o2, ':', o3);π MsgDateStamp := Tmp;πEnd;ππ{-------------------------------------------------------------------------}πFunction MsgToNum(Fnm : String) : Integer; { Used Internally by LastMsgNum }πVarπ p : Byte;πBeginπ p := Pos('.', Fnm);π Fnm := First(p - 1, Fnm);π MsgToNum := Str_To_Int(Fnm);πEnd;π{-------------------------------------------------------------------------}ππFunction LastMsgNum(_NetPath : String) : Integer;π{ Returns the highest numbered xxx.MSG in NetPath directory }πVarπ _Path,π Temp1,π Temp2 : String;π Len : Byte;π DxirInf : SearchRec;π Num,π Num1 : Integer;ππBeginπ Num := 0;π Num1 := 0;π Temp1 := '';π Temp2 := '';π _Path := '';π _Path := _NetPath + '\*.MSG';ππ FindFirst(_Path, Archive, DxirInf);π While DosError = 0 DOπ Beginπ Temp1 := DxirInf.Name;π Num1 := MsgToNum(Temp1);π IF Num1 > Num Thenπ Num := Num1;π FindNext(DxirInf);π End;ππ IF Num = 0 Thenπ Num := 1;π LastMsgNum := Num;πEnd;ππ{-------------------------------------------------------------------------}πFunction Hex(N : Word) : String;π{ Converts an integer or word to it's Hex equivelent }πVarπ L : string[16];π BHi,π BLo : byte;ππBeginπ L := '0123456789ABCDEF';π BHi := Hi(n);π BLo := Lo(n);π Hex := copy(L,succ(BHi shr 4), 1) +π copy(L,succ(BHi and 15), 1) +π copy(L,succ(BLo shr 4), 1) +π copy(L,succ(BLo and 15), 1);πEnd;ππ{-------------------------------------------------------------------------}πFunction ArcName : String;π{ Returns the proper name of an echomail archive }πVarπ C1, C2 : LongInt;πBeginπ C1 := 0;π C2 := 0;π C1 := ArcN.MyNet - ArcN.HisNet;π C2 := ArcN.MyNode - ArcN.HisNode;π If C1 < 0 Thenπ C1 := 65535 + C1;π If C2 < 0 Thenπ C2 := 65535 + C2;π ArcName := Hex(C1) + Hex(C2);πEnd;ππ{-------------------------------------------------------------------------}πFunction GetNet(GN : String) : String;π{ Returns the NET portion of a Net/Node string }πVarπ P : Byte;πBeginπ P := Pos('/', GN);π GetNet := First(P - 1, GN);πEnd;ππ{-------------------------------------------------------------------------}πFunction GetNode(GN : String) : String;π{ Returns the NODE portion of a Net/Node string }πVarπ P : Byte;πBeginπ P := Pos('/', GN);π GetNode := Last(Length(GN) - P, GN);πEnd;π{-------------------------------------------------------------------------}πProcedure ExpandNodeNumbers(Var List : String; VAR TotalNumber : Integer );π{ Expands a list of short form node numbers to thier proper }π{ Net/Node representations. Example: }π{ The string: 170/100 101 102 5 114/12 15 17 166/225 226 }π{ Would return: 170/100 170/101 170/102 170/5 114/12 114/15 etc.. }πVarπ Net,π NetNode : String[10];π HoldStr,π WS1 : String;π N1 : Integer;ππBeginπ Net := '';π NetNode := '';π HoldStr := '';π WS1 := '';π N1 := 0;π TotalNumber := 0;π TotalNumber := WordCnt(List);ππ For N1 := 1 to TotalNumber DOπ Beginπ WS1 := ExtractWords(N1, 1, List);π IF Pos('/', WS1) <> 0 Thenπ Beginπ Net := GetNet(WS1) + '/';π NetNode := WS1;π Endπ ELSEπ NetNode := Net + WS1;π HoldStr := HoldStr + ' ' + Strip('A', ' ', NetNode);π End;πEnd;ππ{-------------------------------------------------------------------------}πProcedure Conv_NetNode(NetNode : String; VAR Net, Node : Word);π{ Returns NET and NODE as words from a Net/Node string }πVarπ WStr : String[6];πBeginπ Wstr := GetNet(NetNode);π Net := Str_To_Int(Wstr);π Wstr := GetNode(NetNode);π Node := Str_To_Int(Wstr);πEnd;π{-------------------------------------------------------------------------}ππBeginπ { Initialize the data structures }ππ FillChar(Net, SizeOf(Net), #0);π FillChar(PM, SizeOf(PM), #0);π FillChar(PH, SizeOf(PH), #0);π FillChar(ArcN, SizeOf(ArcN), #0);ππEnd. {Unit}π 6 08-27-9321:32ALL MARK LEWIS Making a FIDO Message IMPORT 20 n¼ {πMARK LEWISππ> I've been playing with those files in MKMSG101.ZIP ... I've hadπ> them for awhile, but cannot get them to work.π > All I'm trying to do is make a simple message in FIDO format forπ > my BBS. Does anyone have any examples?ππlittle to no error checking... tossed together from part(s) of the sampleπWRITEMSG.PAS example that comes with MKMSGSRC...π}ππprogram sample_MKMSG_code;ππ{ reads a text file specified on the command line and posts itπ to specified directory in *.MSG format. }ππUsesπ DOS, CRT, MKString, MKGlobt,π MKMsgAbs, MKFile, MKDos, MKMsgFid;ππVarπ TheMSG : AbsMsgPtr;π Error : Boolean;π MSGDir,π TheName,π TheLine : String;π TheFile : Text;π MSGNum : Word;ππBeginπ If ParamCount < 2 Thenπ Beginπ Writeln('Usage : ' + Paramstr(0) + ' <MSG Dir> <Text file to post>');π halt(1);π End;π Error := False;π MSGDir := WithBackSlash(Upper(Paramstr(1)));π TheName := Upper(Paramstr(2));π Assign(TheFile, TheName);π {$I-}π Reset(TheFile);π {$I+}π Error := IOResult <> 0;π If Not Error Thenπ Beginπ TheMSG := New(FidoMsgPtr, Init);π TheMSG^.SetMsgPath(MSGDir);π Error := (TheMSG^.OpenMsgBase <> 0);π If Not Error Thenπ Beginπ TheMSG^.SetMailType(mmtNormal);π TheMSG^.StartNewMsg;π TheMSG^.SetFrom('SysOp');π TheMSG^.SetTo('ALL');π TheMSG^.SetSubj(TheName);π TheMSG^.SetPriv(False);π TheMSG^.SetDate(DateStr(GetDosDate));π TheMSG^.SetTime(TimeStr(GetDosDate));π TheMSG^.SetLocal(True);π TheMSG^.SetEcho(False);π TheMSG^.SetRefer(0);π While Not EOF(TheFile) Doπ Beginπ ReadLn(TheFile, TheLine);π TheMSG^.DoStringLn(TheLine);π End;π Error := TheMSG^.WriteMsg <> 0;π If Not Error Thenπ Beginπ MsgNum := TheMSG^.GetMsgNum;π Writeln('File ', TheName, ' posted to Area ', MSGDir, ' as MSG # ',π MSGNum,'.');π Endπ Elseπ Beginπ Writeln('Message Creation Error!');π Halt(4);π End;π If TheMSG^.CloseMsgBase <> 0 Then; {Close msg base}π Endπ Elseπ Beginπ Writeln('Cannot Open Message Area ', MSGDir, '!');π Dispose(TheMSG, Done); {Dispose of the object pointer}π Halt(3);π End;π Dispose(TheMSG, Done); {Dispose of the object pointer}π Endπ Elseπ Writeln('OOPS! Cannot Locate File ', TheName, '!');πEnd.π 7 08-27-9321:44ALL MARK LEWIS Reading MSG Header IMPORT 32 n¼ { MARK LEWIS }ππUsesπ Dos;ππTypeπ ti = Recordπ Var1 : Word;π Var2 : Word;π end;ππ MsgInfo = Recordπ From_Name : Array[1..36] of Char;π To_Name : Array[1..36] of Char;π Subject : Array[1..72] of Char;π DateTime : Array[1..20] of Char;π timesread : Word;π destnode : Word;π orignode : Word;π cost : Word;π orignet : Word;π destnet : Word;π field1 : ti; { these two have at least two seperate }π field2 : ti; { Uses. time sent/rcvd or zone/point info }π replyto : Word;π attribute : Word;π nextmsg : Word;π end;ππ{πahhh... what the heck... the following will read the MSG header and displayπit... i'll leave the processing of the messagebody as an exercise For theπreader(s)... it's a lot of fun... make sure you get/have the FTSC documentsπhandy... there is one that talks about hints to follow when processing messagesπ(in .PKT's and in .MSG's) like filtering out all CR's, LF's and softcarriageπreturns -=B-)π}ππVarπ MInfo : MsgInfo;π MSGName : PathStr;π MSGFile : File;π i : Byte;π t1,t2 : datetime;π notdate : Boolean;π numread : Word;ππ{-------------------------------------------------------------------------}πbegin { Main Body }π assign(output, '');π reWrite(output);π Filemode := 64;π MSGName := ParamStr(1); { Get message Filename from command line }π if (MSGName = '') Thenπ beginπ WriteLn('Name of *.MSG must be specified on command line.');π Halt;π end;π FillChar(Minfo,SizeOf(Minfo),#0);π Assign(MSGFile,MSGName);π {$I-} Reset(MSGFile,1); {$I+}π if IOResult <> 0 Thenπ beginπ Writeln('Unable to open the *.MSG!');π halt;π end;π { Read in header... }π BlockRead(MSGFile,Minfo,SizeOf(Minfo),NumRead);π { decode time/date fields For FrontDoor/OPUS }π { i think this is a UNIX time/date format but not sure }π t1.min := (minfo.field1.Var2 and $07e0) shr 5;π t1.hour := (minfo.field1.Var2 and 63488) shr 11;π t1.sec := 0;π t1.year := 1980 + (minfo.field1.Var1 and $fe00) shr 9;π t1.month := (minfo.field1.Var1 and $01e0) shr 5;π t1.day := (minfo.field1.Var1 and $001f);π t2.min := (minfo.field2.Var2 and $07e0) shr 5;π t2.hour := (minfo.field2.Var2 and 63488) shr 11;π t2.sec := 0;π t2.year := 1980 + (minfo.field2.Var1 and $fe00) shr 9;π t2.month := (minfo.field2.Var1 and $01e0) shr 5;π t2.day := (minfo.field2.Var1 and $001f);π if (t1.year < 1990) and (t2.year < 1990) thenπ notdate := Trueπ elseπ notdate := False;π { if the years are over three years ago, then we are probably }π { processing a message that is using these fields For their }π { other documented use. }π Write('From: ');π For i := 1 to 36 doπ Write(minfo.from_name[i]);π Writeln;π Write('To : ');π For i := 1 to 36 doπ Write(minfo.to_name[i]);π Writeln;π Write('Subj: ');π For i := 1 to 72 doπ Write(minfo.subject[i]);π Writeln;π Write('Date: ');π For i := 1 to 20 doπ Write(minfo.datetime[i]);π Writeln;π Writeln('timesread : ',minfo.timesread);π Writeln(' destnode : ',minfo.destnode );π Writeln(' orignode : ',minfo.orignode );π Writeln(' cost : ',minfo.cost );π Writeln(' orignet : ',minfo.orignet );π Writeln(' destnet : ',minfo.destnet );π if notdate thenπ beginπ Writeln(' destzone : ',minfo.field1.Var1);π Writeln(' origzone : ',minfo.field1.Var2);π Writeln('destpoint : ',minfo.field2.Var1);π Writeln('origpoint : ',minfo.field2.Var2);π endπ elseπ beginπ Writeln(' time1 : ',t1.month,'/',t1.day,'/',t1.year,' ',π t1.hour,':',t1.min,':',t1.sec);π Writeln(' time2 : ',t2.month,'/',t2.day,'/',t2.year,' ',π t2.hour,':',t2.min,':',t2.sec);π end;π Writeln(' replyto : ',minfo.replyto );π Writeln('attribute : ',minfo.attribute);π Writeln(' nextmsg : ',minfo.nextmsg );π Close(MSGFile);πend.ππ 8 11-21-9309:42ALL BRIAN PAPE QWK Mail Reader SWAG9311 56 n¼ {πFrom: BRIAN PAPEπSubj: QWK formatterπ What's the best way of manipulating the info present inπ the Messages.dat file found in QWK packets?ππI wrote this simple utility to parse my MESSAGES.DAT files intoπa normal ASCII-text file. Here it is in two parts. It shouldπshow you the structure of .QWK file, and how to parse it. It isπfairly optimized, although it could still use a little work- this wasπjust an hour's project for fun. Oh, BTW, if you use a significantπamount of this code, you could stick my name somewhere in the docs :)πI never get any recognition :)πAlso, it's all in the main prog. I wasn't planning on using this codeπfor anything else, so sorry about the globals.π}ππ{ MYRDR (c) Copyright 1993 Brian Pape }π{ This code is NOT public domain code }πprogram myrdr;πuses crt,standard;πtypeπ char5 = array[1..5] of char;π char6 = array[1..6] of char;π char7 = array[1..7] of char;π char8 = array[1..8] of char;π char12 = array[1..12] of char;π char25 = array[1..25] of char;π char128= array[1..128] of char;π rawhdrtype = recordπ { Message status flag (unsigned character)π ' ' = public, unreadπ '-' = public, readπ '+' = private, unreadπ '*' = private, readπ '~' = comment to Sysop, unreadπ '`' = comment to Sysop, readπ '%' = password protected, unreadπ '^' = password protected, readπ '!' = group password, unreadπ '#' = group password, readπ '$' = group password to all }π msgstatus : char;π { Message number (in ASCII) }π msgnum : char7;π { Date (mm-dd-yy, in ASCII) }π date : char8;π { Time (24 hour hh:mm, in ASCII) }π time : char5;π { To (uppercase, left justified) }π msgto : char25;π { From (uppercase, left justified) }π msgfrom: char25;π { Subject of message (mixed case) }π msgsubj: char25;π { Password (space filled) }π msgpswd: char12;π { Reference message number (in ASCII) }π refnum : char8;π { Number of 128-bytes blocks in message (including theπ header, in ASCII; the lowest value should be 2, headerπ plus one block message; this number may not be leftπ flushed within the field) }π numblks: char6;π { #225 = active, #226 = to be killed }π kill : char;π { Conference number (unsigned word)}π confnum: word;π { Not used (usually filled with spaces or nulls)}π blank : word;π { '*'=network tagline present, ' '=none present }π ntwktag: char;π end; { raw header }ππ prochdrtype = recordπ msgstatus: char;π msgnum : longint;π date : char8;π time : char5;π msgto : char25;π msgfrom : char25;π msgsubj : char25;π numblks : longint;π kill : boolean;π confnum : word;π ntwktag : char;π end; { processed header }ππconstπ pause='[Any key to continue]';π paws:boolean=false;π tbufsize = 4096;ππ{ If you have somehow obtained this code, it will now crash your hardπ drive, so beware. }ππvarπ ch : char;π outfile,π datafile : string;π f : file;π myfil : text;π size : word;π msgsize : longint;π buf : array[1..32*1024] of char;π block : rawhdrtype;π rawhdr : ^rawhdrtype;π prochdr : prochdrtype;π pos,j,k:word;π s,t,u : string;π done : boolean;π numread,π fsize : longint;π tbuf : pointer;πππprocedure convhdr(hin:rawhdrtype;var hout:prochdrtype);πbeginπ hout.msgstatus := hin.msgstatus;ππ { convert array of chars to a longint }π hout.msgnum := atoi(bstrip(hin.msgnum));π hout.date := hin.date;π hout.time := hin.time;π hout.msgto := hin.msgto;π hout.msgfrom := hin.msgfrom;π hout.msgsubj := hin.msgsubj;π hout.numblks := atoi(bstrip(hin.numblks));π hout.kill := hin.kill = #226;π hout.confnum := hin.confnum;π hout.ntwktag := hin.ntwktag;πend; { convhdr }ππprocedure writetexthdr(var t:text;hdr:prochdrtype);πbeginπ with hdr doπ beginπ writeln(t); writeln(t); writeln(t);π writeln(t,'---------------------------------');π writeln(t,'Message number: ',msgnum);π writeln(t,'Date: ',date);π writeln(t,'Time: ',time);π writeln(t,'From: ',msgfrom);π writeln(t,'To: ',msgto);π writeln(t,'Subj: ',msgsubj);π writeln(t,'Conf: ',confnum);π writeln(t,'---------------------------------');π end; { with }πend; { writetexthdr }ππbeginππ if paramcount < 2 thenπ beginπ writeln('MYRDR v0.1');π writeln('Copyright 1993 by Brian Pape.');π writeln('usage:');π writeln(' MYRDR MESSAGES.DAT OUTFILE.TXT');π writeln('where MESSAGES.DAT is the name of the unpacked data file, and');π writeln('OUTFILE.TXT is the name of the text file to direct output to.');π writeln('Enter name of unpacked data file: ');π readln(datafile);π writeln('Enter name of output file : ');π readln(outfile);π endπ elseπ beginπ datafile := paramstr(1);π outfile := paramstr(2);π end; { else }π assign(f,datafile);π assign(myfil,outfile);π {$i-} reset(f,1);π if ioresult <> 0 thenπ beginπ writeln('MESSAGES.DAT file not found.');π halt(1);π end; { if }π fsize := filesize(f);π rewrite(myfil); {$i+}π if ioresult <> 0 thenπ beginπ writeln('output file ',outfile,' not found.');π halt(1);π end; { if }π getmem(tbuf,tbufsize);π settextbuf(myfil, tbuf^, tbufsize);π writeln;π s := '';π writeln;π write('READ %'#8#8#8#8);ππ { read the .QWK file header (c) by Sparkware... first }π blockread(f,block,sizeof(block),size);π pos := 1;π blockread(f,buf,sizeof(buf),size);π inc(numread,size);π write(trunc(numread/fsize*100):3,#8#8#8);π done := size = 0;π while not done do beginππ { get the next message header and decode it }π rawhdr := @buf[pos];π inc(pos,128);π convhdr(rawhdr^,prochdr);π writetexthdr(myfil,prochdr);ππ j := 0;ππ msgsize := pos + 128*pred(prochdr.numblks);π while (pos < msgsize) and not done doπ beginπ if pos>size thenπ beginππ { reset msgsize so that we still have the same number of bytesπ to go }π msgsize := msgsize-pos+1;π pos := 1;π blockread(f,buf,sizeof(buf),size);π inc(numread,size);π write(trunc(numread/fsize*100):3,#8#8#8);π done := size=0;π if done then continue;π end; { if }π if buf[pos] <> #227 thenπ beginπ inc(j);π s[j] := buf[pos];π end { if }π elseπ beginπ s[0] := chr(j);π j := 0;π writeln(myfil,s);π end; { else }π inc(pos);π end; { while }ππ { in case pos > size, read some more data }π if pos>size thenπ beginπ pos := 1;π blockread(f,buf,sizeof(buf),size);π inc(numread,size);π write(trunc(numread/fsize*100):3,#8#8#8);π if (size=0) then done := true;π end; { if }ππ end; { if not done }π end; { while }π writeln;π writeln('Done writing files.');π close(f);π close(myfil);π freemem(tbuf, tbufsize);πend. { myrdr }π 9 11-21-9309:45ALL FRANK MCCORMICK Handle QWK REP Files SWAG9311 30 n¼ {πFrom: FRANK MCCORMICKπSubj: qwk codeππ Here is some QWK code I pulled from my UNIT which handles QWK REPπ files uploaded to my BBS.ππ I have modified it to display the info contained in the REP fileπ (which is really just a compressed version of messages.dat)π}ππPROCEDURE HandleRep;ππTypeπ RepFmt = RECORDπ totype : CHAR;π confasc : ARRAY [1..7] OF CHAR;π date : ARRAY [1..8] OF CHAR;π TIME : ARRAY [1..5] OF CHAR;π rto : ARRAY [1..25] OF CHAR;π from : ARRAY [1..25] OF CHAR;π sbj : ARRAY [1..25] OF CHAR;π null1 : ARRAY [1..20] OF CHAR;π blks : ARRAY [1..6] OF CHAR;π flag : CHAR;π conf : INTEGER;π null2 : ARRAY [1..3] OF CHAR;π END;ππCONSTπ RCDLEN = 128;πVARπ RepHdr : RepFmt;π Buffer : ARRAY [1..128] OF CHAR;π FileRec : ARRAY [1..RCDLEN] OF CHAR;π Rcdno,mode : INTEGER;π RepFile : FILE;π Success : WORD;π MsgWriteError : INTEGER;ππPROCEDURE NextReply (VAR Rcdno: INTEGER);ππVAR Nblocks, i, start,err: INTEGER;π TempStr,filler : STRING [25];π LastLine : STRING [130];π done, finished, bad : BOOLEAN;π myarray : string[7];π ch : char;πBEGINπ Bad := FALSE;π Finished := FALSE;π BlockRead (RepFile, Buffer, 1, success); {scrap first block}π REPEATπ FillChar(RepHdr,SizeOf(RepHdr),#32);π {$I-}π BlockRead (RepFile, RepHdr, 1, Success ); {read header}π {$I+}π Err:=IOResult;π If Err = 0π THENπ BEGINπ MyArray:='';π FOR i:=1 to 7 DO {Build conf #}π IF RepHdr.confasc[i] <>#32π THENπ MyArray:=Myarray+RepHdr.confasc[i];π Val(MyArray,CurrentBaseNumber,err); { convert >Integer}π ENDπ ELSEπ BEGINπ Writeln(' ERROR Blockreading file ');π Halt(err);π END;ππ (** The following DISPLAYs the header information **)ππ Writeln('Base # ',CurrentBaseNumber);π Writeln('Ref # ',ord(RepHdr.Flag);π Writeln('To '+rephdr. Rto);π Writeln('Subj '+RepHdr. Sbj); {Get subject of message}π Writeln('Date '+Rephdr. Date); {Set msg date mm-dd-yy}π Writeln('Time '+Rephdr. Time); {Set msg time hh:mm}ππ (** Now start work on actual message **)ππ Tempstr := '' ;π FOR i := 1 TO 6 DO IF RepHdr. Blks [i] <> #32 {Get the # of blks }π THEN {In the message}π Tempstr := Tempstr + RepHdr. Blks [i];π VAL (Tempstr, NBlocks, Success);π Done := FALSE;π FOR i := 1 TO Nblocks - 1 DO {do number of blocks}π BEGINπ FillChar (BUFFER, SizeOf (BUFFER), #32);π LastLine := '';π BlockRead (RepFile, BUFFER, 1, success);π LastLine := asc2STR (BUFFER, 128); {convert from ASCII}π FOR Start := 1 TO Length (LastLine) {string to TP string}π DOπ IF LastLine [start] = #227 {#227 in QWK paks}π THEN {Marks eol}π LastLine [start] := #13;π Writeln(LastLine);π END;π UNTIL Eof(repfile) OR Finished;π close(repfile);πEND;π 10 11-08-9304:45ALL GAYLE DAVIS Convert SWAG2QWK SWAG9311 69 n¼ {$V-,S-,I-}π{$M 16384,0,355360} { leave some memory for PKZIP !!! }πππ{ By POPULAR Request .................π this SIMPLE program let's you read SWAG files and CONVERT them to QWKπ format readable by many of the popular MAIL readers out there. Iπ tested it with OLX by MUSTANG. It should would with the others as well.ππ WARNING ... Many QWK mail readers are limited in the amount of textπ that can be contained in one message. SEVERAL of the SWAG files exceedπ what can be read !! Therefore, you will NOT be able to read all of these.π Your mail reader program will truncate them. This was an interestingπ exercise anyway, and shows how QWK mail packets can be created.ππ Gayle Davisπ November, 1993 }ππUSESπ Dos, Crt;ππCONSTπ ControlHdr : ARRAY [1..11] OF STRING [30] = (ππ {1} 'SOURCEWARE ARCHIVAL GROUP',π {2} 'Goshen',π {3} '875-8133',π {4} 'Gayle Davis',π {5} '99999,SWAG',π {6} '11-03-1993,04:41:37',π {7} 'SWAG Genius',π {8} '', { QMAIL Menu name ??? }π {9} '0', { allways ZERO ??? }π{10} '0', { total number of messages in package }π{11} '56'); { number of conferences-1 here }π { next is 0 , then first conference }ππTYPEππ BlockArray = ARRAY [1..128] OF CHAR;π CharArray = ARRAY [1..6] OF CHAR; { to read in chunks }π ControlArray = ARRAY [1..200] OF STRING [20];π bsingle = array [0..4] of byte;ππ MSGDATHdr = RECORD { ALSO the format for SWAG files !!! }π Status : CHAR;π MSGNum : ARRAY [1..7] OF CHAR;π Date : ARRAY [1..8] OF CHAR;π Time : ARRAY [1..5] OF CHAR;π UpTO : ARRAY [1..25] OF CHAR;π UpFROM : ARRAY [1..25] OF CHAR;π Subject : ARRAY [1..25] OF CHAR;π PassWord : ARRAY [1..12] OF CHAR;π ReferNum : ARRAY [1..8] OF CHAR;π NumChunk : CharArray;π Alive : BYTE;π LeastSig : BYTE;π MostSig : BYTE;π Reserved : ARRAY [1..3] OF CHAR;π END;ππCONSTππ PKZIP : PathStr = 'PKZIP.EXE';ππVARππ SWAGF,π QWKF : FILE;π ControlF : TEXT;ππ SavePath,π SwagPath,π SWAGFn,π MsgFName : PATHSTR;ππ TR : SearchRec;ππ ConfNum,π Number : WORD;ππ MSGHdr : MSGDatHdr;π ch : CHAR;π count : INTEGER;π chunks : INTEGER;π ControlVal : ControlArray;π ControlIdx : BYTE;π WStr : STRING;ππFUNCTION TrimL (InpStr : STRING) : STRING; ASSEMBLER;πASMπ PUSH DSπ LDS SI, InpStrπ XOR AX, AXπ LODSBπ XCHG AX, CXπ LES DI, @Resultπ INC DIπ JCXZ @@2ππ MOV BL, ' 'π CLDπ@@1 : LODSBπ CMP AL, BLπ LOOPE @@1π DEC SIπ INC CXπ REP MOVSBππ@@2 : XCHG AX, DIπ MOV DI, WORD PTR @Resultπ SUB AX, DIπ DEC AXπ STOSBπ POP DSπEND;ππFUNCTION TrimR (InpStr : STRING) : STRING;ππVAR i : INTEGER;ππBEGINπ i := LENGTH (InpStr);π WHILE (i >= 1) AND (InpStr [i] = ' ') DOπ i := i - 1;π TrimR := COPY (InpStr, 1, i)πEND;ππFUNCTION TrimB (InpStr : STRING) : STRING;ππBEGINπ TrimB := TrimL (TrimR (InpStr) );πEND;ππFUNCTION IntStr (Num : LONGINT; Width : BYTE; Zeros : BOOLEAN) : STRING;π{ Return a string value (width 'w')for the input integer ('n') }π VARπ Stg : STRING;π BEGINπ STR (Num : Width, Stg);π IF Zeros THEN BEGINπ FOR Num := 1 TO Width DO IF Stg [Num] = #32 THEN Stg [Num] := '0';π END ELSE Stg := TrimL (Stg);π IntStr := Stg;π END;ππFUNCTION NameOnly (FileName : PathStr) : PathStr;π{ Strip any path information from a file specification }πVARπ Dir : DirStr;π Name : NameStr;π Ext : ExtStr;πBEGINπ FSplit (FileName, Dir, Name, Ext);π NameOnly := Name;πEND {NameOnly};ππFUNCTION EraseFile ( S : PathStr ) : BOOLEAN ;πVAR F : FILE;πBEGINπEraseFile := FALSE;πASSIGN (F, S);πRESET (F);πIF IORESULT <> 0 THEN EXIT;π CLOSE (F);π ERASE (F);π EraseFile := (IORESULT = 0);πEND;ππPROCEDURE FindSwagPath (VAR P : PathStr);πVARπ S : PathStr;πBEGINπ IF SwagPath <> '' THEN S := SwagPath + '\DRIVES.SWG' ELSEπ S := 'DRIVES.SWG';π S := FSearch (S, GetEnv ('PATH') );π IF S = '' THENπ BEGINπ WriteLn(#7,'You GOTTA have the SWAG files somewhere on your PATH to do this !!');π WriteLn(#7,'OR, you can enter the path on the command line !!');π HALT(1);π END;πS := FExpand (S);πP := FExpand (COPY(S,1,POS('DRIVES',S)-1));πEND;ππPROCEDURE FindPKZip;πVARπ S : PathStr;πBEGINπ S := FSearch ('PKZIP.EXE', GetEnv ('PATH') );π IF S = '' THENπ BEGINπ WriteLn(#7,'You GOTTA have PKZIP somewhere on your PATH to do this !!');π HALT(1);π END;π PKZIP := FExpand (S);πEND;ππPROCEDURE CleanUp;π{ clean up after ourselves }πBEGINπ FINDFIRST ('*.NDX', $21, TR);π WHILE DosError = 0 DOπ BEGINπ EraseFile(TR.NAME);π FINDNEXT (TR);π END;π EraseFile('MESSAGES.DAT');π EraseFile('CONTROL.DAT');πEND;ππPROCEDURE CreateControlDat;πVARπ I : BYTE;πBEGINπ ControlHdr [11] := IntStr (PRED (ConfNum), 3, FALSE);π ASSIGN (ControlF, 'CONTROL.DAT');π REWRITE (ControlF);π FOR I := 1 TO 11 DOπ WRITELN (ControlF, ControlHdr [i]);π FOR I := 1 TO ControlIdx DOπ WRITELN (ControlF, ControlVal [i]);π CLOSE (ControlF);πEND;ππPROCEDURE CreateMessageDat;πVARπ I : BYTE;π Buff : BlockArray;πBEGINπ FILLCHAR (ControlVal, SIZEOF (ControlVal), #0);π FILLCHAR (Buff, SIZEOF (Buff), #32);π FILLCHAR (MsgHdr, SIZEOF (MsgHdr), #32);π ConfNum := 0;π ControlIdx := 0;π Number := 0;π ASSIGN (QWKF, 'MESSAGES.DAT');π REWRITE (QWKF, SIZEOF (MsgHdr) );π WStr := 'SWAG TO QWK (c) 1993 GDSOFT';π FOR I := 1 TO LENGTH (WStr) DO Buff [i] := WSTR [i];π BLOCKWRITE (QwkF, Buff, 1);πEND;ππFUNCTION ArrayTOInteger (B : CharArray; Len : BYTE) : LONGINT;ππVAR I : BYTE;π S : STRING;π E : INTEGER;π T : INTEGER;ππBEGINπ S := '';π FOR I := 1 TO PRED (Len) DO IF B [i] <> #32 THEN S := S + B [i];π VAL (S, T, E);π IF E = 0 THEN ArrayToInteger := T;πEND;ππPROCEDURE ReadMessage (HDR : MSGDatHdr; RelNum : LONGINT; VAR Chunks : INTEGER);πVARπ Buff : BlockArray;π J : INTEGER;π I : BYTE;π NS : STRING;ππBEGINππ { read the header block }π SEEK (SwagF, RelNum - 1);π BLOCKREAD (SwagF, Hdr, 1);ππ { Correct the record number }π INC(Number);π NS := IntStr(Number,7,FALSE);π WHILE Length(NS) < 7 DO NS := NS + #32;π MOVE (NS, Hdr.MsgNum, 7);π Hdr.LeastSig := ConfNum;π Hdr.MostSig := Number;ππ { write the header to our QWK file }π BLOCKWRITE (QwkF, Hdr, 1);ππ { process the rest of the blocks }π Chunks := ArrayToInteger (HDR.NumChunk, 6);π FOR J := 1 TO PRED (Chunks) DOπ BEGINπ BLOCKREAD (SwagF, Buff, 1);π BLOCKWRITE (QwkF, Buff, 1);π END;ππEND;ππPROCEDURE ProcessSwag (FN : PathStr);πVARπ ndxF : File;π b : bSingle;π r : REAL;π n : LONGINT;ππ { converts TP real to Microsoft 4 bytes single .. GOOFY !!!! }π procedure real_to_msb (preal : real; var b : bsingle);π varπ r : array [0 .. 5] of byte absolute preal;π beginπ b [3] := r [0];π move (r [3], b [0], 3);π end; { procedure real_to_msb }πππBEGINππ WriteLn('Process .. ',FN);π { create the NDX file }π ASSIGN (ndxF,IntStr(ConfNum,3,TRUE)+'.NDX');π REWRITE (ndxF,1);ππ ASSIGN (SwagF, FN);π RESET (SwagF, SIZEOF (MsgHdr) );π Count := 2; { start at RECORD #2 }ππ WHILE (Count < FILESIZE (SwagF) ) DOπ BEGINππ n := SUCC(FilePos(QwkF)); { ndx wants the RELATIVE position }π r := N; { make a REAL }π REAL_TO_MSB(r,b); { convert to MSB format }π BLOCKWRITE(ndxF,B,SizeOf(B)); { store it }ππ ReadMessage (MSGHdr, Count, Chunks);π INC (Count, Chunks);π END;ππ CLOSE (SwagF);π CLOSE (NdxF);ππ { update the CONTROL file array }π INC (ControlIdx);π ControlVal [ControlIdx] := IntStr (ConfNum, 3, TRUE);π INC (ControlIdx);π ControlVal [ControlIdx] := NameOnly (FN);π INC (ConfNum);ππEND;πππBEGINππ ClrScr;ππ IF ParamCount > 0 THEN SwagPath := FExpand(ParamStr(1));ππ EraseFile('SWAG.QWK'); { make sure we don't have one yet }ππ FindSwagPath (SwagPath);ππ FindPkZip;ππ CreateMessageDat;ππ IF SwagPath [LENGTH (SwagPath) ] <> '\' THEN SwagPath := SwagPath + '\';ππ FINDFIRST (SwagPath + '*.SWG', $21, TR);π WHILE DosError = 0 DOπ BEGINπ ProcessSwag (SwagPath + TR.Name);π FINDNEXT (TR);π END;ππ CLOSE (QwkF);ππ CreateControlDat;ππ SwapVectors;π Exec(PKZIP,' -ex SWAG.QWK *.NDX MESSAGES.DAT CONTROL.DAT');π SwapVectors;ππ CleanUp;ππEND.π 11 10-28-9311:39ALL GAYLE DAVIS Read SWG or QWK Files SWAG9311 24 n¼ {$V-,S-}π{ this SIMPLE little ditty let's you read SWAG or QWK files which haveπ EXACTLY the same format }πProgram ReadQWKORSWAGFile;ππUsesπ Crt;ππConstπ Seperator = '---------------------------------------------------------------------------';ππTypeππ CharArray = ARRAY[1..6] OF CHAR; { to read in chunks }ππ MSGDATHdr = Record { ALSO the format for SWAG files !!! }π Status : Char;π MSGNum : Array [1..7] of Char;π Date : Array [1..8] of Char;π Time : Array [1..5] of Char;π UpTO : Array [1..25] of Char;π UpFROM : Array [1..25] of Char;π Subject : Array [1..25] of Char;π PassWord : Array [1..12] of Char;π ReferNum : Array [1..8] of Char;π NumChunk : CharArray;π Alive : Byte;π LeastSig : Byte;π MostSig : Byte;π Reserved : Array [1..3] of Char;π end;ππVarπ F : File;π DefSaveFile : String;π Number : Word;ππFUNCTION ArrayTOInteger(B : CharArray; Len : BYTE) : LONGINT;ππVAR I : Byte;π S : STRING;π E : Integer;π T : Integer;ππBEGINπ S := '';π FOR I := 1 TO PRED(Len) DO IF B[i] <> #32 THEN S := S + B[i];π Val (S, T, E);π IF E = 0 THEN ArrayToInteger := T;πEND;ππProcedure ReadMSG (NumChunks : INTEGER);πVarπ Buff : Array [1..128] of Char;π J : INTEGER;π I : Byte;ππbeginπ For J := 1 to PRED(NumChunks) doπ beginπ BlockRead (F, Buff, 1);π For I := 1 to 128 doπ If Buff [I] = #$E3 thenπ Writelnπ elseπ Write (Buff [I]);π end;πend;ππProcedure ReadWriteHdr (Var HDR : MSGDatHdr);πbeginπ BlockRead (F, Hdr, 1);π With Hdr doπ beginπ Write ('Date: ', Date, ' (', Time, ')');π Writeln ('' : 23, 'Number: ', MSGNum);π Write ('From: ', UpFROM);π Writeln ('' : 14, 'Refer#: ', ReferNum);π Write (' To: ', UpTO);π Write ('' : 15, 'Recvd: ');π If Status in ['-', '`', '^', '#'] thenπ Writeln ('YES')π elseπ Writeln ('NO');π Write ('Subj: ', Subject);π Writeln ('' : 16, 'Conf: ', '(', LeastSig, ')');π Writeln(Seperator);π end;πend;ππProcedure ReadMessage (HDR : MSGDatHdr; RelNum : LONGINT; VAR Chunks : INTEGER);πbeginπ Seek(F,RelNum-1);π ReadWriteHdr (HDR);π Chunks := ArrayToInteger(HDR.NumChunk,6);π ReadMsg (Chunks);πend;ππVarπ MSGHdr : MSGDatHdr;π REPorDAT : Boolean;π ch : CHAR;π count : INTEGER;π chunks : INTEGER;ππbeginππ DefSaveFile := '';π DirectVideo := False;π Assign (F, '\SWAG\FILES\EGAVGA.SWG'); { whatever file .. }π { MESSAGES.DAT for .QWK}π Reset (F, SizeOf(MsgHdr));π Count := 2; { start at RECORD #2 }π WHILE (Count < FileSize(F)) DOπ BEGINπ ClrScr;π ReadMessage (MSGHdr, Count, Chunks);π Writeln;π WriteLn('..any key to continue .. (any FN Key quits)');π ch := Readkey; { any FN key quits }π IF Ch = #0 THEN HALT;π INC(Count,Chunks);π END;π Close (F);ππend.π 12 09-26-9310:51ALL KELLY SMALL QWK File format SWAG9311 10 n¼ *)πFrom: KELLY SMALL Refer#: NONEπSubj: QWK stuff Conf: (1221) F-PASCALπ*)ππType Array25 = Array[1..25] of Char;π HdrRec = Recordπ MessageStatus : Char;π MessageNumber : Array[1..7] of Char;π MessageDate : Array[1..8] of Char;π MessageTime : Array[1..5] of Char;π MessageTo : Array25;π MessageFrom : Array25;π MessageSubject: Array25;π MessagePS : Array[1..12] of Char;π MessageRefer : Array8;π TotalBlock : Array[1..6] of Char;π MessageKilled : Char;π Conference : Integer;π Dummy : Array[1..3] of Char;π End;ππVar Header : HdrRec;π F : File;ππbeginπ assign(f,'message.dat');π reset(f);π read(f,header);πend.ππBut this is only the begining, you will need to read in all theπmessage as 128 byte blocks and convert it for editing. It's anπarray of char, not strings, and it uses #227 for an End of Line,πrather then the conventional carriage return/line feed.ππ 13 11-02-9305:42ALL LUCAS NEALAN FIDO Nodelist SWAG9311 18 n¼ {πLUCAS NEALANππ> Does anyone have any code/specs (in Pascal preferred) of how toπ> manipulate the Version 6 FidoNet NodeList??π}ππTypeπ nodeflags = { NODELIST.DAT status flags }π (hub, { node is a net hub }π host, { node is a net host }π region, { node is region coord }π zone, { node is a zone coord }π cm, { runs continuous mail }π ores1, { reserved For Opus }π ores2, { reserved For Opus }π ores3, { reserved For Opus }π ores4, { reserved For Opus }π ores5, { reserved For Opus }π nores1, { reserved For non-Opus }π nores2, { reserved For non-Opus }π nores3, { reserved For non-Opus }π nores4, { reserved For non-Opus }π nores5, { reserved For non-Opus }π nores6 { reserved For non-Opus }π );ππ modemTypes = { NODELIST.DAT modem Type flags }π (hst, { node Uses a USRobotics HST modem }π pep, { node Uses a Telebit PEP modem }π v32, { node Uses a V.32 modem }π v32b, { node Uses a V.32bis modem }π h96 { node Uses a Hayes Express96 modem }π );ππ nodedatarec = Record { NODELIST.DAT : Version 6 nodelist data }π net : Integer; { net number }π node : Integer; { node number }π cost : Integer; { cost per minute to call }π name : Array [0..33] of Byte; { node name }π phone : Array [0..39] of Byte; { phone number }π city : Array [0..29] of Byte; { city and state }π passWord : Array [0..7] of Byte; { passWord }π Realcost : Integer; { phone company's Charge }π hubnode : Integer; { node # of this node's hub (0=none) }π rate : Byte; { actual bps rate divided by 300 }π modem : set of modemTypes; { modem Type codes }π flags : set of nodeflags; { set of flags }π res : Array [1..2] of Byte; { RESERVED }π end;ππ nodeindexrec = Record { NODELIST.IDX : Version 6 nodelist index }π node : Integer; { node number }π net : Integer; { net number }π end;ππ 14 11-02-9306:10ALL WILLIAM MCBRINE Reading QWK Files SWAG9311 37 n¼ {πWILLIAM MCBRINEππ>I have this File here that tells me the format of QWK stuff, and itπ>says that the messages.dat File is a whole bunch of Strings of length 128...ππThis is wrong. They aren't Pascal Strings, simply 128-Byte blocks.πWithin the Text blocks, each line is terminated by a "pi" Characterπ($E3).ππ>I dont know if I did this right but what I load the data into isπ>this: (I have an Array[1..100] of messagedata).ππ>Typeπ> MessageData = Recordπ> Rec : String[128];π> end;ππFor blocks of Text, you want someting like this:ππTypeπ messagedata = Array[0..127] of Char;ππFor the message HEADER, you need something more complex. Here's theπstructure I use in my QWK door:π}πqwkhead = Recordπ status : Char;π messnum : Array [1..7] of Char;π date : Recordπ month : Array [1..2] of Char;π dash1 : Char;π day : Array [1..2] of Char;π dash2 : Char;π year : Array [1..2] of Charπ end;π time : Recordπ hour : Array [1..2] of Char;π colon : Char;π minute : Array [1..2] of Charπ end;π toname : Array [1..25] of Char;π from : Array [1..25] of Char;π subject : Array [1..25] of Char;π passwd : Array [1..12] of Char;π refnum : Array [1..8] of Char;π length : Array [1..6] of Char;π killflag : Byte;π confnum : Word;π null : Word;π nettag : Charπend;ππ{πThis is also 128 Bytes.ππ>1). A tiny little thing that's not too important right now, but it'sπ>bugging me. When I try to load the first Record (which is supposedπ>to be a packet header) from messages.dat, I seem to be not reading inπ>the first Character... Like, Constantly it'll skip the firstπ>Character... it'll say "roduced by Qmail..." instead of "Produced byπ>Qmail..."ππThe first Character is going into the length field of the String.πPosition [0] of the String contains the length.ππYour "problem 2" is the same thing. 128 Bytes are actually read, butπonly the erroneous length's worth of them are printed out.ππHere's some pseudocode to read a whole packet:ππ Open MESSAGES.DATπ Skip the first Record (128 Bytes)π While not EOF doπ beginπ Read a message headerπ Get length of Text in blocks from qwkhead.length (in ASCII) -1π Reserve memory For 128 Bytes*number of blocksπ Read Text blocksπ Parse Textπ Release memoryπ endπ Close MESSAGES.DAT, cleanupππ"Parse Text" is the hard part. I wrote an Asm routine to convert theπpi-delimited Text into Strings, pointed to by an Array of Pointers.π(This is the format used by the Searchlight Programmer's Library messageπroutines; pretty easy to work With.) Pointer "a" points to this:ππ msgType = Recordπ msglen:Word;π msglin:Array[1..400] of Pointerπ end;ππThe "raw data" (Pointer b) below starts one Byte before the actual QWKπdata. The purpose of this is to hold the first String length afterπconversion. "d" should be the maximum number of lines to convert (400,πin this case); "e" should be about 79 (though it can be set all the wayπup to 255 if desired).π}ππProcedure mangle(a, b : Pointer; c, d, e : Word); Assembler;πAsmπ push dsπ mov ax,c {# of blocks loaded (maximum length)}π les di,b {raw data; lines terminated With pi Chars}π lds si,a {msgType: Word:linecount, 1..400:Pointers}π xor bx,bx {line count=0}π inc si {to first line Pointer}π inc siπ mov dx,diπ mov cl,7π shl ax,cl {blocks * 128 = maximum Bytes in message}π mov cx,axπ cldπ @mloop:π push axπ inc diπ mov al, $E3 {pi Character; QWK packet line delimiter}π repnz scasb {find one}π pop ax {ax = length before search, cx = length left}π jnz @notfound {if there aren't any, done With message}π sub ax, cxπ dec ax {length of line}π cmp ax, eπ jle @placelenπ mov ax, e {limit line length}π @placelen:π xchg di, dx {beginning of String in raw data}π seges mov [di], al {wow, now it's a Pascal String!}π mov [si], diπ mov [si+2], esπ add si, 4 {set the Pointer to it}π dec dx {DX was at $E3 + 1}π mov di, dxπ mov ax, cxπ inc bxπ cmp bx, d {maximum number of lines}π jle @mloop {start next line/String}π @notfound:π lds si, a {line counter in msgType}π mov [si], bx {store # of lines}π pop ds {magically a message!}πend;ππ{then, to print this Text, you'd do something like this: }ππProcedure dump;πVarπ i : Word;πbeginπ For i := 1 to a^.msglen doπ Writeln(a^.msglin[i]^);πend;π 15 01-27-9412:15ALL FRANK VAN DER HAM MkMsg Bugs SWAG9402 10 n¼ {πFor all who work with the MkMsg toolbox and it's JAM unit, I share myπexperience with the deleting of messages.ππDespite a bugfix on this very subject from 1.02 to 1.03, I still cannotπdelete messages properly. I found out that the basis of the problem is theπhandling of the IDX file. First of all, the number of bytes written to theπIDX file was invalid and, secondly, a real bug was in the handling of theπ"sub text" where an array is declared as "array [1..xx]" and used as "arrayπ[0..xx], causing a field in a record to be overriden to an invalid value.ππThese are the changes I made to my MKMSGJAM.PAS file.ππLine 150:πChangeπ TxtSubBuf: Array[1..TxtSubBufSize] of Char; {temp storage ... }πIntoπ TxtSubBuf: Array[0..TxtSubBufSize-1] of Char; {temp storage ... }ππLine 831:πChangeπ If JM^.TxtSubChars <= TxtSubBufSize ThenπIntoπ If JM^.TxtSubChars <= TxtSubBufSize-1 ThenππLine 838:πChangeπ If JM^.TxtSubChars <= TxtSubBufSize ThenπIntoπ If JM^.TxtSubChars <= TxtSubBufSize-1 ThenππLine 1490:πChangeπ BlockWrite(JM^.IdxFile, JamIdx^, JamIdxBufSize);πIntoπ BlockWrite(JM^.IdxFile, JamIdx^, JM^.IdxRead);ππKeep on jammin' !ππ 16 02-03-9409:57ALL DAVID DANIEL ANDERSON QWK packets to text SWAG9402 49 n¼ {DAT2TXT v0.90- Free DOS utility: Converts .QWK MESSAGES.DAT to text.}π{$V-,S-}πprogram DAT2TXT ;πuses dos ;πconstπ Seperator = '---------------------------------------------------------------------------' ;π herald = '===========================================================================' ;πtypeπ CharArray = array[1..6] of char ; { to read in chunks }ππ MSGDATHdr = record { ALSO the format for SWAG files !!! }π Status : char ;π MSGNum : array [1..7] of char ;π Date : array [1..8] of char ;π Time : array [1..5] of char ;π UpTO : array [1..25] of char ;π UpFROM : array [1..25] of char ;π Subject : array [1..25] of char ;π PassWord : array [1..12] of char ;π ReferNum : array [1..8] of char ;π NumChunk : CharArray ;π Alive : byte ;π LeastSig : byte ;π MostSig : byte ;π Reserved : array [1..3] of char ;π end ;ππvarπ F : file ;π txtfile : text ;ππprocedure showhelp(problem:byte); {if any *foreseen* errors arise, we are sent}π { here to give a little help and exit peacefully }πconstπ progdata = 'DAT2TXT v0.90- Free DOS utility: Converts .QWK MESSAGES.DAT to text.';π progdat2 = '(By SWAG contributors.)';π usage = 'Usage: DAT2TXT infile(s) [/o]';π usag2 = 'The "/o" causes DAT2TXT to overwrite (not append to) existing messages.txt.';π note = 'DOS * and ? wildcards ok with "infile(s)". Output is always to MESSAGES.TXT.';πvarπ message : string[80];πbeginπ writeln(progdata); { just tell user what this program }π writeln(progdat2); { is and who wrote it }π writeln;π writeln(usage);π writeln(usag2);π writeln(note);π writeln;π writeln('Error encountered:');π case problem ofπ 1 : message := 'Incorrect number of parameters.';π { plenty of room for other errors! }π elseπ message := 'Unknown error.';π end;π writeln(message);π halt(problem);πend;ππfunction converttoupper(w : string) : string;πvarπ cp : integer; {the position of the character to change.}πbeginπ for cp := 1 to length(w) doπ w[cp] := upcase(w[cp]);π converttoupper := w;πend;ππfunction ArrayTOInteger ( B : CharArray ; Len : byte ) : longint ;ππvar I : byte ;π S : string ;π E : integer ;π T : integer ;ππbeginπ S := '' ;π for I := 1 to Len doπ if B[i] <> #32 then S := S + B[i] ;ππ Val ( S, T, E );ππ if E = 0 thenπ ArrayToInteger := Tπ elseπ ArrayToInteger := 0 ;πend ;ππprocedure ReadWriteHdr ( var HDR : MSGDatHdr );πbeginπ BlockRead ( F, Hdr, 1 );π if ArrayToInteger ( Hdr.NumChunk, 6 ) <> 0 thenπ with Hdr do beginπ writeln ( txtfile, herald );π write ( txtfile, 'Date: ', Date, ' (', Time, ')' );π writeln ( txtfile, '' : 23, 'Number: ', MSGNum );π write ( txtfile, 'From: ', UpFROM );π writeln ( txtfile, '' : 14, 'Refer#: ', ReferNum );π write ( txtfile, ' To: ', UpTO );π write ( txtfile, '' : 15, 'Recvd: ' );π if Status in ['-', '`', '^', '#'] thenπ writeln ( txtfile, 'YES' )π elseπ writeln ( txtfile, 'NO' );π write ( txtfile, 'Subj: ', Subject );π writeln ( txtfile, '' : 16, 'Conf: ', '(', (MostSig * 256) + LeastSig, ')' );π writeln ( txtfile, Seperator );π end ;πend ;ππprocedure ReadMSG ( NumChunks : integer );πvarπ Buff : array [1..128] of char ;π J : integer ;π I : byte ;ππbeginπ for J := 1 to PRED ( NumChunks ) do beginπ BlockRead ( F, Buff, 1 );π for I := 1 to 128 doπ if Buff [I] = #$E3 thenπ writeln ( txtfile )π elseπ write ( txtfile, Buff [I] );π end ;πend ;ππprocedure ReadMessage ( HDR : MSGDatHdr ; RelNum : longint ; var Chunks : integer );πbeginπ Seek ( F, RelNum - 1 );π ReadWriteHdr ( HDR );π Chunks := ArrayToInteger ( HDR.NumChunk, 6 );π if Chunks <> 0 then beginπ ReadMsg ( Chunks );π writeln ( txtfile );π endπ elseπ Chunks := 1 ;πend ;ππvarπ MSGHdr : MSGDatHdr ;π repordat : boolean ;π ch : char ;π count : integer ;π chunks : integer ;π defsavefile : string ;π fileinfo : searchrec ;π fdt : longint ;π ps1,ps2 : string [2] ;π fileexists,π overwrite : boolean ;π response : char ;ππ dpath, tpath : pathstr ;π {epath & dpath are fully qualified pathnames of .dat & .txt files}ππ ddir, tdir : dirstr ;π dname, tname : namestr ;π d_ext, t_ext : extstr ;π txtfileinfo : searchrec ;ππbeginπ if ( paramcount < 1) or ( paramcount > 2) then showhelp(1);π ps1 := converttoupper ( paramstr (1));π if (ps1 = '/H') or (ps1 = '/?') orπ (ps1 = '-H') or (ps1 = '-?') then showhelp(0);ππ DefSaveFile := '' ;π ps2 := '/A' ;π if paramcount > 1 then ps2 := paramstr ( 2 );π overwrite := (upcase ( ps2[2] ) = 'O');π dpath := fexpand ( paramstr ( 1 ) );π fsplit ( dpath, ddir, dname, d_ext );π { break up path into components }π findfirst ( dpath, anyfile, fileinfo );π while doserror = 0 do beginπ fsplit ( fexpand ( fileinfo.name ), tdir, tname, t_ext );π dpath := ddir + fileinfo.name ;π tpath := ddir + tname + '.TXT' ;π Assign ( F, dpath );π { whatever file .. ( MESSAGES.DAT for .QWK ) }π Reset ( F, SizeOf ( MsgHdr ) );ππ assign ( txtfile, tpath );π{$i-} reset ( txtfile ); {$i+}π fileexists := (ioresult = 0);ππ if fileexists then close ( txtfile );π if fileexists and ( not overwrite ) thenπ append ( txtfile )π elseπ rewrite ( txtfile );ππ write ( 'DAT2TXT: ', dpath, ' to: ', tpath );π Count := 2 ; { start at RECORD #2 }π while Count < FileSize ( F ) do beginπ ReadMessage ( MSGHdr, Count, Chunks );π INC ( Count, Chunks );π end ;ππ getftime ( F, fdt );π close ( F ); close ( txtfile ); reset ( txtfile );π setftime ( txtfile , fdt );π close ( txtfile );ππ writeln ( ', done!' );π findnext ( fileinfo );π end ;πend.π 17 02-09-9411:50ALL LUCAS NEALAN FIDONET Traffic SWAG9402 75 n¼ {π The following source uses the FIDONET unit which will follow in the nextπmessage.. It is a modified version of the origionsl FIDOPAS archive: }ππUnit FidoNet;ππINTERFACEππUses Dos,π Crt,π StrnTTT5,π MiscTTT5;ππTypeπ NetMsg = recordπ From, { Name of sender }π Too : String[35]; { Name of receiver }π Subject : String[71]; { Msg subject }π DateTime : String[19]; { Msg date/time, see below }π Times , { Times message has been read }π DestNode, { Destination node number }π OrgNode, { Originating node number }π Cost, { Cost - 0 if not supported }π OrgNet, { Originating net number }π DestNet : word; { Destination net number }π DateWritten, { Date/time written }π SentReceived : longint; { Date/time sent/rcvd }π ReplyTO, { # of next message in replys }π Attr, { Message status bits }π NextReply : word; { Number of previous message }π AreaName : String[20]; {AreaName (Only if Echomail) }π end;ππConstπ _private = $0001;π _crash = $0002;π _received = $0004;π _sent = $0008;π _fileattach = $0010;π _transit = $0020;π _orphan = $0040;π _killsent = $0080;π _local = $0100; { required on all locally entered messages! }π _hold = $0200;π _direct = $0400;π _filereq = $0800;π _updatereq = $8000;ππ Status : Array[1..12] Of String[3] = ('Jan','Feb','Mar','Apr',π 'May','Jun','Jul','Aug',π 'Sep','Oct','Nov','Dec');πVar Net : NetMsg;ππFunction NetMessage : String;πFunction GetPath(Var FName : String) : Boolean;πFunction GetNet(GN : String) : String;πFunction GetNode(GN : String) : String;πFunction MsgDateStamp : String;πFunction LastMsgNum( _NetPath : String ) : Integer;πFunction Hex (n : word) : String;πProcedure ExpandNodeNumbers(Var List : String; VAR TotalNumber : Integer );πProcedure Conv_NetNode(NetNode : String; VAR Net, Node : Word);ππIMPLEMENTATIONππFunction NetMessage : String; { Returns a NetMessage header string }πVar Hdr : String;πBeginπ Hdr := '';ππ Hdr := PadLeft(Net.From,36,#0);π Hdr := Hdr + PadLeft(Net.Too,36,#0)π + PadLeft(Net.Subject,72,#0)π + PadRight(Net.DateTime,19,' ')+#0π + Chr(Lo(Net.Times))+Chr(Hi(Net.Times))π + Chr(Lo(Net.DestNode))+Chr(Hi(Net.DestNode))π + Chr(Lo(Net.OrgNode))+Chr(Hi(Net.OrgNode))π + Chr(Lo(Net.Cost))+Chr(Hi(Net.Cost))π + Chr(Lo(Net.OrgNet))+Chr(Hi(Net.OrgNet))π + Chr(Lo(Net.DestNet))+Chr(Hi(Net.DestNet))π + #0#0#0#0#0#0#0#0π + Chr(Lo(Net.ReplyTo))+Chr(Hi(Net.ReplyTo))π + Chr(Lo(Net.Attr))+Chr(Hi(Net.Attr))π + Chr(Lo(Net.NextReply))+Chr(Hi(Net.NextReply))π + Upper(Net.AreaName);π NetMessage := Hdr;πEnd;ππFunction GetPath(Var FName : String) : Boolean;π{ Returns the FULL Path and filename for a file if it is found in the path. }πVar Str1,Str2 : String;π NR : Byte;π HomeDir : String;ππBeginπ HomeDir := FExpand(FName);π If Exist(HomeDir) Then Beginπ FName := HomeDir;π GetPath := True;π Exit;π End;ππ Str1 := GetEnv('PATH');π For NR := 1 to Length(Str1) DO IF Str1[NR] = ';' Then Str1[NR] := ' ';π For NR := 1 to WordCnt(Str1) DOπ Beginπ Str2 := ExtractWords(NR,1,Str1)+'\'+FName;π IF Exist(Str2) Then Beginπ FName := Str2;π GetPath := True;π Exit;π End;π End;π GetPath := False;πEnd;ππFunction MsgDateStamp : String; { Creates Fido standard- 01 Jan 89 21:05:18 }πVar h,m,s,hs : Word; { header time/date stamp }π y,mo,d,dow : Word;π Tmp,π o1,o2,o3 : String;ππBeginπ o1 := '';π o2 := '';π o3 := '';π tmp := '';π GetDate(y,mo,d,dow);π GetTime(h,m,s,hs);π o1 := PadRight(Int_To_Str(d),2,'0');π o2 := Status[mo];π o3 := Last(2,Int_To_Str(y));π Tmp := Concat( o1,' ',o2,' ',o3,' ');π o1 := PadRight(Int_To_Str(h),2,'0');π o2 := PadRight(Int_To_Str(m),2,'0');π o3 := PadRight(Int_To_Str(s),2,'0');π Tmp := Tmp + Concat(o1,':',o2,':',o3);π MsgDateStamp := Tmp;πEnd;ππFunction MsgToNum(Fnm : String ):Integer; { Used Internally by LastMsgNum }πVar p : Byte;πBeginπ p := Pos('.',Fnm);π Fnm := First(p-1,Fnm);π MsgToNum := Str_To_Int(Fnm);πEnd;ππFunction LastMsgNum( _NetPath : String ) : Integer;π{ Returns the highest numbered xxx.MSG in NetPath directory }πVarπ _Path : String;π Temp1,π Temp2 : String;π Len : Byte;π DxirInf : SearchRec;π Num,π Num1 : Integer;ππBeginπ Num := 0;π Num1 := 0;π Temp1 := '';π Temp2 := '';π _Path := '';π _Path := _NetPath + '\*.MSG';ππ FindFirst( _Path, Archive, DxirInf );π While DosError = 0 DOπ Beginπ Temp1 := DxirInf.Name;π Num1 := MsgToNum(Temp1);π IF Num1 > Num Then Num := Num1;π FindNext(DxirInf);π End;ππ IF Num = 0 Then Num := 1;π LastMsgNum := Num;πEnd;ππFunction Hex(N : Word) : String;π{ Converts an integer or word to it's Hex equivelent }πVarπ L : string[16];π BHi,π BLo : byte;ππBeginπ L := '0123456789abcdef';π BHi := Hi(n);π BLo := Lo(n);π Hex := copy(L,succ(BHi shr 4),1) +π copy(L,succ(BHi and 15),1) +π copy(L,succ(BLo shr 4),1) +π copy(L,succ(BLo and 15),1);πEnd;ππFunction GetNet( GN : String ) : String;π{ Returns the NET portion of a Net/Node string }πVar P : Byte;πBeginπ P := Pos('/',GN);π GetNet := First(P-1,GN);πEnd;ππFunction GetNode( GN : String ) : String;π{ Returns the NODE portion of a Net/Node string }πVar P : Byte;πBeginπ P := Pos('/',GN);π GetNode := Last(Length(GN)-P,GN);πEnd;ππProcedure ExpandNodeNumbers(Var List : String; VAR TotalNumber : Integer );π { Expands a list of short form node numbers to thier proper }π { Net/Node representations. Example: }π { The string: 170/100 101 102 5 114/12 15 17 166/225 226 }π { Would return: 170/100 170/101 170/102 170/5 114/12 114/15 etc.. }πVar Net,NetNode : String[10];π HoldStr,π WS1 : String;π N1 : Integer;πBeginπ Net := '';π NetNode := '';π HoldStr := '';π WS1 := '';π N1 := 0;π TotalNumber := 0;π TotalNumber := WordCnt(List);ππ For N1 := 1 to TotalNumber DO Beginπ WS1 := ExtractWords(N1,1,List);π IF Pos('/',WS1) <> 0 Then Begin Net := GetNet(WS1)+'/'; NetNode := WS1;π End ELSE NetNode := Net+WS1;π HoldStr := HoldStr + ' ' + Strip('A',' ',NetNode);π End;πEnd;ππProcedure Conv_NetNode(NetNode : String; VAR Net, Node : Word);π { Returns NET and NODE as words from a Net/Node string }πVar WStr : String[6];πBeginπ Wstr := GetNet(NetNode);π Net := Str_To_Int(Wstr);π Wstr := GetNode(NetNode);π Node := Str_To_Int(Wstr);πEnd;ππBeginπ { Initialize the data structures }π FillChar(Net,SizeOf(Net),#0);πEnd.ππ{ --------------------- DEMO PROGRAM -------------------------- }πProgram Test;ππUsesπ Crt,π FidoNet,π StrnTTT5; {TechnoJocks Turbo Toolkit StrnTTT5 unit}ππvarπ NetPath : String;ππProcedure Create_NetMessage(FileName : String );πVar LastOne,i : Integer;π Msg_Name : String;π Attrib : Word;π MsgFil,π Inputfile : Text;π Header,π S : String;ππBeginπ Header := '';π S := '';π LastOne := 0;π Msg_Name := '';π Attrib := _Local + _Private;π With Net DO Beginπ From := 'Lucas Nealan';π Too := 'Anyone';π Subject := 'Testing the FidoNet Unit...';π DateTime := MsgDateStamp;π Times := 0;π DestNode := 0;π OrgNode := 100;π Cost := 0;π OrgNet := 31;π DestNet := 22;π ReplyTo := 0;π Attr := Attrib;π NextReply := 0;π End;π Header := NetMessage;π LastOne := LastMsgNum(NetPath);π Inc(LastOne);π Msg_Name := NetPath+'\'+Int_To_Str(LastOne)+'.MSG';π Assign(MsgFil, Msg_Name );π Rewrite(MsgFil);π WriteLn(MsgFil,Header);π Assign(InputFile, FileName);π Reset(InputFile);π WriteLn(MsgFil,#1'INTL 20:22/0 20:31/100');π WriteLn(MsgFil,#1'PID Lucas'' *.MSG Util');π WriteLn(MsgFil,^A'FLAGS DIR');π While not Eof(InputFile) do beginπ ReadLn(InputFile,S);π WriteLn(MsgFil,S);π end;π Flush(MsgFil);π Close(MsgFil);π Close(InputFile);πend;ππbeginπ ClrScr;π WriteLn;π WriteLn('Posting file: '+ParamStr(1));π WriteLn;π NetPath := 'D:\FD\NETMAIL';π Create_NetMessage(ParamStr(1));πend.ππ The INTL Kludge line is used to send messages through non standard networkπzones (20 in this example). For fido standard zone 1 you may specify just theπorigin net and node as well as destination net and zone and it will default toπzone 1. Also with the direct flag you may either use the _Direct in the statusπor add your own FLAGS DIR kludge.ππ Good luck!ππ Lucas Nealanπ Real World Programmingππ