diff --git a/mystic/109to110.pas b/mystic/109to110.pas
new file mode 100644
index 0000000..3e1a9c0
--- /dev/null
+++ b/mystic/109to110.pas
@@ -0,0 +1,976 @@
+Program UP110;
+
+// set lang preferences to defaults
+
+{$I M_OPS.PAS}
+
+Uses
+ CRT,
+ m_Strings;
+
+{$I RECORDS.PAS}
+
+Type
+ ExtAddrType = Record
+ Zone,
+ Net,
+ Node,
+ Point : Word;
+ Desc : String[15];
+ End;
+
+ OldConfigRec = Record { MYSTIC.DAT in root BBS directory }
+ Version : String[8];
+ SysPath, { System path (root BBS directory) }
+ AttachPath, { File attach directory }
+ DataPath, { Data file directory }
+ MsgsPath, { Default JAM directory }
+ ArcsPath, { Archive software directory }
+ QwkPath, { Local QWK directory }
+ ScriptPath, { Script file directory }
+ LogsPath : String[40]; { Log file directory }
+ BBSName, { BBS Name }
+ SysopName : String[30]; { Sysop Name }
+ SysopPW : String[15]; { Sysop Password }
+ SystemPW : String[15]; { System Password }
+ MaxNode : Byte; { Max # of nodes the BBS has }
+ DefStartMenu : String[8]; { Default start menu }
+ DefFallMenu : String[8]; { Default fallback menu }
+ DefThemeFile : String[8]; { Default language file }
+ DefTermMode : Byte; { 0 = Ask }
+ { 1 = Detect }
+ { 2 = Detect, ask if none }
+ { 3 = Force ANSI }
+ ScreenBlank : Byte; { Mins before WFC screen saver starts}
+ ChatStart : SmallInt; { Chat hour start, }
+ ChatEnd : SmallInt; { Chat hour end: mins since midnight }
+ ChatFeedback : Boolean; { E-mail sysop if page isn't answered}
+ AcsSysop : String[20]; { BBS List Editor ACS }
+ AllowNewUsers : Boolean; { Allow new users? }
+ NewUserPW : String[15]; { New user password }
+ NewUserSec : SmallInt; { New user security level }
+ AskRealName, { Ask new users for real name? }
+ AskAlias, { Ask new users for an alias? }
+ AskStreet, { Ask new user for street address? }
+ AskCityState, { Ask new users for city/state? }
+ AskZipCode, { Ask new users for ZIP code }
+ AskHomePhone, { Ask new users for home phone #? }
+ AskDataPhone, { Ask new users for data phone #? }
+ AskBirthdate, { Ask new users for date of birth? }
+ AskGender, { Ask new users for their gender? }
+ AskTheme, { Ask new users to select a language?}
+ AskEmail,
+ AskUserNote,
+ AskOption1,
+ AskOption2,
+ AskOption3,
+ UseUSAPhone : Boolean; { Use XXX-XXX-XXXX format phone #s? }
+ UserEditorType : Byte; { 0 = Line Editor }
+ { 1 = Full Editor }
+ { 2 = Ask }
+ UserDateType : Byte; { 1 = MM/DD/YY }
+ { 2 = DD/MM/YY }
+ { 3 = YY/DD/MM }
+ { 4 = Ask }
+ UseMatrix : Boolean; { Use MATRIX-style login? }
+ MatrixMenu : String[8]; { Matrix Menu Name }
+ MatrixPW : String[15]; { Matrix Password }
+ MatrixAcs : String[20]; { ACS required to see Matrix PW }
+ NewUserEmail : Boolean; { Force new user feedback }
+ UserMailIndex : Byte; { use lightbar email msg index? }
+ UserQuoteWin : Byte; { 0 = no, 1 = ues, 2 = ask }
+ UserReadIndex : Byte; { 0 = no, 1 = yes, 2 = ask }
+ Option1 : String[10];
+ Option2 : String[10];
+ Option3 : String[10];
+ FCompress : Boolean; { Compress file area numbers? }
+ ImportDIZ : Boolean; { Search for FILE_ID.DIZ? }
+ AcsValidate : String[20]; { ACS to auto-validate uploads }
+ AcsSeeUnvalid : String[20]; { ACS to see unvalidated files }
+ AcsDLUnvalid : String[20]; { ACS to download unvalidated files }
+ AcsSeeFailed : String[20]; { ACS to see failed files }
+ AcsDLFailed : String[20]; { ACS to download failed files }
+ TestUploads : Boolean; { Test uploaded files? }
+ TestPassLevel : Byte; { Pass errorlevel }
+ TestCmdLine : String[60]; { Upload processor command line }
+ MaxFileDesc : Byte; { Max # of File Description Lines }
+ FreeUL : LongInt; { Max space required for uploads }
+ FreeCDROM : LongInt; { Free space required for CD Copy }
+ MCompress : Boolean; { Compress message area numbers? }
+ qwkBBSID : String[8]; { QWK packet display name }
+ qwkWelcome : String[8]; { QWK welcome display file }
+ qwkNews : String[8]; { QWK news display file }
+ qwkGoodbye : String[8]; { QWK goodbye display file }
+ qwkArchive : String[3]; { Default QWK archive }
+ qwkMaxBase : SmallInt; { Max # of messages per base (QWK) }
+ qwkMaxPacket : SmallInt; { Max # of messages per packet }
+ NetAddress : Array[1..20] of ExtAddrType; { Network Addresses }
+ Origin : String[50]; { Default origin line }
+ ColorQuote : Byte; { Default quote color }
+ ColorText : Byte; { Default text color }
+ ColorTear : Byte; { Default tear line color }
+ ColorOrigin : Byte; { Default origin line color }
+ SystemCalls : LongInt; { Total calls to the BBS }
+ AcsInvLogin : String[20]; { Invisible login ACS }
+ ChatLogging : Boolean; { Record SysOp chat to CHAT.LOG? }
+ StatusType : Byte; { 0 = 2 line, 1 = 1 line }
+ UserFileList : Byte; { 0 = Normal, 1 = Lightbar, 2 = Ask }
+ FShowHeader : Boolean; { Redisplay file header after pause }
+ SysopMacro : Array[1..4] of String[80]; { Sysop Macros }
+ UploadBase : SmallInt; { Default upload file base }
+ MaxAutoSig : Byte; { Max Auto-Sig lines }
+ FColumns : Byte; { File area list columns }
+ MColumns : Byte; { Message area list columns }
+ netCrash : Boolean; { NetMail CRASH flag? }
+ netHold : Boolean; { NetMail HOLD flag? }
+ netKillSent : Boolean; { NetMail KILLSENT flag? }
+ UserNameFormat : Byte; { user input format }
+ MShowHeader : Boolean; { redisplay message header }
+ DefScreenSize : Byte; { default screen length }
+ DupeScan : Byte; { dupescan: 0=no,1=yes,2=yes global }
+ Inactivity : Word; { Seconds before inactivity timeout }
+ UserReadType : Byte; { 0 = normal, 1 = ansi, 2 = ask }
+ UserHotKeys : Byte; { 0 = no, 1 = yes, 2 = ask }
+ UserIdxPos : LongInt; { permanent user # position }
+ AcsSeeInvis : String[20]; { ACS to see invisible users }
+ FeedbackTo : String[30]; { Feedback to user }
+ AllowMulti : Boolean; { Allow multiple node logins? }
+ StartMGroup : Word; { new user msg group start }
+ StartFGroup : Word; { new user file group start }
+ MShowBases : Boolean;
+ FShowBases : Boolean;
+ UserFullChat : Byte; { 0 = no, 1 = yes, 2 = ask }
+ AskScreenSize : Boolean;
+ inetDomain : String[25];
+ inetSMTPUse : Boolean;
+ inetSMTPPort : Word;
+ inetSMTPMax : Word;
+ inetPOP3Use : Boolean;
+ inetPOP3Port : Word;
+ inetPOP3Max : Word;
+ inetTNUse : Boolean;
+ inetTNPort : Word;
+ inetTNDupes : Byte;
+ inetIPBlocking : Boolean;
+ inetIPLogging : Boolean;
+ inetFTPUse : Boolean;
+ inetFTPPort : Word;
+ inetFTPMax : Word;
+ inetFTPDupes : Byte;
+ inetFTPPortMin : Word;
+ inetFTPPortMax : Word;
+ inetFTPAnon : Boolean;
+ inetFTPTimeout : Word;
+ Reserved : Array[1..192] of Byte;
+ End;
+
+ OldUserRec = Record { USERS.DAT }
+ Flags : Byte; { User Flags }
+ Handle : String[30]; { Handle }
+ RealName : String[30]; { Real Name }
+ Password : String[15]; { Password }
+ Address : String[30]; { Address }
+ City : String[25]; { City }
+ ZipCode : String[9]; { Zipcode }
+ HomePhone : String[15]; { Home Phone }
+ DataPhone : String[15]; { Data Phone }
+ Birthday : LongInt;
+ Gender : Char; { M> Male F> Female }
+ EmailAddr : String[35]; { email address }
+ Option1 : String[35]; { optional question #1 }
+ Option2 : String[35]; { optional question #2 }
+ Option3 : String[35]; { optional question #3 }
+ UserInfo : String[30]; { user comment field }
+ AF1 : AccessFlagType;
+ AF2 : AccessFlagType; { access flags set #2 }
+ Security : SmallInt; { Security Level }
+ StartMenu : String[8]; { Start menu for user }
+ FirstOn : LongInt; { Date/Time of First Call }
+ LastOn : LongInt; { Date/Time of Last Call }
+ Calls : LongInt; { Number of calls to BBS }
+ CallsToday: SmallInt; { Number of calls today }
+ DLs : SmallInt; { # of downloads }
+ DLsToday : SmallInt; { # of downloads today }
+ DLk : LongInt; { # of downloads in K }
+ DLkToday : LongInt; { # of downloaded K today }
+ ULs : LongInt; { total number of uploads }
+ ULk : LongInt; { total number of uploaded K }
+ Posts : LongInt; { total number of msg posts }
+ Emails : LongInt; { total number of sent email }
+ TimeLeft : LongInt; { time left online for today }
+ TimeBank : SmallInt; { number of mins in timebank }
+ Archive : String[3]; { default archive extension }
+ QwkFiles : Boolean; { Include new files in QWK? }
+ DateType : Byte; { Date format (see above) }
+ ScrnPause : Byte; { user's screen length }
+ Language : String[8]; { user's language file }
+ LastFBase : Word; { Last file base }
+ LastMBase : Word; { Last message base }
+ LastMGroup: Word; { Last group accessed }
+ LastFGroup: Word; { Last file group accessed }
+ Vote : Array[1..mysMaxVoteQuestion] of Byte; { Voting booth data }
+ EditType : Byte; { 0 = Line, 1 = Full, 2 = Ask }
+ FileList : Byte; { 0 = Normal, 1 = Lightbar }
+ SigUse : Boolean; { Use auto-signature? }
+ SigOffset : LongInt; { offset to sig in AUTOSIG.DAT }
+ SigLength : Byte; { number of lines in sig }
+ HotKeys : Boolean; { does user have hotkeys on? }
+ MReadType : Byte; { 0 = line 1 = full 2 = ask }
+ PermIdx : LongInt; { permanent user number }
+ UseLBIndex: Boolean; { use lightbar index? }
+ UseLBQuote: Boolean; { use lightbar quote mode }
+ UseLBMIdx : Boolean; { use lightbar index in email? }
+ UserFullChat : Boolean; { use full screen teleconference }
+ Reserved : Array[1..98] of Byte;
+ End;
+
+ OldGroupRec = Record { GROUP_*.DAT }
+ Name : String[30]; { Group name }
+ ACS : String[20]; { ACS required to access group }
+ End;
+
+ OldArcRec = Record { ARCHIVE.DAT }
+ Name : String[20]; { Archive description }
+ Ext : String[3]; { Archive extension }
+ Pack : String[60]; { Pack command line }
+ Unpack : String[60]; { Unpack command line }
+ View : String[60]; { View command line }
+ End;
+
+ OldSecurityRec = Record { SECURITY.DAT }
+ Desc : String[30]; { Description of security level }
+ Time : SmallInt; { Time online (mins) per day }
+ MaxCalls : SmallInt; { Max calls per day }
+ MaxDLs : SmallInt; { Max downloads per day }
+ MaxDLk : SmallInt; { Max download kilobytes per day }
+ MaxTB : SmallInt; { Max mins allowed in time bank }
+ DLRatio : Byte; { Download ratio (# of DLs per UL) }
+ DLKRatio : SmallInt; { DL K ratio (# of DLed K per UL K }
+ AF1 : AccessFlagType; { Access flags for this level A-Z }
+ AF2 : AccessFlagType; { Access flags #2 for this level }
+ Hard : Boolean; { Do a hard AF upgrade? }
+ StartMNU : String[8]; { Start Menu for this level }
+ PCRatio : SmallInt; { Post / Call ratio per 100 calls }
+ Res1 : Byte; { reserved for future use }
+ Res2 : LongInt; { reserved for future use }
+ End;
+
+Var
+ Config : RecConfig;
+
+Function DeleteFile (FN : String) : Boolean;
+Var
+ F : File;
+Begin
+ Assign (F, FN);
+{ SetFAttr (F, Archive);}
+ {$I-} Erase (F); {$I+}
+ DeleteFile := (IoResult = 0);
+End;
+
+Function RenameFile (Old, New: String) : Boolean;
+Var
+ OldF : File;
+Begin
+ DeleteFile(New);
+ Assign (OldF, Old);
+ {$I-} ReName (OldF, New); {$I+}
+
+ Result := (IoResult = 0);
+End;
+
+Procedure WarningDisplay;
+Var
+ Ch : Char;
+Begin
+ TextAttr := 15;
+ ClrScr;
+ WriteLn ('MYSTIC BBS VERSION 1.10 UPGRADE UTILITY');
+ TextAttr := 8;
+ WriteLn ('---------------------------------------');
+ WriteLn;
+ TextAttr := 7;
+ WriteLn ('You must be using a current installation of Mystic BBS 1.09 in');
+ WriteLn ('order for this upgrade to work. If you are not using 1.09, then');
+ WriteLn ('you must upgrade to that version before proceeding with this upgrade');
+ WriteLn;
+ WriteLn ('You will need to have access rights to all of your BBS directory');
+ WriteLn ('structure, otherwise, you may experience crashes during the');
+ WriteLn ('upgrade process.');
+ WriteLn;
+ WriteLn ('Make sure you read the UPGRADE.TXT and follow all steps completely!');
+ WriteLn;
+ TextAttr := 12;
+ WriteLn (^G^G'*WARNING* MAKE A BACKUP OF YOUR BBS BEFORE ATTEMPTING TO UPGRADE!');
+ TextAttr := 7;
+ WriteLn;
+ Repeat
+ Write ('Are you ready to upgrade now (Y/N): ');
+ Ch := UpCase(ReadKey);
+ WriteLn (Ch);
+ Until Ch in ['Y', 'N'];
+ If Ch = 'N' Then Halt;
+ WriteLn;
+End;
+
+Procedure ConvertConfig;
+Var
+ A : LongInt;
+ OldConfigFile : File of OldConfigRec;
+ OldConfig : OldConfigRec;
+ ConfigFile : File of RecConfig;
+Begin
+ Assign (OldConfigFile, 'mystic.dat');
+ {$I-} Reset (OldConfigFile); {$I+}
+ If IoResult <> 0 Then Begin
+ WriteLn ('ERROR: Run this program from the root Mystic BBS directory.');
+ Halt(1);
+ End;
+
+ WriteLn ('[-] Updating system configuration...');
+
+ Read (OldConfigFile, OldConfig);
+ Close (OldConfigFile);
+
+ With OldConfig Do Begin
+ Config.DataChanged := mysDataChanged;
+ Config.SystemCalls := SystemCalls;
+ Config.UserIdxPos := UserIdxPos;
+ Config.SystemPath := SysPath;
+ Config.DataPath := DataPath;
+ Config.LogsPath := LogsPath;
+ Config.MsgsPath := MsgsPath;
+ Config.AttachPath := AttachPath;
+ Config.ScriptPath := ScriptPath;
+ Config.QwkPath := QwkPath;
+ Config.SemaPath := SysPath;
+ Config.BBSName := BBSName;
+ Config.SysopName := SysopName;
+ Config.SysopPW := SysopPW;
+ Config.SystemPW := SystemPW;
+ Config.FeedbackTo := FeedbackTo;
+ Config.Inactivity := Inactivity;
+ Config.DefStartMenu := DefStartMenu;
+ Config.DefFallMenu := DefFallMenu;
+ Config.DefThemeFile := DefThemeFile;
+ Config.DefTermMode := DefTermMode;
+ Config.DefScreenSize := DefScreenSize;
+ Config.UseMatrix := UseMatrix;
+ Config.MatrixMenu := MatrixMenu;
+ Config.MatrixPW := MatrixPW;
+ Config.MatrixAcs := MatrixAcs;
+ Config.AcsSysop := AcsSysop;
+ Config.AcsInvisLogin := AcsInvLogin;
+ Config.AcsSeeInvis := AcsSeeInvis;
+
+ For A := 1 to 4 Do Config.SysopMacro[A] := SysopMacro[A];
+
+ Config.ChatStart := ChatStart;
+ Config.ChatEnd := ChatEnd;
+ Config.ChatFeedback := ChatFeedback;
+ Config.ChatLogging := ChatLogging;
+ Config.AllowNewUsers := AllowNewUsers;
+ Config.NewUserSec := NewUserSec;
+ Config.NewUserPW := NewUserPW;
+ Config.NewUserEMail := NewUserEmail;
+ Config.StartMGroup := StartMGroup;
+ Config.StartFGroup := StartFGroup;
+ Config.UseUSAPhone := UseUSAPhone;
+ Config.UserNameFormat := UserNameFormat;
+ Config.UserDateType := UserDateType;
+ Config.UserEditorType := UserEditorType;
+ Config.UserHotKeys := UserHotkeys;
+ Config.UserFullChat := UserFullChat;
+ Config.UserFileList := UserFileList;
+ Config.UserReadType := UserReadType;
+ Config.UserMailIndex := UserMailIndex;
+ Config.UserReadIndex := UserReadIndex;
+ Config.UserQuoteWin := UserQuoteWin;
+ Config.AskTheme := AskTheme;
+ Config.AskRealName := AskRealName;
+ Config.AskAlias := AskAlias;
+ Config.AskStreet := AskStreet;
+ Config.AskCityState := AskCityState;
+ Config.AskZipCode := AskZipCode;
+ Config.AskHomePhone := AskHomePhone;
+ Config.AskDataPhone := AskDataPhone;
+ Config.AskBirthdate := AskBirthDate;
+ Config.AskGender := AskGender;
+ Config.AskEmail := AskEmail;
+ Config.AskUserNote := AskUserNote;
+ Config.AskScreenSize := AskScreenSize;
+
+ FillChar (Config.OptionalField, SizeOf(Config.OptionalField), #0);
+
+ Config.OptionalField[1].Ask := AskOption1;
+ Config.OptionalField[1].Desc := Option1;
+ Config.OptionalField[1].iType := 1;
+ Config.OptionalField[1].iField := 35;
+ Config.OptionalField[1].iMax := 35;
+ Config.OptionalField[2].Ask := AskOption2;
+ Config.OptionalField[2].Desc := Option2;
+ Config.OptionalField[2].iType := 1;
+ Config.OptionalField[2].iField := 35;
+ Config.OptionalField[2].iMax := 35;
+ Config.OptionalField[3].Ask := AskOption3;
+ Config.OptionalField[3].Desc := Option3;
+ Config.OptionalField[3].iType := 1;
+ Config.OptionalField[3].iField := 35;
+ Config.OptionalField[3].iMax := 35;
+
+ For A := 4 to 10 Do Begin
+ Config.OptionalField[A].Ask := False;
+ Config.OptionalField[A].Desc := 'Unused';
+ Config.OptionalField[A].iType := 1;
+ Config.OptionalField[A].iField := 35;
+ Config.OptionalField[A].iMax := 35;
+ End;
+
+ Config.MCompress := MCompress;
+ Config.MColumns := MColumns;
+ Config.MShowHeader := MShowHeader;
+ Config.MShowBases := MShowBases;
+ Config.MaxAutoSig := MaxAutoSig;
+ Config.qwkMaxBase := qwkMaxBase;
+ Config.qwkMaxPacket := qwkMaxPacket;
+ Config.qwkArchive := qwkArchive;
+ Config.qwkBBSID := qwkBBSID;
+ Config.qwkWelcome := qwkWelcome;
+ Config.qwkNews := qwkNews;
+ Config.qwkGoodbye := qwkGoodbye;
+ Config.Origin := Origin;
+
+ FillChar (Config.NetAddress, SizeOf(Config.NetAddress), #0);
+
+ For A := 1 to 20 Do Begin
+ Config.NetAddress[A].Zone := NetAddress[A].Zone;
+ Config.NetAddress[A].Net := NetAddress[A].Net;
+ Config.NetAddress[A].Node := NetAddress[A].Node;
+ Config.NetAddress[A].Point := NetAddress[A].Point;
+ Config.NetDesc[A] := NetAddress[A].Desc;
+ End;
+
+ Config.NetCrash := NetCrash;
+ Config.NetHold := NetHold;
+ Config.NetKillSent := NetKillSent;
+ Config.ColorQuote := ColorQuote;
+ Config.ColorText := ColorText;
+ Config.ColorTear := ColorTear;
+ Config.ColorOrigin := ColorOrigin;
+ Config.FCompress := FCompress;
+ Config.FColumns := FColumns;
+ Config.FShowHeader := FShowHeader;
+ Config.FShowBases := FShowBases;
+ Config.FDupeScan := DupeScan;
+ Config.UploadBase := UploadBase;
+ Config.ImportDIZ := ImportDIZ;
+ Config.FreeUL := FreeUL;
+ Config.FreeCDROM := FreeCDROM;
+ Config.MaxFileDesc := MaxFileDesc;
+ Config.TestUploads := TestUploads;
+ Config.TestPassLevel := TestPassLevel;
+ Config.TestCmdLine := TestCmdLine;
+ Config.AcsValidate := AcsValidate;
+ Config.AcsSeeUnvalid := AcsSeeUnvalid;
+ Config.AcsDLUnvalid := AcsDLUnvalid;
+ Config.AcsSeeFailed := AcsSeeFailed;
+ Config.AcsDLFailed := AcsDLFailed;
+ Config.inetDomain := inetDomain;
+ Config.inetIPBlocking := inetIPBlocking;
+ Config.inetIPLogging := inetIPLogging;
+ Config.inetSMTPUse := inetSMTPUse;
+ Config.inetSMTPPort := inetSMTPPort;
+ Config.inetSMTPMax := inetSMTPMax;
+ Config.inetPOP3Use := inetPOP3Use;
+ Config.inetPOP3Port := inetPOP3Port;
+ Config.inetPOP3Max := inetPOP3Max;
+ Config.inetTNUse := inetTNUse;
+ Config.inetTNPort := inetTNPort;
+ Config.inetTNDupes := inetTNDupes;
+ Config.inetFTPUse := inetFTPUse;
+ Config.inetFTPPort := inetFTPPort;
+ Config.inetFTPMax := inetFTPMax;
+ Config.inetFTPDupes := inetFTPDupes;
+ Config.inetFTPPortMin := inetFTPPortMin;
+ Config.inetFTPPortMax := inetFTPPortMax;
+ Config.inetFTPAnon := inetFTPAnon;
+ Config.inetFTPTimeout := inetFTPTimeout;
+
+ { new in 1.10 a11 }
+
+ Config.TemplatePath := SysPath + 'template' + PathChar;
+ Config.MenuPath := SysPath + 'menus' + PathChar;
+ Config.TextPath := SysPath + 'text' + PathChar;
+ Config.WebPath := SysPath + 'http' + PathChar;
+
+ Config.PWChange := 0;
+ Config.LoginAttempts := 3;
+ Config.LoginTime := 30;
+ Config.PWInquiry := True;
+
+ Config.DefScreenCols := 80;
+
+ Config.AcsMultiLogin := 's255';
+
+ Config.AskScreenCols := False;
+
+ Config.ColorKludge := 08;
+ Config.AcsCrossPost := 's255';
+ Config.AcsFileAttach := 's255';
+ Config.AcsNodeLookup := 's255';
+ Config.FSEditor := False;
+ Config.FSCommand := '';
+
+ Config.FCommentLines := 10;
+ Config.FCommentLen := 79;
+
+ Config.inetTNMax := MaxNode;
+
+ Config.inetSMTPDupes := 1;
+ Config.inetPOP3Dupes := 1;
+
+ Config.inetNNTPUse := False;
+ Config.inetNNTPPort := 119;
+ Config.inetNNTPMax := 8;
+ Config.inetNNTPDupes := 3;
+
+ Config.UseStatusBar := True;
+ Config.StatusColor1 := 9 + 1 * 16;
+ Config.StatusColor2 := 9 + 1 * 16;
+ Config.StatusColor3 := 15 + 1 * 16;
+
+ Config.PWAttempts := 3;
+ End;
+
+ Assign (ConfigFile, 'mystic.dat');
+ ReWrite (ConfigFile);
+ Write (ConfigFile, Config);
+ Close (ConfigFile);
+End;
+
+Procedure ConvertUsers;
+Var
+ User : RecUser;
+ UserFile : File of RecUser;
+ OldUser : OldUserRec;
+ OldUserFile : File of OldUserRec;
+ A : LongInt;
+Begin
+ WriteLn ('[-] Updating user database...');
+
+ ReNameFile(Config.DataPath + 'users.dat', Config.DataPath + 'users.old');
+
+ Assign (OldUserFile, Config.DataPath + 'users.old');
+ Reset (OldUserFile);
+
+ Assign (UserFile, Config.DataPath + 'users.dat');
+ ReWrite (UserFile);
+
+ While Not Eof(OldUserFile) Do Begin
+ Read (OldUserFile, OldUser);
+
+ FillChar (User, SizeOf(User), #0);
+
+ With OldUser Do Begin
+ User.PermIdx := PermIdx;
+ User.Flags := Flags;
+ User.Handle := Handle;
+ User.RealName := RealName;
+ User.Password := Password;
+ User.Address := Address;
+ User.City := City;
+ User.ZipCode := ZipCode;
+ User.HomePhone := HomePhone;
+ User.DataPhone := DataPhone;
+ User.Birthday := Birthday;
+ User.Gender := Gender;
+ User.Email := EmailAddr;
+
+ FillChar (User.Optional, SizeOf(User.Optional), #0);
+
+ User.Optional[1] := Option1;
+ User.Optional[2] := Option2;
+ User.Optional[3] := Option3;
+
+ User.UserInfo := UserInfo;
+ User.Theme := Language;
+ User.AF1 := AF1;
+ User.AF2 := AF2;
+ User.Security := Security;
+ User.Expires := '00/00/00';
+ User.ExpiresTo := 0;
+ User.LastPWChange := '00/00/00';
+ User.StartMenu := StartMenu;
+ User.Archive := Archive;
+ User.QwkFiles := QwkFiles;
+ User.DateType := DateType;
+ User.ScreenSize := ScrnPause;
+ User.ScreenCols := 80;
+ User.PeerIP := '';
+ User.PeerName := '';
+ User.FirstOn := FirstOn;
+ User.LastOn := LastOn;
+ User.Calls := Calls;
+ User.CallsToday := CallsToday;
+ User.DLs := DLs;
+ User.DLsToday := DLsToday;
+ User.DLk := DLk;
+ User.DLkToday := DLkToday;
+ User.ULs := ULs;
+ User.ULk := ULk;
+ User.Posts := Posts;
+ User.Emails := Emails;
+ User.TimeLeft := TimeLeft;
+ User.TimeBank := TimeBank;
+ User.FileRatings := 0;
+ User.FileComment := 0;
+ User.LastFBase := LastFBase;
+ User.LastMBase := LastMBase;
+ User.LastFGroup := LastFGroup;
+ User.LastMGroup := LastMGroup;
+
+ For A := 1 to 20 Do
+ User.Vote[A] := Vote[A];
+
+ User.EditType := EditType;
+ User.FileList := FileList;
+ User.SigUse := SigUse;
+ User.SigOffset := SigOffset;
+ User.SigLength := SigLength;
+ User.HotKeys := HotKeys;
+ User.MReadType := MReadType;
+ User.UseLBIndex := UseLBIndex;
+ User.UseLBQuote := UseLBQuote;
+ User.UseLBMIdx := UseLBMIdx;
+ User.UseFullChat := UserFullChat;
+ User.Credits := 0;
+ End;
+
+ Write (UserFile, User);
+ End;
+
+ Close (UserFile);
+ Close (OldUserFile);
+
+ DeleteFile (Config.DataPath + 'users.old');
+End;
+
+Procedure ConvertSecurity;
+Var
+ Sec : RecSecurity;
+ SecFile : File of RecSecurity;
+ OldSec : OldSecurityRec;
+ OldSecFile : File of OldSecurityRec;
+ A : LongInt;
+Begin
+ WriteLn ('[-] Updating security definitions...');
+
+ ReNameFile(Config.DataPath + 'security.dat', Config.DataPath + 'security.old');
+
+ Assign (OldSecFile, Config.DataPath + 'security.old');
+ Reset (OldSecFile);
+
+ Assign (SecFile, Config.DataPath + 'security.dat');
+ ReWrite (SecFile);
+
+ While Not Eof(OldSecFile) Do Begin
+ Read (OldSecFile, OldSec);
+
+ FillChar (Sec, SizeOf(Sec), #0);
+
+ With OldSec Do Begin
+ Sec.Desc := Desc;
+ Sec.Time := Time;
+ Sec.MaxCalls := MaxCalls;
+ Sec.MaxDLs := MaxDLs;
+ Sec.MaxDLk := MaxDLk;
+ Sec.MaxTB := MaxTB;
+ Sec.DLRatio := DLRatio;
+ Sec.DLKRatio := DLKRatio;
+ Sec.AF1 := AF1;
+ Sec.AF2 := AF2;
+ Sec.Hard := Hard;
+ Sec.StartMNU := StartMNU;
+ Sec.PCRatio := PCRatio;
+ End;
+
+ Write (SecFile, Sec);
+ End;
+
+ Close (SecFile);
+ Close (OldSecFile);
+
+ DeleteFile (Config.DataPath + 'security.old');
+End;
+
+(*
+Procedure ConvertMessageBases;
+Var
+ MBase : MBaseRec;
+ MBaseFile : File of MBaseRec;
+ OldBase : OldMBaseRec;
+ OldBaseFile : File of OldMBaseRec;
+Begin
+ WriteLn ('[-] Updating message base config...');
+
+ ReNameFile(Config.DataPath + 'mbases.dat', Config.DataPath + 'mbases.old');
+
+ Assign (OldBaseFile, Config.DataPath + 'mbases.old');
+ Reset (OldBaseFile);
+
+ Assign (MBaseFile, Config.DataPath + 'mbases.dat');
+ ReWrite (MBaseFile);
+
+ While Not Eof(OldBaseFile) Do Begin
+ Read (OldBaseFile, OldBase);
+
+ With OldBase Do Begin
+ MBase.Name := Name;
+ MBase.QwkName := QwkName;
+ MBase.FileName := FileName;
+ MBase.Path := Path;
+ MBase.BaseType := BaseType;
+ MBase.NetType := NetType;
+ MBase.PostType := PostType;
+ MBase.ACS := ACS;
+ MBase.ReadACS := ReadACS;
+ MBase.PostACS := PostACS;
+ MBase.SysopACS := SysopACS;
+ MBase.Password := Password;
+ MBase.ColQuote := ColQuote;
+ MBase.ColTear := ColTear;
+ MBase.ColText := ColText;
+ MBase.ColOrigin := ColOrigin;
+ MBase.NetAddr := NetAddr;
+ MBase.Origin := Origin;
+ MBase.UseReal := UseReal;
+ MBase.DefNScan := DefNScan;
+ MBase.DefQScan := DefQScan;
+ MBase.MaxMsgs := MaxMsgs;
+ MBase.MaxAge := MaxAge;
+ MBase.Header := Header;
+ MBase.Index := Index;
+ End;
+
+ Write (MBaseFile, MBase);
+ End;
+
+ Close (MBaseFile);
+ Close (OldBaseFile);
+
+ DeleteFile (Config.DataPath + 'mbases.old');
+End;
+*)
+(*
+Procedure ConvertFileBases;
+Var
+ FBase : FBaseRec;
+ FBaseFile : File of FBaseRec;
+ OldBase : OldFBaseRec;
+ OldBaseFile : File of OldFBaseRec;
+Begin
+ WriteLn ('[-] Updating file base config...');
+
+ ReNameFile(Config.DataPath + 'fbases.dat', Config.DataPath + 'fbases.old');
+
+ Assign (OldBaseFile, Config.DataPath + 'fbases.old');
+ Reset (OldBaseFile);
+
+ Assign (FBaseFile, Config.DataPath + 'fbases.dat');
+ ReWrite (FBaseFile);
+
+ While Not Eof(OldBaseFile) Do Begin
+ Read (OldBaseFile, OldBase);
+
+ With OldBase Do Begin
+ FBase.Name := Name;
+ FBase.FtpName := strStripMCI(FBase.Name);
+ FBase.FileName := FileName;
+ FBase.DispFile := DispFile;
+ FBase.ListACS := ACS;
+ FBase.FtpACS := ACS;
+ FBase.SysopACS := SysopACS;
+ FBase.ULACS := ULACS;
+ FBase.DLACS := DLACS;
+ FBase.Path := Path;
+ FBase.Password := Password;
+ FBase.ShowUL := ShowUL;
+ FBase.DefScan := DefScan;
+ FBase.IsCDROM := IsCDROM;
+ FBase.IsFREE := IsFREE;
+ End;
+
+ Write (FBaseFile, FBase);
+ End;
+
+ Close (FBaseFile);
+ Close (OldBaseFile);
+
+ DeleteFile (Config.DataPath + 'fbases.old');
+End;
+*)
+(*
+Procedure ConvertLanguageDefs;
+Var
+ Lang : LangRec;
+ LangFile : File of LangRec;
+ OldLang : OldLangRec;
+ OldLangFile : File of OldLangRec;
+ TempBar : PercentRec;
+Begin
+ WriteLn ('[-] Updating language definitions...');
+
+ ReNameFile(Config.DataPath + 'language.dat', Config.DataPath + 'language.old');
+
+ Assign (OldLangFile, Config.DataPath + 'language.old');
+ Reset (OldLangFile);
+
+ Assign (LangFile, Config.DataPath + 'language.dat');
+ ReWrite (LangFile);
+
+ While Not Eof(OldLangFile) Do Begin
+ Read (OldLangFile, OldLang);
+
+ TempBar.BarLen := 10;
+ TempBar.LoChar := '°';
+ TempBar.LoAttr := 8;
+ TempBar.HiChar := '²';
+ TempBar.HiAttr := 25;
+
+ With OldLang Do Begin
+ Lang.FileName := FileName;
+ Lang.Desc := Desc;
+ Lang.TextPath := TextPath;
+ Lang.MenuPath := MenuPath;
+ Lang.okASCII := okASCII;
+ Lang.okANSI := okANSI;
+ Lang.BarYN := BarYN;
+ Lang.FieldCol1 := FieldColor;
+ Lang.FieldCol2 := FieldColor;
+ Lang.FieldChar := InputCh;
+ Lang.EchoCh := EchoCh;
+ Lang.QuoteColor := QuoteColor;
+ Lang.TagCh := TagCh;
+ Lang.FileHi := FileHi;
+ Lang.FileLo := FileLo;
+ Lang.NewMsgChar := NewMsgChar;
+
+ Lang.VotingBar := TempBar;
+ Lang.FileBar := TempBar;
+ Lang.MsgBar := TempBar;
+ End;
+
+ Write (LangFile, Lang);
+ End;
+
+ Close (LangFile);
+ Close (OldLangFile);
+
+ DeleteFile (Config.DataPath + 'language.old');
+End;
+*)
+
+Procedure ConvertArchives;
+Var
+ Arc : RecArchive;
+ ArcFile : File of RecArchive;
+ OldArc : OldArcRec;
+ OldArcFile : File of OldArcRec;
+Begin
+ WriteLn ('[-] Updating archives...');
+
+ If Not ReNameFile(Config.DataPath + 'archive.dat', Config.DataPath + 'archive.old') Then Begin
+ WriteLn('[!] UNABLE TO FIND: ' + Config.DataPath + 'archive.dat');
+ Exit;
+ End;
+
+ Assign (OldArcFile, Config.DataPath + 'archive.old');
+ Reset (OldArcFile);
+
+ Assign (ArcFile, Config.DataPath + 'archive.dat');
+ ReWrite (ArcFile);
+
+ While Not Eof(OldArcFile) Do Begin
+ Read (OldArcFile, OldArc);
+
+ Arc.Desc := OldArc.Name;
+ Arc.Ext := OldArc.Ext;
+ Arc.Pack := OldArc.Pack;
+ Arc.Unpack := OldArc.Unpack;
+ Arc.View := OldArc.View;
+ Arc.OSType := OSType;
+ Arc.Active := True;
+
+ Write (ArcFile, Arc);
+ End;
+
+ Close (ArcFile);
+ Close (OldArcFile);
+
+ DeleteFile (Config.DataPath + 'archive.old');
+End;
+
+Procedure ConvertGroups;
+Var
+ Group : RecGroup;
+ GroupFile : File of RecGroup;
+ OldGroup : OldGroupRec;
+ OldGroupFile : File of OldGroupRec;
+ Count : Byte;
+ FN : String;
+Begin
+ WriteLn ('[-] Updating groups...');
+
+ For Count := 1 to 2 Do Begin
+ If Count = 1 Then FN := 'groups_f' Else FN := 'groups_g';
+
+ If Not ReNameFile(Config.DataPath + FN + '.dat', Config.DataPath + FN + '.old') Then Begin
+ WriteLn('[!] UNABLE TO FIND: ' + Config.DataPath + FN + '.dat');
+ Continue;
+ End;
+
+ Assign (OldGroupFile, Config.DataPath + FN + '.old');
+ Reset (OldGroupFile);
+
+ Assign (GroupFile, Config.DataPath + FN + '.dat');
+ ReWrite (GroupFile);
+
+ While Not Eof(OldGroupFile) Do Begin
+ Read (OldGroupFile, OldGroup);
+
+ Group.Name := OldGroup.Name;
+ Group.ACS := OldGroup.ACS;
+ Group.Hidden := False;
+
+ Write (GroupFile, Group);
+ End;
+
+ Close (GroupFile);
+ Close (OldGroupFile);
+
+ DeleteFile (Config.DataPath + FN + '.old');
+ End;
+End;
+
+Var
+ ConfigFile : File of RecConfig;
+Begin
+ WarningDisplay;
+
+// comment this out ONLY IF config needs converting
+ Assign (ConfigFile, 'mystic.dat');
+ Reset (ConfigFile);
+ Read (ConfigFile, Config);
+ Close (ConfigFile);
+
+// ConvertConfig; //1.10a11
+// ConvertUsers; //1.10a11
+//ConvertSecurity; //1.10a11
+
+// ConvertArchives; //1.10a1
+// ConvertGroups; //1.10a1
+
+ TextAttr := 12;
+ WriteLn;
+ WriteLn ('COMPLETE!');
+End.
diff --git a/mystic/ansi_install.ans b/mystic/ansi_install.ans
new file mode 100644
index 0000000..d923f02
--- /dev/null
+++ b/mystic/ansi_install.ans
@@ -0,0 +1,13 @@
+[0;0;40;37m
+
+[61C[1;33m²
+[0;36mÜÜ[37m[11C[1;30mÜÜ [0m°[47;30m²[40;37m[24C[1;36mÜÜÜÜÜ[0m[10C[1;33m±Û±[0m°[47;30m²[40;37m[9C°[47;30m²[1;40mgj!
+ [46;36mÞÛÜ[0;36mÜ[37m[5C[1;30mþ ß Ý [0;36m°[46;30m²[1;40;36m°[46mÞ[37mÛÛ[36mÛÝ[0m[6C[1;30mÝ[0m[6C[1;47mÜ[40;36mÜÜÜÜ[0;36mÜ[1mÜÛ[47;37mÛÛ[46;36mÛ[47;37mÛ[40;36mÜÜÜÜ[33mß ßßÛ[37mÛÛ[33mÛÛÛ²ßß[0;36mÜ[1mÜÜÜÜÜ[46mÜ[40mÛÛ[46mÜ[0;36mÜ
+ [1m°[46mÛ[37mÛÛ[36mÜ[0;36mÜ[37m[6C[1;30mÜß[0;36mÜ[1;46mÜÛÛÛÞÛÛÛÝ[0m [1;47;30m²[40mßß ß [0;36mÜ[1;46mÜÛÛÛ[40mÛÛ[46mÛ[40m²°[46mßÛÛÛÛÛßßßß[0m [1;33mÜÛÛÛÛ²Ü[0;36mß[1;46mÛÛ[47;37mÛÛ[46;36mÛ[47;37mÛ[46;36mÛÛÛÛÛ[40m±[30mþ
+[47;37mß[0m [1;36m²[46mÛÛÛÛÜ[0;36mÜ[1;30mßÛ[47m²[40mß[0;36mÜ[1;46mÜÛ[47;37mÛ[46;36mÛÛ[40m²[46mÞÛÛÛÝ[0m [1;46;30m²[0m [1;46;36mÞÛÛÛ[40m²±²[46mÛÛÛÜ[0;36mÜ [1;30mÜÜ [36m²[46mÛÛÛ[40m² [30mÜ [36mÜÜÜ [33mßß[36mÜÜÜÜÜ[33mßß[0;36mÞ[1;46mÛ²[0m [1;30mÜÜ [0;36mÜ[1;46mÛÛÛ[0;36mÝ[1;30mÞ
+[47m²[0m [1;36m±[46m²²²Û[40mß[46mß[40mÛÜÜÛ[46mß[40mß[46mÛ²²²[40m±[0;36mÞ[1m²[46m²²²[0;36mÜ [1;46mÞ²²[40m²± [0;36mß[1;46mßÛÛ²²±°[0;36mÜ[1;30mß [46;36m°²²²[40m± [46;30m²[0m [1;46;36m²²Ü±°[0m [1;47mÛÛ[46;36mÛÛ[40m²°²[46m²²±[0m [1;30m² [0;36mÞ[1m²[46m²²°[40m [47;30m²[0m
+[1;30mß [0;36mÛ[1;46m°°°[0;36mÛ ß[1;46mßß[0;36mß Û[1;46m°°°[0;36mÛ ß[1;46m°°°°[0;36mÛÜÛ[1;46m°°°[0;36mÛ [1;30mß [0;36mßß[1;46m°°°°[0;36mÛ Û[1;46m°°°[0;36mÛÝ[1;30m± [46;36m°°°°[0;36mÛ°[1;46m²²²²[40m²[0;36mÛ[1m±[46m°°°[0;36mÝ[1;30mß[0m [1;46;36m°°°[0;36mÛ[1m° [46;30m²[0m
+[36m°[46;30m²[40;36m²ÛÛ[46m [40mÛ [1;46;30m²[40mÜÜÜܲ [0;36mÛÛÛÛÛ [1;30m±Ü [0;36mßßßßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ[46;30m°±[40;37m [36mßÛÛÛÛÜÜÛÛÛÛÛ[1m±[46m°°°°°[0m [36mßÛÛÛÛÜÜÜÜÜ [1;30mÜÜÛ
+ÜÜþ [0;36mßß² [1;30m± °°²ÜÜÜÜÜÜܲßßßßß [0;36m²ÛÛÛݲ[46;30m°[40;36m²ÛÛÛ²ßßßß [1;30mÞÜÜ [0;36mßßßßßßßß ßßßßß ßßßßßßß [1;30mÝ°°
+[0m[8C[1;30m°[0m[12C[1;30mÝ[0m[6C[36m°[46;30m²[40;37m[11C[1;30mþ Þßßßßßßßßßßßßß ß[0m[14C[1;30mßÜ
+[0m[21C[1;30mßÜÜ[0m[18C[1;30mß ÜÜß
diff --git a/mystic/aview.pas b/mystic/aview.pas
new file mode 100644
index 0000000..9b53bf5
--- /dev/null
+++ b/mystic/aview.pas
@@ -0,0 +1,164 @@
+Unit AView;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses Dos;
+
+Type
+ ArcSearchRec = Record
+ Name : String[50];
+ Size : LongInt;
+ Time : LongInt;
+ Attr : Byte;
+ End;
+
+Type
+ PGeneralArchive = ^TGeneralArchive;
+ TGeneralArchive = Object
+ ArcFile : File;
+ Constructor Init;
+ Destructor Done; Virtual;
+ Procedure FindFirst (Var SR: ArcSearchRec); Virtual;
+ Procedure FindNext (Var SR: ArcSearchRec); Virtual;
+ End;
+
+Type
+ PArchive = ^TArchive;
+ TArchive = Object
+ Constructor Init;
+ Destructor Done;
+ Function Name (n:string) : Boolean;
+ Procedure FindFirst (Var SR: ArcSearchRec);
+ Procedure FindNext (Var SR: ArcSearchRec);
+ Private
+ _Name : String;
+ _Archive : PGeneralArchive;
+ End;
+
+Function Get_Arc_Type (Name: String) : Char;
+
+Implementation
+
+Uses
+ AViewZIP,
+ AViewARJ,
+ AViewLZH,
+ AViewRAR;
+
+Function Get_Arc_Type (Name: String) : Char;
+Var
+ ArcFile : File;
+ Buf : Array[1..3] of Char;
+ Res : LongInt;
+Begin
+ Get_Arc_Type := '?';
+ If Name = '' Then Exit;
+
+ Assign (ArcFile, Name);
+ {$I-} Reset (ArcFile, 1); {$I+}
+ If IoResult <> 0 Then Exit;
+
+ BlockRead (ArcFile, Buf, SizeOf(Buf), Res);
+ Close (ArcFile);
+
+ If Res = 0 Then Exit;
+
+ If (Buf[1] = 'R') and (Buf[2] = 'a') and (Buf[3] = 'r') Then
+ Get_Arc_Type := 'R'
+ Else
+
+ If (Buf[1] = #$60) And (Buf[2] = #$EA) Then
+ Get_Arc_Type := 'A'
+ Else
+
+ If (Buf[1] = 'P') And (Buf[2] = 'K') Then
+ Get_Arc_Type := 'Z'
+ Else
+
+ If Pos('.LZH', Name) > 0 Then
+ Get_Arc_Type := 'L';
+End;
+
+Constructor TGeneralArchive.Init;
+Begin
+End;
+
+Destructor TGeneralArchive.Done;
+Begin
+End;
+
+Procedure TGeneralArchive.FindFirst(var sr:ArcSearchRec);
+Begin
+End;
+
+Procedure TGeneralArchive.FindNext(var sr:ArcSearchRec);
+Begin
+End;
+
+Constructor TArchive.Init;
+Begin
+ _Name := '';
+ _Archive := Nil;
+End;
+
+Destructor TArchive.Done;
+Begin
+ If _Archive <> Nil Then Begin
+ Close (_Archive^.ArcFile);
+ Dispose (_Archive, Done);
+ End;
+End;
+
+Function TArchive.Name (N: String): Boolean;
+Var
+ SR : SearchRec;
+Begin
+ If _Archive <> Nil Then Begin
+ Close (_Archive^.ArcFile);
+ Dispose (_Archive, Done);
+ _Archive := Nil;
+ End;
+
+ Name := False;
+ _Name := N;
+
+ Dos.FindFirst(_Name, AnyFile, SR);
+ FindClose (SR);
+
+ If DosError <> 0 Then Exit;
+
+ Case Get_Arc_Type(_Name) of
+ '?' : Exit;
+ 'A' : _Archive := New(PArjArchive, Init);
+ 'Z' : _Archive := New(PZipArchive, Init);
+ 'L' : _Archive := New(PLzhArchive, Init);
+ 'R' : _Archive := New(PRarArchive, Init);
+ End;
+
+ Assign(_Archive^.ArcFile, N);
+ {$I-} Reset(_Archive^.ArcFile, 1); {$I+}
+ If IoResult <> 0 Then Begin
+ Dispose (_Archive, Done);
+ Exit;
+ End;
+
+ Name := True;
+End;
+
+Procedure TArchive.FindFirst (Var SR : ArcSearchRec);
+Begin
+ FillChar(SR, SizeOf(SR), 0);
+ If _Archive = Nil Then Exit;
+ _Archive^.FindFirst(SR);
+End;
+
+Procedure TArchive.FindNext(var sr:ArcSearchRec);
+Begin
+ FillChar(SR, SizeOf(SR), 0);
+ If _Archive = Nil Then Exit;
+ _Archive^.FindNext(SR);
+End;
+
+End.
diff --git a/mystic/aviewarj.pas b/mystic/aviewarj.pas
new file mode 100644
index 0000000..03ff505
--- /dev/null
+++ b/mystic/aviewarj.pas
@@ -0,0 +1,111 @@
+Unit AViewARJ;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ Dos,
+ AView;
+
+Const
+ flag_DIR = $10;
+
+Type
+ AFHeader = Record
+ HeadId : Word;
+ BHdrSz : Word;
+ HdrSz : Byte;
+ AVNo : Byte;
+ MAVX : Byte;
+ HostOS : Byte;
+ Flags : Byte;
+ SVer : Byte;
+ FType : Byte;
+ Res1 : Byte;
+ DOS_DT : LongInt;
+ CSize : LongInt;
+ OSize : LongInt;
+ SEFP : LongInt;
+ FSFPos : Word;
+ SEDLgn : Word;
+ Res2 : Word;
+ NameDat : Array[1..120] of Char;
+ Res3 : Array[1..10] of Char;
+ End;
+
+Type
+ PArjArchive = ^TArjArchive;
+ TArjArchive = Object(TGeneralArchive)
+ Constructor Init;
+ Procedure FindFirst (Var SR : ArcSearchRec); Virtual;
+ Procedure FindNext (Var SR : ArcSearchRec); Virtual;
+ Private
+ _FHdr : AFHeader;
+ _SL : LongInt;
+ Procedure GetHeader (Var SR : ArcSearchRec);
+ End;
+
+Implementation
+
+Const
+ BSize = 4096;
+
+Var
+ BUFF : Array[1..BSize] of Byte;
+
+Constructor TArjArchive.Init;
+Begin
+ FillChar (_FHdr, SizeOf(_FHdr), 0);
+End;
+
+Procedure TArjArchive.GetHeader(var sr:ArcSearchRec);
+Var
+ {$IFDEF MSDOS}
+ BC : Word;
+ {$ELSE}
+ BC : LongInt;
+ {$ENDIF}
+ B : Byte;
+Begin
+ FillChar (_FHdr, SizeOf(_FHdr), #0);
+ FillChar (Buff, BSize, #0);
+ Seek (ArcFile, _SL);
+ BlockRead (ArcFile, BUFF, BSIZE, BC);
+ Move(BUFF[1], _FHdr, SizeOf(_FHdr));
+ With _FHdr Do Begin
+ If BHdrSz > 0 Then Begin
+ B := 1;
+ SR.Name := '';
+ While NameDat[B] <> #0 Do Begin
+ If NameDat[B] = '/' Then
+ SR.Name := ''
+ Else
+ SR.Name := SR.Name + NameDat[B];
+ Inc(B);
+ End;
+ SR.Size := BHdrSz + CSize;
+ If FType = 2 Then SR.Size := BHdrSz;
+ If BHdrSz = 0 Then SR.Size := 0;
+ Inc(_SL, SR.Size + 10);
+ SR.Time := DOS_DT;
+{ If Flags and flag_DIR > 0 Then SR.Attr := 16 Else SR.Attr := 0;}
+{ If (SR.Name[Length(SR.Name)] = '/') and (SR.Size = 0) Then SR.Attr := 16;}
+
+ End;
+ End;
+End;
+
+Procedure TArjArchive.FindFirst (Var SR : ArcSearchRec);
+Begin
+ _SL := 0;
+ GetHeader (SR);
+ GetHeader (SR);
+End;
+
+Procedure TArjArchive.FindNext (Var SR : ArcSearchRec);
+Begin
+ GetHeader(SR);
+End;
+
+End.
diff --git a/mystic/aviewlzh.pas b/mystic/aviewlzh.pas
new file mode 100644
index 0000000..d4ac810
--- /dev/null
+++ b/mystic/aviewlzh.pas
@@ -0,0 +1,81 @@
+Unit aviewlzh;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses Dos,aview;
+
+Type LFHeader=Record
+ Headsize,Headchk :byte;
+ HeadID :packed Array[1..5] of char;
+ Packsize,Origsize,Filetime:longint;
+ Attr :word;
+ Filename :string[12];
+ f32 :pathstr;
+ dt :DateTime;
+ end;
+
+
+type PLzhArchive=^TLzhArchive;
+ TLzhArchive=object(TGeneralArchive)
+ constructor Init;
+ procedure FindFirst(var sr:ArcSearchRec);virtual;
+ procedure FindNext(var sr:ArcSearchRec);virtual;
+ private
+ _FHdr:LFHeader;
+ _SL:longint;
+ procedure GetHeader(var sr:ArcSearchRec);
+ end;
+
+
+Implementation
+
+
+constructor TLzhArchive.Init;
+begin
+ _SL:=0;
+ FillChar(_FHdr,sizeof(_FHdr),0);
+end;
+
+
+procedure TLzhArchive.GetHeader(var sr:ArcSearchRec);
+Var
+ {$IFDEF MSDOS}
+ NR : Word;
+ {$ELSE}
+ NR : LongInt;
+ {$ENDIF}
+begin
+ fillchar(sr,sizeof(sr),0);
+ seek(ArcFile,_SL);
+ if eof(ArcFile) then Exit;
+ blockread(ArcFile,_FHdr,sizeof(LFHeader),nr);
+ if _FHdr.headsize=0 then exit;
+ inc(_SL,_FHdr.headsize);
+ inc(_SL,2);
+ inc(_SL,_FHdr.packsize);
+ if _FHdr.headsize<>0 then
+ UnPackTime(_FHdr.FileTime,_FHdr.DT);
+ sr.Name:=_FHdr.FileName;
+ sr.Size:=_FHdr.OrigSize;
+ sr.Time:=_FHdr.FileTime;
+end;
+
+
+procedure TLzhArchive.FindFirst(var sr:ArcSearchRec);
+begin
+ _SL:=0;
+ GetHeader(sr);
+end;
+
+
+procedure TLzhArchive.FindNext(var sr:ArcSearchRec);
+begin
+ GetHeader(sr);
+end;
+
+
+end.
+
+{ CUT ----------------------------------------------------------- }
diff --git a/mystic/aviewrar.pas b/mystic/aviewrar.pas
new file mode 100644
index 0000000..e3fe885
--- /dev/null
+++ b/mystic/aviewrar.pas
@@ -0,0 +1,102 @@
+Unit AViewRAR;
+
+{$I M_OPS.PAS}
+
+(* DOES NOT WORK IF FILE HAS COMMENTS... NEED TO READ SKIP ADDSIZE IF NOT $74
+
+1. Read and check marker block
+2. Read archive header
+3. Read or skip HEAD_SIZE-sizeof(MAIN_HEAD) bytes
+4. If end of archive encountered then terminate archive processing,
+ else read 7 bytes into fields HEAD_CRC, HEAD_TYPE, HEAD_FLAGS,
+ HEAD_SIZE.
+5. Check HEAD_TYPE.
+ if HEAD_TYPE==0x74
+ read file header ( first 7 bytes already read )
+ read or skip HEAD_SIZE-sizeof(FILE_HEAD) bytes
+ if (HEAD_FLAGS & 0x100)
+ read or skip HIGH_PACK_SIZE*0x100000000+PACK_SIZE bytes
+ else
+ read or skip PACK_SIZE bytes
+ else
+ read corresponding HEAD_TYPE block:
+ read HEAD_SIZE-7 bytes
+ if (HEAD_FLAGS & 0x8000)
+ read ADD_SIZE bytes
+6. go to 4.
+*)
+
+Interface
+
+Uses
+ DOS,
+ AView;
+
+Type
+ RarHeaderRec = Record
+ PackSize : LongInt;
+ Size : LongInt;
+ HostOS : Byte;
+ FileCRC : LongInt;
+ Time : LongInt;
+ Version : Byte;
+ Method : Byte;
+ FNSize : SmallInt;
+ Attr : Longint;
+ End;
+
+ PRarArchive = ^TRarArchive;
+ TRarArchive = Object(TGeneralArchive)
+ Constructor Init;
+ Procedure FindFirst (Var SR : ArcSearchRec); Virtual;
+ Procedure FindNext (Var SR : ArcSearchRec); Virtual;
+ Private
+ RAR : RarHeaderRec;
+ Buf : Array[1..12] of Byte;
+ Offset : Word;
+ End;
+
+Implementation
+
+Constructor TRarArchive.Init;
+Begin
+End;
+
+Procedure TRarArchive.FindFirst (Var SR : ArcSearchRec);
+Begin
+ If Eof(ArcFile) Then Exit;
+
+ BlockRead (ArcFile, Buf[1], 12);
+
+ If Buf[10] <> $73 Then Exit;
+
+ BlockRead (ArcFile, offset, 2);
+ BlockRead (ArcFile, Buf[1], 6);
+
+ Seek (ArcFile, FilePos(ArcFile) + (offset - 13));
+ FindNext (SR);
+End;
+
+Procedure TRarArchive.FindNext (Var SR: ArcSearchRec);
+Begin
+ If Eof(ArcFile) Then Exit;
+
+ BlockRead (ArcFile, Buf[1], 5);
+
+ If Buf[3] <> $74 Then Exit;
+
+ BlockRead (ArcFile, Offset, 2);
+ BlockRead (ArcFile, RAR, SizeOf(RAR));
+ BlockRead (ArcFile, SR.Name[1], RAR.FNSize);
+
+ SR.Name[0] := Chr(RAR.FNSize);
+
+ SR.Time := RAR.Time;
+ SR.Size := RAR.Size;
+
+ If RAR.Attr = 16 Then SR.Attr := $10;
+
+ Seek(ArcFile, FilePos(ArcFile) + (Offset - (SizeOf(RAR) + 7 + Length(SR.Name))) + RAR.PackSize);
+End;
+
+End.
\ No newline at end of file
diff --git a/mystic/aviewzip.pas b/mystic/aviewzip.pas
new file mode 100644
index 0000000..b737446
--- /dev/null
+++ b/mystic/aviewzip.pas
@@ -0,0 +1,126 @@
+Unit AViewZip;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ DOS,
+ AView;
+
+Type
+ ZFLocalHeader = Record
+ Signature : LongInt;
+ Version,
+ GPBFlag,
+ Compress,
+ Date,
+ Time : Word;
+ CRC32,
+ CSize,
+ USize : LongInt;
+ FNameLen,
+ ExtraField : Word;
+ End;
+
+ ZFCentralHeader = Record
+ Signature : LongInt;
+ Version : Word;
+ Needed : Word;
+ Flags : Word;
+ Compress : Word;
+ Date : Word;
+ Time : Word;
+ Crc32 : LongInt;
+ CSize : LongInt;
+ USize : LongInt;
+ FNameLen : Word;
+ ExtraField : Word;
+ CommentLen : Word;
+ DiskStart : Word;
+ iFileAttr : Word;
+ eFileAttr : LongInt;
+ Offset : LongInt;
+ End;
+
+Type
+ PZipArchive = ^TZipArchive;
+
+ TZipArchive = Object(TGeneralArchive)
+ Constructor Init;
+ Procedure FindFirst (Var SR : ArcSearchRec); Virtual;
+ Procedure FindNext (Var SR : ArcSearchRec); Virtual;
+
+ Private
+ Hdr : ZFLocalHeader;
+ cHdr : ZFCentralHeader;
+ cFile : Word;
+ tFile : Word;
+ Procedure GetHeader (Var SR : ArcSearchRec);
+ End;
+
+Implementation
+
+Const
+ LocalSig = $04034B50;
+ CentralSig = $02014b50;
+
+Constructor TZipArchive.Init;
+Begin
+ tFile := 0;
+ cFile := 0;
+End;
+
+Procedure TZipArchive.GetHeader (Var SR : ArcSearchRec);
+Var
+ S : String;
+Begin
+ FillChar (SR, SizeOf(SR), 0);
+ S := '';
+
+ If Eof(ArcFile) or (cFile = tFile) Then Exit;
+
+ BlockRead (ArcFile, cHdr, SizeOf(cHdr));
+ BlockRead (ArcFile, S[1], cHdr.FNameLen);
+
+ S[0] := Chr(cHdr.FNameLen);
+
+ If cHdr.Signature = CentralSig Then Begin
+ Inc (cFile);
+
+ If (S[Length(S)] = '/') and (cHdr.uSize = 0) Then SR.Attr := 16;
+
+ SR.Name := S;
+ SR.Size := cHdr.uSize;
+ SR.Time := cHdr.Date + cHdr.Time * LongInt(256 * 256);
+ End;
+
+ Seek (ArcFile, FilePos(ArcFile) + cHdr.ExtraField + cHdr.CommentLen);
+End;
+
+Procedure TZipArchive.FindFirst (Var SR : ArcSearchRec);
+Var
+ CurPos : LongInt;
+ bRead : LongInt;
+Begin
+ BlockRead (ArcFile, Hdr, SizeOf(Hdr));
+
+ While Hdr.Signature = LocalSig Do Begin
+ Inc (tFile);
+ CurPos := FilePos(ArcFile) + Hdr.FNameLen + Hdr.ExtraField + Hdr.cSize;
+ Seek (ArcFile, CurPos);
+ BlockRead (ArcFile, Hdr, SizeOf(Hdr), bRead);
+ If bRead <> SizeOf(Hdr) Then Exit;
+ End;
+
+ Seek (ArcFile, CurPos);
+
+ GetHeader(SR);
+End;
+
+Procedure TZipArchive.FindNext (Var SR : ArcSearchRec);
+Begin
+ GetHeader(SR);
+End;
+
+End.
diff --git a/mystic/bbs_ansi_help.pas b/mystic/bbs_ansi_help.pas
new file mode 100644
index 0000000..aba5292
--- /dev/null
+++ b/mystic/bbs_ansi_help.pas
@@ -0,0 +1,419 @@
+Unit bbs_Ansi_Help;
+
+// very old online-help class from Genesis Engine (my ansi editor)
+// updated to compile with mystic but needs a lot of touch ups.
+// idea is to template this out and have .hlp files that can be used in
+// all help areas if they exist instead of just a display file.
+// and of course a menu command to active this with ANY hlp files so sysops
+// can use it however they'd like
+//
+// hlp files are text files which can have embedded pipe color codes in them
+// and also have keywords and the ability to link around them, sort of like
+// a very basic HTML system for BBS with an ansi interface to scroll around
+// and follow links.
+
+// first port to class system from object -- DONE
+// second make sure it even works --- DONE (buggy)
+// then:
+
+// 1. change " 0 do begin
+ a := pos('' do inc(b);
+ Delete (S, a, 9 + b);
+ a := Pos('', S);
+ If a = 0 Then a := Length(S);
+ Delete (S, a, 4);
+ end;
+ end;
+
+ striplinks := s;
+end;
+
+Constructor TAnsiMenuHelp.Create;
+Begin
+ Inherited Create;
+End;
+
+Destructor TAnsiMenuHelp.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Function TAnsiMenuHelp.ReadKeywordData : Boolean;
+Var
+ Str : String;
+ Key : String;
+ Temp1 : Byte;
+ Temp2 : Byte;
+ Done : Boolean;
+ Buffer : Array[1..2048] of Char;
+Begin
+ SetTextBuf (HelpFile, Buffer);
+ Reset (HelpFile);
+
+ Done := False;
+
+ While Not Eof(HelpFile) And Not Done Do Begin
+ ReadLn (HelpFile, Str);
+
+ Temp1 := Pos(' ', Str);
+ If Temp1 = 0 Then Continue;
+
+ Key := Copy(Str, Temp1 + 10, Length(Str));
+
+ If Key <> CurKey Then Continue;
+
+ Lines := 0;
+
+ While Not Eof(HelpFile) Do Begin
+ ReadLn (HelpFile, Str);
+
+ If Pos('', Str) > 0 Then Begin
+ Done := True;
+ Break;
+ End;
+
+ Inc (Lines);
+
+ Text[Lines].Text := StripLinks(Str);
+ Text[Lines].Links := 0;
+ Str := strStripPipe(Str);
+
+ Repeat
+ Temp1 := Pos(' '>' Do Begin
+ Key := Key + Str[Temp1 + 8 + Temp2];
+ Inc(Temp2);
+ End;
+
+ Delete (Str, Temp1, 9 + Temp2);
+ Temp2 := Pos('', Str);
+ Delete (Str, Temp2, 4);
+
+ Text[Lines].Link[Text[Lines].Links].LinkLen := Temp2 - Temp1;
+ Text[Lines].Link[Text[Lines].Links].Key := Key;
+ Until False;
+ End;
+ End;
+
+ Close (HelpFile);
+
+ Result := Done And (Lines > 0);
+End;
+
+Procedure TAnsiMenuHelp.OpenHelp (X1, Y1, X2, Y2: Byte; FN, Keyword: String);
+Var
+ TopPage : Integer;
+ CurLine : Integer;
+ CurLPos : Byte;
+ WinSize : Integer;
+ LastPos : Byte;
+ LastKey : Array[1..10] of String[geMaxHelpKeyLen];
+
+ Procedure LinkOFF (LineNum: Word; YPos, LPos: Byte);
+ Var
+ S : String;
+ Begin
+ If Text[LineNum].Links = 0 Then Exit;
+
+ With Text[LineNum] Do
+ S := Copy(strStripPipe(Text), Link[LPos].LinkPos, Link[LPos].LinkLen);
+
+ WriteXY (X1 + Text[LineNum].Link[LPos].LinkPos, YPos, 9, S);
+ End;
+
+ Procedure DrawPage;
+ Var
+ Count1 : Byte;
+ Count2 : Byte;
+ Begin
+ For Count1 := Y1 to WinSize Do Begin
+ If TopPage + Count1 - Y1 <= Lines Then Begin
+ WriteXYPipe (X1 + 1, (Count1 - Y1) + Y1 + 1, 7, X2 - X1 - 1, Text[TopPage + (Count1 - Y1)].Text);
+ For Count2 := 1 to Text[TopPage + Count1 - 1].Links Do
+ LinkOFF (TopPage + Count1 - 1, Count1 - Y1 + Y1 + 1, Count2);
+ End Else
+ WriteXYPipe (X1 + 1, (Count1 - Y1) + Y1 + 1, 7, X2 - X1 - 1, '');
+ End;
+ End;
+
+ Procedure LinkON;
+ Var
+ S : String;
+ Begin
+ With Text[TopPage + CurLine - 1] Do
+ S := Copy(strStripPipe(Text), Link[CurLPos].LinkPos, Link[CurLPos].LinkLen);
+
+ WriteXY (X1 + Text[TopPage + CurLine - 1].Link[CurLPos].LinkPos, Y1 + CurLine, 31, S);
+
+ Session.io.AnsiGotoXY (X1 + Text[TopPage + CurLine - 1].Link[CurLPos].LinkPos, Y1 + CurLine);
+ End;
+
+ Procedure UpdateCursor;
+ Begin
+ If Text[TopPage + CurLine - 1].Links > 0 Then Begin
+ If CurLPos > Text[TopPage + CurLine - 1].Links Then CurLPos := Text[TopPage + CurLine - 1].Links;
+ If CurLPos < 1 Then CurLPos := 1;
+ LinkON;
+ End Else Begin
+ CurLPos := 1;
+ Session.io.AnsiGotoXY (X1 + 1, Y1 + CurLine);
+ End;
+ End;
+
+ Procedure PageDown;
+ Begin
+ If Lines > WinSize Then Begin
+ If TopPage + WinSize <= Lines - WinSize Then Begin
+ Inc (TopPage, WinSize);
+ Inc (CurLine, WinSize);
+ End Else Begin
+ TopPage := Lines - WinSize - 1;
+ CurLine := WinSize;
+ End;
+ End Else
+ CurLine := Lines;
+ End;
+
+Var
+ OK : Boolean;
+ Count : Byte;
+ Ch : Char;
+Begin
+ Assign (HelpFile, FN);
+ Reset (HelpFile);
+
+ If IoResult <> 0 Then Exit;
+
+ Close (HelpFile);
+
+ TopPage := 1;
+ CurLine := 1;
+ LastPos := 0;
+ WinSize := Y2 - Y1 - 1;
+ CurKey := Keyword;
+ OK := ReadKeywordData;
+
+ If Not OK and (CurKey <> 'INDEX') Then Begin
+ CurKey := 'INDEX';
+ OK := ReadKeywordData;
+ End;
+
+ If Not OK Then Exit;
+
+ Box := TAnsiMenuBox.Create;
+
+ Box.Shadow := False;
+ Box.FrameType := 1;
+ Box.BoxAttr := 8;
+ Box.BoxAttr2 := 8;
+ Box.HeadAttr := 15;
+ Box.Box3D := False;
+ Box.Header := ' Section : ' + CurKey + ' ';
+
+ Box.Open (X1, Y1, X2, Y2);
+
+ DrawPage;
+ UpdateCursor;
+
+ While OK Do Begin
+// Box.UpdateHeader (' Section : ' + CurKey + ' ');
+
+ TopPage := 1;
+ CurLine := 1;
+
+ DrawPage;
+
+ For Count := 1 to WinSize Do
+ If Text[Count].Links > 0 Then Begin
+ CurLine := Count;
+ Break;
+ End;
+
+ UpdateCursor;
+
+ Session.io.AllowArrow := True;
+
+ Repeat
+ Ch := Session.io.GetKey;
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #71 : If (TopPage > 1) or (CurLine > 1) Then Begin
+ TopPage := 1;
+ CurLine := 1;
+ DrawPage;
+ UpdateCursor;
+ End;
+ #72 : Begin
+ If (CurLine = 1) and (TopPage > 1) Then Begin
+ Dec (TopPage);
+ DrawPage;
+ End Else If CurLine > 1 Then Begin
+ LinkOFF(TopPage + CurLine - 1, CurLine + 1, CurLPos);
+ Dec (CurLine)
+ End;
+ UpdateCursor;
+ End;
+ #73 : Begin
+ If TopPage - WinSize > 0 Then Begin
+ Dec (TopPage, WinSize);
+ Dec (CurLine, WinSize);
+ End Else Begin
+ TopPage := 1;
+ CurLine := 1;
+ End;
+ DrawPage;
+ UpdateCursor;
+ End;
+ #75 : If (CurLPos > 1) and (Text[TopPage + CurLine - 1].Links > 0) Then Begin
+ LinkOFF(TopPage + CurLine - 1, CurLine + 1, CurLPos);
+ Dec(CurLPos);
+ LinkON;
+ End;
+ #77 : If CurLPos < Text[TopPage + CurLine - 1].Links Then Begin
+ LinkOFF(TopPage + CurLine - 1, CurLine + 1, CurLPos);
+ Inc(CurLPos);
+ LinkON;
+ End;
+ #79 : Begin
+ Repeat
+ PageDown;
+ Until TopPage >= Lines - WinSize - 1;
+ DrawPage;
+ UpdateCursor;
+ End;
+ #80 : Begin
+ If (CurLine = WinSize) and (TopPage + WinSize <= Lines) Then Begin
+ Inc(TopPage);
+ DrawPage;
+ End Else
+ If (CurLine < WinSize) And (TopPage + CurLine <= Lines) Then Begin
+ LinkOFF(TopPage + CurLine - 1, CurLine + 1, CurLPos);
+ Inc(CurLine);
+ End;
+ UpdateCursor;
+ End;
+ #81 : Begin
+ PageDown;
+ DrawPage;
+ UpdateCursor;
+ End;
+ End;
+ End Else Begin
+ Case Ch of
+ #13 : If Text[CurLine].Links > 0 Then Begin
+ If Text[CurLine].Link[CurLPos].Key = '@PREV' Then Begin
+ If LastPos = 0 Then
+ CurKey := 'INDEX'
+ Else Begin
+ CurKey := LastKey[LastPos];
+ Dec (LastPos);
+ End;
+ End Else Begin
+ If LastPos < 10 Then
+ Inc (LastPos)
+ Else
+ For Count := 1 to 9 Do LastKey[Count] := LastKey[Count + 1];
+
+ LastKey[LastPos] := CurKey;
+ CurKey := Text[CurLine].Link[CurLPos].Key;
+ End;
+
+ OK := ReadKeywordData;
+
+ If Not OK Then Begin
+ CurKey := 'INDEX';
+ OK := ReadKeywordData;
+ End;
+
+ Break;
+ End;
+ #27 : Begin
+ OK := False;
+ Break;
+ End;
+ End;
+ End;
+ Until False;
+ End;
+
+ Box.Close;
+ Box.Free;
+End;
+
+End.
diff --git a/mystic/bbs_ansi_menubox.pas b/mystic/bbs_ansi_menubox.pas
new file mode 100644
index 0000000..459f83a
--- /dev/null
+++ b/mystic/bbs_ansi_menubox.pas
@@ -0,0 +1,592 @@
+Unit bbs_Ansi_MenuBox;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ m_Types;
+
+Procedure WriteXY (X, Y, A: Byte; S: String);
+Procedure WriteXYPipe (X, Y, A, SZ : Byte; S: String);
+Function InXY (X, Y, Field, Max, Mode: Byte; Default: String) : String;
+Procedure VerticalLine (X, Y1, Y2 : Byte);
+Function ShowMsgBox (BoxType : Byte; Str : String) : Boolean;
+
+Type
+ TAnsiMenuBox = Class
+ Image : TConsoleImageRec;
+ HideImage : ^TConsoleImageRec;
+ FrameType : Byte;
+ BoxAttr : Byte;
+ Box3D : Boolean;
+ BoxAttr2 : Byte;
+ BoxAttr3 : Byte;
+ BoxAttr4 : Byte;
+ Shadow : Boolean;
+ ShadowAttr : Byte;
+ HeadAttr : Byte;
+ HeadType : Byte;
+ Header : String;
+ WasOpened : Boolean;
+
+ Constructor Create;
+ Destructor Destroy; Override;
+ Procedure Open (X1, Y1, X2, Y2: Byte);
+ Procedure Close;
+ Procedure Hide;
+ Procedure Show;
+ End;
+
+ TAnsiMenuListStatusProc = Procedure (Num: Word; Str: String);
+
+ TAnsiMenuListBoxRec = Record
+ Name : String;
+ Tagged : Byte; { 0 = false, 1 = true, 2 = never }
+ End;
+
+ TAnsiMenuList = Class
+ List : Array[1..65535] of ^TAnsiMenuListBoxRec;
+ Box : TAnsiMenuBox;
+ HiAttr : Byte;
+ LoAttr : Byte;
+ PosBar : Boolean;
+ Format : Byte;
+ LoChars : String;
+ HiChars : String;
+ ExitCode : Char;
+ Picked : Integer;
+ TopPage : Integer;
+ NoWindow : Boolean;
+ ListMax : Integer;
+ AllowTag : Boolean;
+ TagChar : Char;
+ TagKey : Char;
+ TagPos : Byte;
+ TagAttr : Byte;
+ Marked : Word;
+ StatusProc : TAnsiMenuListStatusProc;
+ Width : Integer;
+ WinSize : Integer;
+ X1 : Byte;
+ Y1 : Byte;
+ NoInput : Boolean;
+
+ Constructor Create;
+ Destructor Destroy; Override;
+ Procedure Open (BX1, BY1, BX2, BY2: Byte);
+ Procedure Close;
+ Procedure Add (Str: String; B: Byte);
+ Procedure Get (Num: Word; Var Str: String; Var B: Boolean);
+ Procedure SetStatusProc (P: TAnsiMenuListStatusProc);
+ Procedure Clear;
+ Procedure Delete (RecPos : Word);
+ Procedure Update;
+ End;
+
+Implementation
+
+Uses
+ m_Strings,
+ BBS_Core,
+ BBS_IO,
+ BBS_Common;
+
+Procedure WriteXY (X, Y, A: Byte; S: String);
+Begin
+ Session.io.AnsiGotoXY(X, Y);
+ Session.io.AnsiColor(A);
+ Session.io.OutRaw(S);
+End;
+
+Procedure WriteXYPipe (X, Y, A, SZ: Byte; S: String);
+Begin
+ Session.io.AnsiGotoXY(X, Y);
+ Session.io.AnsiColor(A);
+ Session.io.OutPipe(S);
+
+ While Screen.CursorX < SZ Do Session.io.BufAddChar(' ');
+End;
+
+Function InXY (X, Y, Field, Max, Mode: Byte; Default: String) : String;
+Begin
+ Session.io.AnsiGotoXY (X, Y);
+
+ InXY := Session.io.GetInput (Field, Max, Mode, Default);
+End;
+
+Procedure VerticalLine (X, Y1, Y2: Byte);
+Var
+ Count : Byte;
+Begin
+ For Count := Y1 to Y2 Do
+ WriteXY (X, Count, 112, '³');
+End;
+
+Function ShowMsgBox (BoxType : Byte; Str : String) : Boolean;
+Var
+ Len : Byte;
+ Len2 : Byte;
+ Pos : Byte;
+ MsgBox : TAnsiMenuBox;
+ Ch : Char;
+Begin
+ Result := True;
+
+{ 0 = ok box }
+{ 1 = y/n box }
+{ 2 = just box }
+{ 3 = just box dont close }
+
+ MsgBox := TAnsiMenuBox.Create;
+
+ Len := (80 - (Length(Str) + 3)) DIV 2;
+ Pos := 1;
+
+ MsgBox.Header := ' Info ';
+
+ If BoxType < 2 Then
+ MsgBox.Open (Len, 10, Len + Length(Str) + 3, 15)
+ Else
+ MsgBox.Open (Len, 10, Len + Length(Str) + 3, 14);
+
+ WriteXY (Len + 2, 12, 113, Str);
+
+ Case BoxType of
+ 0 : Begin
+ Len2 := (Length(Str) - 4) DIV 2;
+ WriteXY (Len + Len2 + 2, 14, 30, ' OK ');
+ Ch := Session.io.GetKey;
+ End;
+ 1 : Repeat
+ Len2 := (Length(Str) - 9) DIV 2;
+
+ WriteXY (Len + Len2 + 2, 14, 113, ' YES ');
+ WriteXY (Len + Len2 + 7, 14, 113, ' NO ');
+
+ If Pos = 1 Then
+ WriteXY (Len + Len2 + 2, 14, 30, ' YES ')
+ Else
+ WriteXY (Len + Len2 + 7, 14, 30, ' NO ');
+
+ Ch := Session.io.GetKey;
+
+ If Session.io.IsArrow Then
+ Case Ch of
+ #75 : Pos := 1;
+ #77 : Pos := 0;
+ End
+ Else
+ Case Ch of
+ #13 : Begin
+ Result := Boolean(Pos);
+ Break;
+ End;
+ #32 : If Pos = 0 Then Inc(Pos) Else Pos := 0;
+ 'N' : Pos := 0;
+ 'Y' : Pos := 1;
+ End;
+ Until False;
+ End;
+
+ MsgBox.Close;
+ MsgBox.Free;
+End;
+
+Constructor TAnsiMenuBox.Create;
+Begin
+ Inherited Create;
+
+ Shadow := True;
+ ShadowAttr := 0;
+ Header := '';
+ FrameType := 6;
+ Box3D := True;
+ BoxAttr := 15 + 7 * 16;
+ BoxAttr2 := 8 + 7 * 16;
+ BoxAttr3 := 15 + 7 * 16;
+ BoxAttr4 := 8 + 7 * 16;
+ HeadAttr := 0 + 7 * 16;
+ HeadType := 0;
+ HideImage := NIL;
+ WasOpened := False;
+
+ FillChar(Image, SizeOf(TConsoleImageRec), 0);
+
+ Session.io.BufFlush;
+End;
+
+Destructor TAnsiMenuBox.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Procedure TAnsiMenuBox.Open (X1, Y1, X2, Y2: Byte);
+Const
+ BF : Array[1..8] of String[8] =
+ ('ÚÄ¿³³ÀÄÙ',
+ 'ÉÍ»ººÈͼ',
+ 'ÖÄ·ººÓĽ',
+ 'Õ͸³³Ô;',
+ 'ÛßÛÛÛÛÜÛ',
+ 'ÛßÜÛÛßÜÛ',
+ ' ',
+ '.-.||`-''');
+Var
+ A : Integer;
+ B : Integer;
+ Ch : Char;
+Begin
+ If Not WasOpened Then
+ If Shadow Then
+ Screen.GetScreenImage(X1, Y1, X2 + 2{3}, Y2 + 1, Image)
+ Else
+ Screen.GetScreenImage(X1, Y1, X2, Y2, Image);
+
+ WasOpened := True;
+
+ B := X2 - X1 - 1;
+
+ If Not Box3D Then Begin
+ BoxAttr2 := BoxAttr;
+ BoxAttr3 := BoxAttr;
+ BoxAttr4 := BoxAttr;
+ End;
+
+ WriteXY (X1, Y1, BoxAttr, BF[FrameType][1] + strRep(BF[FrameType][2], B));
+ WriteXY (X2, Y1, BoxAttr4, BF[FrameType][3]);
+
+ For A := Y1 + 1 To Y2 - 1 Do Begin
+ WriteXY (X1, A, BoxAttr, BF[FrameType][4] + strRep(' ', B));
+ WriteXY (X2, A, BoxAttr2, BF[FrameType][5]);
+ End;
+
+ WriteXY (X1, Y2, BoxAttr3, BF[FrameType][6]);
+ WriteXY (X1+1, Y2, BoxAttr2, strRep(BF[FrameType][7], B) + BF[FrameType][8]);
+
+ If Header <> '' Then
+ Case HeadType of
+ 0 : WriteXY (X1 + 1 + (B - Length(Header)) DIV 2, Y1, HeadAttr, Header);
+ 1 : WriteXY (X1 + 1, Y1, HeadAttr, Header);
+ 2 : WriteXY (X2 - Length(Header), Y1, HeadAttr, Header);
+ End;
+
+ If Shadow Then Begin
+ For A := Y1 + 1 to Y2 + 1 Do
+ For B := X2 to X2 + 1 Do Begin
+ Ch := Screen.ReadCharXY(B, A);
+ WriteXY (B + 1, A, ShadowAttr, Ch);
+ End;
+
+ A := Y2 + 1;
+
+ For B := (X1 + 2) To (X2 + 2) Do Begin
+ Ch := Screen.ReadCharXY(B, A);
+ WriteXY (B, A, ShadowAttr, Ch);
+ End;
+ End;
+End;
+
+Procedure TAnsiMenuBox.Close;
+Begin
+ If WasOpened Then Session.io.RemoteRestore(Image);
+End;
+
+Procedure TAnsiMenuBox.Hide;
+Begin
+ If Assigned(HideImage) Then FreeMem(HideImage, SizeOf(TConsoleImageRec));
+
+ GetMem (HideImage, SizeOf(TConsoleImageRec));
+
+ Screen.GetScreenImage (Image.X1, Image.Y1, Image.X2, Image.Y2, HideImage^);
+
+ Session.io.RemoteRestore(Image);
+End;
+
+Procedure TAnsiMenuBox.Show;
+Begin
+ If Assigned (HideImage) Then Begin
+ Session.io.RemoteRestore(HideImage^);
+ FreeMem (HideImage, SizeOf(TConsoleImageRec));
+ HideImage := NIL;
+ End;
+End;
+
+Constructor TAnsiMenuList.Create;
+Begin
+ Inherited Create;
+
+ Box := TAnsiMenuBox.Create;
+ ListMax := 0;
+ HiAttr := 15 + 1 * 16;
+ LoAttr := 1 + 7 * 16;
+ PosBar := True;
+ Format := 0;
+ LoChars := #13#27;
+ HiChars := '';
+ NoWindow := False;
+ AllowTag := False;
+ TagChar := '*';
+ TagKey := #32;
+ TagPos := 0;
+ TagAttr := 15 + 7 * 16;
+ Marked := 0;
+ Picked := 1;
+ NoInput := False;
+ StatusProc := NIL;
+
+ Session.io.BufFlush;
+End;
+
+Procedure TAnsiMenuList.Clear;
+Var
+ Count : Word;
+Begin
+ For Count := 1 to ListMax Do
+ Dispose(List[Count]);
+
+ ListMax := 0;
+ Marked := 0;
+End;
+
+Procedure TAnsiMenuList.Delete (RecPos : Word);
+Var
+ Count : Word;
+Begin
+ If List[RecPos] <> NIL Then Begin
+ Dispose (List[RecPos]);
+
+ For Count := RecPos To ListMax - 1 Do
+ List[Count] := List[Count + 1];
+
+ Dec (ListMax);
+ End;
+End;
+
+Destructor TAnsiMenuList.Destroy;
+Begin
+ Box.Free;
+
+ Clear;
+
+ Inherited Destroy;
+End;
+
+// this class is very inefficient and needs to have updates redone
+// BarON
+// BarOFF
+// UpdatePercent
+
+Procedure TAnsiMenuList.Update;
+Var
+ A : LongInt;
+ S : String;
+ B : Integer;
+ C : Integer;
+Begin
+ For A := 0 to WinSize - 1 Do Begin
+ C := TopPage + A;
+
+ If C <= ListMax Then Begin
+ S := ' ' + List[C]^.Name + ' ';
+ Case Format of
+ 0 : S := strPadR (S, Width, ' ');
+ 1 : S := strPadL (S, Width, ' ');
+ 2 : S := strPadC (S, Width, ' ');
+ End;
+ End Else
+ S := strRep(' ', Width);
+
+ If C = Picked Then B := HiAttr Else B := LoAttr;
+
+ WriteXY (X1 + 1, Y1 + 1 + A, B, S);
+
+ If PosBar Then
+ WriteXY (X1 + Width + 1, Y1 + 1 + A, Box.BoxAttr2, #176);
+
+ If AllowTag Then
+ If (C <= ListMax) and (List[C]^.Tagged = 1) Then
+ WriteXY (TagPos, Y1 + 1 + A, TagAttr, TagChar)
+ Else
+ WriteXY (TagPos, Y1 + 1 + A, TagAttr, ' ');
+ End;
+
+ If PosBar Then
+ If (ListMax > 0) and (WinSize > 0) Then Begin
+ A := (Picked * WinSize) DIV ListMax;
+ If Picked >= ListMax Then A := Pred(WinSize);
+ If (A < 0) or (Picked = 1) Then A := 0;
+ WriteXY (X1 + Width + 1, Y1 + 1 + A, Box.BoxAttr2, #178);
+ End;
+End;
+
+Procedure TAnsiMenuList.Open (BX1, BY1, BX2, BY2 : Byte);
+Var
+ Ch : Char;
+ A : Word;
+ sPos : Word;
+ ePos : Word;
+ First : Boolean;
+Begin
+ If Not NoWindow Then
+ Box.Open (BX1, BY1, BX2, BY2);
+
+ X1 := BX1;
+ Y1 := BY1;
+
+ If (Picked < TopPage) or (Picked < 1) or (Picked > ListMax) or (TopPage < 1) or (TopPage > ListMax) Then Begin
+ Picked := 1;
+ TopPage := 1;
+ End;
+
+ Width := BX2 - X1 - 1;
+ WinSize := BY2 - Y1 - 1;
+ TagPos := X1 + 1;
+
+ If NoInput Then Exit;
+
+ Update;
+
+ Repeat
+ If Assigned(StatusProc) Then
+ If ListMax > 0 Then
+ StatusProc(Picked, List[Picked]^.Name)
+ Else
+ StatusProc(Picked, '');
+
+ Ch := Session.io.GetKey;
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #71 : If Picked > 1 Then Begin { home }
+ Picked := 1;
+ TopPage := 1;
+ Update;
+ End;
+ #72 : If (TopPage > 1) Or (Picked > 1) Then Begin { up arrow }
+ If Picked > 1 Then Dec (Picked);
+ If Picked < TopPage Then Dec (TopPage);
+ Update;
+ End;
+ #73,
+ #75 : If (TopPage > 1) or (Picked > 1) Then Begin { page up / left arrow }
+ If Picked - WinSize > 1 Then Dec (Picked, WinSize) Else Picked := 1;
+ If TopPage - WinSize < 1 Then TopPage := 1 Else Dec(TopPage, WinSize);
+ Update;
+ End;
+ #79 : If Picked < ListMax Then Begin { end }
+ If ListMax > WinSize Then TopPage := ListMax - WinSize + 1;
+ Picked := ListMax;
+ Update;
+ End;
+ #80 : Begin { down arrow }
+ If Picked < ListMax Then Inc (Picked);
+ If Picked > TopPage + WinSize - 1 Then Inc (TopPage);
+ Update;
+ End;
+ #77,
+ #81 : If ListMax > 0 Then Begin { page down / right arrow }
+ If ListMax > WinSize Then Begin
+ If Picked + WinSize > ListMax Then
+ Picked := ListMax
+ Else
+ Inc (Picked, WinSize);
+ Inc (TopPage, WinSize);
+ If TopPage + WinSize > ListMax Then TopPage := ListMax - WinSize + 1;
+ End Else Begin
+ Picked := ListMax;
+ End;
+ Update;
+ End;
+ Else
+ If Pos(Ch, HiChars) > 0 Then Begin
+ ExitCode := Ch;
+ Exit;
+ End;
+ End;
+ End Else
+ If AllowTag and (Ch = TagKey) and (List[Picked]^.Tagged <> 2) Then Begin
+ If (List[Picked]^.Tagged = 1) Then Begin
+ Dec (List[Picked]^.Tagged);
+ Dec (Marked);
+ End Else Begin
+ List[Picked]^.Tagged := 1;
+ Inc (Marked);
+ End;
+ If Picked < ListMax Then Inc (Picked);
+ If Picked > TopPage + WinSize - 1 Then Inc (TopPage);
+ End Else
+ If Pos(Ch, LoChars) > 0 Then Begin
+ ExitCode := Ch;
+ Exit;
+ End Else Begin
+ Ch := UpCase(Ch);
+ First := True;
+ sPos := Picked + 1;
+ ePos := ListMax;
+
+ If sPos > ListMax Then sPos := 1;
+
+ A := sPos;
+
+ While (A <= ePos) Do Begin
+ If UpCase(List[A]^.Name[1]) = Ch Then Begin
+ While A <> Picked Do Begin
+ If Picked < A Then Begin
+ If Picked < ListMax Then Inc (Picked);
+ If Picked > TopPage + WinSize - 1 Then Inc (TopPage);
+ End Else
+ If Picked > A Then Begin
+ If Picked > 1 Then Dec (Picked);
+ If Picked < TopPage Then Dec (TopPage);
+ End;
+ End;
+ Break;
+ End;
+
+ If (A = ListMax) and First Then Begin
+ A := 0;
+ sPos := 1;
+ ePos := Picked - 1;
+ First := False;
+ End;
+
+ Inc (A);
+ End;
+ End;
+ Until False;
+End;
+
+Procedure TAnsiMenuList.Close;
+Begin
+ If Not NoWindow Then Box.Close;
+End;
+
+Procedure TAnsiMenuList.Add (Str : String; B : Byte);
+Begin
+ Inc (ListMax);
+ New (List[ListMax]);
+
+ List[ListMax]^.Name := Str;
+ List[ListMax]^.Tagged := B;
+
+ If B = 1 Then Inc(Marked);
+End;
+
+Procedure TAnsiMenuList.Get (Num : Word; Var Str : String; Var B : Boolean);
+Begin
+ Str := '';
+ B := False;
+
+ If Num <= ListMax Then Begin
+ Str := List[Num]^.Name;
+ B := List[Num]^.Tagged = 1;
+ End;
+End;
+
+Procedure TAnsiMenuList.SetStatusProc (P : TAnsiMenuListStatusProc);
+Begin
+ StatusProc := P;
+End;
+
+End.
diff --git a/mystic/bbs_ansi_menuform.pas b/mystic/bbs_ansi_menuform.pas
new file mode 100644
index 0000000..f3c87f7
--- /dev/null
+++ b/mystic/bbs_ansi_menuform.pas
@@ -0,0 +1,700 @@
+Unit bbs_Ansi_MenuForm;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ m_Types,
+ bbs_ansi_MenuInput;
+
+Const
+ FormMaxItems = 50;
+
+Const
+ YesNoStr : Array[False..True] of String[03] = ('No', 'Yes');
+
+Type
+ FormItemType = (
+ ItemNone,
+ ItemString,
+ ItemBoolean,
+ ItemByte,
+ ItemWord,
+ ItemLong,
+ ItemToggle,
+ ItemPath,
+ ItemChar,
+ ItemAttr,
+ ItemFlags,
+ ItemDate,
+ ItemPass,
+ ItemPipe,
+ ItemCaps,
+ ItemBits
+ );
+
+ FormItemPTR = ^FormItemRec;
+ FormItemRec = Record
+ HotKey : Char;
+ Desc : String[60];
+ Help : String[120];
+ DescX : Byte;
+ DescY : Byte;
+ DescSize : Byte;
+ FieldX : Byte;
+ FieldY : Byte;
+ FieldSize : Byte;
+ ItemType : FormItemType;
+ MaxSize : Byte;
+ MinNum : LongInt;
+ MaxNum : LongInt;
+ S : ^String;
+ O : ^Boolean;
+ B : ^Byte;
+ W : ^Word;
+ L : ^LongInt;
+ C : ^Char;
+ F : ^TMenuFormFlagsRec;
+ Toggle : String[68];
+ End;
+
+ TAnsiMenuFormHelpProc = Procedure (Item: FormItemRec);
+ TAnsiMenuFormDrawProc = Procedure (Hi: Boolean); // not functional
+ TAnsiMenuFormDataProc = Procedure; // not functional
+
+ TAnsiMenuForm = Class
+ Private
+ Function GetColorAttr (C: Byte) : Byte;
+ Function DrawAccessFlags (Var Flags: TMenuFormFlagsRec) : String;
+ Procedure EditAccessFlags (Var Flags: TMenuFormFlagsRec);
+ Procedure AddBasic (HK: Char; D: String; X, Y, FX, FY, DS, FS, MS: Byte; I: FormItemType; P: Pointer; H: String);
+ Procedure BarON;
+ Procedure BarOFF (RecPos: Word);
+ Procedure FieldWrite (RecPos : Word);
+ Procedure EditOption;
+ Public
+ Input : TAnsiMenuInput;
+ HelpProc : TAnsiMenuFormHelpProc;
+ DrawProc : TAnsiMenuFormDrawProc;
+ DataProc : TAnsiMenuFormDataProc;
+ ItemData : Array[1..FormMaxItems] of FormItemPTR;
+ Items : Word;
+ ItemPos : Word;
+ Changed : Boolean;
+ ExitOnFirst : Boolean;
+ ExitOnLast : Boolean;
+ WasHiExit : Boolean;
+ WasFirstExit : Boolean;
+ WasLastExit : Boolean;
+ LoExitChars : String[30];
+ HiExitChars : String[30];
+ HelpX : Byte;
+ HelpY : Byte;
+ HelpSize : Byte;
+ HelpColor : Byte;
+ cLo : Byte;
+ cHi : Byte;
+ cData : Byte;
+ cLoKey : Byte;
+ cHiKey : Byte;
+ cField1 : Byte;
+ cField2 : Byte;
+
+ Constructor Create;
+ Destructor Destroy; Override;
+
+ Procedure Clear;
+ Procedure AddNone (HK: Char; D: String; X, Y, DS: Byte; H: String);
+ Procedure AddStr (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+ Procedure AddPipe (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+ Procedure AddPath (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+ Procedure AddPass (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+ Procedure AddBol (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; P: Pointer; H: String);
+ Procedure AddByte (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Byte; P: Pointer; H: String);
+ Procedure AddWord (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Word; P: Pointer; H: String);
+ Procedure AddLong (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: LongInt; P: Pointer; H: String);
+ Procedure AddTog (HK: Char; D: String; X, Y, FX, FY, DS, FS, MN, MX: Byte; TG: String; P: Pointer; H: String);
+ Procedure AddChar (HK: Char; D: String; X, Y, FX, FY, DS, MN, MX: Byte; P: Pointer; H: String);
+ Procedure AddAttr (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
+ Procedure AddFlag (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
+ Procedure AddDate (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
+ Procedure AddCaps (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+ Procedure AddBits (HK: Char; D: String; X, Y, FX, FY, DS: Byte; Flag: LongInt; P: Pointer; H: String);
+ Function Execute : Char;
+ End;
+
+Implementation
+
+Uses
+ m_FileIO,
+ m_Strings,
+ bbs_Core,
+ bbs_Ansi_MenuBox;
+
+Constructor TAnsiMenuForm.Create;
+Begin
+ Inherited Create;
+
+ Input := TAnsiMenuInput.Create;
+ HelpProc := NIL;
+ DrawProc := NIL;
+ DataProc := NIL;
+ cLo := 0 + 7 * 16;
+ cHi := 11 + 1 * 16;
+ cData := 1 + 7 * 16;
+ cLoKey := 15 + 7 * 16;
+ cHiKey := 15 + 1 * 16;
+ cField1 := 15 + 1 * 16;
+ cField2 := 7 + 1 * 16;
+ HelpX := 5;
+ HelpY := 24;
+ HelpColor := 15;
+ HelpSize := 75;
+ WasHiExit := False;
+ WasFirstExit := False;
+ ExitOnFirst := False;
+ WasLastExit := False;
+ ExitOnLast := False;
+
+ Clear;
+End;
+
+Destructor TAnsiMenuForm.Destroy;
+Begin
+ Clear;
+
+ Input.Free;
+
+ Inherited Destroy;
+End;
+
+Procedure TAnsiMenuForm.Clear;
+Var
+ Count : Word;
+Begin
+ For Count := 1 to Items Do
+ Dispose(ItemData[Count]);
+
+ Items := 0;
+ ItemPos := 1;
+ Changed := False;
+End;
+
+Function TAnsiMenuForm.DrawAccessFlags (Var Flags: TMenuFormFlagsRec) : String;
+Var
+ Str : String;
+ Ch : Char;
+Begin
+ Str := '';
+
+ For Ch := 'A' to 'Z' Do
+ If Ord(Ch) - 64 in Flags Then Str := Str + Ch Else Str := Str + '-';
+
+ Result := Str;
+End;
+
+Procedure TAnsiMenuForm.EditAccessFlags (Var Flags: TMenuFormFlagsRec);
+Var
+ Box : TAnsiMenuBox;
+ Ch : Char;
+Begin
+ Box := TAnsiMenuBox.Create;
+
+ Box.Open (25, 11, 56, 14);
+
+ WriteXY (28, 13, 113, 'A-Z to toggle, ESC to Quit');
+
+ Repeat
+ WriteXY (28, 12, 112, DrawAccessFlags(Flags));
+
+ Ch := UpCase(Session.io.GetKey);
+
+ Case Ch of
+ #27 : Break;
+ 'A'..
+ 'Z' : Begin
+ If Ord(Ch) - 64 in Flags Then
+ Flags := Flags - [Ord(Ch) - 64]
+ Else
+ Flags := Flags + [Ord(Ch) - 64];
+
+ Changed := True;
+ End;
+ End;
+ Until False;
+
+ Box.Close;
+ Box.Free;
+End;
+
+Function TAnsiMenuForm.GetColorAttr (C: Byte) : Byte;
+Var
+ FG : Byte;
+ BG : Byte;
+ Box : TAnsiMenuBox;
+ A : Byte;
+ B : Byte;
+ Ch : Char;
+Begin
+ FG := C AND $F;
+ BG := (C SHR 4) AND 7;
+
+ Box := TAnsiMenuBox.Create;
+
+ Box.Header := ' Select color ';
+
+ Box.Open (30, 7, 51, 18);
+
+ Repeat
+ For A := 0 to 9 Do
+ WriteXY (31, 8 + A, Box.BoxAttr, ' ');
+
+ For A := 0 to 7 Do
+ For B := 0 to 15 Do
+ WriteXY (33 + B, 9 + A, B + A * 16, 'þ');
+
+ WriteXY (37, 18, FG + BG * 16, ' Sample ');
+
+ WriteXYPipe (31 + FG, 8 + BG, 15, 5, 'Û|23ßßß|08Ü');
+ WriteXYPipe (31 + FG, 9 + BG, 15, 5, 'Û|23 |08Û');
+ WriteXYPipe (31 + FG, 10 + BG, 15, 5, '|23ß|08ÜÜÜ|08Û');
+ WriteXY (33 + FG, 9 + BG, FG + BG * 16, 'þ');
+
+ Ch := Session.io.GetKey;
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #72 : If BG > 0 Then Dec(BG);
+ #75 : If FG > 0 Then Dec(FG);
+ #77 : If FG < 15 Then Inc(FG);
+ #80 : If BG < 7 Then Inc(BG);
+ End;
+ End Else
+ Case Ch of
+ #13 : Begin
+ GetColorAttr := FG + BG * 16;
+ Break;
+ End;
+ #27 : Begin
+ GetColorAttr := C;
+ Break;
+ End;
+ End;
+ Until False;
+
+ Box.Close;
+ Box.Free;
+End;
+
+Procedure TAnsiMenuForm.AddBasic (HK: Char; D: String; X, Y, FX, FY, DS, FS, MS: Byte; I: FormItemType; P: Pointer; H: String);
+Begin
+ Inc (Items);
+
+ New (ItemData[Items]);
+
+ With ItemData[Items]^ Do Begin
+ HotKey := HK;
+ Desc := D;
+ DescX := X;
+ DescY := Y;
+ DescSize := DS;
+ Help := H;
+ ItemType := I;
+ FieldSize := FS;
+ MaxSize := MS;
+ FieldX := FX;
+ FieldY := FY;
+
+ Case ItemType of
+ ItemCaps,
+ ItemPipe,
+ ItemPass,
+ ItemDate,
+ ItemPath,
+ ItemString : S := P;
+ ItemBoolean : O := P;
+ ItemAttr,
+ ItemToggle,
+ ItemByte : B := P;
+ ItemWord : W := P;
+ ItemBits,
+ ItemLong : L := P;
+ ItemChar : C := P;
+ ItemFlags : F := P;
+ End;
+ End;
+End;
+
+Procedure TAnsiMenuForm.AddNone (HK: Char; D: String; X, Y, DS: Byte; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, 0, 0, DS, 0, 0, ItemNone, NIL, H);
+End;
+
+Procedure TAnsiMenuForm.AddChar (HK: Char; D: String; X, Y, FX, FY, DS, MN, MX: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, 1, 1, ItemChar, P, H);
+
+ ItemData[Items]^.MinNum := MN;
+ ItemData[Items]^.MaxNum := MX;
+End;
+
+Procedure TAnsiMenuForm.AddStr (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemString, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddPipe (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemPipe, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddCaps (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemCaps, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddPass (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemPass, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddPath (HK: Char; D: String; X, Y, FX, FY, DS, FS, MX: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemPath, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddBol (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, 3, ItemBoolean, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddBits (HK: Char; D: String; X, Y, FX, FY, DS: Byte; Flag: LongInt; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, 3, 3, ItemBits, P, H);
+
+ ItemData[Items]^.MaxNum := Flag;
+End;
+
+Procedure TAnsiMenuForm.AddByte (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, Length(strI2S(MX)), ItemByte, P, H);
+
+ ItemData[Items]^.MinNum := MN;
+ ItemData[Items]^.MaxNum := MX;
+End;
+
+Procedure TAnsiMenuForm.AddWord (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: Word; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, Length(strI2S(MX)), ItemWord, P, H);
+
+ ItemData[Items]^.MinNum := MN;
+ ItemData[Items]^.MaxNum := MX;
+End;
+
+Procedure TAnsiMenuForm.AddLong (HK: Char; D: String; X, Y, FX, FY, DS, FS: Byte; MN, MX: LongInt; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, Length(strI2S(MX)), ItemLong, P, H);
+
+ ItemData[Items]^.MinNum := MN;
+ ItemData[Items]^.MaxNum := MX;
+End;
+
+Procedure TAnsiMenuForm.AddTog (HK: Char; D: String; X, Y, FX, FY, DS, FS, MN, MX: Byte; TG: String; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, FS, MX, ItemToggle, P, H);
+
+ ItemData[Items]^.Toggle := TG;
+ ItemData[Items]^.MinNum := MN;
+End;
+
+Procedure TAnsiMenuForm.AddAttr (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, 8, 8, ItemAttr, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddFlag (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, 26, 26, ItemFlags, P, H);
+End;
+
+Procedure TAnsiMenuForm.AddDate (HK: Char; D: String; X, Y, FX, FY, DS: Byte; P: Pointer; H: String);
+Begin
+ If Items = FormMaxItems Then Exit;
+
+ AddBasic (HK, D, X, Y, FX, FY, DS, 8, 8, ItemDate, P, H);
+End;
+
+Procedure TAnsiMenuForm.BarON;
+Var
+ A : Byte;
+Begin
+ If ItemPos = 0 Then Exit;
+
+ WriteXY (ItemData[ItemPos]^.DescX, ItemData[ItemPos]^.DescY, cHi, strPadR(ItemData[ItemPos]^.Desc, ItemData[ItemPos]^.DescSize, ' '));
+
+ A := Pos(ItemData[ItemPos]^.HotKey, strUpper(ItemData[ItemPos]^.Desc));
+
+ If A > 0 Then
+ WriteXY (ItemData[ItemPos]^.DescX + A - 1, ItemData[ItemPos]^.DescY, cHiKey, ItemData[ItemPos]^.Desc[A]);
+
+ If HelpSize > 0 Then
+ If Assigned(HelpProc) Then
+ HelpProc(ItemData[ItemPos]^)
+ Else
+ WriteXYPipe (HelpX, HelpY, HelpColor, HelpSize, ItemData[ItemPos]^.Help);
+End;
+
+Procedure TAnsiMenuForm.BarOFF (RecPos: Word);
+Var
+ A : Byte;
+Begin
+ If RecPos = 0 Then Exit;
+
+ With ItemData[RecPos]^ Do Begin
+ WriteXY (DescX, DescY, cLo, strPadR(Desc, DescSize, ' '));
+
+ A := Pos(HotKey, strUpper(Desc));
+
+ If A > 0 Then
+ WriteXY (DescX + A - 1, DescY, cLoKey, Desc[A]);
+ End;
+End;
+
+Procedure TAnsiMenuForm.FieldWrite (RecPos : Word);
+Begin
+ // This could be changed to case itemtype and save display into string
+ // variable. Then we would only require a single Screen.WriteXY function.
+ // It would be a tiny bit slower (obviously, not really noticable) but
+ // would reduce code size.
+
+ With ItemData[RecPos]^ Do Begin
+ Case ItemType of
+ ItemPass : WriteXY (FieldX, FieldY, cData, strPadR(strRep('*', Length(S^)), FieldSize, ' '));
+ ItemCaps,
+ ItemDate,
+ ItemPath,
+ ItemString : WriteXY (FieldX, FieldY, cData, strPadR(S^, FieldSize, ' '));
+ ItemBoolean : WriteXY (FieldX, FieldY, cData, strPadR(YesNoStr[O^], FieldSize, ' '));
+ ItemByte : WriteXY (FieldX, FieldY, cData, strPadR(strI2S(B^), FieldSize, ' '));
+ ItemWord : WriteXY (FieldX, FieldY, cData, strPadR(strI2S(W^), FieldSize, ' '));
+ ItemLong : WriteXY (FieldX, FieldY, cData, strPadR(strI2S(L^), FieldSize, ' '));
+ ItemToggle : WriteXY (FieldX, FieldY, cData, StrPadR(strWordGet(B^ + 1 - MinNum, Toggle, ' '), FieldSize, ' '));
+ ItemChar : WriteXY (FieldX, FieldY, cData, C^);
+ ItemAttr : WriteXY (FieldX, FieldY, B^, ' Sample ');
+ ItemFlags : WriteXY (FieldX, FieldY, cData, DrawAccessFlags(F^));
+ ItemPipe : WriteXYPipe (FieldX, FieldY, 7, FieldSize, S^);
+ ItemBits : WriteXY (FieldX, FieldY, cData, strPadR(YesNoStr[L^ AND MaxNum <> 0], FieldSize, ' '));
+ End;
+ End;
+End;
+
+Procedure TAnsiMenuForm.EditOption;
+Var
+ TempStr : String;
+ TempByte : Byte;
+ TempLong : LongInt;
+Begin
+ With ItemData[ItemPos]^ Do
+ Case ItemType of
+ ItemPass,
+ ItemCaps : S^ := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 2, S^);
+ ItemDate : S^ := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 3, S^);
+ ItemPipe,
+ ItemString : S^ := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 1, S^);
+ ItemBoolean : Begin
+ O^ := Not O^;
+ Changed := True;
+ End;
+ ItemByte : B^ := Byte(Input.GetNum(FieldX, FieldY, FieldSize, MaxSize, MinNum, MaxNum, B^));
+ ItemWord : W^ := Word(Input.GetNum(FieldX, FieldY, FieldSize, MaxSize, MinNum, MaxNum, W^));
+ ItemLong : L^ := LongInt(Input.GetNum(FieldX, FieldY, FieldSize, MaxSize, MinNum, MaxNum, L^));
+ ItemToggle : Begin
+ If B^ < MaxSize Then Inc(B^) Else B^ := MinNum;
+ Changed := True;
+ End;
+ ItemPath : S^ := DirSlash(Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 1, S^));
+ ItemChar : Begin
+ TempStr := Input.GetStr(FieldX, FieldY, FieldSize, MaxSize, 1, C^);
+ Changed := TempStr[1] <> C^;
+ C^ := TempStr[1];
+ End;
+ ItemAttr : Begin
+ TempByte := GetColorAttr(B^);
+ Changed := TempByte <> B^;
+ B^ := TempByte;
+ End;
+ ItemFlags : EditAccessFlags(F^);
+ ItemBits : Begin
+ Changed := True;
+ TempLong := L^;
+ TempLong := TempLong XOR MaxNum;
+ L^ := TempLong;
+ End;
+ End;
+
+ FieldWrite (ItemPos);
+
+ Changed := Changed or Input.Changed;
+End;
+
+Function TAnsiMenuForm.Execute : Char;
+Var
+ Count : Word;
+ Ch : Char;
+ NewPos : Word;
+ NewXPos : Word;
+Begin
+ Session.io.AllowArrow := True;
+
+ WasHiExit := False;
+
+ Input.Attr := cField1;
+ Input.FillAttr := cField2;
+
+ For Count := 1 to Items Do Begin
+ BarOFF(Count);
+ FieldWrite(Count);
+ End;
+
+ BarON;
+
+ Repeat
+ Changed := Changed or Input.Changed;
+
+ Ch := UpCase(Session.io.GetKey);
+
+ If Session.io.IsArrow Then Begin
+ If Pos(Ch, HiExitChars) > 0 Then Begin
+ WasHiExit := True;
+ Result := Ch;
+ Break;
+ End;
+
+ Case Ch of
+ #72 : If ItemPos > 1 Then Begin
+ BarOFF(ItemPos);
+ Dec(ItemPos);
+ BarON;
+ End Else
+ If ExitOnFirst Then Begin
+ WasFirstExit := True;
+ Result := Ch;
+ Break;
+ End;
+ #75 : Begin
+ NewPos := 0;
+ NewXPos := 0;
+
+ For Count := 1 to Items Do
+ If (ItemData[Count]^.DescY = ItemData[ItemPos]^.DescY) and
+ (ItemData[Count]^.DescX < ItemData[ItemPos]^.DescX) and
+ (ItemData[Count]^.DescX > NewXPos) Then Begin
+ NewXPos := ItemData[Count]^.DescX;
+ NewPos := Count;
+ End;
+
+ If NewPos > 0 Then Begin
+ BarOFF(ItemPos);
+ ItemPos := NewPos;
+ BarON;
+ End;
+ End;
+ #77 : Begin
+ NewPos := 0;
+ NewXPos := 80;
+
+ For Count := 1 to Items Do
+ If (ItemData[Count]^.DescY = ItemData[ItemPos]^.DescY) and
+ (ItemData[Count]^.DescX > ItemData[ItemPos]^.DescX) and
+ (ItemData[Count]^.DescX < NewXPos) Then Begin
+ NewXPos := ItemData[Count]^.DescX;
+ NewPos := Count;
+ End;
+
+ If NewPos > 0 Then Begin
+ BarOFF(ItemPos);
+ ItemPos := NewPos;
+ BarON;
+ End;
+ End;
+ #80 : If ItemPos < Items Then Begin
+ BarOFF(ItemPos);
+ Inc(ItemPos);
+ BarON;
+ End Else
+ If ExitOnLast Then Begin
+ WasLastExit := True;
+ Result := Ch;
+ Break;
+ End;
+ End;
+ End Else Begin
+ Case Ch of
+ #13 : If ItemPos > 0 Then
+ If ItemData[ItemPos]^.ItemType = ItemNone Then Begin
+ Result := ItemData[ItemPos]^.HotKey;
+ Break;
+ End Else
+ EditOption;
+ #27 : Begin
+ Result := #27;
+ Break;
+ End;
+ Else
+ If Pos(Ch, LoExitChars) > 0 Then Begin
+ Result := Ch;
+ Break;
+ End;
+ End;
+
+ For Count := 1 to Items Do
+ If ItemData[Count]^.HotKey = Ch Then Begin
+ BarOFF(ItemPos);
+ ItemPos := Count;
+ BarON;
+
+ If ItemData[ItemPos]^.ItemType = ItemNone Then Begin
+ Execute := ItemData[ItemPos]^.HotKey;
+ BarOFF(ItemPos);
+ Exit;
+ End Else
+ EditOption;
+ End;
+ End;
+ Until False;
+
+ BarOFF(ItemPos);
+End;
+
+End.
\ No newline at end of file
diff --git a/mystic/bbs_ansi_menuinput.pas b/mystic/bbs_ansi_menuinput.pas
new file mode 100644
index 0000000..5a7126c
--- /dev/null
+++ b/mystic/bbs_ansi_menuinput.pas
@@ -0,0 +1,199 @@
+Unit bbs_Ansi_MenuInput;
+
+// ANSI ports of MDL menu/input libraries
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ m_Strings,
+ bbs_Ansi_MenuBox;
+
+Type
+ TAnsiMenuInput = Class
+ HiChars : String[40];
+ LoChars : String[40];
+ ExitCode : Char;
+ Attr : Byte;
+ FillChar : Char;
+ FillAttr : Byte;
+ Changed : Boolean;
+
+ Constructor Create;
+ Destructor Destroy; Override;
+
+ Function GetStr (X, Y, Field, Len, Mode: Byte; Default: String) : String;
+ Function GetNum (X, Y, Field, Len: Byte; Min, Max, Default: LongInt) : LongInt;
+ Function GetChar (X, Y : Byte; Default: Char) : Char;
+ Function GetEnter (X, Y, Len: Byte; Default : String) : Boolean;
+ Function GetYN (X, Y : Byte; Default: Boolean) : Boolean;
+ End;
+
+Implementation
+
+Uses
+ bbs_Core,
+ bbs_Common,
+ bbs_IO;
+
+Constructor TAnsiMenuInput.Create;
+Begin
+ Inherited Create;
+
+ LoChars := #13;
+ HiChars := '';
+ Attr := 15 + 1 * 16;
+ FillAttr := 7 + 1 * 16;
+ FillChar := '°';
+ Changed := False;
+End;
+
+Destructor TAnsiMenuInput.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Function TAnsiMenuInput.GetYN (X, Y : Byte; Default: Boolean) : Boolean;
+Var
+ Ch : Char;
+ Res : Boolean;
+ YS : Array[False..True] of String[3] = ('No ', 'Yes');
+Begin
+ ExitCode := #0;
+ Changed := False;
+
+ Session.io.AnsiGotoXY (X, Y);
+
+ Res := Default;
+
+ Repeat
+ WriteXY (X, Y, Attr, YS[Res]);
+
+ Ch := Session.io.GetKey;
+
+ If Session.io.IsArrow Then Begin
+ If Pos(Ch, HiChars) > 0 Then Begin
+ ExitCode := Ch;
+ Break;
+ End;
+ End Else
+ Case Ch of
+ #13,
+ #32 : Res := Not Res;
+ Else
+ If Pos(Ch, LoChars) > 0 Then Begin
+ ExitCode := Ch;
+ Break;
+ End;
+ End;
+ Until False;
+
+ Changed := (Res <> Default);
+ GetYN := Res;
+End;
+
+Function TAnsiMenuInput.GetChar (X, Y : Byte; Default: Char) : Char;
+Var
+ Ch : Char;
+ Res : Char;
+Begin
+ ExitCode := #0;
+ Changed := False;
+ Res := Default;
+
+ Session.io.AnsiGotoXY (X, Y);
+
+ Repeat
+ WriteXY (X, Y, Attr, Res);
+
+ Ch := Session.io.GetKey;
+
+ If Session.io.IsArrow Then Begin
+ If Pos(Ch, HiChars) > 0 Then Begin
+ ExitCode := Ch;
+ Break;
+ End;
+ End Else Begin
+ If Ch = #27 Then Res := Default;
+
+ If Pos(Ch, LoChars) > 0 Then Begin
+ ExitCode := Ch;
+ Break;
+ End;
+
+ If Ord(Ch) > 31 Then Res := Ch;
+ End;
+ Until False;
+
+ GetChar := Res;
+End;
+
+Function TAnsiMenuInput.GetEnter (X, Y, Len: Byte; Default : String) : Boolean;
+Var
+ Ch : Char;
+ Res : Boolean;
+Begin
+ ExitCode := #0;
+ Changed := False;
+
+ WriteXY (X, Y, Attr, strPadR(Default, Len, ' '));
+
+ Session.io.AnsiGotoXY (X, Y);
+
+ Repeat
+ Ch := Session.io.GetKey;
+ Res := Ch = #13;
+
+ If Session.io.IsArrow Then Begin
+ If Pos(Ch, HiChars) > 0 Then Begin
+ ExitCode := Ch;
+ Break;
+ End;
+ End Else
+ If Pos(Ch, LoChars) > 0 Then Begin
+ ExitCode := Ch;
+ Break;
+ End;
+ Until Res;
+
+ Changed := Res;
+ GetEnter := Res;
+End;
+
+Function TAnsiMenuInput.GetStr (X, Y, Field, Len, Mode : Byte; Default : String) : String;
+{ mode options: }
+{ 0 = numbers only }
+{ 1 = as typed }
+{ 2 = all caps }
+{ 3 = date input }
+Var
+ Str : String;
+Begin
+ Session.io.AnsiGotoXY(X, Y);
+
+ Case Mode of
+ 0,
+ 1 : Str := Session.io.GetInput(Field, Len, 11, Default);
+ 2 : Str := Session.io.GetInput(Field, Len, 12, Default);
+ 3 : Str := Session.io.GetInput(Field, Len, 15, Default);
+ End;
+
+ Changed := (Str <> Default);
+ Result := Str;
+End;
+
+Function TAnsiMenuInput.GetNum (X, Y, Field, Len: Byte; Min, Max, Default: LongInt) : LongInt;
+Var
+ N : LongInt;
+Begin
+ N := Default;
+ N := strS2I(Self.GetStr(X, Y, Field, Len, 0, strI2S(N)));
+
+ If N < Min Then N := Min;
+ If N > Max Then N := Max;
+
+ GetNum := N;
+End;
+
+End.
diff --git a/mystic/bbs_cfg_archive.pas b/mystic/bbs_cfg_archive.pas
new file mode 100644
index 0000000..b6c9a19
--- /dev/null
+++ b/mystic/bbs_cfg_archive.pas
@@ -0,0 +1,148 @@
+Unit bbs_cfg_Archive;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Configuration_ArchiveEditor;
+
+Implementation
+
+Uses
+ m_FileIO,
+ m_Strings,
+ bbs_Common,
+ bbs_Ansi_MenuBox,
+ bbs_Ansi_MenuForm;
+
+Procedure EditArchive (Var Arc: RecArchive);
+Var
+ Box : TAnsiMenuBox;
+ Form : TAnsiMenuForm;
+ Topic : String;
+Begin
+ Topic := '';
+ Box := TAnsiMenuBox.Create;
+ Form := TAnsiMenuForm.Create;
+
+ Box.Header := ' Archive Editor: ' + Arc.Desc + ' ';
+
+ Box.Open (13, 5, 67, 15);
+
+ Form.HelpSize := 0;
+
+ VerticalLine (28, 7, 13);
+
+ Form.AddBol ('A', ' Active ' , 20, 7, 30, 7, 8, 3, @Arc.Active, '');
+ Form.AddStr ('X', ' Extension ' , 17, 8, 30, 8, 11, 4, 4, @Arc.Ext, '');
+ Form.AddTog ('O', ' OS ' , 24, 9, 30, 9, 4, 7, 0, 2, 'Windows Linux OSX', @Arc.OSType, '');
+ Form.AddStr ('D', ' Description ' , 15, 10, 30, 10, 13, 30, 30, @Arc.Desc, '');
+ Form.AddStr ('P', ' Pack Cmd ' , 18, 11, 30, 11, 10, 35, 80, @Arc.Pack, '');
+ Form.AddStr ('U', ' Unpack Cmd ' , 16, 12, 30, 12, 12, 35, 80, @Arc.Unpack, '');
+ Form.AddStr ('V', ' View Cmd ' , 18, 13, 30, 13, 10, 35, 80, @Arc.View, '');
+
+ Form.Execute;
+ Box.Close;
+
+ Form.Free;
+ Box.Free;
+End;
+
+Procedure Configuration_ArchiveEditor;
+Var
+ Box : TAnsiMenuBox;
+ List : TAnsiMenuList;
+ F : TBufFile;
+ Arc : RecArchive;
+
+ // SORT THIS LIST BY NON CASE SENSITIVE ARCHIVE EXTENSION
+ Procedure MakeList;
+ Var
+ OS : String;
+ Begin
+ List.Clear;
+
+ F.Reset;
+ While Not F.Eof Do Begin
+ F.Read (Arc);
+
+ Case Arc.OSType of
+ 0 : OS := 'Windows';
+ 1 : OS := 'Linux ';
+ 2 : OS := 'OSX';
+ End;
+
+ List.Add (strPadR(YesNoStr[Arc.Active], 5, ' ') + strPadR(Arc.Ext, 7, ' ') + OS + ' ' + Arc.Desc, 0);
+ End;
+
+ List.Add ('', 2);
+ End;
+
+Begin
+ F := TBufFile.Create(SizeOf(RecArchive));
+
+ F.Open (Config.DataPath + 'archive.dat', fmOpenCreate, fmReadWrite + fmDenyNone, SizeOf(RecArchive));
+
+ Box := TAnsiMenuBox.Create;
+ List := TAnsiMenuList.Create;
+
+ Box.Header := ' Archive Editor ';
+ List.NoWindow := True;
+ List.LoChars := #01#04#13#27;
+
+ Box.Open (13, 5, 67, 20);
+
+ WriteXY (15, 6, 112, 'Use Ext OSID Description');
+ WriteXY (15, 7, 112, strRep('Ä', 51));
+ WriteXY (15, 18, 112, strRep('Ä', 51));
+ WriteXY (18, 19, 112, '(CTRL/A) Add (CTRL/D) Delete (ENTER) Edit');
+
+ Repeat
+ MakeList;
+
+ List.Open (13, 7, 67, 18);
+ List.Close;
+
+ Case List.ExitCode of
+ #04 : If List.Picked < List.ListMax Then
+ If ShowMsgBox(1, 'Delete this entry?') Then Begin
+ F.RecordDelete (List.Picked);
+ MakeList;
+ End;
+ #01 : Begin
+ F.RecordInsert (List.Picked);
+
+ Arc.OSType := OSType;
+ Arc.Active := False;
+ Arc.Desc := 'New archive';
+ Arc.Ext := 'NEW';
+ Arc.Pack := '';
+ Arc.Unpack := '';
+ Arc.View := '';
+
+ F.Write (Arc);
+
+ MakeList;
+ End;
+ #13 : If List.Picked <> List.ListMax Then Begin
+ F.Seek (List.Picked - 1);
+ F.Read (Arc);
+
+ EditArchive(Arc);
+
+ F.Seek (List.Picked - 1);
+ F.Write (Arc);
+ End;
+ #27 : Break;
+ End;
+ Until False;
+
+ F.Close;
+ F.Free;
+
+ Box.Close;
+ List.Free;
+ Box.Free;
+End;
+
+End.
diff --git a/mystic/bbs_cfg_events.pas b/mystic/bbs_cfg_events.pas
new file mode 100644
index 0000000..6644519
--- /dev/null
+++ b/mystic/bbs_cfg_events.pas
@@ -0,0 +1,125 @@
+Unit bbs_cfg_Events;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Event_Editor;
+
+Implementation
+
+Uses
+ m_Strings,
+ m_DateTime,
+ bbs_Core,
+ bbs_Common,
+ bbs_User;
+
+Procedure Event_Editor;
+Var
+ A, B : Integer;
+Begin
+ Session.SystemLog ('*EVENT EDITOR*');
+
+ Assign (Session.EventFile, Config.DataPath + 'events.dat');
+ Reset (Session.EventFile);
+ Repeat
+ Session.io.OutFullLn ('|CL|14Event Editor|CR|CR|09### Name|CR--- ------------------------------ -----|14');
+ Reset (Session.EventFile);
+ While Not Eof(Session.EventFile) do begin
+ read (Session.EventFile, session.event);
+ if session.event.active then Session.io.BufAddChar('+') else Session.io.BufAddChar('-');
+ Session.io.OutFullLn ('|15' + strPadR(strI2S(filepos(Session.EventFile)), 4, ' ') + '|14' + strPadR(session.event.name, 32, ' ') +
+ strZero(session.event.exectime div 60) + ':' + strZero(session.event.exectime mod 60));
+ end;
+ Session.io.OutFull ('|CR|09(I)nsert, (D)elete, (E)dit, (Q)uit? ');
+ case Session.io.OneKey ('DIEQ', True) of
+ 'D' : begin
+ Session.io.OutRaw ('Delete which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ KillRecord (Session.EventFile, A, SizeOf(EventRec));
+ end;
+ 'I' : begin
+ Session.io.OutRaw ('Insert before? (1-' + strI2S(filesize(Session.EventFile)+1) + '): ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.EventFile)+1) then begin
+ AddRecord (Session.EventFile, A, SizeOf(EventRec));
+ session.event.active := false;
+ Session.Event.Name := 'New Event';
+ Session.Event.errlevel := 0;
+ Session.Event.exectime := 0;
+ Session.Event.warning := 0;
+ Session.Event.lastran := 0;
+ Session.Event.offhook := false;
+ Session.Event.node := 0;
+ write (Session.EventFile, Session.event);
+ end;
+ end;
+ 'E' : begin
+ Session.io.OutRaw ('Edit which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.EventFile)) then begin
+ seek (Session.EventFile, a-1);
+ read (Session.EventFile, Session.event);
+ repeat
+ Session.io.OutFullLn ('|CL|14Event ' + strI2S(FilePos(Session.EventFile)) + ' of ' + strI2S(FileSize(Session.EventFile)) + '|CR|03');
+ Session.io.OutRawln ('!. Active : ' + Session.io.OutYN(Session.Event.active));
+ Session.io.OutRawln ('A. Description : ' + Session.Event.Name);
+ Session.io.OutRawln ('B. Forced : ' + Session.io.OutYN(Session.Event.forced));
+ Session.io.OutRawln ('C. Errorlevel : ' + strI2S(Session.Event.ErrLevel));
+ Session.io.OutRaw ('D. Execution Time : ');
+ a := Session.Event.exectime div 60;
+ b := Session.Event.exectime mod 60;
+ Session.io.OutRawln (strZero(a) + ':' + strZero(b));
+ Session.io.OutRawln ('E. Busy Warning : ' + strI2S(Session.Event.Warning));
+ Session.io.OutRawln ('F. Last Ran on : ' + DateDos2Str(Session.Event.LastRan, Session.User.ThisUser.DateType));
+ Session.io.OutRawln ('G. Offhook Modem : ' + Session.io.OutYN(Session.Event.Offhook));
+ Session.io.OutRaw ('H. Node Number : ');
+ If Session.Event.Node = 0 Then
+ Session.io.OutRawLn ('All')
+ Else
+ Session.io.OutRawLn (strI2S(Session.Event.Node));
+ Session.io.OutFull ('|CR|09Command (Q/Quit): ');
+ case Session.io.OneKey('[]!ABCDEFGHQ', True) of
+ '[' : If FilePos(Session.EventFile) > 1 Then Begin
+ Seek (Session.EventFile, FilePos(Session.EventFile)-1);
+ Write (Session.EventFile, Session.Event);
+ Seek (Session.EventFile, FilePos(Session.EventFile)-2);
+ Read (Session.EventFile, Session.Event);
+ End;
+ ']' : If FilePos(Session.EventFile) < FileSize(Session.EventFile) Then Begin
+ Seek (Session.EventFile, FilePos(Session.EventFile)-1);
+ Write (Session.EventFile, Session.Event);
+ Read (Session.EventFile, Session.Event);
+ End;
+ '!' : Session.Event.active := not Session.Event.active;
+ 'A' : Session.Event.name := Session.io.InXY(21, 4, 30, 30, 11, Session.Event.name);
+ 'B' : Session.Event.forced := not Session.Event.forced;
+ 'C' : Session.Event.errlevel := strS2I(Session.io.InXY(21, 6, 3, 3, 12, strI2S(Session.Event.errlevel)));
+ 'D' : Begin
+ a := strS2I(Session.io.InXY(21, 7, 2, 2, 12, ''));
+ b := strS2I(Session.io.InXY(24, 7, 2, 2, 12, ''));
+ if (a > -1) and (a < 24) and (b >= 0) and (b < 60) then
+ Session.Event.exectime := (a * 60) + b;
+ end;
+ 'E' : Session.Event.Warning := strS2I(Session.io.InXY(21, 8, 2, 2, 12, strI2S(Session.Event.Warning)));
+ 'F' : Session.Event.LastRan := DateStr2Dos(Session.io.InXY(21, 9, 8, 8, 15, DateDos2Str(Session.Event.lastran, Session.User.ThisUser.DateType)));
+ 'G' : Session.Event.Offhook := Not Session.Event.Offhook;
+ 'H' : Session.Event.Node := strS2I(Session.io.InXY(21, 11, 3, 3, 12, strI2S(Session.Event.Node)));
+ 'Q' : Break;
+ end
+ until false;
+ seek (Session.EventFile, filepos(Session.EventFile)-1);
+ write (Session.EventFile, Session.Event);
+ end;
+ end;
+ 'Q' : break;
+ end;
+ until False;
+
+ Close (Session.EventFile);
+
+ Session.FindNextEvent;
+End;
+
+End.
diff --git a/mystic/bbs_cfg_filebase.pas b/mystic/bbs_cfg_filebase.pas
new file mode 100644
index 0000000..448785e
--- /dev/null
+++ b/mystic/bbs_cfg_filebase.pas
@@ -0,0 +1,167 @@
+Unit bbs_cfg_FileBase;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure File_Base_Editor;
+
+Implementation
+
+Uses
+ m_FileIO,
+ m_Strings,
+ bbs_Common,
+ bbs_Core,
+ bbs_User;
+
+Procedure File_Base_Editor;
+Const
+ ST : Array[0..2] of String[6] = ('No', 'Yes', 'Always');
+Var
+ A,
+ B : LongInt;
+Begin
+ Session.SystemLog ('*FBASE EDITOR*');
+ Reset(Session.FileBase.FBaseFile);
+
+ Repeat
+ Session.io.AllowPause := True;
+
+ Session.io.OutFullLn ('|CL|14File Base Editor|CR|CR|09### Name|CR--- |$D40-');
+
+ Reset (Session.FileBase.FBaseFile);
+ While Not Eof(Session.FileBase.FBaseFile) Do Begin
+ Read (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ Session.io.OutFullLn ('|15' + strPadR(strI2S(FilePos(Session.FileBase.FBaseFile)), 3, ' ') + ' |14|FB');
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Session.io.OutFull ('|CR|09(I)nsert, (D)elete, (E)dit, (M)ove, (Q)uit? ');
+ Case Session.io.OneKey (#13'DEIMQ', True) of
+ 'D' : begin
+ Session.io.OutRaw ('Delete which base? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ If (A > 0) and (A <= FileSize(Session.FileBase.FBaseFile)) Then Begin
+ Seek (Session.FileBase.FBaseFile, A - 1);
+ Read (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ FileErase (config.datapath + Session.FileBase.FBase.filename + '.dir');
+ FileErase (config.datapath + Session.FileBase.FBase.filename + '.des');
+ FileErase (config.datapath + Session.FileBase.FBase.filename + '.scn');
+ KillRecord (Session.FileBase.FBaseFile, A, SizeOf(FBaseRec));
+ End;
+ End;
+ 'I' : begin
+ Session.io.OutRaw ('Insert before which? (1-' + strI2S(filesize(Session.FileBase.FBaseFile)+1) + '): ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.FileBase.FBaseFile)+1) then begin
+ AddRecord (Session.FileBase.FBaseFile, A, SizeOf(Session.FileBase.FBaseFile));
+
+ Session.FileBase.FBase.Name := 'New File Base';
+ Session.FileBase.FBase.FtpName := 'New_File_Base';
+ Session.FileBase.FBase.Filename := 'NEW';
+ Session.FileBase.FBase.Dispfile := '';
+ Session.FileBase.FBase.ListACS := 's255';
+ Session.FileBase.FBase.FtpACS := 's255';
+ Session.FileBase.FBase.SysopACS := 's255';
+ Session.FileBase.FBase.UlACS := 's255';
+ Session.FileBase.FBase.DlACS := 's255';
+ Session.FileBase.FBase.Path := '';
+ Session.FileBase.FBase.Password := '';
+ Session.FileBase.FBase.ShowUL := True;
+ Session.FileBase.FBase.IsCDROM := False;
+ Session.FileBase.FBase.DefScan := 1;
+
+ Write (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ end;
+ end;
+ 'E' : begin
+ Session.io.OutRaw ('Edit which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.FileBase.FBaseFile)) then begin
+ seek (Session.FileBase.FBaseFile, a-1);
+ read (Session.FileBase.FBaseFile, Session.FileBase.fbase);
+ repeat
+ Session.io.OutFullLn ('|CL|14File Base ' + strI2S(FilePos(Session.FileBase.FBaseFile)) + ' of ' + strI2S(FileSize(Session.FileBase.FBaseFile)) + '|CR|03');
+ Session.io.OutRawln ('A. Name : ' + Session.FileBase.FBase.name);
+ Session.io.OutRawln ('B. Filename : ' + Session.FileBase.FBase.filename);
+ Session.io.OutRawln ('C. Display File : ' + Session.FileBase.FBase.dispfile);
+ Session.io.OutRawln ('D. List ACS : ' + Session.FileBase.FBase.Listacs);
+ Session.io.OutRawln ('E. Sysop ACS : ' + Session.FileBase.FBase.SysopACS);
+ Session.io.OutRawln ('F. Upload ACS : ' + Session.FileBase.FBase.ulacs);
+ Session.io.OutRawln ('G. Download ACS : ' + Session.FileBase.FBase.dlacs);
+ Session.io.OutRawln ('H. Storage Path : ' + Session.FileBase.FBase.path);
+ Session.io.OutRawln ('I. Password : ' + Session.FileBase.FBase.password);
+ Session.io.OutRawln ('J. Show Uploader : ' + Session.io.OutYN(Session.FileBase.FBase.ShowUL));
+ Session.io.OutRawLn ('K. Default New Scan : ' + ST[Session.FileBase.FBase.DefScan]);
+ Session.io.OutRawLn ('L. CD-ROM Area : ' + Session.io.OutYN(Session.FileBase.FBase.IsCDROM));
+ Session.io.OutRawLn ('M. All Files Free : ' + Session.io.OutYN(Session.FileBase.FBase.IsFREE));
+ Session.io.OutRawLn ('N. FTP Base Name : ' + Session.FileBase.FBase.FTPName);
+ Session.io.OutRawLn ('O. FTP List ACS : ' + Session.FileBase.FBase.FTPACS);
+ Session.io.OutFull ('|CR|09([) Prev, (]) Next, (Q)uit: ');
+ case Session.io.OneKey('[]ABCDEFGHIJKLMNOQ', True) of
+ '[' : If FilePos(Session.FileBase.FBaseFile) > 1 Then Begin
+ Seek (Session.FileBase.FBaseFile, FilePos(Session.FileBase.FBaseFile)-1);
+ Write (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ Seek (Session.FileBase.FBaseFile, FilePos(Session.FileBase.FBaseFile)-2);
+ Read (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ End;
+ ']' : If FilePos(Session.FileBase.FBaseFile) < FileSize(Session.FileBase.FBaseFile) Then Begin
+ Seek (Session.FileBase.FBaseFile, FilePos(Session.FileBase.FBaseFile)-1);
+ Write (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ Read (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ End;
+ 'A' : Session.FileBase.FBase.Name := Session.io.InXY(23, 3, 40, 40, 11, Session.FileBase.FBase.Name);
+ 'B' : Session.FileBase.FBase.FileName := Session.io.InXY(23, 4, 40, 40, 11, Session.FileBase.FBase.FileName);
+ 'C' : Session.FileBase.FBase.DispFile := Session.io.InXY(23, 5, 8, 8, 11, Session.FileBase.FBase.DispFile);
+ 'D' : Session.FileBase.FBase.ListACS := Session.io.InXY(23, 6, 20, 20, 11, Session.FileBase.FBase.ListACS);
+ 'E' : Session.FileBase.FBase.SysopACS := Session.io.InXY(23, 7, 20, 20, 11, Session.FileBase.FBase.SysopACS);
+ 'F' : Session.FileBase.FBase.ULacs := Session.io.InXY(23, 8, 20, 20, 11, Session.FileBase.FBase.ULacs);
+ 'G' : Session.FileBase.FBase.DLacs := Session.io.InXY(23, 9, 20, 20, 11, Session.FileBase.FBase.DLacs);
+ 'H' : Session.FileBase.FBase.Path := CheckPath(Session.io.InXY(23, 10, 39, 39, 11, Session.FileBase.FBase.Path));
+ 'I' : Session.FileBase.FBase.Password := Session.io.InXY(23, 11, 15, 15, 12, Session.FileBase.FBase.Password);
+ 'J' : Session.FileBase.FBase.ShowUL := Not Session.FileBase.FBase.ShowUL;
+ 'K' : If Session.FileBase.FBase.DefScan > 1 Then Session.FileBase.FBase.DefScan := 0 Else Inc(Session.FileBase.FBase.DefScan);
+ 'L' : Session.FileBase.FBase.IsCDROM := Not Session.FileBase.FBase.IsCDROM;
+ 'M' : Session.FileBase.FBase.IsFREE := Not Session.FileBase.FBase.IsFREE;
+ 'N' : Session.FileBase.FBase.FtpName := Session.io.InXY(23, 16, 40, 60, 11, Session.FileBase.FBase.FtpName);
+ 'O' : Session.FileBase.FBase.FtpACS := Session.io.InXY(23, 17, 30, 30, 11, Session.FileBase.FBase.FtpACS);
+ 'Q' : Break;
+ End;
+ Until False;
+ Seek (Session.FileBase.FBaseFile, FilePos(Session.FileBase.FBaseFile) - 1);
+ Write (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+ End;
+ End;
+
+ 'M' : Begin
+ Session.io.OutRaw ('Move which? ');
+ A := strS2I(Session.io.GetInput(3, 3, 12, ''));
+
+ Session.io.OutRaw ('Move before? (1-' + strI2S(FileSize(Session.FileBase.FBaseFile) + 1) + '): ');
+ B := strS2I(Session.io.GetInput(3, 3, 12, ''));
+
+ If (A > 0) and (A <= FileSize(Session.FileBase.FBaseFile)) and (B > 0) and (B <= FileSize(Session.FileBase.FBaseFile) + 1) Then Begin
+ Seek (Session.FileBase.FBaseFile, A - 1);
+ Read (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+
+ AddRecord (Session.FileBase.FBaseFile, B, SizeOf(FBaseRec));
+ Write (Session.FileBase.FBaseFile, Session.FileBase.FBase);
+
+ If A > B Then Inc(A);
+
+ KillRecord (Session.FileBase.FBaseFile, A, SizeOf(FBaseRec));
+ End;
+ End;
+ 'Q' : Break;
+ End;
+ Until False;
+ Close (Session.FileBase.FBaseFile);
+End;
+
+End.
diff --git a/mystic/bbs_cfg_groups.pas b/mystic/bbs_cfg_groups.pas
new file mode 100644
index 0000000..3414bcc
--- /dev/null
+++ b/mystic/bbs_cfg_groups.pas
@@ -0,0 +1,149 @@
+Unit bbs_cfg_Groups;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Group_Editor;
+
+Implementation
+
+Uses
+ m_Strings,
+ bbs_Common,
+ bbs_Core;
+
+Procedure File_Group;
+var
+ a : SmallInt;
+fgroup : recgroup;
+Begin
+ Reset (Session.FileBase.FGroupFile);
+ Repeat
+ Session.io.OutFullLn ('|CL|14File Group Editor|CR|CR|09### Name|CR--- ------------------------------');
+ Reset (Session.FileBase.FGroupFile);
+ while not eof(Session.FileBase.FGroupFile) do begin
+ read (Session.FileBase.FGroupFile, FGroup);
+ Session.io.OutFullLn ('|15' + strPadR(strI2S(filepos(Session.FileBase.FGroupFile)), 5, ' ') + '|14' + FGroup.Name);
+ end;
+ Session.io.OutFull ('|CR|09(I)nsert, (D)elete, (E)dit, (Q)uit? ');
+ case Session.io.OneKey ('DIEQ', True) of
+ 'D' : begin
+ Session.io.OutRaw ('Delete which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ KillRecord (Session.FileBase.FGroupFile, A, SizeOf(RecGroup));
+ end;
+ 'I' : begin
+ Session.io.OutRaw ('Insert before which? (1-' + strI2S(filesize(Session.FileBase.FGroupFile)+1) + '): ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.FileBase.FGroupFile)+1) then begin
+ AddRecord (Session.FileBase.FGroupFile, A, SizeOf(RecGroup));
+ FGroup.Name := '';
+ FGroup.ACS := 's255';
+ write (Session.FileBase.FGroupFile, FGroup);
+ end;
+ end;
+ 'E' : begin
+ Session.io.OutRaw ('Edit which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.FileBase.FGroupFile)) then begin
+ seek (Session.FileBase.FGroupFile, a-1);
+ read (Session.FileBase.FGroupFile, FGroup);
+ repeat
+ Session.io.OutFullLn ('|CL|14File Group '+strI2S(FilePos(Session.FileBase.FGroupFile)) + ' of ' + strI2S(FileSize(Session.FileBase.FGroupFile))+'|CR|03');
+ Session.io.OutRawln ('A. Name : ' + FGroup.Name);
+ Session.io.OutRawln ('B. ACS : ' + FGroup.acs);
+ Session.io.OutRawLn ('C. Hidden : ' + Session.io.OutYN(FGroup.Hidden));
+ Session.io.OutFull ('|CR|09Command (Q/Quit): ');
+ case Session.io.OneKey('ABCQ', True) of
+ 'A' : FGroup.name := Session.io.InXY(13, 3, 30, 30, 11, Fgroup.name);
+ 'B' : FGroup.acs := Session.io.InXY(13, 4, 20, 20, 11, Fgroup.acs);
+ 'C' : FGroup.Hidden := Not FGroup.Hidden;
+ 'Q' : break;
+ end;
+ until false;
+ seek (Session.FileBase.FGroupFile, filepos(Session.FileBase.FGroupFile)-1);
+ write (Session.FileBase.FGroupFile, FGroup);
+ end;
+ end;
+ 'Q' : break;
+ end;
+
+ until False;
+ close (Session.FileBase.FGroupFile);
+
+End;
+
+Procedure Message_Group;
+var
+ a : SmallInt;
+ group:Recgroup;
+Begin
+ Reset (Session.Msgs.GroupFile);
+ Repeat
+ Session.io.OutFullLn ('|CL|14Message Group Editor|CR|CR|09### Name|CR--- ------------------------------');
+ Reset (Session.Msgs.GroupFile);
+ while not Eof(Session.Msgs.GroupFile) do begin
+ read (Session.Msgs.GroupFile, Group);
+ Session.io.OutFullLn ('|15' + strPadR(strI2S(filepos(Session.Msgs.GroupFile)), 5, ' ') + '|14' + Group.Name);
+ end;
+ Session.io.OutFull ('|CR|09(I)nsert, (D)elete, (E)dit, (Q)uit? ');
+ case Session.io.OneKey ('DIEQ', True) of
+ 'D' : begin
+ Session.io.OutRaw ('Delete which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ KillRecord (Session.Msgs.GroupFile, A, SizeOf(RecGroup));
+ end;
+ 'I' : begin
+ Session.io.OutRaw ('Insert before? (1-' + strI2S(filesize(Session.Msgs.GroupFile)+1) + '): ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.Msgs.GroupFile)+1) then begin
+ AddRecord (Session.Msgs.GroupFile, A, SizeOf(RecGroup));
+ Group.Name := '';
+ Group.ACS := 's255';
+ write (Session.Msgs.GroupFile, Group);
+ end;
+ end;
+ 'E' : begin
+ Session.io.OutRaw ('Edit which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.Msgs.GroupFile)) then begin
+ seek (Session.Msgs.GroupFile, a-1);
+ read (Session.Msgs.GroupFile, Group);
+ repeat
+ Session.io.OutFullLn ('|CL|14Group ' + strI2S(FilePos(Session.Msgs.GroupFile)) + ' of ' + strI2S(FileSize(Session.Msgs.GroupFile)) + '|CR|03');
+ Session.io.OutRawln ('A. Name : ' + Group.Name);
+ Session.io.OutRawln ('B. ACS : ' + Group.acs);
+ Session.io.OutRawLn ('C. Hidden : ' + Session.io.OutYN(Group.Hidden));
+
+ Session.io.OutFull ('|CR|09Command (Q/Quit): ');
+ case Session.io.OneKey('ABCQ', True) of
+ 'A' : Group.name := Session.io.InXY(13, 3, 30, 30, 11, group.name);
+ 'B' : Group.acs := Session.io.InXY(13, 4, 20, 20, 11, group.acs);
+ 'C' : Group.Hidden := Not Group.Hidden;
+ 'Q' : break;
+ end;
+ until false;
+ seek (Session.Msgs.GroupFile, filepos(Session.Msgs.GroupFile)-1);
+ write (Session.Msgs.GroupFile, Group);
+ end;
+ end;
+ 'Q' : break;
+ end;
+
+ until False;
+ close (Session.Msgs.GroupFile);
+End;
+
+Procedure Group_Editor;
+Begin
+ Session.SystemLog ('*GROUP EDITOR*');
+
+ Session.io.OutFull ('|CL|09Edit Groups: (M)essage, (F)ile, (Q)uit? ');
+ Case Session.io.OneKey('QMF', True) of
+ 'M' : Message_Group;
+ 'F' : File_Group;
+ End;
+End;
+
+End.
diff --git a/mystic/bbs_cfg_language.pas b/mystic/bbs_cfg_language.pas
new file mode 100644
index 0000000..b54af82
--- /dev/null
+++ b/mystic/bbs_cfg_language.pas
@@ -0,0 +1,130 @@
+Unit bbs_cfg_Language;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Lang_Editor;
+
+Implementation
+
+Uses
+ m_Strings,
+ bbs_Common,
+ bbs_Core;
+
+Procedure Lang_Editor;
+var
+ a : SmallInt;
+ Old : LangRec;
+Begin
+ Session.SystemLog ('*LANG EDITOR*');
+ Old := Session.Lang;
+{ Reset (LangFile);}
+ Repeat
+ Session.io.OutFullLn ('|CL|14Language Editor|CR|CR|15## FileName Description|CR|09-- -------- ------------------------------');
+ Reset (Session.LangFile);
+ while not eof(Session.LangFile) do begin
+ read (Session.LangFile, Session.Lang);
+ Session.io.OutFullLn ('|15' + strPadR(strI2S(filepos(Session.LangFile)), 4, ' ') +
+ '|14' + strPadR(Session.Lang.FileName, 10, ' ') + '|10' + Session.Lang.Desc);
+ end;
+ Session.Lang := Old;
+ Session.io.OutFull ('|CR|09(I)nsert, (D)elete, (E)dit, (Q)uit? ');
+ case Session.io.OneKey ('DIEQ', True) of
+ 'D' : begin
+ Session.io.OutRaw ('Delete which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if filesize(Session.LangFile) = 1 then
+ Session.io.OutFullLn ('|CR|14You must have at least one language definition.|CR|PA')
+ Else
+ KillRecord (Session.LangFile, A, SizeOf(LangRec));
+ end;
+ 'I' : begin
+ Session.io.OutRaw ('Insert before? (1-' + strI2S(filesize(Session.LangFile)+1) + '): ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.LangFile)+1) then begin
+ AddRecord (Session.LangFile, A, SizeOf(LangRec));
+ Session.lang.filename := '';
+ Session.lang.textpath := '';
+ Session.lang.menupath := '';
+ write (Session.LangFile, Session.Lang);
+ end;
+ end;
+ 'E' : begin
+ Session.io.OutRaw ('Edit which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.LangFile)) then begin
+ seek (Session.LangFile, a-1);
+ read (Session.LangFile, Session.Lang);
+ repeat
+ Session.io.OutFullLn ('|CL|14Language ' + strI2S(FilePos(Session.LangFile)) + ' of ' + strI2S(FileSize(Session.LangFile)) + '|CR|03');
+ Session.io.OutRawln ('A. Description: ' + Session.Lang.Desc);
+ Session.io.OutRawln ('B. Filename : ' + Session.Lang.FileName);
+ Session.io.OutRawln ('C. Text Path : ' + Session.Lang.TextPath);
+ Session.io.OutRawln ('D. Menu Path : ' + Session.Lang.MenuPath);
+ Session.io.OutRawln ('M. Allow ASCII: ' + Session.io.OutYN(Session.Lang.okASCII));
+ Session.io.OutRawln ('N. Allow ANSI : ' + Session.io.OutYN(Session.Lang.okANSI));
+
+ Session.io.OutFullLn ('|CRE. Use Lightbar Y/N : ' + Session.io.OutYN(Session.Lang.BarYN));
+ Session.io.OutFull ('|03|16H. Input Field Color: ');
+ Session.io.AnsiColor(Session.Lang.FieldCol1);
+ Session.io.OutFullLn ('Test|03|16');
+
+ Session.io.OutRaw ('I. Quote Bar Color : ');
+ Session.io.AnsiColor(Session.Lang.QuoteColor);
+ Session.io.OutFullLn ('Test|03|16');
+
+ Session.io.OutRawLn ('J. Echo Character : ' + Session.Lang.EchoCh);
+ Session.io.OutRawLn ('K. Input Character : ' + Session.Lang.FieldChar);
+ Session.io.OutRawLn ('L. File Tag Char : ' + Session.Lang.TagCh);
+
+ Session.io.OutRaw ('O. File Search Hi : ');
+ Session.io.AnsiColor(Session.Lang.FileHI);
+ Session.io.OutFullLn ('Test|03|16');
+
+ Session.io.OutRaw ('P. File Desc. Lo : ');
+ Session.io.AnsiColor(Session.Lang.FileLO);
+ Session.io.OutFullLn ('Test|03|16');
+
+ Session.io.OutRawLn ('R. LB New Msg Char : ' + Session.Lang.NewMsgChar);
+
+ Session.io.OutFull ('|CR|09Command (Q/Quit): ');
+ case Session.io.onekey('ABCDEFGHIJKLMNOPQR', True) of
+ 'A' : Session.Lang.Desc := Session.io.InXY(17, 3, 30, 30, 11, Session.Lang.Desc);
+ 'B' : Session.Lang.filename := Session.io.InXY(17, 4, 8, 8, 11, Session.Lang.filename);
+ 'C' : Session.Lang.textpath := CheckPath(Session.io.InXY(17, 5, 40, 40, 11, Session.Lang.textpath));
+ 'D' : Session.Lang.menupath := CheckPath(Session.io.InXY(17, 6, 40, 40, 11, Session.Lang.MenuPath));
+ 'E' : Session.Lang.BarYN := Not Session.Lang.BarYN;
+ 'H' : Session.Lang.FieldCol1 := getColor(Session.Lang.FieldCol1);
+ 'I' : Session.Lang.QuoteColor := getColor(Session.Lang.QuoteColor);
+ 'J' : Begin Session.io.OutRaw ('Char: '); Session.Lang.EchoCh := Session.io.GetKey; End;
+ 'K' : Begin
+ Session.io.OutRaw ('Char: ');
+ Session.Lang.FieldChar := Session.io.GetKey;
+ If Not (Session.Lang.FieldChar in [#32..#255]) Then
+ Session.Lang.FieldChar := ' ';
+ End;
+ 'L' : Begin Session.io.OutRaw ('Char: '); Session.Lang.TagCh := Session.io.GetKey; End;
+ 'M' : Session.Lang.okASCII := Not Session.Lang.okASCII;
+ 'N' : Session.Lang.okANSI := Not Session.Lang.okANSI;
+ 'O' : Session.Lang.FileHI := getColor(Session.Lang.FileHI);
+ 'P' : Session.Lang.FileLo := GetColor(Session.Lang.FileLO);
+ 'Q' : break;
+ 'R' : Begin Session.io.OutRaw('Char: '); Session.Lang.NewMsgChar := Session.io.GetKey; End;
+ end;
+ until false;
+ seek (Session.LangFile, filepos(Session.LangFile)-1);
+ write (Session.LangFile, Session.Lang);
+ end;
+ end;
+ 'Q' : break;
+ end;
+
+ until False;
+ close (Session.LangFile);
+
+ If Not Session.LoadThemeData(Old.FileName) Then Session.Lang := Old;
+End;
+
+End.
diff --git a/mystic/bbs_cfg_menuedit.pas b/mystic/bbs_cfg_menuedit.pas
new file mode 100644
index 0000000..debef37
--- /dev/null
+++ b/mystic/bbs_cfg_menuedit.pas
@@ -0,0 +1,302 @@
+Unit bbs_cfg_menuedit;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ DOS,
+ m_Strings,
+ m_FileIO,
+ bbs_Common,
+ bbs_Core,
+ bbs_User,
+ bbs_Menus;
+
+Procedure Menu_Editor;
+
+Implementation
+
+Var
+ MenuFile : Text;
+
+Procedure Menu_Editor;
+
+Procedure ModifyMenu;
+var a,b{,c} : byte;
+{ tempcmd : menucmdrec;}
+Begin
+ Session.io.OutRaw ('Menu to Edit: ');
+ Session.Menu.MenuName := Session.io.GetInput(mysMaxMenuNameLen, mysMaxMenuNameLen, 11, '');
+
+ If Session.Menu.LoadMenu(False, False, False) <> 1 Then Exit;
+
+ Repeat
+ Session.io.OutFullLn ('|CL|14Menu Command List|CR|03');
+ Session.io.OutFullLn ('|15## Hot-Key Cmd Text ## Hot-Key Cmd Text');
+ Session.io.OutFullLn ('|09-- -------- --- --------------------- -- -------- --- ---------------------|03');
+
+ For A := 1 to Session.Menu.CmdNum Do Begin
+ Session.io.OutRaw (strPadR(strI2S(A), 3, ' ') + strPadR(Session.Menu.MenuList[A].HotKey, 9, ' ') +
+ strPadR(Session.Menu.MenuList[A].Command, 4, ' ') + strPadR(Session.Menu.MenuList[A].Text, 21, ' ') + ' ');
+ If (A = Session.Menu.CmdNum) or (A Mod 2 = 0) Then Session.io.OutRawLn('');
+ End;
+
+ Session.io.OutFull ('|CR|09(E)dit, (I)nsert, (D)elete, (F)lags, (V)iew, (Q)uit: ');
+ Case Session.io.OneKey('EIDFVQ', True) of
+ 'D' : begin
+ Session.io.OutRaw('Delete which? ');
+ a := strS2I(Session.io.GetInput(2, 2, 11, ''));
+ if (a > 0) and (a <= Session.Menu.CmdNum) then begin
+ for b := a to Session.Menu.CmdNum do
+ Session.Menu.Menulist[b] := Session.Menu.Menulist[b+1];
+ dec (Session.Menu.cmdnum);
+ end;
+ end;
+ 'I' : if Session.Menu.CmdNum < mysMaxMenuCmds Then Begin
+ Session.io.OutRaw ('Insert before which (1-' + strI2S(Session.Menu.CmdNum + 1) + '): ');
+ A := strS2I(Session.io.GetInput(2, 2, 11, ''));
+ If (A > 0) And (A <= Session.Menu.CmdNum + 1) Then Begin
+ Inc (Session.Menu.CmdNum);
+ For B := Session.Menu.CmdNum DownTo A + 1 Do
+ Session.Menu.MenuList[B] := Session.Menu.MenuList[B - 1];
+ Session.Menu.MenuList[A].Text := '[XXX] New Command';
+ Session.Menu.MenuList[A].HotKey := 'XXX';
+ Session.Menu.MenuList[A].LongKey := 'XXX';
+ Session.Menu.MenuList[A].ACS := '';
+ Session.Menu.MenuList[A].Command := '';
+ Session.Menu.MenuList[A].X := 0;
+ Session.Menu.MenuList[A].Y := 0;
+ Session.Menu.MenuList[A].lText := '';
+ Session.Menu.MenuList[A].lhText := '';
+ End;
+ End;
+ 'F' : Begin
+ repeat
+ Session.io.OutFullLn ('|CL|14Menu Flags (' + Session.Menu.MenuName + ')|CR|03');
+ Session.io.OutRawLn ('A. Menu Header : ' + strPadR(Session.Menu.Menu.header, 59, ' '));
+ Session.io.OutRawLn ('B. Menu Prompt : ' + strPadR(Session.Menu.menu.prompt, 59, ' '));
+ Session.io.OutRawLn ('C. Display Cols : ' + strI2S(Session.Menu.Menu.DispCols));
+ Session.io.OutRawLn ('D. ACS : ' + Session.Menu.menu.acs);
+ Session.io.OutRawLn ('E. Password : ' + Session.Menu.menu.password);
+ Session.io.OutRawLn ('F. Display File : ' + Session.Menu.Menu.TextFile);
+ Session.io.OutRawLn ('G. Fallback Menu : ' + Session.Menu.Menu.Fallback);
+ Session.io.OutRaw ('H. Menu Type : ');
+
+ Case Session.Menu.Menu.MenuType of
+ 0 : Session.io.OutRawLn ('Standard');
+ 1 : Session.io.OutRawLn ('Lightbar');
+ 2 : Session.io.OutRawLn ('Lightbar Grid');
+ End;
+
+ Session.io.OutRawLn ('I. Finish X/Y : ' + strPadR(strI2S(Session.Menu.menu.donex), 3, ' ') + strI2S(Session.Menu.menu.doney));
+ Session.io.OutRawLn ('J. Use Global MNU: ' + Session.io.OutYN(Session.Menu.Menu.Global=1));
+ Session.io.OutRaw ('K. Input Type : ');
+
+ Case Session.Menu.Menu.InputType of
+ 0 : Session.io.OutRawLn ('User setting');
+ 1 : Session.io.OutRawLn ('Hotkey');
+ 2 : Session.io.OutRawLn ('Longkey');
+ End;
+
+ Session.io.OutFull ('|CR|09(V)iew or (Q)uit: ');
+ Case Session.io.OneKey('ABCDEFGHIJKQV', True) of
+ 'A' : Session.Menu.Menu.Header := Session.io.InXY(20, 3, 60, 255, 11, Session.Menu.Menu.Header);
+ 'B' : Session.Menu.Menu.Prompt := Session.io.InXY(20, 4, 60, 255, 11, Session.Menu.Menu.Prompt);
+ 'C' : Begin
+ Session.Menu.Menu.DispCols := strS2I(Session.io.InXY(20, 5, 1, 1, 12, strI2S(Session.Menu.Menu.DispCols)));
+ If Session.Menu.Menu.DispCols < 1 Then Session.Menu.Menu.DispCols := 1;
+ If Session.Menu.Menu.DispCols > 3 Then Session.Menu.Menu.DispCols := 3;
+ End;
+ 'D' : Session.Menu.Menu.ACS := Session.io.InXY(20, 6, 20, 20, 11, Session.Menu.Menu.ACS);
+ 'E' : Session.Menu.Menu.Password := Session.io.InXY(20, 7, 15, 15, 12, Session.Menu.Menu.Password);
+ 'F' : Session.Menu.Menu.TextFile := Session.io.InXY(20, 8, 20, 20, 11, Session.Menu.Menu.TextFile);
+ 'G' : Session.Menu.Menu.Fallback := Session.io.InXY(20, 9, mysMaxMenuNameLen, mysMaxMenuNameLen, 11, Session.Menu.Menu.Fallback);
+ 'H' : If Session.Menu.Menu.MenuType = 2 Then Session.Menu.Menu.MenuType := 0 Else Inc(Session.Menu.Menu.MenuType);
+ 'I' : Begin
+ Session.Menu.Menu.donex := strS2I(Session.io.InXY(20, 11, 2, 2, 12, strI2S(Session.Menu.Menu.donex)));
+ Session.Menu.Menu.doney := strS2I(Session.io.InXY(23, 11, 2, 2, 12, strI2S(Session.Menu.Menu.doney)));
+ End;
+ 'J' : If Session.Menu.Menu.Global = 1 Then dec(Session.Menu.Menu.global) else Session.Menu.Menu.global := 1;
+ 'K' : If Session.Menu.Menu.InputType = 2 Then Session.Menu.Menu.InputType := 0 Else Inc(Session.Menu.Menu.InputType);
+ 'Q' : Break;
+ 'V' : Session.Menu.ExecuteMenu (False, False, True);
+ End;
+ Until False;
+ End;
+ 'E' : Begin
+ Session.io.OutRaw ('Edit which? ');
+ a := strS2I(Session.io.GetInput(2, 2, 11, ''));
+ If (a > 0) and (a <= Session.Menu.CmdNum) then Begin
+ Repeat
+ Session.io.OutFullLn ('|CL|14Menu command ' + strI2S(a) + ' of ' + strI2S(Session.Menu.CmdNum) + '|CR|03');
+ Session.io.OutRawln ('A. Text : ' + Session.Menu.MenuList[A].text);
+ Session.io.OutRawln ('B. Hot Key : ' + Session.Menu.MenuList[A].HotKey);
+ Session.io.OutRawLn ('C. Long Key: ' + Session.Menu.MenuList[A].LongKey);
+ Session.io.OutRawln ('D. ACS : ' + Session.Menu.MenuList[A].acs);
+ Session.io.OutRawln ('E. Command : ' + Session.Menu.MenuList[A].command);
+ Session.io.OutRawln ('F. Data : ' + Session.Menu.MenuList[A].data);
+ Session.io.OutFullLn ('|CRG. Lightbar X/Y : ' + strPadR(strI2S(Session.Menu.MenuList[a].x), 3, ' ') + strI2S(Session.Menu.MenuList[a].y));
+ Session.io.OutRawln ('H. Lightbar Text : ' + Session.Menu.MenuList[a].ltext);
+ Session.io.OutRawln ('I. Lightbar High : ' + Session.Menu.MenuList[a].lhtext);
+ Session.io.OutRawln ('');
+ Session.io.OutRawln ('J. Lightbar Up : ' + strI2S(Session.Menu.MenuList[a].cUP));
+ Session.io.OutRawln ('K. Lightbar Down : ' + strI2S(Session.Menu.MenuList[a].cDOWN));
+ Session.io.OutRawln ('L. Lightbar Left : ' + strI2S(Session.Menu.MenuList[a].cLEFT));
+ Session.io.OutRawln ('M. Lightbar Right: ' + strI2S(Session.Menu.MenuList[a].cRIGHT));
+
+ Session.io.OutFull ('|CR|09([) Previous, (]) Next, (Q)uit: ');
+ case session.io.onekey('[]ABCDEFGHIJKLMQ', True) of
+ '[' : If A > 1 Then Dec(A);
+ ']' : If A < Session.Menu.CmdNum Then Inc(A);
+ 'A' : Session.Menu.MenuList[A].Text := Session.io.InXY(14, 3, 60, 79, 11, Session.Menu.MenuList[A].Text);
+ 'B' : Session.Menu.MenuList[A].HotKey := Session.io.InXY(14, 4, 8, 8, 12, Session.Menu.MenuList[A].HotKey);
+ 'C' : Session.Menu.MenuList[A].LongKey := Session.io.InXY(14, 5, 8, 8, 12, Session.Menu.MenuList[A].LongKey);
+ 'D' : Session.Menu.MenuList[A].ACS := Session.io.InXY(14, 6, 20, 20, 11, Session.Menu.MenuList[A].ACS);
+ 'E' : Repeat
+ Session.io.OutFull ('|09Menu Command (?/List): ');
+ Session.Menu.MenuList[A].command := Session.io.GetInput(2, 2, 12, '');
+ If Session.Menu.MenuList[A].Command = '?' Then
+ session.io.OutFile ('menucmds', True, 0)
+ Else
+ Break;
+ Until False;
+ 'F' : Session.Menu.MenuList[A].Data := Session.io.InXY(14, 8, 60, 79, 11, Session.Menu.MenuList[a].data);
+ 'G' : Begin
+ Session.Menu.MenuList[A].X := strS2I(Session.io.InXY(20, 10, 2, 2, 12, strI2S(Session.Menu.MenuList[A].X)));
+ Session.Menu.MenuList[A].Y := strS2I(Session.io.InXY(23, 10, 2, 2, 12, strI2S(Session.Menu.MenuList[A].Y)));
+ End;
+ 'H' : Session.Menu.MenuList[A].LText := Session.io.InXY(20, 11, 59, 79, 11, Session.Menu.MenuList[A].LText);
+ 'I' : Session.Menu.MenuList[A].LHText := Session.io.InXY(20, 12, 59, 79, 11, Session.Menu.MenuList[A].LHText);
+ 'J' : Session.Menu.MenuList[A].cUP := strS2I(Session.io.InXY(20, 14, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cUP)));
+ 'K' : Session.Menu.MenuList[A].cDOWN := strS2I(Session.io.InXY(20, 15, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cDOWN)));
+ 'L' : Session.Menu.MenuList[A].cLEFT := strS2I(Session.io.InXY(20, 16, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cLEFT)));
+ 'M' : Session.Menu.MenuList[A].cRIGHT := strS2I(Session.io.InXY(20, 17, 2, 2, 12, strI2S(Session.Menu.MenuList[A].cRIGHT)));
+ 'Q' : Break;
+ end;
+ until false;
+ End;
+ End;
+(*
+ 'P' : begin
+ Session.io.OutRaw('Move which? ');
+ a := strS2I(Session.io.GetInput(2, 2, 11, ''));
+ Session.io.OutRaw('Move before which (1-' + strI2S(Session.Menu.CmdNum+1) + '): ');
+ b := strS2I(Session.io.GetInput(2, 2, 11, ''));
+ end;
+*)
+ 'Q' : break;
+ 'V' : Session.Menu.ExecuteMenu(False, False, True);
+
+ end;
+ Until false;
+
+ Session.io.OutFullLn ('|14Saving...');
+ assign (menufile, Session.lang.menupath + Session.Menu.menuname + '.mnu');
+ rewrite (menufile);
+ writeln (menufile, Session.Menu.Menu.header);
+ writeln (menufile, Session.Menu.Menu.prompt);
+ writeln (menufile, Session.Menu.Menu.dispcols);
+ writeln (menufile, Session.Menu.Menu.acs);
+ writeln (menufile, Session.Menu.Menu.password);
+ writeln (menufile, Session.Menu.Menu.textfile);
+ WriteLn (MenuFile, Session.Menu.Menu.Fallback);
+ writeln (menufile, Session.Menu.Menu.MenuType);
+ WriteLn (MenuFile, Session.Menu.Menu.InputType);
+ WriteLn (MenuFile, Session.Menu.Menu.DoneX);
+ WriteLn (MenuFile, Session.Menu.Menu.DoneY);
+ WriteLn (MenuFile, Session.Menu.Menu.Global);
+ for a := 1 to Session.Menu.CmdNum do begin
+ writeln (menufile, Session.Menu.MenuList[a].text);
+ writeln (menufile, Session.Menu.MenuList[a].HotKey);
+ WriteLn (MenuFile, Session.Menu.MenuList[A].LongKey);
+ writeln (menufile, Session.Menu.MenuList[a].acs);
+ writeln (menufile, Session.Menu.MenuList[a].command);
+ writeln (menufile, Session.Menu.MenuList[a].data);
+ writeln (menufile, Session.Menu.MenuList[a].x);
+ writeln (menufile, Session.Menu.MenuList[a].y);
+ writeln (menufile, Session.Menu.MenuList[a].cUP);
+ WriteLn (MenuFile, Session.Menu.MenuList[A].cDOWN);
+ WriteLn (MenuFile, Session.Menu.MenuList[A].cLEFT);
+ WriteLn (MenuFile, Session.Menu.MenuList[A].cRIGHT);
+ writeln (menufile, Session.Menu.MenuList[a].ltext);
+ writeln (menufile, Session.Menu.MenuList[a].lhtext);
+ end;
+ close (menufile);
+End;
+
+Var
+ Old : String[8];
+ OldLang : LangRec;
+ DirInfo: SearchRec;
+ A : Byte; {format dir output}
+Begin
+ Old := Session.Menu.MenuName;
+ OldLang := Session.Lang;
+ Session.SystemLog ('*MENU EDITOR*');
+
+ Session.io.OutFull ('|CL');
+ Session.User.GetLanguage;
+
+ Repeat
+ Session.io.OutFullLn ('|CL|14Menu Editor (Language: ' + Session.Lang.Desc + ')|CR');
+ Session.io.OutFullLn ('|08Directory of ' + Session.lang.MenuPath + '*.MNU|CR|03');
+
+ a := 0;
+ FindFirst (Session.lang.MenuPath + '*.mnu', Archive, DirInfo);
+ While DosError = 0 Do Begin
+ inc (a);
+ Session.io.OutRaw (strPadR(DirInfo.Name, 25, ' '));
+ FindNext (DirInfo);
+ if (a = 3) or (DosError <> 0) then begin
+ Session.io.OutRawln('');
+ a := 0
+ end;
+
+ End;
+
+ Session.io.OutFull ('|CR|09(E)dit, (I)nsert, (D)elete, (Q)uit? ');
+ Case session.io.OneKey('EIDQ', True) of
+ 'E' : ModifyMenu;
+ 'I' : Begin;
+ Session.io.OutRaw ('Menu Name: ');
+ Session.menu.MenuName := Session.io.GetInput(mysMaxMenuNameLen, mysMaxMenuNameLen, 11, '');
+ If Session.Menu.MenuName <> '' Then Begin
+ Assign (MenuFile, Session.Lang.MenuPath + Session.Menu.MenuName + '.mnu');
+ {$I-} Reset(MenuFile); {$I+}
+ If IoResult = 0 Then
+ Session.io.OutRawLn ('Menu already exists')
+ Else Begin
+ Rewrite (MenuFile);
+ WriteLn (MenuFile, 'New Menu');
+ WriteLn (MenuFile, 'Command: ');
+ WriteLn (MenuFile, '2');
+ WriteLn (MenuFile, '');
+ WriteLn (MenuFile, '');
+ WriteLn (MenuFile, '');
+ WriteLn (MenuFile, 'main');
+ WriteLn (MenuFile, '0');
+ WriteLn (MenuFile, '0');
+ WriteLn (MenuFile, '0');
+ WriteLn (MenuFile, '0');
+ WriteLn (MenuFile, '1');
+ Close (MenuFile);
+ End;
+ End;
+ End;
+ 'D' : Begin
+ Session.io.OutRaw ('Menu to delete: ');
+ Session.Menu.MenuName := Session.io.GetInput(mysMaxMenuNameLen, mysMaxMenuNameLen, 11, '');
+ FileErase(Session.Lang.MenuPath + Session.Menu.MenuName + '.mnu');
+ End;
+ 'Q' : Break;
+ End;
+ Until False;
+ Session.Menu.MenuName := Old;
+ Session.Lang := OldLang;
+ Close (Session.PromptFile);
+ Assign (Session.PromptFile, Config.DataPath + Session.Lang.FileName + '.lng');
+ Reset (Session.PromptFile);
+End;
+
+End.
diff --git a/mystic/bbs_cfg_msgbase.pas b/mystic/bbs_cfg_msgbase.pas
new file mode 100644
index 0000000..5c8c33e
--- /dev/null
+++ b/mystic/bbs_cfg_msgbase.pas
@@ -0,0 +1,236 @@
+Unit bbs_cfg_MsgBase;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Message_Base_Editor;
+
+Implementation
+
+Uses
+ m_FileIO,
+ m_Strings,
+ bbs_Common,
+ bbs_Core,
+ bbs_User;
+
+Procedure Message_Base_Editor;
+Const
+ BT : Array[0..1] of String[6] = ('JAM', 'Squish');
+ NT : Array[0..3] of String[8] = ('Local ', 'EchoMail', 'UseNet ', 'NetMail ');
+ ST : Array[0..2] of String[6] = ('No', 'Yes', 'Always');
+Var
+ A,
+ B : Word; { was integer }
+Begin
+ Session.SystemLog ('*MBASE EDITOR*');
+
+ Repeat
+ Session.io.AllowPause := True;
+
+ Session.io.OutFullLn ('|CL|14Message Base Editor|CR|CR|09### Name|$D37 Type Format|CR--- |$D40- ------- ------');
+
+ Reset (Session.Msgs.MBaseFile);
+ While Not Eof(Session.Msgs.MBaseFile) Do Begin
+ Read (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+
+ Session.io.OutFullLn ('|15' + strPadR(strI2S(FilePos(Session.Msgs.MBaseFile) - 1), 3, ' ') + ' |14|$R41|MB|10' +
+ NT[Session.Msgs.MBase.NetType] + ' ' + BT[Session.Msgs.MBase.BaseType]);
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+ Session.io.OutFull ('|CR|09(I)nsert, (D)elete, (E)dit, (M)ove, (Q)uit? ');
+ case Session.io.OneKey (#13'DIEMQ', True) of
+ 'D' : begin
+ Session.io.OutFull ('Delete which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ If (A > 0) and (A <= FileSize(Session.Msgs.MBaseFile)) Then Begin
+ Seek (Session.Msgs.MBaseFile, A);
+ Read (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+
+ FileErase (config.msgspath + Session.Msgs.MBase.filename + '.jhr');
+ FileErase (config.msgspath + Session.Msgs.MBase.filename + '.jlr');
+ FileErase (config.msgspath + Session.Msgs.MBase.filename + '.jdt');
+ FileErase (config.msgspath + Session.Msgs.MBase.filename + '.jdx');
+ FileErase (config.msgspath + Session.Msgs.MBase.filename + '.sqd');
+ FileErase (config.msgspath + Session.Msgs.MBase.filename + '.sqi');
+ FileErase (config.msgspath + Session.Msgs.MBase.filename + '.sql');
+
+ KillRecord (Session.Msgs.MBaseFile, A+1, SizeOf(MBaseRec));
+ End;
+ end;
+ 'I' : begin
+ Session.io.OutFull ('Insert before? (1-' + strI2S(filesize(Session.Msgs.MBaseFile)) + '): ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(Session.Msgs.MBaseFile)) then begin
+ AddRecord (Session.Msgs.MBaseFile, A, SizeOf(Session.Msgs.MBaseFile));
+
+ {find permanent mbase index}
+ b := a + 1;
+ reset (Session.Msgs.MBaseFile);
+ while not eof(Session.Msgs.MBaseFile) do begin
+ read (Session.Msgs.MBaseFile, Session.Msgs.mbase);
+ if B = Session.Msgs.MBase.index then begin
+ inc (b);
+ reset (Session.Msgs.MBaseFile);
+ end;
+ end;
+ Session.Msgs.MBase.name := 'New Message Base';
+ Session.Msgs.MBase.qwkname := 'New Messages';
+ Session.Msgs.MBase.filename := 'NEW';
+ Session.Msgs.MBase.Path := config.msgspath;
+ Session.Msgs.MBase.nettype := 0;
+ Session.Msgs.MBase.posttype := 0;
+ Session.Msgs.MBase.acs := 's255';
+ Session.Msgs.MBase.readacs := 's255';
+ Session.Msgs.MBase.postacs := 's255';
+ Session.Msgs.MBase.sysopacs := 's255';
+ Session.Msgs.MBase.index := B;
+ Session.Msgs.MBase.netaddr := 1;
+ Session.Msgs.MBase.origin := config.origin;
+ Session.Msgs.MBase.usereal := false;
+ Session.Msgs.MBase.colquote := config.colorquote;
+ Session.Msgs.MBase.coltext := config.colortext;
+ Session.Msgs.MBase.coltear := config.colortear;
+ Session.Msgs.MBase.colorigin := config.colororigin;
+ Session.Msgs.MBase.defnscan := 1;
+ Session.Msgs.MBase.defqscan := 1;
+ Session.Msgs.MBase.basetype := 0;
+ seek (Session.Msgs.MBaseFile, a);
+ write (Session.Msgs.MBaseFile, Session.Msgs.mbase);
+ end;
+ end;
+ 'E' : begin
+ Session.io.OutFull ('Edit which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a >= 0) and (a < filesize(Session.Msgs.MBaseFile)) then begin
+ seek (Session.Msgs.MBaseFile, a);
+ read (Session.Msgs.MBaseFile, Session.Msgs.mbase);
+ repeat
+ Session.io.OutFullLn ('|CL|14Message Base '+strI2S(FilePos(Session.Msgs.MBaseFile)-1)+' of '+strI2S(FileSize(Session.Msgs.MBaseFile)-1)+' |08[Perm Idx:' + strI2S(Session.Msgs.MBase.index) + ']|CR|03');
+ Session.io.OutRawln ('A. Name : ' + Session.Msgs.MBase.name);
+ Session.io.OutRawln ('B. QWK Name : ' + Session.Msgs.MBase.qwkname);
+ Session.io.OutRawln ('C. Filename : ' + Session.Msgs.MBase.filename);
+ Session.io.OutRawln ('D. Storage Path : ' + Session.Msgs.MBase.path);
+ Session.io.OutRaw ('E. Post Type : ');
+ If Session.Msgs.MBase.PostType = 0 Then Session.io.OutRaw ('Public ') Else Session.io.OutRaw ('Private');
+ Session.io.OutRawLn (strRep(' ', 23) + 'Y. Base Format : ' + BT[Session.Msgs.MBase.BaseType]);
+
+ Session.io.OutFull ('|CRF. List ACS : ' + strPadR(Session.Msgs.MBase.acs, 30, ' '));
+ Session.io.OutFull ('O. Quote Color : ');
+ Session.io.AnsiColor(Session.Msgs.MBase.ColQuote);
+ Session.io.OutFullLn ('XX> Quote|03|16');
+
+ Session.io.OutRaw ('G. Read ACS : ' + strPadR(Session.Msgs.MBase.readacs, 30, ' '));
+ Session.io.OutFull ('P. Text Color : ');
+ Session.io.AnsiColor(Session.Msgs.MBase.ColText);
+ Session.io.OutFullLn ('Text|03|16');
+
+ Session.io.OutRaw ('H. Post ACS : ' + strPadR(Session.Msgs.MBase.postacs, 30, ' '));
+ Session.io.OutFull ('R. Tear Color : ');
+ Session.io.AnsiColor(Session.Msgs.MBase.ColTear);
+ Session.io.OutFullLn ('--- Tear|03|16');
+
+ Session.io.OutRaw ('I. Sysop ACS : ' + strPadR(Session.Msgs.MBase.sysopacs, 30, ' '));
+ Session.io.OutFull ('S. Origin Color : ');
+ Session.io.AnsiColor(Session.Msgs.MBase.ColOrigin);
+ Session.io.OutFullLn ('* Origin:|03|16');
+
+ Session.io.OutRaw ('J. Password : ' + strPadR(Session.Msgs.MBase.password, 30, ' '));
+ Session.io.OutRawln ('T. Header File : ' + Session.Msgs.MBase.Header);
+ Session.io.OutRawLn ('K. Base Type : ' + NT[Session.Msgs.MBase.NetType]);
+ Session.io.OutRawln ('L. Net Address : ' + strAddr2Str(config.netaddress[Session.Msgs.MBase.netaddr]) + ' (' + Config.NetDesc[Session.Msgs.MBase.NetAddr] + ')');
+ Session.io.OutRawln ('M. Origin line : ' + Session.Msgs.MBase.origin);
+ Session.io.OutRawLn ('N. Use Realnames: ' + Session.io.OutYN(Session.Msgs.MBase.UseReal));
+
+ Session.io.OutFullLn ('|CRU. Default New Scan: ' + strPadR(ST[Session.Msgs.MBase.DefNScan], 27, ' ') +
+ 'W. Max Messages : ' + strI2S(Session.Msgs.MBase.MaxMsgs));
+
+ Session.io.OutRawLn ('V. Default QWK Scan: ' + strPadR(ST[Session.Msgs.MBase.DefQScan], 27, ' ') +
+ 'X. Max Msg Age : ' + strI2S(Session.Msgs.MBase.MaxAge) + ' days');
+
+ Session.io.OutFull ('|CR|09([) Prev, (]) Next, (Q)uit: ');
+ case Session.io.OneKey('[]ABCDEFGHIJKLMNOPQRSTUVWXY', True) of
+ '[' : If FilePos(Session.Msgs.MBaseFile) > 1 Then Begin
+ Seek (Session.Msgs.MBaseFile, FilePos(Session.Msgs.MBaseFile)-1);
+ Write (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+ Seek (Session.Msgs.MBaseFile, FilePos(Session.Msgs.MBaseFile)-2);
+ Read (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+ End;
+ ']' : If FilePos(Session.Msgs.MBaseFile) < FileSize(Session.Msgs.MBaseFile) Then Begin
+ Seek (Session.Msgs.MBaseFile, FilePos(Session.Msgs.MBaseFile)-1);
+ Write (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+ Read (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+ End;
+ 'A' : Session.Msgs.MBase.Name := Session.io.InXY(19, 3, 40, 40, 11, Session.Msgs.MBase.Name);
+ 'B' : Session.Msgs.MBase.QwkName := Session.io.InXY(19, 4, 13, 13, 11, Session.Msgs.MBase.QwkName);
+ 'C' : Session.Msgs.MBase.FileName := Session.io.InXY(19, 5, 40, 40, 11, Session.Msgs.MBase.filename);
+ 'D' : Session.Msgs.MBase.Path := CheckPath(Session.io.InXY(19, 6, 39, 39, 11, Session.Msgs.MBase.Path));
+ 'E' : If Session.Msgs.MBase.PostType = 0 Then Inc(Session.Msgs.MBase.PostType) Else Dec(Session.Msgs.MBase.PostType);
+ 'F' : Session.Msgs.MBase.ACS := Session.io.InXY(19, 9, 20, 20, 11, Session.Msgs.MBase.acs);
+ 'G' : Session.Msgs.MBase.ReadACS := Session.io.InXY(19, 10, 20, 20, 11, Session.Msgs.MBase.readacs);
+ 'H' : Session.Msgs.MBase.PostACS := Session.io.InXY(19, 11, 20, 20, 11, Session.Msgs.MBase.postacs);
+ 'I' : Session.Msgs.MBase.SysopACS := Session.io.InXY(19, 12, 20, 20, 11, Session.Msgs.MBase.sysopacs);
+ 'J' : Session.Msgs.MBase.Password := Session.io.InXY(19, 13, 15, 15, 12, Session.Msgs.MBase.password);
+ 'K' : If Session.Msgs.MBase.NetType < 3 Then Inc(Session.Msgs.MBase.NetType) Else Session.Msgs.MBase.NetType := 0;
+ 'L' : begin
+ Session.io.OutFullLn ('|03');
+ For A := 1 to 30 Do Begin
+ Session.io.OutRaw (strPadR(strI2S(A) + '.', 5, ' ') + strPadR(strAddr2Str(Config.NetAddress[A]), 30, ' '));
+ If A Mod 2 = 0 then Session.io.OutRawLn('');
+ End;
+ Session.io.OutFull ('|CR|09Address: ');
+ a := strS2I(Session.io.GetInput(2, 2, 12, ''));
+ if (a > 0) and (a < 31) then Session.Msgs.MBase.netaddr := a;
+ end;
+ 'M' : Session.Msgs.MBase.origin := Session.io.InXY(19, 16, 50, 50, 11, Session.Msgs.MBase.origin);
+ 'N' : Session.Msgs.MBase.usereal := Not Session.Msgs.MBase.UseReal;
+ 'O' : Session.Msgs.MBase.ColQuote := getColor(Session.Msgs.MBase.ColQuote);
+ 'P' : Session.Msgs.MBase.ColText := getColor(Session.Msgs.MBase.ColText);
+ 'R' : Session.Msgs.MBase.ColTear := getColor(Session.Msgs.MBase.ColTear);
+ 'S' : Session.Msgs.MBase.ColOrigin := getColor(Session.Msgs.MBase.ColOrigin);
+ 'T' : Session.Msgs.MBase.Header := Session.io.InXY(67, 13, 8, 8, 11, Session.Msgs.MBase.Header);
+ 'U' : If Session.Msgs.MBase.DefNScan < 2 Then Inc(Session.Msgs.MBase.DefNScan) Else Session.Msgs.MBase.DefNScan := 0;
+ 'V' : If Session.Msgs.MBase.DefQScan < 2 Then Inc(Session.Msgs.MBase.DefQScan) Else Session.Msgs.MBase.DefQScan := 0;
+ 'W' : Session.Msgs.MBase.MaxMsgs := strS2I(Session.io.InXY(67, 19, 5, 5, 12, strI2S(Session.Msgs.MBase.MaxMsgs)));
+ 'X' : Session.Msgs.MBase.MaxAge := strS2I(Session.io.InXY(67, 20, 5, 5, 12, strI2S(Session.Msgs.MBase.MaxAge)));
+ 'Y' : If Session.Msgs.MBase.BaseType = 0 Then Session.Msgs.MBase.BaseType := 1 Else Session.Msgs.MBase.BaseType := 0;
+ 'Q' : Break;
+ End;
+ Until False;
+ Seek (Session.Msgs.MBaseFile, FilePos(Session.Msgs.MBaseFile) - 1);
+ Write (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+ End;
+ End;
+ 'M' : Begin
+ Session.io.OutRaw ('Move which? ');
+ A := strS2I(Session.io.GetInput(3, 3, 12, ''));
+
+ Session.io.OutRaw ('Move before? (1-' + strI2S(FileSize(Session.Msgs.MBaseFile)) + '): ');
+ B := strS2I(Session.io.GetInput(3, 3, 12, ''));
+
+ If (A > 0) and (A <= FileSize(Session.Msgs.MBaseFile)) and (B > 0) and (B <= FileSize(Session.Msgs.MBaseFile)) Then Begin
+ Seek (Session.Msgs.MBaseFile, A);
+ Read (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+
+ AddRecord (Session.Msgs.MBaseFile, B+1, SizeOf(MBaseRec));
+ Write (Session.Msgs.MBaseFile, Session.Msgs.MBase);
+
+ If A > B Then Inc(A);
+
+ KillRecord (Session.Msgs.MBaseFile, A+1, SizeOf(MBaseRec));
+ End;
+ End;
+ 'Q' : break;
+ end;
+
+ until False;
+ close (Session.Msgs.MBaseFile);
+end;
+
+end.
diff --git a/mystic/bbs_cfg_protocol.pas b/mystic/bbs_cfg_protocol.pas
new file mode 100644
index 0000000..70f0653
--- /dev/null
+++ b/mystic/bbs_cfg_protocol.pas
@@ -0,0 +1,148 @@
+Unit bbs_cfg_Protocol;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Configuration_ProtocolEditor;
+
+Implementation
+
+Uses
+ m_FileIO,
+ m_Strings,
+ bbs_Common,
+ bbs_ansi_MenuBox,
+ bbs_ansi_MenuForm;
+
+Procedure EditProtocol (Var Prot: RecProtocol);
+Var
+ Box : TAnsiMenuBox;
+ Form : TAnsiMenuForm;
+Begin
+ Box := TAnsiMenuBox.Create;
+ Form := TAnsiMenuForm.Create;
+
+ Form.HelpSize := 0;
+
+ Box.Header := ' Protocol Editor: ' + Prot.Desc + ' ';
+
+ Box.Open (6, 5, 75, 15);
+
+ VerticalLine (22, 7, 13);
+
+ Form.AddBol ('A', ' Active ' , 14, 7, 24, 7, 8, 3, @Prot.Active, '');
+ Form.AddTog ('O', ' OS ' , 18, 8, 24, 8, 4, 7, 0, 2, 'Windows Linux OSX', @Prot.OSType, '');
+ Form.AddBol ('B', ' Batch ' , 15, 9, 24, 9, 7, 3, @Prot.Batch, '');
+ Form.AddChar ('K', ' Hot Key ' , 13, 10, 24, 10, 9, 1, 254, @Prot.Key, '');
+ Form.AddStr ('D', ' Description ' , 9, 11, 24, 11, 13, 40, 40, @Prot.Desc, '');
+ Form.AddStr ('S', ' Send Command ', 8, 12, 24, 12, 14, 50, 100, @Prot.SendCmd, '');
+ Form.AddStr ('R', ' Recv Command ', 8, 13, 24, 13, 14, 50, 100, @Prot.RecvCmd, '');
+
+ Form.Execute;
+ Box.Close;
+
+ Form.Free;
+ Box.Free;
+End;
+
+Procedure Configuration_ProtocolEditor;
+Var
+ Box : TAnsiMenuBox;
+ List : TAnsiMenuList;
+ F : TBufFile;
+ Prot : RecProtocol;
+
+ Procedure MakeList;
+ Var
+ OS : String;
+ Begin
+ List.Clear;
+
+ F.Reset;
+
+ While Not F.Eof Do Begin
+ F.Read (Prot);
+
+ Case Prot.OSType of
+ 0 : OS := 'Windows';
+ 1 : OS := 'Linux ';
+ 2 : OS := 'OSX';
+ End;
+
+ //'Active OSID Batch Key Description');
+
+ List.Add (strPadR(strYN(Prot.Active), 6, ' ') + ' ' + strPadR(OS, 7, ' ') + ' ' + strPadR(strYN(Prot.Batch), 5, ' ') + ' ' + strPadR(Prot.Key, 4, ' ') + Prot.Desc, 0);
+ End;
+
+ List.Add ('', 2);
+ End;
+
+Begin
+ F := TBufFile.Create(SizeOf(RecProtocol));
+
+ F.Open (Config.DataPath + 'protocol.dat', fmOpenCreate, fmReadWrite + fmDenyNone, SizeOf(RecProtocol));
+
+ Box := TAnsiMenuBox.Create;
+ List := TAnsiMenuList.Create;
+
+ Box.Header := ' Protocol Editor ';
+ List.NoWindow := True;
+ List.LoChars := #01#04#13#27;
+
+ Box.Open (13, 5, 67, 20);
+
+ WriteXY (15, 6, 112, 'Active OSID Batch Key Description');
+ WriteXY (15, 7, 112, strRep('Ä', 51));
+ WriteXY (15, 18, 112, strRep('Ä', 51));
+ WriteXY (18, 19, 112, '(CTRL/A) Add (CTRL/D) Delete (ENTER) Edit');
+
+ Repeat
+ MakeList;
+
+ List.Open (13, 7, 67, 18);
+ List.Close;
+
+ Case List.ExitCode of
+ #04 : If List.Picked < List.ListMax Then
+ If ShowMsgBox(1, 'Delete this entry?') Then Begin
+ F.RecordDelete (List.Picked);
+ MakeList;
+ End;
+ #01 : Begin
+ F.RecordInsert (List.Picked);
+
+ Prot.OSType := OSType;
+ Prot.Desc := 'New protocol';
+ Prot.Key := '!';
+ Prot.Active := False;
+ Prot.Batch := False;
+ Prot.SendCmd := '';
+ Prot.RecvCmd := '';
+
+ F.Write (Prot);
+
+ MakeList;
+ End;
+ #13 : If List.Picked <> List.ListMax Then Begin
+ F.Seek (List.Picked - 1);
+ F.Read (Prot);
+
+ EditProtocol(Prot);
+
+ F.Seek (List.Picked - 1);
+ F.Write (Prot);
+ End;
+ #27 : Break;
+ End;
+ Until False;
+
+ F.Close;
+ F.Free;
+
+ Box.Close;
+ List.Free;
+ Box.Free;
+End;
+
+End.
\ No newline at end of file
diff --git a/mystic/bbs_cfg_seclevel.pas b/mystic/bbs_cfg_seclevel.pas
new file mode 100644
index 0000000..b5b5c44
--- /dev/null
+++ b/mystic/bbs_cfg_seclevel.pas
@@ -0,0 +1,107 @@
+Unit bbs_cfg_SecLevel;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Levels_Editor;
+
+Implementation
+
+Uses
+ m_Strings,
+ bbs_Common,
+ bbs_Core,
+ bbs_User;
+
+Procedure Levels_Editor;
+Var
+ A : Integer;
+ Old : RecSecurity;
+Begin
+ Session.SystemLog('*LEVEL EDITOR*');
+
+ Old := Session.User.Security;
+
+ Reset (Session.User.SecurityFile);
+ Read (Session.User.SecurityFile, Session.User.Security);
+ Repeat
+ Session.io.OutFullLn ('|CL|14Security Level ' + strI2S(FilePos(Session.User.SecurityFile)) + ' of 255|CR|03');
+ Session.io.OutRawLn ('A. Description : ' + Session.User.Security.Desc);
+ Session.io.OutRawLn ('B. Time allowed/day : ' + strI2S(Session.User.Security.Time));
+ Session.io.OutRawLn ('C. Max calls/day : ' + strI2S(Session.User.Security.MaxCalls));
+ Session.io.OutRawLn ('D. Max downloads/day : ' + strI2S(Session.User.Security.MaxDLs));
+ Session.io.OutRawLn ('E. Max download K/day : ' + strI2S(Session.User.Security.MaxDLk));
+ Session.io.OutRawLn ('F. Max mins in time bank: ' + strI2S(Session.User.Security.MaxTB));
+
+ Session.io.OutRaw ('G. UL/DL ratio : ');
+ If Session.User.Security.DLRatio = 0 Then
+ Session.io.OutRawLn ('Disabled')
+ Else
+ Session.io.OutRawLn ('1 UL for every ' + strI2S(Session.User.Security.DLRatio) + ' DLs');
+
+ Session.io.OutRaw ('H. UL/DL Kb ratio : ');
+ If Session.User.Security.DLKRatio = 0 Then
+ Session.io.OutRawLn ('Disabled')
+ Else
+ Session.io.OutRawLn ('1 UL kb for every ' + strI2S(Session.User.Security.DLKRatio) + ' DL kb');
+
+ Session.io.OutRaw ('I. Post / Call Ratio : ');
+ If Session.User.Security.PCRatio = 0 Then
+ Session.io.OutRawLn ('Disabled')
+ Else
+ Session.io.OutRawLn (strI2S(Session.User.Security.PCRatio) + ' posts for every 100 calls');
+
+ Session.io.OutFullLn ('|CRK. Upgraded Flags Set 1 : ' + DrawAccessFlags(Session.User.Security.AF1));
+ Session.io.OutFullLn ('L. Upgraded Flags Set 2 : ' + DrawAccessFlags(Session.User.Security.AF2));
+
+ Session.io.OutFullLn ('|CRM. Hard AF Upgrade : ' + Session.io.OutYN(Session.User.Security.Hard));
+
+ Session.io.OutRawLn ('N. Start Menu : ' + Session.User.Security.StartMeNU);
+
+ Session.io.OutFull ('|CR|09([) Previous, (]), Next, (J)ump, (Q)uit: ');
+ Case Session.io.OneKey('[]ABCDEFGHIJKLMNQ', True) of
+ '[' : If FilePos(Session.User.SecurityFile) > 1 Then Begin
+ Seek (Session.User.SecurityFile, FilePos(Session.User.SecurityFile)-1);
+ Write (Session.User.SecurityFile, Session.User.Security);
+ Seek (Session.User.SecurityFile, FilePos(Session.User.SecurityFile)-2);
+ Read (Session.User.SecurityFile, Session.User.Security);
+ End;
+ ']' : If FilePos(Session.User.SecurityFile) < 255 Then Begin
+ Seek (Session.User.SecurityFile, FilePos(Session.User.SecurityFile)-1);
+ Write (Session.User.SecurityFile, Session.User.Security);
+ Read (Session.User.SecurityFile, Session.User.Security);
+ End;
+ 'A' : Session.User.Security.Desc := Session.io.InXY(27, 3, 30, 30, 11, Session.User.Security.Desc);
+ 'B' : Session.User.Security.Time := strS2I(Session.io.InXY(27, 4, 3, 3, 12, strI2S(Session.User.Security.Time)));
+ 'C' : Session.User.Security.MaxCalls := strS2I(Session.io.InXY(27, 5, 4, 4, 11, strI2S(Session.User.Security.MaxCalls)));
+ 'D' : Session.User.Security.MaxDLs := strS2I(Session.io.InXY(27, 6, 4, 4, 11, strI2S(Session.User.Security.MaxDLs)));
+ 'E' : Session.User.Security.MaxDLK := strS2I(Session.io.InXY(27, 7, 4, 4, 11, strI2S(Session.User.Security.MaxDLK)));
+ 'F' : Session.User.Security.MaxTB := strS2I(Session.io.InXY(27, 8, 4, 4, 11, strI2S(Session.User.Security.MaxTB)));
+ 'G' : Session.User.Security.DLRatio := strS2I(Session.io.InXY(27, 9, 2, 2, 12, strI2S(Session.User.Security.DLRatio)));
+ 'H' : Session.User.Security.DLKRatio := strS2I(Session.io.InXY(27, 10, 4, 4, 12, strI2S(Session.User.Security.DLKRatio)));
+ 'I' : Session.User.Security.PCRatio := strS2I(Session.io.InXY(27, 11, 4, 4, 12, strI2S(Session.User.Security.PCRatio)));
+ 'J' : Begin
+ Session.io.OutRaw ('Jump to (1-255): ');
+ A := strS2I(Session.io.GetInput(3, 3, 12, ''));
+ If (A > 0) and (A < 256) Then Begin
+ Seek (Session.User.SecurityFile, FilePos(Session.User.SecurityFile)-1);
+ Write (Session.User.SecurityFile, Session.User.Security);
+ Seek (Session.User.SecurityFile, A-1);
+ Read (Session.User.SecurityFile, Session.User.Security);
+ End;
+ End;
+ 'K' : EditAccessFlags(Session.User.Security.AF1);
+ 'L' : EditAccessFlags(Session.User.Security.AF2);
+ 'M' : Session.User.Security.Hard := Not Session.User.Security.Hard;
+ 'N' : Session.User.Security.StartMenu := Session.io.InXY(27, 17, 8, 8, 11, Session.User.Security.startmenu);
+ 'Q' : Break;
+ End;
+ Until False;
+ Seek (Session.User.SecurityFile, FilePos(Session.User.SecurityFile)-1);
+ Write (Session.User.SecurityFile, Session.User.Security);
+ Close (Session.User.SecurityFile);
+ Session.User.Security := Old;
+End;
+
+End.
diff --git a/mystic/bbs_cfg_useredit.pas b/mystic/bbs_cfg_useredit.pas
new file mode 100644
index 0000000..b4afa50
--- /dev/null
+++ b/mystic/bbs_cfg_useredit.pas
@@ -0,0 +1,346 @@
+Unit bbs_cfg_UserEdit;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ m_Types,
+ m_DateTime,
+ m_Strings,
+ bbs_Common,
+ bbs_Core;
+
+Procedure User_Editor (LocalEdit, OneUser : Boolean);
+
+Implementation
+
+Uses
+ bbs_User,
+ bbs_NodeInfo,
+ bbs_General;
+
+Procedure User_Editor (LocalEdit, OneUser : Boolean);
+Const
+ ModeTypeStr : Array[0..1] of String[8] = ('Standard', 'Lightbar');
+ More : Boolean = False;
+Var
+ ValidStr : String;
+ UserNode : Word;
+ LocalSave : Boolean;
+ Image : TConsoleImageRec;
+ Str : String;
+ A : LongInt;
+Begin
+ Reset (Session.User.UserFile);
+
+ If Eof(Session.User.UserFile) Then Begin
+ Close (Session.User.UserFile);
+ Exit;
+ End;
+
+ Session.SystemLog ('*USER EDIT*');
+
+ Session.InUserEdit := True;
+
+ {$IFNDEF UNIX}
+ If LocalEdit Then Begin
+ Screen.GetScreenImage(1, 1, 80, 25, Image);
+ LocalSave := Session.LocalMode;
+ Session.LocalMode := True;
+ Session.User.TempUser := Session.User.ThisUser;
+ End;
+ {$ENDIF}
+
+ If Not OneUser Then Begin
+ Read (Session.User.UserFile, Session.User.TempUser);
+
+ If Session.User.UserNum = FilePos(Session.User.UserFile) Then
+ Session.User.TempUser := Session.User.ThisUser;
+ End;
+
+ Repeat
+ UserNode := Is_User_Online(Session.User.TempUser.Handle);
+
+ Session.io.OutFull ('|16|CL|14User Editor: ' + strI2S(FilePos(Session.User.UserFile)) + ' of ' + strI2S(FileSize(Session.User.UserFile)) +
+ ' |03(Idx: ' + strI2S(Session.User.TempUser.PermIdx) + ')');
+
+ If UserNode > 0 Then
+ Session.io.OutFull (' |10(On Node ' + strI2S(UserNode) + ')');
+
+ If LocalEdit Then
+ Session.io.OutFullLn (' |12(Local Display)')
+ Else
+ Session.io.OutRawLn ('');
+
+ Session.io.OutFullLn ('|08|$D79Ä|03');
+
+ If More Then Begin
+ Session.io.OutFullLn ('|12Additional settings for ' + Session.User.TempUser.Handle + ':|03|CR');
+
+ Session.io.OutRawLn ('A. Full NodeChat ' + Session.io.OutYN(Session.User.TempUser.UseFullChat));
+ Session.io.OutRawLn ('B. Expires Date ' + Session.User.TempUser.Expires);
+ Session.io.OutRawLn ('C. Expires To ' + strI2S(Session.User.TempUser.ExpiresTo));
+
+ For A := 1 to 10 Do Session.io.OutRawLn('');
+
+ Session.io.OutFullLn ('|10(1)|08|$D24Ä|10(2)|08|$D23Ä|10(3)|08|$D23Ä|03');
+
+ Session.io.OutRawLn ('Calls ' + strPadR(strI2S(Session.User.TempUser.Calls), 14, ' ') +
+ 'First Call ' + strPadR(DateDos2Str(Session.User.TempUser.FirstOn, Session.User.ThisUser.DateType), 14, ' ') +
+ 'Msg Posts ' + strI2S(Session.User.TempUser.Posts));
+ Session.io.OutRawLn ('Calls Today ' + strPadR(strI2S(Session.User.TempUser.CallsToday), 14, ' ') +
+ 'Last Call ' + strPadR(DateDos2Str(Session.User.TempUser.LastOn, Session.User.ThisUser.DateType), 14, ' ') +
+ 'Sent Email ' + strI2S(Session.User.TempUser.Emails));
+ Session.io.OutRawLn ('Downloads ' + strPadR(strI2S(Session.User.TempUser.DLs), 14, ' ') +
+ 'Download K ' + strPadR(strI2S(Session.User.TempUser.DLk), 14, ' ') +
+ 'Uploads ' + strI2S(Session.User.TempUser.ULs));
+ Session.io.OutRawLn ('DLs Today ' + strPadR(strI2S(Session.User.TempUser.DLsToday), 14, ' ') +
+ 'DLk Today ' + strPadR(strI2S(Session.User.TempUser.DLkToday), 14, ' ') +
+ 'Upload KB ' + strI2S(Session.User.TempUser.ULk));
+
+ Session.io.OutFullLn ('|08|$D79Ä');
+ Session.io.OutFull ('|09(Q)uit: ');
+
+ If UserNode > 0 Then
+ ValidStr := 'Q'
+ Else
+ ValidStr := 'ABC123Q';
+
+ Case Session.io.OneKey(ValidStr, True) of
+ 'A' : Session.User.TempUser.UseFullChat := Not Session.User.TempUser.UseFullChat;
+ 'B' : Session.User.TempUser.Expires := Session.io.InXY(18, 6, 8, 8, 5, Session.User.TempUser.Expires);
+ 'C' : Session.User.TempUser.ExpiresTo := strS2I(Session.io.InXY(18, 7, 3, 3, 1, strI2S(Session.User.TempUser.ExpiresTo)));
+ 'Q' : More := False;
+ '1' : Begin
+ Session.User.TempUser.Calls := strS2I(Session.io.InXY(14, 17, 5, 5, 12, strI2S(Session.User.TempUser.Calls)));
+ Session.User.TempUser.CallsToday := strS2I(Session.io.InXY(14, 18, 5, 5, 12, strI2S(Session.User.TempUser.CallsToday)));
+ Session.User.TempUser.DLs := strS2I(Session.io.InXY(14, 19, 5, 5, 12, strI2S(Session.User.TempUser.DLs)));
+ Session.User.TempUser.DLsToday := strS2I(Session.io.InXY(14, 20, 5, 5, 12, strI2S(Session.User.TempUser.DLsToday)));
+ End;
+ '2' : Begin
+ Session.User.TempUser.FirstOn := DateStr2Dos(Session.io.InXY(40, 17, 8, 8, 15, DateDos2Str(Session.User.TempUser.FirstOn, Session.User.ThisUser.DateType)));
+ Session.User.TempUser.LastOn := DateStr2Dos(Session.io.InXY(40, 18, 8, 8, 15, DateDos2Str(Session.User.TempUser.LastOn, Session.User.ThisUser.DateType)));
+ Session.User.TempUser.DLK := strS2I(Session.io.InXY(40, 19, 10, 10, 12, strI2S(Session.User.TempUser.DLK)));
+ Session.User.TempUser.DLKToday := strS2I(Session.io.InXY(40, 20, 10, 10, 12, strI2S(Session.User.TempUser.DLKToday)));
+ End;
+ '3' : Begin
+ Session.User.TempUser.Posts := strS2I(Session.io.InXY(66, 17, 10, 10, 12, strI2S(Session.User.TempUser.Posts)));
+ Session.User.TempUser.Emails := strS2I(Session.io.InXY(66, 18, 10, 10, 12, strI2S(Session.User.TempUser.Emails)));
+ Session.User.TempUser.ULS := strS2I(Session.io.InXY(66, 19, 10, 10, 12, strI2S(Session.User.TempUser.ULS)));
+ Session.User.TempUser.ULK := strS2I(Session.io.InXY(66, 20, 10, 10, 12, strI2S(Session.User.TempUser.ULK)));
+ End;
+ End;
+ End Else Begin
+ Session.io.OutRawLn ('A. Alias ' + strPadR(Session.User.TempUser.Handle, 32, ' ') +
+ 'V. Start Menu ' + Session.User.TempUser.StartMeNU);
+
+ Session.io.OutRawLn ('B. Real Name ' + strPadR(Session.User.TempUser.RealName, 32, ' ') +
+ 'W. Language ' + Session.User.TempUser.Theme);
+
+ Session.io.OutRawLn ('C. Address ' + strPadR(Session.User.TempUser.Address, 32, ' ') +
+ 'X. Hot Keys ' + Session.io.OutYN(Session.User.TempUser.HotKeys));
+
+ Session.io.OutRawLn ('D. City ' + strPadR(Session.User.TempUser.City, 32, ' ') +
+ 'Y. Date Type ' + DateTypeStr[Session.User.TempUser.DateType]);
+
+ Session.io.OutRawLn ('E. Zip Code ' + strPadR(Session.User.TempUser.ZipCode, 32, ' ') +
+ 'Z. FList Type ' + ModeTypeStr[Session.User.TempUser.FileList]);
+
+ Session.io.OutRaw ('F. Birthdate ' + DateJulian2Str(Session.User.TempUser.Birthday, Session.User.ThisUser.DateType) +
+ ' - Age ' + strPadR(strI2S(DaysAgo(Session.User.TempUser.Birthday) DIV 365), 17, ' ') +
+ '1. Msg Editor ');
+
+ Case Session.User.TempUser.EditType of
+ 0 : Session.io.OutRawLn ('Line');
+ 1 : Session.io.OutRawLn ('Full');
+ 2 : Session.io.OutRawLn ('Ask');
+ End;
+
+ Session.io.OutRawLn ('G. Gender ' + strPadR(Session.User.TempUser.Gender, 32, ' ') +
+ '2. Msg Quote ' + ModeTypeStr[Ord(Session.User.TempUser.UseLBQuote)]);
+
+ Session.io.OutRawLn ('H. Home Phone ' + strPadR(Session.User.TempUser.HomePhone, 32, ' ') +
+ '3. Msg Reader ' + ModeTypeStr[Session.User.TempUser.MReadType]);
+
+ Session.io.OutRawLn ('I. Data Phone ' + strPadR(Session.User.TempUser.DataPhone, 32, ' ') +
+ '4. Index ' + Session.io.OutYN(Session.User.TempUser.UseLBIndex));
+
+ Session.io.OutRawLn ('J. E-mail ' + strPadR(Session.User.TempUser.Email, 32, ' ') +
+ '5. Mail Index ' + Session.io.OutYN(Session.User.TempUser.UseLBMIdx));
+
+ Session.io.OutRawLn ('K. ' + strPadL(Config.OptionalField[1].Desc, 10, ' ') + ' ' + strPadR(Session.User.TempUser.Optional[1], 32, ' ') +
+ '6. Time Left ' + strI2S(Session.User.TempUser.TimeLeft));
+
+ Session.io.OutRawLn ('L. ' + strPadL(Config.OptionalField[2].Desc, 10, ' ') + ' ' + strPadR(Session.User.TempUser.Optional[2], 32, ' ') +
+ '7. Time Bank ' + strI2S(Session.User.TempUser.TimeBank));
+
+ Session.io.OutRawLn ('N. ' + strPadL(Config.OptionalField[3].Desc, 10, ' ') + ' ' + strPadR(Session.User.TempUser.Optional[3], 32, ' ') +
+ '8. Screen Size ' + strI2S(Session.User.TempUser.ScreenSize));
+
+ Session.io.OutRawLn ('O. User Note ' + strPadR(Session.User.TempUser.UserInfo, 32, ' ') +
+ '!. Ignore LC ' + Session.io.OutYN(Session.User.TempUser.Flags AND UserNoCaller <> 0));
+
+ Session.io.OutRawLn ('P. Security ' + strPadR(strI2S(Session.User.TempUser.Security), 36, ' ') +
+ 'Locked out ' + Session.io.OutYN(Session.User.TempUser.Flags AND UserLockedOut <> 0));
+
+ Session.io.OutRawLn ('R. Password ' + strPadR(strRep('*', Length(Session.User.TempUser.Password)), 39, ' ') +
+ 'Deleted ' + Session.io.OutYN(Session.User.TempUser.Flags AND UserDeleted <> 0));
+
+ Session.io.OutRawLn ('S. Flags #1 ' + DrawAccessFlags(Session.User.TempUser.AF1) + ' ' +
+ 'No Delete ' + Session.io.OutYN(Session.User.TempUser.Flags AND UserNoKill <> 0));
+
+ Session.io.OutRawLn ('T. Flags #2 ' + DrawAccessFlags(Session.User.TempUser.AF2) + ' ' +
+ 'No Ratios ' + Session.io.OutYN(Session.User.TempUser.Flags AND UserNoRatio <> 0));
+
+ Session.io.OutFullLn ('|08|$D79Ä');
+ Session.io.OutFull ('|09([) Prev, (]) Next, (U)pgrade, (*) Search, (M)ore, (Q)uit: ');
+
+ If UserNode > 0 Then
+ ValidStr := '[]*Q'
+ Else
+ ValidStr := '[]*ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678!';
+
+ Case Session.io.OneKey(ValidStr, True) of
+ 'A' : Session.User.TempUser.Handle := Session.io.InXY(16, 3, 30, 30, 18, Session.User.TempUser.Handle);
+ 'B' : Session.User.TempUser.RealName := Session.io.InXY(16, 4, 30, 30, 18, Session.User.TempUser.RealName);
+ 'C' : Session.User.TempUser.Address := Session.io.InXY(16, 5, 30, 30, 18, Session.User.TempUser.Address);
+ 'D' : Session.User.TempUser.City := Session.io.InXY(16, 6, 25, 25, 18, Session.User.TempUser.City);
+ 'E' : Session.User.TempUser.ZipCode := Session.io.InXY(16, 7, 9, 9, 12, Session.User.TempUser.ZipCode);
+ 'F' : Session.User.TempUser.Birthday := DateStr2Julian(Session.io.InXY (16, 8, 8, 8, 15, DateJulian2Str(Session.User.TempUser.Birthday, Session.User.ThisUser.DateType)));
+ 'G' : If Session.User.TempUser.Gender = 'M' Then Session.User.TempUser.Gender := 'F' Else Session.User.TempUser.Gender := 'M';
+ 'H' : Session.User.TempUser.HomePhone := Session.io.InXY (16, 10, 15, 15, 12, Session.User.TempUser.HomePhone);
+ 'I' : Session.User.TempUser.DataPhone := Session.io.InXY (16, 11, 15, 15, 12, Session.User.TempUser.DataPhone);
+ 'J' : Session.User.TempUser.Email := Session.io.InXY (16, 12, 30, 35, 11, Session.User.TempUser.Email);
+ 'K' : Session.User.TempUser.Optional[1] := Session.io.InXY (16, 13, 30, 35, 11, Session.User.TempUser.Optional[1]);
+ 'L' : Session.User.TempUser.Optional[2] := Session.io.InXY (16, 14, 30, 35, 11, Session.User.TempUser.Optional[2]);
+ 'N' : Session.User.TempUser.Optional[3] := Session.io.InXY (16, 15, 30, 35, 11, Session.User.TempUser.Optional[3]);
+ 'O' : Session.User.TempUser.UserInfo := Session.io.InXY (16, 16, 30, 30, 11, Session.User.TempUser.UserInfo);
+ 'P' : Begin
+ Session.User.TempUser.Security := strS2I(Session.io.InXY(16, 17, 3, 3, 12, strI2S(Session.User.TempUser.Security)));
+ If (Session.User.TempUser.Security > 255) or (Session.User.TempUser.Security < 0) Then Session.User.TempUser.Security := 0;
+ End;
+ 'R' : Session.User.TempUser.Password := Session.io.InXY (16, 18, 15, 15, 12, Session.User.TempUser.Password);
+ 'S' : EditAccessFlags(Session.User.TempUser.AF1);
+ 'T' : EditAccessFlags(Session.User.TempUser.AF2);
+ 'V' : Session.User.TempUser.StartMeNU := Session.io.InXY (64, 3, 8, 8, 11, Session.User.TempUser.StartMeNU);
+ 'W' : Session.User.TempUser.Theme := Session.io.InXY (64, 4, 8, 8, 11, Session.User.TempUser.Theme);
+ 'X' : Session.User.TempUser.HotKeys := Not Session.User.TempUser.HotKeys;
+ 'Y' : If Session.User.TempUser.DateType < 3 Then Inc (Session.User.TempUser.DateType) Else Session.User.TempUser.DateType := 1;
+ 'Z' : Session.User.TempUser.FileList := Ord(Not Boolean(Session.User.TempUser.FileList));
+ '1' : If Session.User.TempUser.EditType < 2 Then Inc (Session.User.TempUser.EditType) Else Session.User.TempUser.EditType := 0;
+ '2' : Session.User.TempUser.UseLBQuote := Not Session.User.TempUser.UseLBQuote;
+ '3' : Session.User.TempUser.MReadType := Ord(Not Boolean(Session.User.TempUser.MReadType));
+ '4' : Session.User.TempUser.UseLBIndex := Not Session.User.TempUser.UseLBIndex;
+ '5' : Session.User.TempUser.UseLBMIdx := Not Session.User.TempUser.UseLBMIdx;
+ '6' : Begin
+ Session.User.TempUser.TimeLeft := strS2I(Session.io.InXY(64, 13, 3, 3, 12, strI2S(Session.User.TempUser.TimeLeft)));
+ If OneUser or (Session.User.UserNum = FilePos(Session.User.UserFile)) Then
+ Session.SetTimeLeft(Session.User.TempUser.TimeLeft);
+ End;
+ '7' : Session.User.TempUser.TimeBank := strS2I(Session.io.InXY(64, 14, 3, 3, 12, strI2S(Session.User.TempUser.TimeBank)));
+ '8' : Session.User.TempUser.ScreenSize := strS2I(Session.io.InXY(64, 15, 2, 2, 12, strI2S(Session.User.TempUser.ScreenSize)));
+ '!' : Begin
+ Session.io.OutRaw ('(C)aller, (D)elete, (I)gnore Ratios, (L)ockOut, (N)oKill, (Q)uit: ');
+ Case Session.io.OneKey('CDILNQ', True) of
+ 'C' : Session.User.TempUser.Flags := Session.User.TempUser.Flags XOR UserNoCaller;
+ 'D' : Session.User.TempUser.Flags := Session.User.TempUser.Flags XOR UserDeleted;
+ 'I' : Session.User.TempUser.Flags := Session.User.TempUser.Flags XOR UserNoRatio;
+ 'L' : Session.User.TempUser.Flags := Session.User.TempUser.Flags XOR UserLockedOut;
+ 'N' : Session.User.TempUser.Flags := Session.User.TempUser.Flags XOR UserNoKill;
+ End;
+ End;
+ '[' : If Not OneUser Then Begin
+
+ If Session.User.UserNum = FilePos(Session.User.UserFile) Then
+ Session.User.ThisUser := Session.User.TempUser;
+
+ Seek (Session.User.UserFile, Pred(FilePos(Session.User.UserFile)));
+ Write (Session.User.UserFile, Session.User.TempUser);
+
+ If FilePos(Session.User.UserFile) > 1 Then Begin
+ Seek (Session.User.UserFile, FilePos(Session.User.UserFile)-2);
+ Read (Session.User.UserFile, Session.User.TempUser);
+ End Else Begin
+ Seek (Session.User.UserFile, FileSize(Session.User.UserFile) - 1);
+ Read (Session.User.UserFile, Session.User.TempUser);
+ End;
+ End;
+ ']' : If Not OneUser Then Begin
+ If Session.User.UserNum = FilePos(Session.User.UserFile) Then
+ Session.User.ThisUser := Session.User.TempUser;
+
+ Seek (Session.User.UserFile, Pred(FilePos(Session.User.UserFile)));
+ Write (Session.User.UserFile, Session.User.TempUser);
+ If Eof(Session.User.UserFile) Then Reset(Session.User.UserFile);
+ Read (Session.User.UserFile, Session.User.TempUser);
+ End;
+ '*' : If Not OneUser Then Begin
+ Session.io.OutFull ('User name / number: ');
+ Str := Session.io.GetInput(30, 30, 12, '');
+
+ If Session.User.UserNum = FilePos(Session.User.UserFile) Then
+ Session.User.ThisUser := Session.User.TempUser;
+
+ A := FilePos(Session.User.UserFile) - 1;
+ Seek (Session.User.UserFile, A);
+ Write (Session.User.UserFile, Session.User.TempUser);
+
+ If (strS2I(Str) > 0) and (strS2I(Str) < FileSize(Session.User.UserFile)) Then
+ A := strS2I(Str) - 1
+ Else Begin
+ Reset (Session.User.UserFile);
+ While Not Eof(Session.User.UserFile) Do Begin
+ Read (Session.User.UserFile, Session.User.TempUser);
+ If (Pos(Str, strUpper(Session.User.TempUser.Handle)) > 0) or (Pos(Str, strUpper(Session.User.TempUser.RealName)) > 0) Then Begin
+ Session.io.PromptInfo[1] := Session.User.TempUser.Handle;
+ If Session.io.GetYN(Session.GetPrompt(155), True) Then Begin
+ A := FilePos(Session.User.UserFile) - 1;
+ Break;
+ End;
+ End;
+ End;
+ End;
+
+ Seek (Session.User.UserFile, A);
+ Read (Session.User.UserFile, Session.User.TempUser);
+ End;
+ 'M' : More := True;
+ 'Q' : Break;
+ 'U' : Begin
+ Session.io.OutFull ('|CR|09Upgrade to level (0-255): ');
+ A := strS2I(Session.io.GetInput(3, 3, 12, strI2S(Session.User.TempUser.Security)));
+ If (A > 255) or (A <= 0) Then A := 1;
+ Upgrade_User_Level(False, Session.User.TempUser, A);
+ End;
+ End;
+ End;
+
+ Until False;
+
+ If Not OneUser Then Begin
+ If Session.User.UserNum = FilePos(Session.User.UserFile) Then
+ Session.User.ThisUser := Session.User.TempUser;
+
+ Seek (Session.User.UserFile, Pred(FilePos(Session.User.UserFile)));
+ Write (Session.User.UserFile, Session.User.TempUser);
+ End;
+
+ {$IFNDEF UNIX}
+ If LocalEdit Then Begin
+ Session.LocalMode := LocalSave;
+ Session.User.ThisUser := Session.User.TempUser;
+
+ Screen.PutScreenImage(Image);
+
+ Session.SetTimeLeft (Session.User.TempUser.TimeLeft);
+ Update_Status_Line (StatusPtr, '');
+ End;
+ {$ENDIF}
+
+ Close (Session.User.UserFile);
+
+ Session.InUserEdit := False;
+End;
+
+End.
diff --git a/mystic/bbs_cfg_vote.pas b/mystic/bbs_cfg_vote.pas
new file mode 100644
index 0000000..c2cafdc
--- /dev/null
+++ b/mystic/bbs_cfg_vote.pas
@@ -0,0 +1,144 @@
+Unit bbs_cfg_Vote;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Vote_Editor;
+
+Implementation
+
+Uses
+ m_Strings,
+ bbs_Common,
+ bbs_Core,
+ bbs_User;
+
+Procedure Vote_Editor;
+var
+ A,
+ B : Integer;
+ C : Byte;
+ Temp : String[2];
+Begin
+ Session.SystemLog ('*VOTE EDITOR*');
+ Repeat
+ Session.io.OutFullLn ('|CL|14Voting Booth Editor|CR|CR|15## Question|CR|09-- ---------------------------------------');
+ Reset (VoteFile);
+ While Not Eof(VoteFile) do begin
+ Read (VoteFile, Vote);
+ Session.io.OutFullLn ('|15' + strPadR(strI2S(filepos(VoteFile)), 4, ' ') + '|14' + Vote.Question);
+ End;
+ Session.io.OutFull ('|CR|09(A)dd, (D)elete, (E)dit, (Q)uit? ');
+ case Session.io.OneKey ('ADEQ', True) of
+ 'A' : If FileSize(VoteFile) = mysMaxVoteQuestion Then
+ Session.io.OutFullLn ('|CR|14Max # of questions is ' + strI2S(mysMaxVoteQuestion))
+ Else Begin
+ Vote.Votes := 0;
+ Vote.AnsNum := 1;
+ Vote.ACS := 's999';
+ Vote.AddACS := 's999';
+ Vote.ForceACS := 's999';
+ Vote.Question := 'New Question';
+ Vote.Answer[1].Text := 'New voting answer';
+ Vote.Answer[1].Votes := 0;
+ Seek (VoteFile, FileSize(VoteFile));
+ Write (VoteFile, Vote);
+ End;
+ 'D' : begin
+ Session.io.OutRaw ('Delete which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ If (A > 0) And (A <= FileSize(VoteFile)) Then Begin
+ Session.io.OutFullLn ('|CRDeleting...');
+ KillRecord (VoteFile, A, SizeOf(VoteRec));
+
+ Reset (Session.User.UserFile);
+ While Not Eof(Session.User.UserFile) Do Begin
+ Read (Session.User.UserFile, Session.User.TempUser);
+ For C := A To 19 Do
+ Session.User.TempUser.Vote[C] := Session.User.TempUser.Vote[C+1];
+ Session.User.TempUser.Vote[20] := 0;
+ Seek (Session.User.UserFile, FilePos(Session.User.UserFile) - 1);
+ Write (Session.User.UserFile, Session.User.TempUser);
+ End;
+ Close (Session.User.UserFile);
+ For C := A to 19 Do
+ Session.User.ThisUser.Vote[C] := Session.User.ThisUser.Vote[C+1];
+ Session.User.ThisUser.Vote[20] := 0;
+ End;
+ end;
+ 'E' : begin
+ Session.io.OutRaw ('Edit which? ');
+ a := strS2I(Session.io.GetInput(3, 3, 11, ''));
+ if (a > 0) and (a <= filesize(VoteFile)) then begin
+ seek (VoteFile, a-1);
+ read (VoteFile, Vote);
+ repeat
+ Session.io.OutFullLn ('|CL|14Question ' + strI2S(FilePos(VoteFile)) + ' of ' + strI2S(FileSize(VoteFile)) + '|CR|03');
+ Session.io.OutRawln ('A. Question : ' + strPadR(Vote.Question, 60, ' '));
+ Session.io.OutRawLn ('B. Votes : ' + strI2S(Vote.Votes));
+ Session.io.OutRawLn ('C. Vote ACS : ' + Vote.ACS);
+ Session.io.OutRawLn ('E. Add ACS : ' + Vote.AddACS);
+ Session.io.OutRawLn ('F. Forced ACS : ' + Vote.ForceACS);
+ Session.io.OutFullLn ('|CR|15## Answer ## Answer');
+ Session.io.OutFullLn ('|09-- ----------------------------------- -- ------------------------------------');
+ For B := 1 to Vote.AnsNum Do Begin
+ Session.io.OutFull ('|11' + strZero(B) + ' |14' + strPadR(Vote.Answer[B].Text, 35, ' ') + ' ');
+ If (B Mod 2 = 0) or (B = Vote.AnsNum) Then Session.io.OutRawLn ('');
+ End;
+ Session.io.OutFull ('|CR|09(D)elete, (I)nsert, (Q)uit: ');
+ Temp := Session.io.GetInput(2, 2, 12, '');
+ If Temp = 'A' Then Vote.Question := Session.io.InXY(17, 3, 60, 70, 11, Vote.Question) Else
+ If Temp = 'B' Then Vote.Votes := strS2I(Session.io.InXY(17, 4, 5, 5, 12, strI2S(Vote.Votes))) Else
+ If Temp = 'C' Then Vote.ACS := Session.io.InXY(17, 5, 20, 20, 11, Vote.ACS) Else
+ If Temp = 'D' Then Begin
+ Session.io.OutFull ('Delete which answer? ');
+ A := strS2I(Session.io.GetInput(2, 2, 12, ''));
+ If (A > 0) and (A <= Vote.AnsNum) Then Begin
+ For C := A to Vote.AnsNum-1 Do
+ Vote.Answer[C] := Vote.Answer[C+1];
+ Dec (Vote.AnsNum);
+
+ Reset (Session.User.UserFile);
+ While Not Eof(Session.User.UserFile) Do Begin
+ Read (Session.User.UserFile, Session.User.TempUser);
+ If Session.User.TempUser.Vote[FilePos(VoteFile)] = A Then Begin
+ Session.User.TempUser.Vote[FilePos(VoteFile)] := 0;
+ Seek (Session.User.UserFile, FilePos(Session.User.UserFile) - 1);
+ Write (Session.User.UserFile, Session.User.TempUser);
+ End;
+ End;
+ Close (Session.User.UserFile);
+ If Session.User.ThisUser.Vote[FilePos(VoteFile)] = A Then
+ Session.User.ThisUser.Vote[FilePos(VoteFile)] := 0;
+ End;
+ End Else
+ If Temp = 'E' Then Vote.AddACS := Session.io.InXY(17, 6, 20, 20, 11, Vote.AddACS) Else
+ If Temp = 'F' Then Vote.ForceACS := Session.io.InXY(17, 7, 20, 20, 11, Vote.ForceACS) Else
+ If (Temp = 'I') and (Vote.AnsNum < 15) Then Begin
+ Inc (Vote.AnsNum);
+ Vote.Answer[Vote.AnsNum].Text := '';
+ Vote.Answer[Vote.AnsNum].Votes := 0;
+ End Else
+ If Temp = 'Q' Then Break Else Begin
+ A := strS2I(Temp);
+ If (A > 0) and (A < 21) Then Begin
+ Session.io.OutRaw ('Answer: ');
+ Vote.Answer[A].Text := Session.io.GetInput (40, 40, 11, Vote.Answer[A].Text);
+ Session.io.OutRaw ('Votes : ');
+ Vote.Answer[A].Votes := strS2I(Session.io.GetInput(5, 5, 12, strI2S(Vote.Answer[A].Votes)));
+ End;
+ End;
+ until false;
+ seek (VoteFile, filepos(VoteFile)-1);
+ write (VoteFile, Vote);
+ end;
+ end;
+ 'Q' : break;
+ end;
+
+ until False;
+ close (VoteFile);
+End;
+
+End.
diff --git a/mystic/bbs_common.pas b/mystic/bbs_common.pas
new file mode 100644
index 0000000..cea806a
--- /dev/null
+++ b/mystic/bbs_common.pas
@@ -0,0 +1,514 @@
+Unit bbs_Common;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ {$IFDEF UNIX}
+ Unix,
+ {$ENDIF}
+ m_Types,
+ m_Strings,
+ m_Output,
+ m_Input,
+ m_DateTime,
+ m_FileIO,
+ m_Socket_Class;
+
+{$I RECORDS.PAS}
+
+// This unit is very old (like 1994) and its functions need to be phased out
+// This is the stuff that hasn't been worked into a class somewhere or
+// replace with MDL/FP RTL functions
+
+Const
+ WinConsoleTitle = 'Mystic Node ';
+ {$IFDEF UNIX}
+ FileMask = '*';
+ {$ELSE}
+ FileMask = '*.*';
+ {$ENDIF}
+ CopyID = 'Copyright (C) 1997-2012 By James Coyle. All Rights Reserved.';
+ DateTypeStr : Array[1..4] of String[8] = ('MM/DD/YY', 'DD/MM/YY', 'YY/DD/MM', 'Ask ');
+ GetKeyFunc : Function (Forced : Boolean) : Boolean = NIL;
+
+Var
+ Screen : TOutput;
+ Input : TInput;
+ // input will be gone, client and screen will be passed.
+
+ CurRoom : Byte;
+ NodeMsgFile : File of NodeMsgRec;
+ NodeMsg : NodeMsgRec;
+ ConfigFile : File of RecConfig;
+ ChatFile : File of ChatRec;
+ RoomFile : File of RoomRec;
+ VoteFile : File of VoteRec;
+ Vote : VoteRec;
+ Chat : ChatRec;
+ Room : RoomRec;
+ LastOnFile : File of LastOnRec;
+ LastOn : LastOnRec;
+ Config : RecConfig;
+ StatusPtr : Byte = 1;
+
+Procedure EditAccessFlags (Var Flags : AccessFlagType);
+Function DrawAccessFlags (Var Flags : AccessFlagType) : String;
+Function NoGetKeyFunc (Forced : Boolean) : Boolean;
+Function getColor (A: Byte) : Byte;
+Procedure KillRecord (var dFile; RecNum: LongInt; RecSize: Word);
+Procedure AddRecord (var dFile; RecNum: LongInt; RecSize: Word);
+Function Bool_Search (Mask: String; Str: String) : Boolean;
+Function strAddr2Str (Addr: RecEchoMailAddr) : String;
+Function strStr2Addr (S : String; Var Addr: RecEchoMailAddr) : Boolean;
+Procedure CleanDirectory (Path: String; Exempt: String);
+Function ChangeDir (Dir : String) : Boolean;
+Function CopyFile (Source, Target : String): Boolean;
+Function CheckPath (Str: String) : String;
+Function ShellDOS (ExecPath: String; Command: String) : LongInt;
+
+{$IFNDEF UNIX}
+Procedure Update_Status_Line (Mode: Byte; Str: String);
+Procedure Process_Sysop_Cmd (Cmd: Char);
+{$ENDIF}
+
+Implementation
+
+Uses
+ DOS,
+ bbs_Core,
+ {$IFNDEF UNIX}
+ bbs_SysOpChat,
+ {$ENDIF}
+ bbs_cfg_UserEdit,
+ bbs_General,
+ MPL_Execute;
+
+Function DrawAccessFlags (Var Flags : AccessFlagType) : String;
+Var
+ S : String;
+ Ch : Char;
+Begin
+ S := '';
+
+ For Ch := 'A' to 'Z' Do
+ If Ord(Ch) - 64 in Flags Then S := S + Ch Else S := S + '-';
+
+ DrawAccessFlags := S;
+End;
+
+Procedure EditAccessFlags (Var Flags : AccessFlagType);
+Var
+ Ch : Char;
+Begin
+ Repeat
+ Session.io.OutFull ('Toggle: [' + DrawAccessFlags(Flags) + '] (Enter/Done): ');
+
+ Ch := Session.io.OneKey('ABCDEFGHIJKLMNOPQRSTUVWXYZ'#13, True);
+
+ If Ch = #13 Then Break;
+
+ If Ord(Ch) - 64 in Flags Then
+ Flags := Flags - [Ord(Ch) - 64]
+ Else
+ Flags := Flags + [Ord(Ch) - 64];
+ Until False;
+End;
+
+Function GetColor (A: Byte) : Byte;
+{ Used by SYSOPx.PAS files only }
+Var
+ FG,
+ BG : Byte;
+Begin
+ Session.io.OutFull ('|CRFG Color: ');
+ FG := strS2I(Session.io.GetInput(2, 2, 12, strI2S(A AND $F)));
+ Session.io.OutFull ('BG Color: ');
+ BG := strS2I(Session.io.GetInput(2, 2, 12, strI2S((A SHR 4) AND 7)));
+ getColor := FG + BG * 16;
+End;
+
+Procedure AddRecord (var dFile; RecNum: LongInt; RecSize: Word);
+Var
+ F : File Absolute dFile;
+ A : LongInt;
+ Buffer : Pointer;
+Begin
+ If (RecNum < 1) or (RecNum > FileSize(F) + 1) Then Exit;
+
+ GetMem (Buffer, RecSize);
+
+ Dec (RecNum);
+
+ For A := FileSize(F) - 1 DownTo RecNum Do Begin
+ Seek (F, A);
+ BlockRead (F, Buffer^, 1);
+ BlockWrite (F, Buffer^, 1);
+ End;
+
+ Seek (F, RecNum);
+
+ FreeMem (Buffer, RecSize);
+End;
+
+Procedure KillRecord (var dFile; RecNum: LongInt; RecSize: Word);
+Var
+ F : File Absolute dFile;
+ Count : LongInt;
+ Buffer : Pointer;
+Begin
+ If (RecNum < 1) or (RecNum > FileSize(F)) Then Exit;
+
+ GetMem (Buffer, RecSize);
+
+ Dec (RecNum);
+
+ For Count := RecNum to FileSize(F) - 2 Do Begin
+ Seek (F, Count + 1);
+ BlockRead (F, Buffer^, 1);
+ Seek (F, Count);
+ BlockWrite (F, Buffer^, 1);
+ End;
+
+ Seek (F, FileSize(F) - 1);
+ Truncate (F);
+
+ FreeMem (Buffer, RecSize);
+End;
+
+Function Bool_Search (Mask: String; Str: String) : Boolean;
+{ place holder for this functionality someday... need to pass in a buffer }
+{ to search }
+Begin
+ Bool_Search := True;
+ If Mask = '' Then Exit;
+ Bool_Search := Pos(strUpper(Mask), strUpper(Str)) > 0;
+End;
+
+Function strStr2Addr (S : String; Var Addr: RecEchoMailAddr) : Boolean;
+{ converts address string to type. returns false is invalid string }
+Var
+ A : Byte;
+ B : Byte;
+ C : Byte;
+ Point : Boolean;
+Begin
+ Result := False;
+ Point := True;
+
+ A := Pos(':', S);
+ B := Pos('/', S);
+ C := Pos('.', S);
+
+ If (A = 0) or (B = 0) Then Exit;
+
+ If C = 0 Then Begin
+ Point := False;
+ C := Length(S) + 1;
+ Addr.Point := 0;
+ End;
+
+ Addr.Zone := strS2I(Copy(S, 1, A - 1));
+ Addr.Net := strS2I(Copy(S, A + 1, B - 1 - A));
+ Addr.Node := strS2I(Copy(S, B + 1, C - 1 - B));
+
+ If Point Then Addr.Point := strS2I(Copy(S, C + 1, Length(S)));
+
+ Result := True;
+End;
+
+Function strAddr2Str (Addr : RecEchoMailAddr) : String;
+Var
+ Temp : String[20];
+Begin
+ Temp := strI2S(Addr.Zone) + ':' + strI2S(Addr.Net) + '/' +
+ strI2S(Addr.Node);
+
+ If Addr.Point <> 0 Then Temp := Temp + '.' + strI2S(Addr.Point);
+
+ Result := Temp;
+End;
+
+Function NoGetKeyFunc (Forced : Boolean): Boolean;
+Begin
+ Result := False;
+End;
+
+Function CopyFile (Source, Target : String): Boolean;
+Var
+ SF,
+ TF : File;
+ BRead,
+ BWrite : LongInt;
+ FileBuf : Array[1..4096] of Char;
+begin
+ CopyFile := False;
+
+ Assign(SF, Source);
+ {$I-} Reset(SF, 1); {$I+}
+
+ If IOResult <> 0 Then Exit;
+
+ Assign(TF, Target);
+ {$I-} ReWrite(TF, 1); {$I+}
+
+ If IOResult <> 0 then Exit;
+
+ Repeat
+ BlockRead (SF, FileBuf, SizeOf(FileBuf), BRead);
+ BlockWrite (TF, FileBuf, Bread, BWrite);
+ Until (BRead = 0) or (BRead <> BWrite);
+
+ Close(SF);
+ Close(TF);
+
+ If BRead = BWrite Then CopyFile := True;
+End;
+
+Procedure CleanDirectory (Path: String; Exempt: String);
+Var
+ DirInfo: SearchRec;
+Begin
+ FindFirst(Path + '*.*', Archive, DirInfo);
+ While DosError = 0 Do Begin
+ If strUpper(Exempt) <> strUpper(DirInfo.Name) Then
+ FileErase(Path + DirInfo.Name);
+ FindNext(DirInfo);
+ End;
+ FindClose(DirInfo);
+End;
+
+Function ChangeDir (Dir : String) : Boolean;
+Begin
+{ fpc linux needs trailing backslash}
+{ fpc and vp windows doesnt matter}
+{ tpx cannot have trailing backslash }
+
+ While Dir[Length(Dir)] = PathChar Do Dec(Dir[0]);
+
+ Dir := Dir + PathChar;
+
+ {$I-} ChDir(Dir); {$I+}
+
+ ChangeDir := IoResult = 0;
+End;
+
+Function CheckPath (Str: String) : String;
+Begin
+ While Str[Length(Str)] = PathChar Do Dec(Str[0]);
+
+ If Not FileDirExists(Str) Then Begin
+ If Session.io.GetYN ('|CR|12Directory doesn''t exist. Create? |11', True) Then Begin
+
+ {$I-} MkDir (Str); {$I+}
+
+ If IoResult <> 0 Then
+ Session.io.OutFull ('|CR|14Error creating directory!|CR|PA');
+ End;
+ End;
+
+ CheckPath := Str + PathChar;
+End;
+
+Function ShellDOS (ExecPath: String; Command: String) : LongInt;
+Var
+ RetVal : Integer;
+ {$IFNDEF UNIX}
+ Image : TConsoleImageRec;
+ {$ENDIF}
+Begin
+ {$IFDEF WINDOWS}
+ ExecInheritsHandles := True;
+ {$ENDIF}
+
+ If Session.User.UserNum <> -1 Then Begin
+ Reset (Session.User.UserFile);
+ Seek (Session.User.UserFile, Session.User.UserNum - 1);
+ Write (Session.User.UserFile, Session.User.ThisUser);
+ Close (Session.User.UserFile);
+ End;
+
+ {$IFNDEF UNIX}
+ Screen.GetScreenImage(1, 1, 80, 25, Image);
+ Screen.SetWindow (1, 1, 80, 25, False);
+ Screen.TextAttr := 7;
+ Screen.ClearScreen;
+ {$ENDIF}
+
+ {$IFDEF UNIX}
+ Screen.SetRawMode(False);
+ {$ENDIF}
+
+ If ExecPath <> '' Then ChangeDir(ExecPath);
+
+ {$IFDEF UNIX}
+ RetVal := Shell (Command);
+ {$ENDIF}
+
+ {$IFDEF WINDOWS}
+ If Command <> '' Then Command := '/C' + Command;
+ Exec (GetEnv('COMSPEC'), Command);
+ RetVal := DosExitCode;
+ {$ENDIF}
+
+ {$IFDEF UNIX}
+ Screen.SetRawMode(True);
+ {$ENDIF}
+
+ {$IFDEF WIN32}
+ Screen.SetWindowTitle (WinConsoleTitle + strI2S(Session.NodeNum));
+ {$ENDIF}
+
+ ChangeDir(Config.SystemPath);
+
+ If Session.User.UserNum <> -1 Then Begin
+ Reset (Session.User.UserFile);
+ Seek (Session.User.UserFile, Session.User.UserNum - 1);
+ Read (Session.User.UserFile, Session.User.ThisUser);
+ Close (Session.User.UserFile);
+ End;
+
+ Reset (Session.PromptFile);
+
+ {$IFNDEF UNIX}
+ Screen.PutScreenImage(Image);
+ Update_Status_Line(StatusPtr, '');
+ {$ENDIF}
+
+ Session.TimeOut := TimerSeconds;
+ ShellDOS := RetVal;
+End;
+
+{$IFNDEF UNIX}
+Procedure Update_Status_Line (Mode: Byte; Str: String);
+Begin
+ If Not Config.UseStatusBar Then Exit;
+
+ Screen.SetWindow (1, 1, 80, 25, False);
+
+ Case Mode of
+ 0 : Screen.WriteXY (1, 25, 120, strPadC(Str, 80, ' '));
+ 1 : Begin
+ Screen.WriteXY ( 1, 25, 112, ' [Alias] [Baud] [Sec] [Time] ');
+ Screen.WriteXY (10, 25, 112, Session.User.ThisUser.Handle);
+ Screen.WriteXY (48, 25, 112, strI2S(Session.Baud));
+ Screen.WriteXY (63, 25, 112, strI2S(Session.User.ThisUser.Security));
+ Screen.WriteXY (76, 25, 112, strI2S(Session.TimeLeft));
+ End;
+ 2 : Begin
+ Screen.WriteXY ( 1, 25, 112, ' [Name] [Flag1] ');
+ Screen.WriteXY ( 9, 25, 112, Session.User.ThisUser.RealName);
+ Screen.WriteXY (48, 25, 112, DrawAccessFlags(Session.User.ThisUser.AF1));
+ End;
+ 3 : Begin
+ Screen.WriteXY ( 1, 25, 112, ' [Address] ');
+ Screen.WriteXY (12, 25, 112, Session.User.ThisUser.Address);
+ Screen.WriteXY (43, 25, 112, Session.User.ThisUser.City);
+ Screen.WriteXY (69, 25, 112, Session.User.ThisUser.ZipCode);
+ End;
+ 4 : Begin
+ Screen.WriteXY ( 1, 25, 112, ' [BDay] [Sex] [Home PH] [Data PH] ');
+ Screen.WriteXY ( 9, 25, 112, DateDos2Str(Session.User.ThisUser.Birthday, Session.User.ThisUser.DateType));
+ Screen.WriteXY (25, 25, 112, Session.User.ThisUser.Gender);
+ Screen.WriteXY (39, 25, 112, Session.User.ThisUser.HomePhone);
+ Screen.WriteXY (65, 25, 112, Session.User.ThisUser.DataPhone);
+ End;
+ 5 : Begin
+ Screen.WriteXY ( 1, 25, 112, ' [Email] [Flag2] ');
+ Screen.WriteXY (10, 25, 112, Session.User.ThisUser.Email);
+ Screen.WriteXY (54, 25, 112, DrawAccessFlags(Session.User.ThisUser.AF2));
+ End;
+ 6 : Screen.WriteXY ( 1, 25, 112, ' ALT (C)hat (S)plit (E)dit (H)angup (J) DOS (U)pgrade (B) Status Bar ');
+ End;
+
+ Screen.SetWindow (1, 1, 80, 24, False);
+End;
+
+Procedure Process_Sysop_Cmd (Cmd: Char);
+Var
+ A : Integer;
+ X,
+ Y : Byte;
+ LS : Boolean;
+Begin
+ If Not Screen.Active And (Cmd <> #47) Then Exit;
+
+ Case Cmd of
+{U} #22 : Begin
+ X := Screen.CursorX;
+ Y := Screen.CursorY;
+ Update_Status_Line (0, 'Upgrade Security Level: ');
+ Screen.SetWindow (1, 25, 80, 25, False);
+ Screen.TextAttr := 8 + 7 * 16;
+ Screen.CursorXY (52, 2);
+ LS := Session.LocalMode;
+ Session.LocalMode := True;
+ A := strS2I(Session.io.GetInput(3, 3, 9, strI2S(Session.User.ThisUser.Security)));
+ Session.LocalMode := LS;
+ If (A > 0) and (A < 256) Then Begin
+ Upgrade_User_Level (True, Session.User.ThisUser, A);
+ Session.SetTimeLeft(Session.User.ThisUser.TimeLeft);
+ End;
+
+ Update_Status_Line(StatusPtr, '');
+
+ Screen.CursorXY (X, Y);
+ End;
+{E} #18 : If (Not Session.InUserEdit) and (Session.User.UserNum <> -1) Then User_Editor(True, True);
+{T} #20 : Begin
+// X := Screen.CursorX;
+// Y := Screen.CursorY;
+
+ Config.UseStatusBar := Not Config.UseStatusBar;
+
+ If Not Config.UseStatusBar Then Begin
+ Screen.WriteXY (1, 25, 0, strRep(' ', 80));
+ Screen.SetWindow (1, 1, 80, 25, False);
+ End Else
+ Update_Status_Line (StatusPtr, '');
+ End;
+{S} #31 : If Not Session.User.InChat Then OpenChat(True);
+{H} #35 : Begin
+ Session.SystemLog('SysOp hungup on user.');
+ Halt(0);
+ End;
+{C} #46 : If Not Session.User.InChat Then OpenChat(False);
+{V} #47 : If Screen.Active Then
+ Session.io.LocalScreenDisable
+ Else
+ Session.io.LocalScreenEnable;
+{B} #48 : Begin
+ If StatusPtr < 6 Then
+ Inc (StatusPtr)
+ Else
+ StatusPtr := 1;
+
+ Update_Status_Line (StatusPtr, '');
+ End;
+ #59..
+ #62 : Begin
+ Session.io.InMacroStr := Config.SysopMacro[Ord(Cmd) - 58];
+
+ If Session.io.InMacroStr[1] = '!' Then
+ ExecuteMPL (NIL, Copy(Session.io.InMacroStr, 2, 255))
+ Else Begin
+ Session.io.InMacroPos := 1;
+ Session.io.InMacro := Session.io.InMacroStr <> '';
+ End;
+ End;
+{+} #130: If Session.TimeLeft > 1 Then Begin
+ Session.SetTimeLeft(Session.TimeLeft-1);
+ Update_Status_Line(StatusPtr, '');
+ End;
+{-} #131: If Session.TimeLeft < 999 Then Begin
+ Session.SetTimeLeft(Session.TimeLeft+1);
+ Update_Status_Line(StatusPtr, '');
+ End;
+ End;
+End;
+{$ENDIF}
+
+Begin
+ GetKeyFunc := NoGetKeyFunc;
+End.
diff --git a/mystic/bbs_doors.pas b/mystic/bbs_doors.pas
new file mode 100644
index 0000000..023ee00
--- /dev/null
+++ b/mystic/bbs_doors.pas
@@ -0,0 +1,348 @@
+Unit bbs_Doors;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure ExecuteDoor (Format: Byte; Cmd: String);
+
+Implementation
+
+Uses
+ {$IFDEF WIN32}
+ Windows,
+ {$ENDIF}
+ m_Types,
+ m_Strings,
+ m_DateTime,
+ bbs_Common,
+ bbs_Core,
+ bbs_User;
+
+Const
+ Ending : String[2] = #13#10;
+
+Procedure Write_DOOR32 (cHandle : LongInt);
+Var
+ tFile : Text;
+Begin
+ Assign (tFile, Session.TempPath + 'door32.sys');
+ ReWrite (tFile);
+
+ If Session.LocalMode Then
+ Write (tFile, '0' + Ending)
+ Else
+ Write (tFile, '2' + Ending);
+
+ If Session.LocalMode Then
+ Write (tFile, '0' + Ending)
+ Else
+ Write (tFile, cHandle, Ending);
+
+ Write (tFile, Session.Baud, Ending);
+ Write (tFile, 'Mystic ' + mysVersion + Ending);
+ Write (tFile, Session.User.UserNum, Ending);
+ Write (tFile, Session.User.ThisUser.RealName + Ending);
+ Write (tFile, Session.User.ThisUser.Handle + Ending);
+ Write (tFile, Session.User.ThisUser.Security, Ending);
+ Write (tFile, Session.TimeLeft, Ending);
+ Write (tFile, Session.io.Graphics, Ending);
+ Write (tFile, Session.NodeNum, Ending);
+
+ Close (tFile);
+End;
+
+Procedure Write_DORINFO;
+Var
+ tFile : Text;
+ A : Byte;
+Begin
+ Assign (tFile, Session.TempPath + 'DORINFO1.DEF');
+ Rewrite (tFile);
+
+ Write (tFile, Config.BBSName + Ending);
+
+ A := Pos(' ', Config.SysopName);
+ If A > 0 Then
+ Write (tFile, Copy(Config.SysopName, 1, A-1) + Ending)
+ Else
+ Write (tFile, Config.SysopName + Ending);
+
+ If A > 0 Then
+ Write (tFile, Copy(Config.SysopName, A+1, 255) + Ending)
+ Else
+ Write (tFile, '' + Ending);
+
+ If Session.LocalMode Then Write (tFile, 'COM0' + Ending) Else Write (tFile, 'COM1', Ending);
+ Write (tFile, Session.Baud, ' BAUD,N,8,1' + Ending);
+ Write (tFile, '0' + Ending);
+
+ A := Pos(' ', Session.User.ThisUser.Handle);
+ If A > 0 Then
+ Write (tFile, Copy(Session.User.ThisUser.Handle, 1, A-1) + Ending)
+ Else
+ Write (tFile, Session.User.ThisUser.Handle + Ending);
+
+ If A > 0 Then
+ Write (tFile, Copy(Session.User.ThisUser.Handle, A+1, 255) + Ending)
+ Else
+ Write (tFile, '' + Ending);
+
+ Write (tFile, Session.User.ThisUser.City + Ending);
+ Write (tFile, Session.io.Graphics, Ending);
+ Write (tFile, Session.User.ThisUser.Security, Ending);
+ Write (tFile, Session.TimeLeft, Ending);
+ Write (tFile, '-1' + Ending); {-1 FOSSIL, 0=NOT... ???}
+
+ Close (tFile);
+End;
+
+Procedure Write_CHAINTXT;
+Var
+ tFile : Text;
+Begin
+ Assign (tFile, Session.TempPath + 'CHAIN.TXT');
+ ReWrite (tFile);
+
+ Write (tFile, Session.User.UserNum, Ending);
+ Write (tFile, Session.User.ThisUser.Handle + Ending);
+ Write (tFile, Session.User.ThisUser.RealName + Ending);
+ Write (tFile, '' + Ending);
+ Write (tFile, DaysAgo(Session.User.ThisUser.Birthday) DIV 365, Ending); { User's AGE }
+ Write (tFile, Session.User.ThisUser.Gender + Ending);
+ Write (tFile, '0' + Ending); { User's gold }
+ Write (tFile, DateDos2Str(Session.User.ThisUser.LastOn, 1) + Ending);
+ Write (tFile, '80' + Ending);
+ Write (tFile, Session.User.ThisUser.ScreenSize, Ending);
+ Write (tFile, Session.User.ThisUser.Security, Ending);
+ Write (tFile, '0' + Ending);
+ Write (tFile, '0' + Ending);
+ Write (tFile, Session.io.Graphics, Ending);
+ Write (tFile, Ord(Not Session.LocalMode), Ending);
+ Write (tFile, (Session.TimeLeft * 60), Ending);
+ Write (tFile, Session.Lang.TextPath + Ending);
+ Write (tFile, Config.DataPath + Ending);
+ Write (tFile, 'SYSOP.', Session.NodeNum, Ending);
+ If Session.LocalMode Then
+ Write (tFile, 'KB' + Ending)
+ Else
+ Write (tFile, Session.Baud, Ending);
+ Write (tFile, '1', Ending);
+ Write (tFile, Config.BBSName + Ending);
+ Write (tFile, Config.SysopName + Ending);
+ Write (tFile, TimerSeconds, Ending);
+ Write (tFile, '0' + Ending); {seconds online}
+ Write (tFile, Session.User.ThisUser.ULk, Ending);
+ Write (tFile, Session.User.ThisUser.ULs, Ending);
+ Write (tFile, Session.User.ThisUser.DLk, Ending);
+ Write (tFile, Session.User.ThisUser.DLs, Ending);
+ Write (tFile, '8N1' + Ending);
+ Close (tFile);
+End;
+
+Procedure Write_DOORSYS;
+Var
+ tFile : Text;
+{ Temp : LongInt;}
+Begin
+ Assign (tFile, Session.TempPath + 'DOOR.SYS');
+ Rewrite (tFile);
+
+ If Session.LocalMode Then Write (tFile, 'COM0:' + Ending) Else Write (tFile, 'COM1:' + Ending);
+ Write (tFile, Session.Baud, Ending);
+ Write (tFile, '8' + Ending);
+ Write (tFile, Session.NodeNum, Ending);
+ Write (tFile, Session.Baud, Ending); {locked rate}
+ Write (tFile, 'Y' + Ending); {screen display}
+ Write (tFile, 'N' + Ending);
+ Write (tFile, 'Y' + Ending); {page bell}
+ Write (tFile, 'Y' + Ending);
+ Write (tFile, Session.User.ThisUser.RealName + Ending);
+ Write (tFile, Session.User.ThisUser.City + Ending);
+ Write (tFile, Session.User.ThisUser.HomePhone + Ending);
+ Write (tFile, Session.User.ThisUser.DataPhone + Ending);
+ Write (tFile, Session.User.ThisUser.Password + Ending);
+ Write (tFile, Session.User.ThisUser.Security, Ending);
+ Write (tFile, Session.User.ThisUser.Calls, Ending);
+ Write (tFile, DateDos2Str(Session.User.ThisUser.LastOn, 1) + Ending);
+
+ Write (tFile, (Session.TimeLeft * 60), Ending); {seconds left}
+ Write (tFile, Session.TimeLeft, Ending); {mins left}
+
+ If Session.io.Graphics = 1 Then Write (tFile, 'GR' + Ending) Else Write (tFile, 'NG' + Ending);
+
+ Write (tFile, Session.User.ThisUser.ScreenSize, Ending); {page length}
+ Write (tFile, 'N' + Ending); {Y=expert, N=novice}
+ Write (tFile, '' + Ending);
+ Write (tFile, '' + Ending);
+ Write (tFile, '' + Ending); {user account expiration date}
+ Write (tFile, Session.User.UserNum, Ending); {user record number}
+ Write (tFile, '' + Ending); {default protocol}
+ Write (tFile, Session.User.ThisUser.ULs, Ending);
+ Write (tFile, Session.User.ThisUser.DLs, Ending);
+ Write (tFile, Session.User.ThisUser.DLk, Ending);
+ Write (tFile, Session.User.Security.MaxDLk, Ending);
+ Write (tFile, Session.User.ThisUser.Birthday, Ending);
+ Write (tFile, Config.DataPath + Ending);
+ Write (tFile, Config.MsgsPath + Ending);
+ Write (tFile, Config.SysopName + Ending);
+ Write (tFile, Session.User.ThisUser.Handle + Ending);
+ Write (tFile, TimeDos2Str(Session.NextEvent.ExecTime, False) + Ending); {next event start time hh:mm}
+ Write (tFile, 'Y' + Ending); {error-free connection}
+ Write (tFile, 'N' + Ending); {ansi in NG mode}
+ Write (tFile, 'Y' + Ending); {record locking}
+ Write (tFile, '3' + Ending); {default BBS color}
+ Write (tFile, '0' + Ending); {time credits per minute}
+ Write (tFile, '00/00/00' + Ending); {last new filescan date}
+ Write (tFile, TimeDos2Str(Session.User.ThisUser.LastOn, False) + Ending); {time of this call}
+ Write (tFile, TimeDos2Str(Session.User.ThisUser.LastOn, False) + Ending); {time of last call}
+ Write (tFile, '32768' + Ending); {max daily files (??) }
+ Write (tFile, Session.User.ThisUser.DLsToday, Ending);
+ Write (tFile, Session.User.ThisUser.ULk, Ending);
+ Write (tFile, Session.User.ThisUser.DLk, Ending);
+ Write (tFile, '' + Ending); {user comment}
+ Write (tFile, '0' + Ending); {total doors opened}
+ Write (tFile, Session.User.ThisUser.Posts, Ending); {total posts}
+ Close (tFile);
+End;
+
+{$IFDEF WIN32}
+Procedure Shell_DOOR32 (Cmd : String);
+Var
+ PI : TProcessInformation;
+ SI : TStartupInfo;
+ Image : TConsoleImageRec;
+ PassHandle : LongInt;
+Begin
+ PassHandle := 0;
+
+ If Not Session.LocalMode Then
+ PassHandle := Session.Client.FSocketHandle;
+
+ If Session.User.UserNum <> -1 Then Begin
+ Reset (Session.User.UserFile);
+ Seek (Session.User.UserFile, Session.User.UserNum - 1);
+ Write (Session.User.UserFile, Session.User.ThisUser);
+ Close (Session.User.UserFile);
+ End;
+
+ WRITE_DOOR32(PassHandle);
+
+ Screen.GetScreenImage(1,1,80,25, Image);
+
+ Cmd := Cmd + #0;
+
+ FillChar(SI, SizeOf(SI), 0);
+ FillChar(PI, SizeOf(PI), 0);
+
+ SI.CB := SizeOf(TStartupInfo);
+ SI.wShowWindow := SW_SHOWMINNOACTIVE;
+ SI.dwFlags := SI.dwFlags or STARTF_USESHOWWINDOW;
+
+ If CreateProcess(NIL, @Cmd[1],
+ NIL,
+ NIL,
+ True,
+ CREATE_SEPARATE_WOW_VDM,
+ NIL,
+ NIL,
+ SI,
+ PI) Then
+ WaitForSingleObject (PI.hProcess, INFINITE);
+
+ ChangeDir(Config.SystemPath);
+
+ If Session.User.UserNum <> -1 Then Begin
+ Reset (Session.User.UserFile);
+ Seek (Session.User.UserFile, Session.User.UserNum - 1);
+ Read (Session.User.UserFile, Session.User.ThisUser);
+ Close (Session.User.UserFile);
+ End;
+
+ Screen.SetWindowTitle(WinConsoleTitle + strI2S(Session.NodeNum));
+ Screen.PutScreenImage(Image);
+
+ Update_Status_Line(StatusPtr, '');
+
+ Session.TimeOut := TimerSeconds;
+End;
+{$ENDIF}
+
+Procedure ExecuteDoor (Format: Byte; Cmd: String);
+{Format:
+ 0 = None
+ 1 = DORINFO1.DEF
+ 2 = DOOR.SYS
+ 3 = CHAIN.TXT
+}
+Var
+ A : LongInt;
+ Temp : String;
+Begin
+ A := Pos('/DOS', strUpper(Cmd));
+
+ If A > 0 Then Begin
+ Delete (Cmd, A, 4);
+ Ending := #13#10;
+ End Else
+ Ending := LineTerm;
+
+ Temp := '';
+ A := 1;
+
+ While A <= Length(Cmd) Do Begin
+ If Cmd[A] = '%' Then Begin
+ Inc(A);
+ {$IFDEF UNIX}
+ If Cmd[A] = '0' Then Temp := Temp + '1' Else
+ {$ELSE}
+ If Cmd[A] = '0' Then Temp := Temp + strI2S(Session.Client.FSocketHandle) Else
+ {$ENDIF}
+ If Cmd[A] = '1' Then Temp := Temp + '1' Else
+ If Cmd[A] = '2' Then Temp := Temp + strI2S(Session.Baud) Else
+ If Cmd[A] = '3' Then Temp := Temp + strI2S(Session.NodeNum) Else
+ If Cmd[A] = '4' Then Temp := Temp + Session.UserIPInfo Else
+ If Cmd[A] = '5' Then Temp := Temp + Session.UserHostInfo Else
+ If Cmd[A] = '#' Then Temp := Temp + strI2S(Session.User.ThisUser.PermIdx) Else
+ If Cmd[A] = 'T' Then Temp := Temp + strI2S(Session.TimeLeft) Else
+ If Cmd[A] = 'P' Then Temp := Temp + Session.TempPath Else
+ If Cmd[A] = 'U' Then Temp := Temp + strReplace(Session.User.ThisUser.Handle, ' ', '_');
+ End Else
+ Temp := Temp + Cmd[A];
+
+ Inc (A);
+ End;
+
+ Session.SystemLog ('Executed Door: ' + Temp);
+
+ A := TimerMinutes; { save current timer for event check after door }
+
+ Case Format of
+ 1 : Write_DORINFO;
+ 2 : Write_DOORSYS;
+ 3 : Write_CHAINTXT;
+ {$IFDEF UNIX}
+ 4 : Write_DOOR32(0);
+ {$ENDIF}
+ End;
+
+ {$IFDEF WIN32}
+ If Format = 4 Then
+ Shell_DOOR32(Temp)
+ Else
+ If ShellDOS ('', Temp) = 0 Then;
+ {$ELSE}
+ If ShellDOS ('', Temp) = 0 Then;
+ {$ENDIF}
+
+ { Check to see if event was missed while user was in door }
+
+ If Session.NextEvent.Active Then
+ If (TimerMinutes < A) and (A < Session.NextEvent.ExecTime) Then Begin { midnight roll over }
+ If Session.MinutesUntilEvent(Session.NextEvent.ExecTime) = 0 Then;
+ End Else
+ If (A < Session.NextEvent.ExecTime) and (TimerMinutes > Session.NextEvent.ExecTime) Then
+ If Session.MinutesUntilEvent(Session.NextEvent.ExecTime) = 0 Then;
+End;
+
+End.
diff --git a/mystic/bbs_edit_full.pas b/mystic/bbs_edit_full.pas
new file mode 100644
index 0000000..a9c4cdf
--- /dev/null
+++ b/mystic/bbs_edit_full.pas
@@ -0,0 +1,848 @@
+Unit bbs_Edit_Full;
+
+{ this editor really should be rewritten from scratch again, since i wrote }
+{ it sooo many years ago. i'm sure i could do a better job now! in any }
+{ case, some things which could be added or included in new editor: }
+{ }
+{ 1. cut/paste line functions }
+{ 2. move word left/right commands }
+{ 3. optional spell checker? }
+{ 4. reformat paragraph command }
+{ 5. tag lines? }
+{ 6. ability to show embedded ansi/pipes }
+{ 7. ability to toggle off word wrap? }
+{ 8. ability to set foreground/background color }
+{ 9. ability to change extended character sets }
+{ 10. maybe remove Squish msgbase support and add a OWNER field to each }
+{ message. Owners can be assigned by Sysop ACS or the original poster }
+{ Ownership allows message editing but not deleting. this combined with }
+{ the above allows artists to collaborate ANSIs in the message bases with}
+{ the FSE. totally badass feature no one has ever done. }
+{ CTRL-A = change attribute CTRL-C change character set? CTRL-W on off}
+{ then ahve a PUBLISH feature which can allow downloads or moving to the}
+{ ANSI gallery? }
+{ add SAUCE editor if this happens too }
+{ how will message quoting work though? has to strip colors }
+{ hmm what happens if two users want to work together though? }
+
+{$I M_OPS.PAS}
+
+Interface
+
+Function AnsiEditor (Var Lines: SmallInt; WrapPos: Byte; MaxLines: SmallInt; TEdit, Forced: Boolean; Var Subj: String) : Boolean;
+
+Implementation
+
+Uses
+ m_Strings,
+ bbs_Common,
+ bbs_Core;
+
+Procedure Print (S: String);
+Begin
+ {$IFNDEF UNIX}
+ If Not Session.LocalMode Then Session.io.BufAddStr(S);
+ {$ENDIF}
+
+ Screen.WriteStr(S);
+End;
+
+Procedure PrintLn (S: String);
+Begin
+ Print (S + #13#10);
+End;
+
+Function AnsiEditor (Var Lines: Integer; WrapPos: Byte; MaxLines: Integer; TEdit, Forced: Boolean; Var Subj: String) : Boolean;
+Const
+ WinStart : Byte = 2;
+ WinEnd : Byte = 22;
+ InsertMode : Boolean = True;
+
+Var
+ Done : Boolean;
+ Save : Boolean;
+ Ch : Char;
+ tColor : Byte;
+ CurX : Byte;
+ CurY : Integer;
+ CurLine : Integer;
+ TotalLine : Integer;
+ QuoteCurLine : Integer;
+ QuoteTopPage : Integer;
+
+Procedure UpdatePosition;
+Begin
+ If CurLine > TotalLine Then TotalLine := CurLine;
+ If CurX > Length(Session.Msgs.MsgText[CurLine]) Then CurX := Length(Session.Msgs.MsgText[CurLine]) + 1;
+ Session.io.AnsiGotoXY (CurX, CurY);
+End;
+
+Procedure ReFresh_Part;
+Var
+ A,
+ B : Integer;
+Begin
+ Session.io.AnsiGotoXY (1, CurY);
+
+ A := CurY;
+ B := CurLine;
+
+ Repeat
+ If B <= TotalLine Then Print(Session.Msgs.MsgText[B]);
+ If B <= TotalLine + 1 Then Begin
+ Session.io.AnsiClrEOL;
+ PrintLn('');
+ End;
+
+ Inc (A);
+ Inc (B);
+ Until A > WinEnd;
+
+ UpdatePosition;
+End;
+
+Procedure Refresh_Text;
+Var
+ A,
+ B : Integer;
+Begin
+ { b = first line at top of window }
+ { cury = yposition of last line. }
+
+ CurY := WinStart + 5;
+ B := CurLine - 5;
+
+ If B < 1 Then Begin
+ CurY := WinStart + (5 + B - 1);
+ B := 1;
+ End;
+
+ Session.io.AnsiGotoXY (1, WinStart);
+
+ A := WinStart;
+
+ Repeat
+ If B <= TotalLine Then Print(Session.Msgs.MsgText[B]);
+ Session.io.AnsiClrEOL;
+ PrintLn('');
+ Inc (A);
+ Inc (B);
+ Until A > WinEnd;
+
+ UpdatePosition;
+End;
+
+Procedure Insert_Line (Num: Integer);
+Var
+ A : Integer;
+Begin
+ Inc (TotalLine);
+
+ For A := TotalLine DownTo Num + 1 Do
+ Session.Msgs.MsgText[A] := Session.Msgs.MsgText[A - 1];
+
+ Session.Msgs.MsgText[Num] := '';
+End;
+
+Procedure Format_Text;
+Var
+ OldStr : String; { holds the line text to be wrapped }
+ NewStr : String;
+ Line : Integer; { holds current line number being wrapped }
+ A : Integer;
+ NewY : Integer; { holds new y position on screen }
+ NewLine : Integer; { holds new line number }
+ Moved : Boolean;
+Begin
+ If TotalLine = MaxLines Then Exit;
+
+ Line := CurLine;
+ OldStr := Session.Msgs.MsgText[Line];
+ NewY := CurY;
+ NewLine := CurLine;
+ Moved := False;
+
+ Repeat
+ If Pos(' ', OldStr) = 0 Then Begin
+ Inc (Line);
+ Insert_Line (Line);
+
+ Session.Msgs.MsgText[Line] := Copy(OldStr, CurX, Length(OldStr));
+ Session.Msgs.MsgText[Line-1][0] := Chr(CurX - 1);
+
+ If CurX > WrapPos Then Begin
+ Inc (NewLine);
+ Inc (NewY);
+ CurX := 1;
+ End;
+
+ If NewY <= WinEnd Then Refresh_Part;
+
+ CurY := NewY;
+ CurLine := NewLine;
+
+ If CurY > WinEnd Then Refresh_Text Else UpdatePosition;
+
+ Exit;
+ End Else Begin
+ A := strWrap (OldStr, NewStr, WrapPos);
+
+ If (A > 0) And (Not Moved) And (CurX > Length(OldStr) + 1) Then Begin
+ CurX := CurX - A;
+ Moved := True;
+ Inc (NewLine);
+ Inc (NewY);
+ End;
+
+ Session.Msgs.MsgText[Line] := OldStr;
+ Inc (Line);
+
+ If (Session.Msgs.MsgText[Line] = '') or ((Pos(' ', Session.Msgs.MsgText[Line]) = 0) And (Length(Session.Msgs.MsgText[Line]) >= WrapPos)) Then Begin
+ Insert_Line(Line);
+ OldStr := NewStr;
+ End Else
+ OldStr := NewStr + ' ' + Session.Msgs.MsgText[Line];
+ End;
+ Until Length(OldStr) <= WrapPos;
+
+ Session.Msgs.MsgText[Line] := OldStr;
+
+ If NewY <= WinEnd Then Begin
+ Session.io.AnsiGotoXY(1, CurY);
+
+ A := CurLine;
+
+ Repeat
+ If (CurY + (A - CurLine) <= WinEnd) and (A <= TotalLine) Then Begin
+ Print(Session.Msgs.MsgText[A]);
+ Session.io.AnsiClrEOL;
+ PrintLn('');
+ End Else
+ Break;
+
+ Inc (A);
+ Until False;
+ End;
+
+ CurY := NewY;
+ CurLine := NewLine;
+
+ If CurY > WinEnd Then Refresh_Text Else UpdatePosition;
+End;
+
+Procedure Do_Enter;
+Begin
+ If TotalLine = MaxLines Then Exit;
+
+ Insert_Line (CurLine + 1);
+
+ If CurX < Length(Session.Msgs.MsgText[CurLine]) + 1 Then Begin
+ Session.Msgs.MsgText[CurLine+1] := Copy(Session.Msgs.MsgText[CurLine], CurX, Length(Session.Msgs.MsgText[CurLine]));
+ Delete (Session.Msgs.MsgText[CurLine], CurX, Length(Session.Msgs.MsgText[CurLine]));
+ End;
+
+ If CurY + 1 > WinEnd Then Refresh_Text Else Refresh_Part;
+
+ CurX := 1;
+
+ Inc(CurY);
+ Inc(CurLine);
+
+ UpdatePosition;
+End;
+
+Procedure Down_Key;
+Begin
+ If CurLine = TotalLine Then Exit;
+
+ If CurY = WinEnd Then
+ ReFresh_Text
+ Else Begin
+ Inc (CurY);
+ Inc (CurLine);
+ UpdatePosition;
+ End;
+End;
+
+Procedure Up_Key (EOL: Boolean);
+Begin
+{ if curline = 1 then exit;}
+{ appearently, exit is larger and slower than the statement below: }
+
+ If CurLine > 1 Then Begin
+ If EOL then begin
+ CurX := Length(Session.Msgs.MsgText[CurLine - 1]) + 1;
+ If CurX > WrapPos Then CurX := WrapPos + 1;
+ End;
+
+ If CurY = WinStart Then
+ Refresh_Text
+ Else Begin
+ Dec (CurY);
+ Dec (CurLine);
+ UpdatePosition;
+ End;
+ End;
+End;
+
+Procedure Delete_Line (Num : Integer);
+Var
+ A : Integer;
+Begin
+ For A := Num To TotalLine - 1 Do
+ Session.Msgs.MsgText[A] := Session.Msgs.MsgText[A + 1];
+
+ Session.Msgs.MsgText[TotalLine] := '';
+ Dec (TotalLine);
+End;
+
+Procedure Backspace;
+Var
+ A : Integer;
+Begin
+ If CurX > 1 Then Begin
+ Session.io.OutBS(1, True);
+ Dec (CurX);
+ Delete (Session.Msgs.MsgText[CurLine], CurX, 1);
+ If CurX < Length(Session.Msgs.MsgText[CurLine]) + 1 Then Begin
+ Print (Copy(Session.Msgs.MsgText[CurLine], CurX, Length(Session.Msgs.MsgText[CurLine])) + ' ');
+ UpdatePosition;
+ End;
+ End Else
+ If CurLine > 1 Then Begin
+ If Length(Session.Msgs.MsgText[CurLine - 1]) + Length(Session.Msgs.MsgText[CurLine]) <= WrapPos Then Begin
+ CurX := Length(Session.Msgs.MsgText[CurLine - 1]) + 1;
+ Session.Msgs.MsgText[CurLine - 1] := Session.Msgs.MsgText[CurLine - 1] + Session.Msgs.MsgText[CurLine];
+ Delete_Line (CurLine);
+ Dec (CurLine);
+ Dec (CurY);
+ If CurY < WinStart Then Refresh_Text Else Refresh_Part;
+ End Else
+ If Pos(' ', Session.Msgs.MsgText[CurLine]) > 0 Then Begin
+ For A := Length(Session.Msgs.MsgText[CurLine]) DownTo 1 Do
+ If (Session.Msgs.MsgText[CurLine][A] = ' ') and (Length(Session.Msgs.MsgText[CurLine - 1]) + A - 1 <= WrapPos) Then Begin
+ CurX := Length(Session.Msgs.MsgText[CurLine - 1]) + 1;
+ Session.Msgs.MsgText[CurLine - 1] := Session.Msgs.MsgText[CurLine - 1] + Copy(Session.Msgs.MsgText[CurLine], 1, A - 1);
+ Delete (Session.Msgs.MsgText[CurLine], 1, A);
+ Dec (CurLine);
+ Dec (CurY);
+ If CurY < WinStart Then Refresh_Text Else Refresh_Part;
+ Exit;
+ End;
+ Up_Key(True);
+ End;
+ End;
+End;
+
+procedure left_key;
+begin
+ if curx > 1 then Begin
+ Dec (CurX);
+ UpdatePosition;
+ end else
+ up_key(true);
+End;
+
+procedure right_key;
+Begin
+ if curx < length(Session.Msgs.MsgText[curline])+1 then begin
+ Inc (CurX);
+ UpdatePosition;
+ end else begin
+ if curY < totalline then curx := 1;
+ down_key;
+ end;
+End;
+
+Procedure Insert_Ch (Ch: Char);
+Begin
+ If InsertMode Then Begin
+ Insert (Ch, Session.Msgs.MsgText[Curline], CurX);
+ Print (Copy(Session.Msgs.MsgText[CurLine], CurX, Length(Session.Msgs.MsgText[CurLine])));
+ End Else Begin
+ If CurX > Length(Session.Msgs.MsgText[CurLine]) Then Inc(Session.Msgs.MsgText[CurLine][0]);
+ Session.Msgs.MsgText[CurLine][CurX] := Ch;
+ Print (Ch); {outchar}
+ End;
+ Inc (CurX);
+ UpdatePosition;
+End;
+
+Procedure ToggleInsert (Toggle: Boolean);
+Begin
+ If Toggle Then InsertMode := Not InsertMode;
+
+ Session.io.AnsiColor (Session.io.ScreenInfo[3].A);
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[3].X, Session.io.ScreenInfo[3].Y);
+
+ If InsertMode Then Print('INS') else Print('OVR'); { ++lang }
+
+ Session.io.AnsiGotoXY (CurX, CurY);
+ Session.io.AnsiColor (tColor);
+End;
+
+Procedure Draw_Screen;
+Begin
+ If TEdit Then Session.io.OutFile ('ansitext', True, 0) Else Session.io.OutFile ('ansiedit', True, 0);
+
+ WinStart := Session.io.ScreenInfo[1].Y;
+ WinEnd := Session.io.ScreenInfo[2].Y;
+ tColor := Session.io.ScreenInfo[1].A;
+
+ ToggleInsert (False);
+
+ Refresh_Text;
+End;
+
+Procedure Quote;
+Var
+ InFile : Text;
+ Start,
+ Finish : Integer;
+ NumLines : Integer;
+ Text : Array[1..mysMaxMsgLines] of String[80];
+ PI1 : String;
+ PI2 : String;
+Begin
+ Assign (InFile, Session.TempPath + 'msgtmp');
+ {$I-} Reset (InFile); {$I+}
+ If IoResult <> 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(158));
+ Exit;
+ End;
+
+ NumLines := 0;
+ Session.io.AllowPause := True;
+
+ While Not Eof(InFile) Do Begin
+ Inc (NumLines);
+ ReadLn (InFile, Text[NumLines]);
+ End;
+
+ Close (InFile);
+
+ PI1 := Session.io.PromptInfo[1];
+ PI2 := Session.io.PromptInfo[2];
+
+ Session.io.OutFullLn(Session.GetPrompt(452));
+
+ For Start := 1 to NumLines Do Begin
+ Session.io.PromptInfo[1] := strI2S(Start);
+ Session.io.PromptInfo[2] := Text[Start];
+
+ Session.io.OutFullLn (Session.GetPrompt(341));
+
+ If (Session.io.PausePtr >= Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Session.io.AllowPause := True;
+
+ Session.io.OutFull (Session.GetPrompt(159));
+ Start := strS2I(Session.io.GetInput(3, 3, 11, ''));
+
+ Session.io.OutFull (Session.GetPrompt(160));
+ Finish := strS2I(Session.io.GetInput(3, 3, 11, ''));
+
+ If (Start > 0) and (Start <= NumLines) and (Finish <= NumLines) Then Begin
+ If Finish = 0 Then Finish := Start;
+ For NumLines := Start to Finish Do Begin
+ If TotalLine = mysMaxMsgLines Then Break;
+ If Session.Msgs.MsgText[CurLine] <> '' Then Begin
+ Inc (CurLine);
+ Insert_Line (CurLine);
+ End;
+ Session.Msgs.MsgText[CurLine] := Text[NumLines];
+ End;
+ If CurLine < MaxLines then Inc(CurLine);
+ End;
+
+ Session.io.PromptInfo[1] := PI1;
+ Session.io.PromptInfo[2] := PI2;
+End;
+
+Procedure QuoteWindow;
+Var
+ QText : Array[1..mysMaxMsgLines] of String[80];
+ InFile : Text;
+ QuoteLines : Integer;
+ NoMore : Boolean;
+
+ Procedure UpdateBar (On: Boolean);
+ Begin
+ Session.io.AnsiGotoXY (1, QuoteCurLine + Session.io.ScreenInfo[2].Y);
+ If On Then
+ Session.io.AnsiColor (Session.Lang.QuoteColor)
+ Else
+ Session.io.AnsiColor (Session.io.ScreenInfo[2].A);
+
+ Print (strPadR(QText[QuoteTopPage + QuoteCurLine], 79, ' '));
+ End;
+
+ Procedure UpdateWindow;
+ Var
+ A : Integer;
+ Begin
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[2].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[2].A);
+ For A := QuoteTopPage to QuoteTopPage + 5 Do Begin
+ If A <= QuoteLines Then Print (QText[A]);
+ Session.io.AnsiClrEOL;
+ If A <= QuoteLines Then PrintLn('');
+ End;
+ UpdateBar(True);
+ End;
+
+Var
+ Scroll : Integer;
+ Temp1 : Integer;
+ Ch : Char;
+ Added : Boolean;
+Begin
+ Added := False;
+
+ Assign (InFile, Session.TempPath + 'msgtmp');
+ {$I-} Reset(InFile); {$I+}
+ If IoResult <> 0 Then Exit;
+
+ QuoteLines := 0;
+ NoMore := False;
+ Scroll := CurLine + 4;
+
+ While Not Eof(InFile) Do Begin
+ Inc (QuoteLines);
+ ReadLn (InFile, QText[QuoteLines]);
+ End;
+
+ Close (InFile);
+
+ Session.io.OutFile ('ansiquot', True, 0);
+
+ If CurY >= Session.io.ScreenInfo[1].Y Then Begin
+ Session.io.AnsiColor(tColor);
+ Temp1 := WinEnd;
+ WinEnd := Session.io.ScreenInfo[1].Y;
+ Refresh_Text;
+ WinEnd := Temp1;
+ End;
+
+ UpdateWindow;
+
+ Repeat
+ Ch := Session.io.GetKey;
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #71 : If QuoteCurLine > 0 Then Begin
+ QuoteTopPage := 1;
+ QuoteCurLine := 0;
+ UpdateWindow;
+ End;
+ #72 : Begin
+ If QuoteCurLine > 0 Then Begin
+ UpdateBar(False);
+ Dec(QuoteCurLine);
+ UpdateBar(True);
+ End Else
+ If QuoteTopPage > 1 Then Begin
+ Dec (QuoteTopPage);
+ UpdateWindow;
+ End;
+ NoMore := False;
+ End;
+ #73,
+ #75 : Begin
+ If QuoteTopPage > 6 Then
+ Dec (QuoteTopPage, 6)
+ Else Begin
+ QuoteTopPage := 1;
+ QuoteCurLine := 0;
+ End;
+ NoMore := False;
+ UpdateWindow;
+ End;
+ #79 : Begin
+ If QuoteLines <= 6 Then
+ QuoteCurLine := QuoteLines - QuoteTopPage
+ Else Begin
+ QuoteTopPage := QuoteLines - 5;
+ QuoteCurLine := 5;
+ End;
+
+ UpdateWindow;
+ End;
+ #80 : If QuoteTopPage + QuoteCurLine < QuoteLines Then Begin
+ If QuoteCurLine = 5 Then Begin
+ Inc (QuoteTopPage);
+ UpdateWindow;
+ End Else Begin
+ UpdateBar(False);
+ Inc (QuoteCurLine);
+ UpdateBar(True);
+ End;
+ End;
+ #77,
+ #81 : Begin
+ If QuoteLines <= 6 Then
+ QuoteCurLine := QuoteLines - QuoteTopPage
+ Else
+ If QuoteTopPage + 6 < QuoteLines - 6 Then
+ Inc (QuoteTopPage, 6)
+ Else Begin
+ QuoteTopPage := QuoteLines - 5;
+ QuoteCurLine := 5;
+ End;
+
+ UpdateWindow;
+ End;
+ End;
+ End Else
+ Case Ch of
+ #27 : Break;
+ #13 : If (TotalLine < mysMaxMsgLines) and (Not NoMore) Then Begin
+ Added := True;
+
+ If QuoteTopPage + QuoteCurLine = QuoteLines Then NoMore := True;
+
+ Insert_Line (CurLine);
+ Session.Msgs.MsgText[CurLine] := QText[QuoteTopPage + QuoteCurLine];
+ Inc (CurLine);
+
+ Session.io.AnsiColor(tColor);
+
+ Temp1 := WinEnd;
+ WinEnd := Session.io.ScreenInfo[1].Y;
+ If CurLine - Scroll + WinStart + 4 >= WinEnd Then Begin
+ Refresh_Text;
+ Scroll := CurLine;
+ End Else Begin
+ Dec (CurLine);
+ Refresh_Part;
+ Inc (CurLine);
+ Inc (CurY);
+ End;
+ WinEnd := Temp1;
+
+ If QuoteTopPage + QuoteCurLine < QuoteLines Then
+ If QuoteCurLine = 5 Then Begin
+ Inc (QuoteTopPage);
+ UpdateWindow;
+ End Else Begin
+ UpdateBar(False);
+ Inc (QuoteCurLine);
+ UpdateBar(True);
+ End;
+ End;
+ End;
+ Until False;
+ Session.io.OutFull('|16');
+ If (CurLine < mysMaxMsgLines) And Added Then Inc(CurLine);
+End;
+
+Procedure Commands;
+Var
+ Ch : Char;
+ Str : String;
+Begin
+ Done := False;
+ Save := False;
+
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(354));
+ Ch := Session.io.OneKey ('?ACHQRSTU', True);
+ Case Ch of
+ '?' : Session.io.OutFullLn (Session.GetPrompt(355));
+ 'A' : If Forced Then Begin
+ Session.io.OutFull (Session.GetPrompt(307));
+ Exit;
+ End Else Begin
+ Done := Session.io.GetYN(Session.GetPrompt(356), False);
+ Exit;
+ End;
+ 'C' : Exit;
+ 'H' : Begin
+ Session.io.OutFile ('fshelp', True, 0);
+ Exit;
+ End;
+ 'Q' : Begin
+ If Session.User.ThisUser.UseLBQuote Then
+ QuoteWindow
+ Else
+ Quote;
+ Exit;
+ End;
+ 'R' : Exit;
+ 'S' : Begin
+ Save := True;
+ Done := True;
+ End;
+ 'T' : Begin
+ Session.io.OutFull(Session.GetPrompt(463));
+ Str := Session.io.GetInput(60, 60, 11, Subj);
+ If Str <> '' Then Subj := Str;
+ Session.io.PromptInfo[2] := Subj;
+ Exit;
+ End;
+ 'U' : Begin
+ Session.Msgs.MessageUpload(CurLine);
+ TotalLine := CurLine;
+ Exit;
+ End;
+ End;
+ Until Done;
+End;
+
+Procedure Page_Up;
+Begin
+ If CurLine > 1 Then Begin
+ If LongInt(CurLine - (WinEnd - WinStart)) >= 1 Then
+ Dec (CurLine, (WinEnd - WinStart)) {scroll one page up}
+ Else
+ CurLine := 1;
+ Refresh_Text;
+ End;
+End;
+
+Procedure Page_Down;
+Begin
+ If CurLine < TotalLine Then Begin
+ If CurLine + (WinEnd - WinStart) <= TotalLine Then
+ Inc (CurLine, (WinEnd - WinStart))
+ Else
+ CurLine := TotalLine;
+ Refresh_Text;
+ End;
+End;
+
+Var
+ A : Integer;
+Begin
+ QuoteCurLine := 0;
+ QuoteTopPage := 1;
+
+ CurLine := Lines;
+ If Lines = 0 Then CurLine := 1;
+ Done := False;
+ CurX := 1;
+ CurY := WinStart;
+ TotalLine := CurLine;
+
+ Dec (WrapPos); { Kludge to make sure text length = WrapPos length }
+
+ For A := Lines + 1 to mysMaxMsgLines Do Session.Msgs.MsgText[A] := '';
+
+ Draw_Screen;
+
+ Session.io.AllowArrow := True;
+
+ Repeat
+ Ch := Session.io.GetKey;
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #71 : Begin
+ CurX := 1;
+ UpdatePosition;
+ End;
+ #72 : Up_Key(False);
+ #73 : Page_Up;
+ #75 : Left_Key;
+ #77 : Right_Key;
+ #79 : Begin
+ CurX := Length(Session.Msgs.MsgText[CurLine]) + 1;
+ If CurX > WrapPos Then CurX := WrapPos + 1; {since we DEC(WrapPos) on start}
+ UpdatePosition;
+ End;
+ #80 : Down_Key;
+ #81 : Page_Down;
+ #82 : ToggleInsert (True);
+ #83 : If CurX <= Length(Session.Msgs.MsgText[CurLine]) Then Begin
+ Delete (Session.Msgs.MsgText[CurLine], CurX, 1);
+ Print (Copy(Session.Msgs.MsgText[CurLine], CurX, Length(Session.Msgs.MsgText[CurLine])) + ' ');
+ UpdatePosition;
+ End Else
+ If CurLine < TotalLine Then
+ If (Session.Msgs.MsgText[CurLine] = '') and (TotalLine > 1) Then Begin
+ Delete_Line (CurLine);
+ Refresh_Part;
+ End Else
+ If TotalLine > 1 Then
+ If Length(Session.Msgs.MsgText[CurLine]) + Length(Session.Msgs.MsgText[CurLine + 1]) <= WrapPos Then Begin
+ Session.Msgs.MsgText[CurLine] := Session.Msgs.MsgText[CurLine] + Session.Msgs.MsgText[CurLine + 1];
+ Delete_Line (CurLine + 1);
+ Refresh_Part;
+ End Else
+ For A := Length(Session.Msgs.MsgText[CurLine + 1]) DownTo 1 Do
+ If (Session.Msgs.MsgText[CurLine + 1][A] = ' ') and (Length(Session.Msgs.MsgText[CurLine]) + A <= WrapPos) Then Begin
+ Session.Msgs.MsgText[CurLine] := Session.Msgs.MsgText[CurLine] + Copy(Session.Msgs.MsgText[CurLine + 1], 1, A - 1);
+ Delete (Session.Msgs.MsgText[CurLine + 1], 1, A);
+ Refresh_Part;
+ End;
+ End;
+ End Else
+ Case Ch of
+ ^A : Begin
+ Done := True;
+ Save := False;
+ End;
+{B} #2 : Draw_Screen;
+ #8 : Backspace;
+{I} #9,
+ #13 : Begin
+ Session.io.PurgeInputBuffer;
+ Do_Enter;
+ End;
+ ^Q : Begin
+ If Session.User.ThisUser.UseLBQuote Then
+ QuoteWindow
+ Else
+ Quote;
+ Draw_Screen;
+ End;
+ ^V : ToggleInsert (True);
+{Y} #25 : begin
+ delete_line (curline);
+ refresh_part;
+ end;
+ #27 : Begin
+ Commands;
+ If (Not Save) and (Not Done) Then Draw_Screen;
+ Session.io.AllowArrow := True; { just in case... }
+ End;
+ #32..
+ #254: Begin
+ If Length(Session.Msgs.MsgText[CurLine]) >= WrapPos Then begin
+ If TotalLine < MaxLines Then Begin
+ insert_ch (ch);
+ format_text;
+ End;
+ End Else
+ If (CurX = 1) and (Ch = '/') Then begin
+ Commands;
+ If (Not Save) and (Not Done) Then Draw_Screen;
+ Session.io.AllowArrow := True; { just in case ... }
+ End Else
+ insert_ch (ch);
+ End;
+ End;
+ Until Done;
+
+ Session.io.AllowArrow := False;
+
+ If Save Then Begin
+ A := TotalLine;
+ While (Session.Msgs.MsgText[A] = '') and (A > 1) Do Begin
+ Dec(A);
+ Dec(TotalLine);
+ End;
+ Lines := TotalLine;
+ End;
+
+ AnsiEditor := (Save = True);
+ Session.io.AnsiGotoXY (1, Session.User.ThisUser.ScreenSize);
+{ Session.io.AnsiGotoXY (1, WinEnd + 1);}
+End;
+
+End.
diff --git a/mystic/bbs_edit_line.pas b/mystic/bbs_edit_line.pas
new file mode 100644
index 0000000..946091b
--- /dev/null
+++ b/mystic/bbs_edit_line.pas
@@ -0,0 +1,200 @@
+Unit bbs_Edit_Line;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Function LineEditor (Var Lines : SmallInt; MaxLen: Byte; MaxLine: SmallInt; TEdit: Boolean; Forced: Boolean;
+ Var Subj: String) : Boolean;
+
+Implementation
+
+Uses
+ m_Strings,
+ bbs_Common,
+ bbs_Core,
+ bbs_FileBase,
+ bbs_User;
+
+Var
+ CurLine : Integer;
+ Done,
+ Save : Boolean;
+
+Procedure Quote;
+Var
+ InFile : Text;
+ Start,
+ Finish : Integer;
+ Lines : Integer;
+ Text : Array[1..mysMaxMsgLines] of String[80];
+Begin
+ Assign (InFile, Session.TempPath + 'msgtmp');
+ {$I-} Reset (InFile); {$I+}
+ If IoResult <> 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(158));
+ Exit;
+ End;
+
+ Lines := 0;
+ Session.io.AllowPause := True;
+
+ While Not Eof(InFile) Do Begin
+ Inc (Lines);
+ ReadLn (InFile, Text[Lines]);
+ End;
+
+ Close (InFile);
+
+ Session.io.OutFullLn(Session.GetPrompt(452));
+
+ For Start := 1 to Lines Do Begin
+ Session.io.PromptInfo[1] := strI2S(Start);
+ Session.io.PromptInfo[2] := Text[Start];
+
+ Session.io.OutFullLn (Session.GetPrompt(341));
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Session.io.AllowPause := True;
+
+ Session.io.OutFull (Session.GetPrompt(159));
+ Start := strS2I(Session.io.GetInput(3, 3, 11, ''));
+
+ Session.io.OutFull (Session.GetPrompt(160));
+ Finish := strS2I(Session.io.GetInput(3, 3, 11, ''));
+
+ If (Start > 0) and (Start <= Lines) and (Finish <= Lines) Then Begin
+ If Finish = 0 Then Finish := Start;
+ For Lines := Start to Finish Do Begin
+ If CurLine = mysMaxMsgLines Then Break;
+ Session.Msgs.MsgText[CurLine] := Text[Lines];
+ Inc (CurLine);
+ End;
+ End;
+End;
+
+Function LineEditor (Var Lines : Integer; MaxLen: Byte; MaxLine: Integer; TEdit, Forced : Boolean; Var Subj: String) : Boolean;
+
+ Procedure Commands;
+ Var
+ Ch : Char;
+ Begin
+ Done := False;
+ Save := False;
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(166));
+ Ch := Session.io.OneKey ('?ACQRSU', True);
+ Case Ch of
+ '?' : Session.io.OutFullLn (Session.GetPrompt(167));
+ 'A' : If Forced Then Begin
+ Session.io.OutFull (Session.GetPrompt(307));
+ Exit;
+ End Else
+ Done := Session.io.GetYN(Session.GetPrompt(168), False);
+ 'C' : Exit;
+ 'Q' : Begin
+ Quote;
+ Exit;
+ End;
+ 'R' : Exit;
+ 'S' : Begin
+ Save := True;
+ Done := True;
+ End;
+ 'U' : Begin
+ Session.Msgs.MessageUpload(CurLine);
+ Exit;
+ End;
+ End;
+ Until Done;
+ End;
+
+ Procedure FullReDraw;
+ Var
+ A : Integer;
+ Begin
+ Session.io.PromptInfo[1] := strI2S(MaxLen);
+ Session.io.PromptInfo[2] := strI2S(MaxLine);
+
+ Session.io.OutFullLn(Session.GetPrompt(162));
+
+ Session.io.OutFullLn(Session.GetPrompt(163));
+ For A := 1 to CurLine Do Begin
+ Session.io.OutRaw (Session.Msgs.MsgText[A]);
+ If A <> CurLine Then Session.io.OutRawLn('');
+ End;
+ End;
+
+ Procedure GetText;
+ Var
+ Ch : Char;
+ Begin
+ Repeat
+ Ch := Session.io.GetKey;
+ Case Ch of
+ ^R : FullReDraw;
+ #8 : If Length(Session.Msgs.MsgText[CurLine]) > 0 Then Begin
+ Session.io.OutBS(1, True);
+ Dec(Session.Msgs.MsgText[CurLine][0]);
+ End Else If CurLine > 1 Then Begin
+ Dec(CurLine);
+ Session.io.PromptInfo[1] := strI2S(CurLine);
+ Session.io.OutFullLn (Session.GetPrompt(165));
+ Session.io.OutRaw (Session.Msgs.MsgText[CurLine]);
+ If Session.Msgs.MsgText[CurLine] <> '' Then Begin
+ Session.io.OutBS(1, True);
+ Dec(Session.Msgs.MsgText[CurLine][0]);
+ End;
+ End;
+ #13 : Begin
+ If CurLine < MaxLine Then Begin
+ Inc(CurLine);
+ Session.io.OutRaw (#13#10);
+ End;
+ End;
+ Else
+ If (Ch = '/') and (Length(Session.Msgs.MsgText[CurLine]) = 0) Then Begin
+ Commands;
+ If (Not Save) and (Not Done) Then FullReDraw;
+ End Else
+ If Ch in [#32..#254] Then Begin
+ If Length(Session.Msgs.MsgText[Curline]) < MaxLen Then Begin
+ Session.Msgs.MsgText[CurLine] := Session.Msgs.MsgText[CurLine] + Ch;
+ Session.io.BufAddChar (Ch);
+ End;
+ If (Length(Session.Msgs.MsgText[CurLine]) > MaxLen-1) and (CurLine < MaxLine) Then Begin
+ strWrap (Session.Msgs.MsgText[CurLine], Session.Msgs.MsgText[Succ(CurLine)], MaxLen);
+ Inc(CurLine);
+ Session.io.OutBS (Length(Session.Msgs.MsgText[CurLine]), True);
+ Session.io.OutRawLn ('');
+ Session.io.OutRaw (Session.Msgs.MsgText[CurLine]);
+ End;
+ End;
+ End;
+ Until Done;
+ End;
+
+Var
+ A : Integer;
+Begin
+ CurLine := Lines;
+ If CurLine < MaxLine Then Inc(CurLine);
+ Done := False;
+ For A := Lines + 1 to mysMaxMsgLines Do Session.Msgs.MsgText[A] := '';
+ FullReDraw;
+ GetText;
+
+ If Save Then Begin
+ Lines := CurLine - 1;
+ LineEditor := True;
+ End Else
+ LineEditor := False;
+End;
+
+End.
diff --git a/mystic/bbs_filebase.pas b/mystic/bbs_filebase.pas
new file mode 100644
index 0000000..0d45b85
--- /dev/null
+++ b/mystic/bbs_filebase.pas
@@ -0,0 +1,3408 @@
+Unit bbs_FileBase;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ DOS,
+ mkCrap,
+ m_Strings,
+ m_FileIO,
+ m_DateTime,
+ bbs_Common,
+ bbs_General,
+ bbs_NodeInfo,
+ AView;
+
+Type
+ BatchRec = Record
+ FileName : String[70];
+ Area : Integer;
+ Size : LongInt;
+ End;
+
+ TFileBase = Class
+ FBaseFile : File of FBaseRec;
+ FDirFile : File of FDirRec;
+ FScanFile : File of FScanRec;
+ ProtocolFile : File of RecProtocol;
+ FGroupFile : File of RecGroup;
+ ArcFile : File of RecArchive;
+ FBase : FBaseRec;
+ FGroup : RecGroup;
+ FScan : FScanRec;
+ FDir : FDirRec;
+ Arc : RecArchive;
+ Protocol : RecProtocol;
+ BatchNum : Byte;
+ Batch : Array[1..mysMaxBatchQueue] of BatchRec;
+
+ Constructor Create (Var Owner: Pointer);
+ Destructor Destroy; Override;
+
+ Procedure dszGetFile (Var LogFile: Text; Var FName: String; Var Res: Boolean);
+ Function dszSearch (FName: String) : Boolean;
+ Procedure GetTransferTime (Size: Longint; Var Mins : Integer; Var Secs: Byte);
+ Procedure ExecuteArchive (FName: String; Temp: String; Mask: String; Mode: Byte);
+ Procedure ExecuteProtocol (Send: Boolean; FName: String);
+ Function SelectArchive : Boolean;
+ Function ListFileAreas (Compress: Boolean) : Integer;
+ Procedure ChangeFileArea (Data: String);
+ Procedure DownloadFile;
+ Procedure BatchClear;
+ Procedure BatchAdd;
+ Procedure BatchList;
+ Procedure BatchDelete;
+ Procedure SetFileScan;
+ Procedure GetFileScan;
+ Function SelectProtocol (Batch : Boolean) : Char;
+ Function WildcardMatch (Wildcard, FName: String) : Boolean;
+ Procedure CheckFileNameLength (FPath : String; Var FName : String);
+ Procedure GetFileDescription (FN : String);
+ Function CheckFileLimits (DL: Byte; DLK: Integer) : Byte;
+ Function ArchiveList (FName : String) : Boolean; { was string }
+ Function ImportDIZ (FN: String) : Boolean;
+ Function IsDupeFile (FileName : String; Global : Boolean) : Boolean;
+ Function ListFiles (Mode : Byte; Data : String) : Byte;
+ Procedure SetFileScanDate;
+ Function CopiedToTemp (FName: String) : Boolean;
+ Function SendFile (Data: String) : Boolean;
+ Procedure DownloadFileList (Data: String);
+ Function ExportFileList (NewFiles: Boolean; Qwk: Boolean) : Boolean;
+ Function ArchiveView (FName : String) : Boolean;
+ Procedure FileGroupChange (Ops: String; FirstBase, Intro : Boolean);
+ Procedure UploadFile;
+ Procedure DownloadBatch;
+ Procedure NewFileScan (Mode: Char);
+ Procedure ViewFile;
+ Procedure ToggleFileNewScan;
+ Procedure FileSearch;
+ Procedure DirectoryEditor (Edit: Boolean; Mask: String);
+ Procedure MassUpload;
+ End;
+
+Implementation
+
+Uses
+ bbs_Core,
+ MPL_Execute;
+
+Constructor TFileBase.Create (Var Owner: Pointer);
+Begin
+ Inherited Create;
+
+ FBase.Name := 'None';
+ FGroup.Name := 'None';
+ BatchNum := 0;
+End;
+
+Destructor TFileBase.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Procedure TFileBase.dszGetFile (Var LogFile: Text; Var FName: String; Var Res: Boolean);
+Type
+ TLineBuf = Array[0..1024] of Char;
+Var
+ LineBuf : TLineBuf;
+ TempStr1 : DirStr;
+ TempStr2 : NameStr;
+ TempStr3 : ExtStr;
+ WordPos : Integer;
+ Count : Integer;
+Begin
+ FName := '';
+ Res := False;
+ WordPos := 1;
+ Count := 1;
+
+ If EOF(LogFile) Then Exit;
+
+ FillChar(LineBuf, SizeOf(LineBuf), #0);
+
+ ReadLn (LogFile, LineBuf);
+
+ If LineBuf[0] = #0 Then Exit;
+
+ Res := Pos(UpCase(LineBuf[0]), 'RSZ') > 0;
+
+ While WordPos < 11 Do Begin
+ If LineBuf[Count] = #32 Then Begin
+ Inc (WordPos);
+ Repeat
+ Inc (Count);
+ Until LineBuf[Count] <> #32;
+ End Else
+ Inc (Count);
+ End;
+
+ Repeat
+ FName := FName + LineBuf[Count];
+ Inc (Count);
+ Until (LineBuf[Count] = #32) or (LineBuf[Count] = #0) or (Count = 1024);
+
+ FSplit(FName, TempStr1, TempStr2, TempStr3);
+
+ FName := TempStr2 + TempStr3;
+End;
+
+Function TFileBase.dszSearch (FName: String) : Boolean;
+Var
+ LogFile : Text;
+ FileName : String;
+ Status : Boolean;
+Begin
+ Result := False;
+
+ Assign (LogFile, Session.TempPath + 'xfer.log');
+ {$I-} Reset(LogFile); {$I+}
+ If IoResult <> 0 Then Begin
+ Session.SystemLog('ERROR: Can''t find xfer.log');
+ Exit;
+ End;
+
+ While Not Eof(LogFile) Do Begin
+ dszGetFile(LogFile, FileName, Status);
+
+ {$IFDEF FS_SENSITIVE}
+ If FileName = FName Then Begin
+ {$ELSE}
+ If strUpper(FileName) = strUpper(FName) Then Begin
+ {$ENDIF}
+ Result := Status;
+ Break;
+ End;
+ End;
+
+ Close (LogFile);
+End;
+
+Procedure TFileBase.ExecuteProtocol (Send: Boolean; FName: String);
+Var
+ T : Text;
+ Cmd : String;
+ Count : Byte;
+ Res : String;
+ Path : String;
+Begin
+ If Send Then
+ Cmd := Protocol.SendCmd
+ Else
+ Cmd := Protocol.RecvCmd;
+
+ Res := '';
+ Path := '';
+ Count := 1;
+
+ While Count <= Length(Cmd) Do Begin
+ If Cmd[Count] = '%' Then Begin
+ Inc(Count);
+ {$IFNDEF UNIX}
+ If Cmd[Count] = '0' Then Res := Res + strI2S(Session.Client.FSocketHandle) Else
+ {$ENDIF}
+ If Cmd[Count] = '1' Then Res := Res + '1' Else
+ If Cmd[Count] = '2' Then Res := Res + strI2S(Session.Baud) Else
+ If Cmd[Count] = '3' Then Res := Res + FName Else
+ If Cmd[Count] = '4' Then Res := Res + Session.UserIPInfo Else
+ If Cmd[Count] = '5' Then Res := Res + Session.UserHostInfo Else
+ If Cmd[Count] = '6' Then Res := Res + strReplace(Session.User.ThisUser.Handle, ' ', '_') Else
+ If Cmd[Count] = '7' Then Res := Res + strI2S(Session.NodeNum);
+ End Else
+ Res := Res + Cmd[Count];
+
+ Inc (Count);
+ End;
+
+ {$IFDEF UNIX}
+ Assign (T, Session.TempPath + 'xfer.sh');
+ ReWrite (T);
+ WriteLn (T, 'export DSZLOG=' + Session.TempPath + 'xfer.log');
+ WriteLn (T, Res);
+ Close (T);
+ {$ELSE}
+ Assign (T, Session.TempPath + 'xfer.bat');
+ ReWrite (T);
+ WriteLn (T, 'SET DSZLOG=' + Session.TempPath + 'xfer.log');
+ WriteLn (T, Res);
+ Close (T);
+ {$ENDIF}
+
+ { If uploading and batch, switch to upload directory via shelldos }
+ If Not Send And Protocol.Batch Then Path := FName;
+
+ If Res[1] = '!' Then Begin
+ Delete (Res, 1, 1);
+ ExecuteMPL(NIL, Res);
+ End Else
+ {$IFDEF UNIX}
+ ShellDOS (Path, 'sh ' + Session.TempPath + 'xfer.sh');
+ {$ELSE}
+ ShellDOS (Path, Session.TempPath + 'xfer.bat');
+ {$ENDIF}
+
+ ChangeDir(Config.SystemPath);
+End;
+
+Procedure TFileBase.GetTransferTime (Size: Longint; Var Mins : Integer; Var Secs: Byte);
+Var
+ B : LongInt;
+Begin
+ B := 0;
+ If Not Session.LocalMode Then B := Size DIV (Session.Baud DIV 10);
+ Mins := B DIV 60;
+ Secs := B MOD 60;
+End;
+
+Function TFileBase.ImportDIZ (FN: String) : Boolean;
+
+ Procedure RemoveLine (Num: Byte);
+ Var
+ A : Byte;
+ Begin
+ For A := Num To FDir.Lines - 1 Do
+ Session.Msgs.Msgtext[A] := Session.Msgs.MsgText[A + 1];
+
+ Session.Msgs.MsgText[FDir.Lines] := '';
+
+ Dec (FDir.Lines);
+ End;
+
+Var
+ tFile : Text;
+Begin
+ Result := False;
+
+ ExecuteArchive (FBase.Path + FN, '', 'file_id.diz', 2);
+
+ Assign (tFile, Session.TempPath + 'file_id.diz');
+ {$I-} Reset (tFile); {$I+}
+
+ If IoResult = 0 Then Begin
+ Result := True;
+ FDir.Lines := 0;
+
+ While Not Eof(tFile) Do Begin
+ Inc (FDir.Lines);
+ ReadLn (tFile, Session.Msgs.MsgText[FDir.Lines]);
+ Session.Msgs.MsgText[FDir.Lines] := strStripLOW(Session.Msgs.MsgText[FDir.Lines]);
+ If Length(Session.Msgs.MsgText[FDir.Lines]) > mysMaxFileDescLen Then Session.Msgs.MsgText[FDir.Lines][0] := Chr(mysMaxFileDescLen);
+ If FDir.Lines = Config.MaxFileDesc Then Break;
+ End;
+
+ Close (tFile);
+
+ FileErase(Session.TempPath + 'file_id.diz');
+
+ While (Session.Msgs.MsgText[1] = '') and (FDir.Lines > 0) Do
+ RemoveLine(1);
+
+ While (Session.Msgs.MsgText[FDir.Lines] = '') And (FDir.Lines > 0) Do
+ Dec (FDir.Lines);
+ End;
+End;
+
+Procedure TFileBase.SetFileScan;
+Var
+ A : Integer;
+ Temp : FScanRec;
+Begin
+ Temp.NewScan := FBase.DefScan;
+ Temp.LastNew := CurDateDos;
+
+ If Temp.NewScan = 2 Then Dec(Temp.NewScan);
+
+ Assign (FScanFile, Config.DataPath + FBase.FileName + '.scn');
+ {$I-} Reset (FScanFile); {$I+}
+ If IoResult <> 0 Then ReWrite (FScanFile);
+
+ If FileSize(FScanFile) < Session.User.UserNum - 1 Then Begin
+ Seek (FScanFile, FileSize(FScanFile));
+ For A := FileSize(FScanFile) to Session.User.UserNum - 1 Do
+ Write (FScanFile, Temp);
+ End;
+
+ Seek (FScanFile, Session.User.UserNum - 1);
+ Write (FScanFile, FScan);
+ Close (FScanFile);
+End;
+
+Procedure TFileBase.GetFileScan;
+Begin
+ FScan.NewScan := FBase.DefScan;
+ FScan.LastNew := CurDateDos;
+
+ If FScan.NewScan = 2 Then Dec(FScan.NewScan);
+
+ Assign (FScanFile, Config.DataPath + FBase.FileName + '.scn');
+ {$I-} Reset (FScanFile); {$I+}
+
+ If IoResult <> 0 Then Exit;
+
+ If FileSize(FScanFile) >= Session.User.UserNum Then Begin
+ Seek (FScanFile, Session.User.UserNum - 1);
+ Read (FScanFile, FScan);
+ End;
+
+ Close (FScanFile);
+End;
+
+Procedure TFileBase.SetFileScanDate;
+Var
+ L : LongInt;
+ Old : FBaseRec;
+ Str : String;
+Begin
+ Session.io.OutFull (Session.GetPrompt(255));
+
+ If FBase.FileName <> '' Then Begin
+ GetFileScan;
+ L := FScan.LastNew;
+ End Else
+ L := CurDateDos;
+
+ Str := Session.io.GetInput(8, 8, 15, DateDos2Str(L, Session.User.ThisUser.DateType));
+
+ If Not DateValid(Str) Then Exit;
+
+ L := DateStr2Dos(Str);
+
+ If Session.io.GetYN (Session.GetPrompt(256), True) Then Begin
+ Reset (FBaseFile);
+ Old := FBase;
+
+ While Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, FBase);
+ GetFileScan;
+ FScan.LastNew := L;
+ SetFileScan;
+ End;
+
+ Close (FBaseFile);
+ FBase := Old;
+ End Else Begin
+ If FBase.FileName = '' Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(38));
+ Exit;
+ End;
+ GetFileScan;
+ FScan.LastNew := L;
+ SetFileScan;
+ End;
+
+ Session.io.PromptInfo[1] := DateDos2Str(L, Session.User.ThisUser.DateType);
+ Session.io.OutFull (Session.GetPrompt(257));
+End;
+
+Function TFileBase.SendFile (Data: String) : Boolean;
+Begin
+ Result := False;
+
+ If Not FileExist(Data) Then Exit;
+
+ If SelectProtocol(False) = 'Q' Then Exit;
+
+ ExecuteProtocol(True, Data);
+
+ Session.io.OutRawLn ('');
+
+ If dszSearch(JustFile(Data)) Then Begin
+ Result := True;
+ Session.io.OutFullLn (Session.GetPrompt(385));
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(386));
+
+ CleanDirectory(Session.TempPath, '');
+End;
+
+Procedure TFileBase.DownloadFileList (Data: String);
+Var
+ A : Byte;
+ NewFiles : Boolean;
+ FileName : String[12];
+Begin
+ NewFiles := False;
+ FileName := 'allfiles.';
+
+ For A := 1 to strWordCount(Data, ' ') Do
+ If Pos('/NEW', strWordGet(A, Data, ' ')) > 0 Then Begin
+ NewFiles := True;
+ FileName := 'newfiles.';
+ End Else
+ If Pos('/ALLGROUP', strWordGet(A, Data, ' ')) > 0 Then
+ Session.User.IgnoreGroup := True;
+
+ If ExportFileList(NewFiles, False) Then Begin
+ If Session.io.GetYN (Session.GetPrompt(227), True) Then Begin
+ FileName := FileName + Session.User.ThisUser.Archive;
+ ExecuteArchive (Session.TempPath + FileName, Session.User.ThisUser.Archive, Session.TempPath + FileMask, 1);
+ End Else
+ FileName := FileName + 'txt';
+
+ SendFile (Session.TempPath + FileName);
+ End;
+
+ CleanDirectory(Session.TempPath, '');
+
+ Session.User.IgnoreGroup := False;
+End;
+
+Function TFileBase.ExportFileList (NewFiles : Boolean; Qwk: Boolean) : Boolean;
+{ ADD: templates for file listing }
+Var
+ TF : Text;
+ DF : File;
+ A : Byte;
+ Temp : String[mysMaxFileDescLen];
+ Str : String;
+ AreaFiles : Integer;
+ AreaSize : LongInt;
+ TotalFiles : LongInt;
+Begin
+ If NewFiles Then Begin
+ If Qwk Then Temp := 'newfiles.dat' Else Temp := 'newfiles.txt';
+ Session.io.OutFullLn (Session.GetPrompt(219));
+ End Else Begin
+ Temp := 'allfiles.txt';
+ Session.io.OutFullLn (Session.GetPrompt(220));
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(221));
+
+ Assign (TF, Session.TempPath + Temp);
+ ReWrite (TF);
+
+ TotalFiles := 0;
+
+ Reset (FBaseFile);
+ While Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, FBase);
+ If Session.User.Access(FBase.ListACS) Then Begin
+ Session.io.OutFull (Session.GetPrompt(222));
+
+ GetFileScan;
+
+ AreaFiles := 0;
+ AreaSize := 0;
+
+ WriteLn (TF, '');
+ WriteLn (TF, '.-' + strRep('-', Length(FBase.Name)) + '-.');
+ WriteLn (TF, '| ' + FBase.Name + ' |');
+ WriteLn (TF, '`-' + strRep('-', Length(FBase.Name)) + '-''');
+ WriteLn (TF, '.' + strRep('-', 77) + '.');
+ WriteLn (TF, '| File Size Date Description |');
+ WriteLn (TF, '`' + strRep('-', 77) + '''');
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult = 0 Then Begin
+ Assign (DF, Config.DataPath + FBase.FileName + '.des');
+ {$I-} Reset (DF, 1); {$I+}
+ If IoResult <> 0 Then ReWrite (DF, 1);
+ While Not Eof(FDirFile) Do Begin
+ Read (FDirFile, FDir);
+ If (NewFiles and (FDir.DateTime > FScan.LastNew)) or Not NewFiles Then
+ If FDir.Flags And FDirDeleted = 0 Then Begin
+ Inc (TotalFiles);
+ Inc (AreaFiles);
+ Inc (AreaSize, FDir.Size DIV 1024);
+
+ WriteLn (TF, FDir.FileName);
+ Write (TF, ' `- ' + strPadL(strComma(FDir.Size), 11, ' ') + ' ' + DateDos2Str(FDir.DateTime, Session.User.ThisUser.DateType) + ' ');
+
+ Seek (DF, FDir.Pointer);
+ For A := 1 to FDir.Lines Do Begin
+ BlockRead (DF, Temp[0], 1);
+ BlockRead (DF, Temp[1], Ord(Temp[0]));
+ If A = 1 Then WriteLn (TF, Temp) Else WriteLn (TF, strRep(' ', 27) + Temp);
+ End;
+ End;
+ End;
+
+ Session.io.PromptInfo[2] := strI2S(FileSize(FDirFile));
+
+ Close (FDirFile);
+ Close (DF);
+
+ SetFileScan;
+
+ Str := 'Total files: ' + strI2S(AreaFiles) + ' (' + strI2S(AreaSize) + 'k)';
+
+ WriteLn (TF, '.' + strRep('-', 77) + '.');
+ WriteLn (TF, '| ' + strPadR(Str, 76, ' ') + '|');
+ WriteLn (TF, '`' + strRep('-', 77) + '''');
+ End Else
+ Session.io.PromptInfo[2] := '0';
+
+ Session.io.PromptInfo[1] := FBase.Name;
+ Session.io.PromptInfo[3] := strI2S(AreaFiles);
+
+ Session.io.OutBS (Screen.CursorX, False);
+ Session.io.OutFullLn (Session.GetPrompt(223));
+ End;
+ End;
+ Close (FBaseFile);
+ Close (TF);
+
+ Session.io.OutFullLn (Session.GetPrompt(225));
+
+ Result := (TotalFiles = 0);
+
+ If Not Result Then Session.io.OutFullLn(Session.GetPrompt(425));
+End;
+
+Function TFileBase.WildcardMatch (Wildcard, FName: String) : Boolean;
+Begin
+ Result := False;
+
+ If FName = '' Then Exit;
+
+ Case Wildcard[1] of
+ '*' : Begin
+ If FName[1] = '.' Then Exit;
+ If Length(Wildcard) = 1 Then Result := True;
+ If (Length(Wildcard) > 1) and (Wildcard[2] = '.') and (Length(FName) > 0) Then
+ Result := WildCardMatch(Copy(Wildcard, 3, Length(Wildcard) - 2), Copy(FName, Pos('.', FName) + 1, Length(FName)-Pos('.', FName)));
+ End;
+ '?' : If Ord(Wildcard[0]) = 1 Then
+ Result := True
+ Else
+ Result := WildCardMatch(Copy(Wildcard, 2, Length(Wildcard) - 1), Copy(FName, 2, Length(FName) - 1));
+ Else
+ If FName[1] = Wildcard[1] Then
+ If Length(wildcard) > 1 Then
+ Result := WildCardMatch(Copy(Wildcard, 2, Length(Wildcard) - 1), Copy(FName, 2, Length(FName) - 1))
+ Else
+ Result := (Length(FName) = 1) And (Length(Wildcard) = 1);
+ End;
+End;
+
+Function TFileBase.ArchiveList (FName : String) : Boolean;
+Var
+ Arc : PArchive;
+ SR : ArcSearchRec;
+ D : DirStr;
+ N : NameStr;
+ E : ExtStr;
+Begin
+ Result := False;
+ Arc := New(PArchive, Init);
+
+ If Not Arc^.Name(FName) Then Begin
+ Dispose (Arc, Done);
+
+ If FileExist(FName) Then Begin
+ ExecuteArchive (FName, '', '_view_.tmp', 3);
+ Session.io.OutFile (Session.TempPath + '_view_.tmp', True, 0);
+ FileErase (Session.TempPath + '_view_.tmp');
+ End;
+ Exit;
+ End;
+
+ Session.io.AllowPause := True;
+ Session.io.PausePtr := 1;
+
+ FSplit (FName, D, N, E); {make getpath and getfname functions???}
+
+ Session.io.PromptInfo[1] := N + E;
+
+ Session.io.OutFullLn (Session.GetPrompt(192));
+
+ Arc^.FindFirst(SR);
+
+ While SR.Name <> '' Do Begin
+ Session.io.PromptInfo[1] := SR.Name;
+
+ If SR.Attr = $10 Then
+ Session.io.PromptInfo[2] := '' {++lang}
+ Else
+ Session.io.PromptInfo[2] := strComma(SR.Size);
+
+ Session.io.PromptInfo[3] := DateDos2Str(SR.Time, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[4] := TimeDos2Str(SR.Time, True);
+
+ Session.io.OutFullLn (Session.GetPrompt(193));
+
+ If (Session.io.PausePtr >= Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+
+ Arc^.FindNext(SR);
+ End;
+
+ Dispose (Arc, Done);
+
+ Result := True;
+
+ Session.io.OutFull (Session.GetPrompt(194));
+End;
+
+Function TFileBase.CheckFileLimits (DL: Byte; DLK: Integer) : Byte;
+{ 0 = OK to download }
+{ 1 = Offline or Invalid or Failed : NO ACCESS (prompt 224)}
+{ 2 = DL per day limit exceeded (prompt 58) }
+{ 3 = UL/DL file ratio bad (prompt 211) }
+Var
+ A : Byte;
+Begin
+ Result := 1;
+
+ If FDir.Flags And FDirOffline <> 0 Then Exit;
+
+ If (FDir.Flags And FDirInvalid <> 0) And Not Session.User.Access(Config.AcsDLUnvalid) Then Exit;
+ If (FDir.Flags And FDirFailed <> 0) And Not Session.User.Access(Config.AcsDLFailed) Then Exit;
+
+ If (FDir.Flags And FDirFree <> 0) or (Session.User.ThisUser.Flags and UserNoRatio <> 0) or (FBase.IsFREE) Then Begin
+ Result := 0;
+ Exit;
+ End;
+
+ If (Session.User.ThisUser.DLsToday + BatchNum + DL > Session.User.Security.MaxDLs) and (Session.User.Security.MaxDLs > 0) Then Begin
+ Result := 2;
+ Exit;
+ End;
+
+ If Session.User.Security.DLRatio > 0 Then
+ If (Session.User.ThisUser.ULs * Session.User.Security.DLRatio) <= (Session.User.ThisUser.DLs + BatchNum + DL) Then Begin
+ Result := 3;
+ Exit;
+ End;
+
+ If BatchNum > 0 Then
+ For A := 1 to BatchNum Do
+ Inc (DLK, Batch[A].Size DIV 1024);
+
+ If Session.User.Security.DLKRatio > 0 Then
+ If (Session.User.ThisUser.ULk * Session.User.Security.DLkRatio) <= (Session.User.ThisUser.DLk + DLk) Then Begin
+ Result := 3;
+ Exit;
+ End;
+
+ If (Session.User.ThisUser.DLkToday + DLk > Session.User.Security.MaxDLk) and (Session.User.Security.MaxDLk > 0) Then Begin
+ Result := 2;
+ Exit;
+ End;
+
+ Result := 0;
+End;
+
+Function TFileBase.ArchiveView (FName : String) : Boolean;
+Var
+ Mask : String[70];
+Begin
+ Result := False;
+
+ If Not ArchiveList(FName) Then Exit;
+
+ Result := True;
+
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(304));
+ Case Session.io.OneKey('DQRV', True) of
+ 'D' : Begin
+ Session.io.OutFull (Session.GetPrompt(384));
+ Mask := Session.io.GetInput (70, 70, 11, '');
+ If Mask <> '' Then Begin
+ ExecuteArchive (FName, '', Mask, 2);
+ If FileExist(Session.TempPath + Mask) Then Begin
+ Case CheckFileLimits (1, GetFileSize(Session.TempPath + Mask) DIV 1024) of
+ 0 : If SendFile (Session.TempPath + Mask) Then Begin;
+ Session.SystemLog ('Download from ' + FName + ': ' + Mask);
+{ make a temp var that is fdir.size div 1024 here?? smaller/faster!?}
+ Inc (Session.User.ThisUser.DLs);
+ Inc (Session.User.ThisUser.DLsToday);
+ Inc (Session.User.ThisUser.DLk, FDir.Size DIV 1024);
+ Inc (Session.User.ThisUser.DLkToday, FDir.Size DIV 1024);
+ Inc (Session.HistoryDLs);
+ Inc (Session.HistoryDLKB, FDir.Size DIV 1024);
+ End;
+ 1 : Session.io.OutFullLn (Session.GetPrompt(224));
+ 2 : Session.io.OutFullLn (Session.GetPrompt(58));
+ 3 : Session.io.OutFullLn (Session.GetPrompt(211));
+ End;
+
+ FileErase(Session.TempPath + Mask);
+ End;
+ End;
+ End;
+ 'Q' : Exit;
+ 'R' : ArchiveList(FName);
+ 'V' : Begin
+ Session.io.OutFull (Session.GetPrompt(384));
+ Mask := Session.io.GetInput (70, 70, 11, '');
+ If Mask <> '' Then Begin
+ ExecuteArchive (FName, '', Mask, 2);
+ Session.io.PromptInfo[1] := Mask;
+ Session.io.OutFullLn(Session.GetPrompt(306));
+ Session.io.AllowMCI := False;
+ Session.io.OutFile (Session.TempPath + Mask, True, 0);
+ Session.io.AllowMCI := True;
+ If Session.io.NoFile Then
+ Session.io.OutFullLn (Session.GetPrompt(305))
+ Else
+ FileErase(Session.TempPath + Mask);
+ End;
+ End;
+ End;
+ Until False;
+End;
+
+Procedure TFileBase.ToggleFileNewScan;
+Var
+ Total : Word;
+
+ Procedure List_Bases;
+ Begin
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ Session.io.OutFullLn (Session.GetPrompt(200));
+
+ Total := 0;
+ Reset (FBaseFile);
+ While Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, FBase);
+ If Session.User.Access(FBase.ListACS) Then Begin
+ Inc (Total);
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := FBase.Name;
+
+ GetFileScan;
+
+ Session.io.PromptInfo[3] := Session.io.OutYN(FScan.NewScan > 0);
+ Session.io.OutFull (Session.GetPrompt(201));
+ If (Total MOD 2 = 0) And (Total > 0) Then Session.io.OutRawLn('');
+ End;
+ If EOF(FBaseFile) and (Total MOD 2 <> 0) Then Session.io.OutRawLn('');
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(449));
+ End;
+
+ Procedure ToggleBase (A : Word);
+ Var
+ B : Word;
+ Begin
+ B := 0;
+ Reset (FBaseFile);
+ Repeat
+ Read (FBaseFile, FBase);
+ If Session.User.Access(FBase.ListACS) Then Inc(B);
+ If A = B Then Break;
+ Until False;
+
+ GetFileScan;
+ Session.io.PromptInfo[1] := FBase.Name;
+
+ If FBase.DefScan = 2 Then Begin
+ FScan.NewScan := 1;
+ Session.io.OutFullLn (Session.GetPrompt(289));
+ End Else
+ If FScan.NewScan = 0 Then Begin
+ FScan.NewScan := 1;
+ Session.io.OutFullLn (Session.GetPrompt(204));
+ End Else Begin
+ FScan.NewScan := 0;
+ Session.io.OutFullLn (Session.GetPrompt(203));
+ End;
+
+ SetFileScan;
+ End;
+
+Var
+ Old : FBaseRec;
+ Temp : String[11];
+ A : Word;
+ N1 : Word;
+ N2 : Word;
+Begin
+ Old := FBase;
+
+ List_Bases;
+
+ If Total = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(37));
+ FBase := Old;
+ Exit;
+ End;
+
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(202));
+ Temp := Session.io.GetInput(11, 11, 12, '');
+ If (Temp = '') or (Temp = 'Q') Then Break;
+ If Temp = '?' Then
+ List_Bases
+ Else Begin
+ If Pos('-', Temp) > 0 Then Begin
+ N1 := strS2I(Copy(Temp, 1, Pos('-', Temp) - 1));
+ N2 := strS2I(Copy(Temp, Pos('-', Temp) + 1, Length(Temp)));
+ End Else Begin
+ N1 := strS2I(Temp);
+ N2 := N1;
+ End;
+ For A := N1 to N2 Do
+ If (A > 0) and (A <= Total) Then ToggleBase(A);
+ End;
+ Until False;
+
+ Close (FBaseFile);
+ FBase := Old;
+End;
+
+Function TFileBase.SelectArchive : Boolean;
+Var
+ A : SmallInt;
+Begin
+ Result := False;
+
+ Session.io.OutRawLn ('');
+
+ Reset (ArcFile);
+
+ If Eof(ArcFile) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(169));
+ Close (ArcFile);
+ Exit;
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(73));
+
+ While Not Eof(ArcFile) Do Begin
+ Read (ArcFile, Arc);
+ Session.io.PromptInfo[1] := strI2S(FilePos(ArcFile));
+ Session.io.PromptInfo[2] := Arc.Desc;
+ Session.io.PromptInfo[3] := Arc.Ext;
+ Session.io.OutFullLn (Session.GetPrompt(170));
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(171));
+
+ A := strS2I(Session.io.GetInput(2, 2, 12, ''));
+
+ If (A > 0) and (A <= FileSize(ArcFile)) Then Begin
+ Seek (ArcFile, A - 1);
+ Read (ArcFile, Arc);
+ End Else Begin
+ Close (ArcFile);
+ Exit;
+ End;
+
+ Close (ArcFile);
+
+ Session.io.PromptInfo[1] := Arc.Ext;
+
+ Session.io.OutFullLn (Session.GetPrompt(74));
+
+ Session.User.ThisUser.Archive := Arc.Ext;
+
+ Result := True;
+End;
+
+Function TFileBase.SelectProtocol (Batch: Boolean) : Char;
+Var
+ Keys : String;
+ OldP1 : String;
+ Ch : Char;
+Begin
+ OldP1 := Session.io.PromptInfo[1];
+ Result := 'Q';
+ Keys := 'Q';
+
+ Session.io.OutFullLn(Session.GetPrompt(359));
+
+ Reset (ProtocolFile);
+
+ While Not Eof(ProtocolFile) Do Begin
+ Read (ProtocolFile, Protocol);
+ If Protocol.Active And (Protocol.Batch = Batch) And (Protocol.OSType = OSTYpe) Then Begin
+ Keys := Keys + Protocol.Key;
+
+ Session.io.PromptInfo[1] := Protocol.Key;
+ Session.io.PromptInfo[2] := Protocol.Desc;
+
+ Session.io.OutFullLn (Session.GetPrompt(61));
+ End;
+ End;
+
+ Close (ProtocolFile);
+
+ Session.io.OutFull (Session.GetPrompt(62));
+
+ Session.io.PromptInfo[1] := OldP1;
+
+ Ch := Session.io.OneKey(Keys, True);
+
+ If Ch = 'Q' Then Exit;
+
+ Reset (ProtocolFile);
+ While Not Eof(ProtocolFile) Do Begin
+ Read(ProtocolFile, Protocol);
+ If ((Protocol.Active) And (Ch = Protocol.Key) And (Protocol.Batch = Batch) And (Protocol.OSType = OSType)) Then Break;
+ End;
+ Close(ProtocolFile);
+
+ Session.io.OutFullLn (Session.GetPrompt(65));
+
+ Result := Ch;
+End;
+
+Procedure TFileBase.ExecuteArchive (FName: String; Temp: String; Mask: String; Mode: Byte);
+{mode: 1 = pack, 2 = unpack, 3 = view}
+Var
+ A : Byte;
+ Temp2 : String[60];
+Begin
+ If Temp = '' Then
+ Case Get_Arc_Type(FName) of
+ 'A' : Temp := 'ARJ';
+ 'L' : Temp := 'LZH';
+ 'R' : Temp := 'RAR';
+ 'Z' : Temp := 'ZIP';
+ '?' : Temp := strUpper(Copy(FName, Succ(Pos('.', FName)), Length(FName))); //get ext function
+ End;
+
+ Reset (ArcFile);
+
+ Repeat
+ If Eof(ArcFile) Then Begin
+ Close (ArcFile);
+ Exit;
+ End;
+ Read (ArcFile, Arc);
+ If (Not Arc.Active) or (Arc.OSType <> OSType) Then Continue;
+ If Arc.Ext = Temp Then Break;
+ Until False;
+
+ Close (ArcFile);
+
+ Case Mode of
+ 1 : Temp2 := Arc.Pack;
+ 2 : Temp2 := Arc.Unpack;
+ 3 : Temp2 := Arc.View;
+ End;
+
+ Temp := '';
+ A := 1;
+
+ While A <= Length(Temp2) Do Begin
+ If Temp2[A] = '%' Then Begin
+ Inc(A);
+ If Temp2[A] = '1' Then Temp := Temp + FName Else
+ If Temp2[A] = '2' Then Temp := Temp + Mask Else
+ If Temp2[A] = '3' Then Temp := Temp + Session.TempPath;
+ End Else
+ Temp := Temp + Temp2[A];
+
+ Inc(A);
+ End;
+
+ If ShellDOS ('', Temp) = 0 Then;
+End;
+
+(*************************************************************************)
+
+Procedure TFileBase.ViewFile;
+Var
+ FName : String[70];
+ Old : FBaseRec;
+Begin
+ Session.io.OutFull (Session.GetPrompt(353));
+
+ FName := Session.io.GetInput(70, 70, 11, '');
+
+ If FName = '' Then Exit;
+
+ Old := FBase;
+
+ Reset (FBaseFile);
+
+ While Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, FBase);
+
+ If Session.User.Access(FBase.ListACS) Then Begin
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult <> 0 Then ReWrite (FDirFile);
+
+ While Not Eof(FDirFile) Do Begin
+ Read (FDirFile, FDir);
+ If FDir.FileName = FName Then Begin
+ If Not ArchiveView (FBase.Path + FName) Then Session.io.OutFullLn(Session.GetPrompt(191));
+ Close (FDirFile);
+ Close (FBaseFile);
+ FBase := Old;
+ Exit;
+ End;
+ End;
+ Close (FDirFile);
+ End;
+ End;
+ Close (FBaseFile);
+
+ FBase := Old;
+
+ Session.io.OutFullLn (Session.GetPrompt(51));
+End;
+
+Procedure TFileBase.BatchList;
+Var
+ A : Byte;
+ M : Integer;
+ S : Byte;
+Begin
+ If BatchNum = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(52));
+ Exit;
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(56));
+
+ For A := 1 to BatchNum Do Begin
+ GetTransferTime (Batch[A].Size, M, S);
+
+ Session.io.PromptInfo[1] := strI2S(A);
+ Session.io.PromptInfo[2] := Batch[A].FileName;
+ Session.io.PromptInfo[3] := strComma(Batch[A].Size);
+ Session.io.PromptInfo[4] := strI2S(M);
+ Session.io.PromptInfo[5] := strI2S(S);
+
+ Session.io.OutFullLn (Session.GetPrompt(57));
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(428));
+End;
+
+Procedure TFileBase.BatchClear;
+Begin
+ BatchNum := 0;
+ Session.io.OutFullLn (Session.GetPrompt(59));
+End;
+
+Procedure TFileBase.BatchAdd;
+Var
+ FName : String[70];
+ A : Byte;
+ Old : FBaseRec;
+ OkSave : Boolean;
+Begin
+ If BatchNum = mysMaxBatchQueue Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(46));
+ Exit;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(47));
+ FName := Session.io.GetInput(70, 70, 11, '');
+
+ If FName = '' Then Exit;
+
+ Old := FBase;
+
+ Reset (FBaseFile);
+ While Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, FBase);
+
+ If Session.User.Access(FBase.ListACS) and Session.User.Access(FBase.DLACS) Then Begin
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult <> 0 Then ReWrite (FDirFile);
+
+ While Not Eof(FDirFile) Do Begin
+ Read (FDirFile, FDir);
+ {$IFDEF FS_SENSITIVE}
+ If (FDir.FileName = FName) And (FDir.Flags And FDirDeleted = 0) Then Begin
+ {$ELSE}
+ If (strUpper(FDir.FileName) = strUpper(FName)) And (FDir.Flags And FDirDeleted = 0) Then Begin
+ {$ENDIF}
+ okSave := False;
+ Case CheckFileLimits(1, FDir.Size DIV 1024) of
+ 0 : okSave := True;
+ 1 : Session.io.OutFullLn (Session.GetPrompt(224));
+ 2 : Session.io.OutFullLn (Session.GetPrompt(58));
+ 3 : Session.io.OutFullLn (Session.GetPrompt(211));
+ End;
+
+ For A := 1 to BatchNum Do
+ If FName = Batch[A].FileName Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(49));
+ OkSave := False;
+ End;
+
+ If OkSave Then Begin
+ Session.io.PromptInfo[1] := FName;
+ Session.io.PromptInfo[2] := strComma(FDir.Size);
+ Session.io.OutFullLn (Session.GetPrompt(50));
+ Inc (BatchNum);
+ Batch[BatchNum].FileName := FName;
+ Batch[BatchNum].Area := FilePos(FBaseFile);
+ Batch[BatchNum].Size := FDir.Size;
+ End;
+
+ Close (FDirFile);
+ Close (FBaseFile);
+ FBase := Old;
+ Exit;
+ End;
+ End;
+ Close (FDirFile);
+ End;
+ End;
+
+ Close (FBaseFile);
+
+ FBase := Old;
+
+ Session.io.OutFullLn (Session.GetPrompt(51));
+End;
+
+Procedure TFileBase.BatchDelete;
+Var
+ A : Byte;
+ B : Byte;
+Begin
+ If BatchNum = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(52));
+ Exit;
+ End;
+
+ Session.io.PromptInfo[1] := strI2S(BatchNum);
+ Session.io.OutFull (Session.GetPrompt(53));
+ A := strS2I(Session.io.GetInput(2, 2, 12, ''));
+
+ If (A > 0) and (A <= BatchNum) Then Begin
+ Session.io.PromptInfo[1] := FDir.FileName;
+ Session.io.PromptInfo[2] := strComma(Batch[A].Size);
+ Session.io.OutFullLn (Session.GetPrompt(54));
+ For B := A to BatchNum do
+ Batch[B] := Batch[B+1];
+ Dec (BatchNum);
+ End;
+End;
+
+Procedure TFileBase.FileGroupChange (Ops: String; FirstBase, Intro: Boolean);
+Var
+ A : Word;
+ Total : Word;
+ tGroup : recGroup;
+ tFBase : FBaseRec;
+ tLast : Word;
+ Areas : Word;
+ Data : Word;
+Begin
+ tGroup := FGroup;
+
+ If (Ops = '+') or (Ops = '-') Then Begin
+ Reset (FGroupFile);
+
+ A := Session.User.ThisUser.LastFGroup - 1;
+
+ Repeat
+ Case Ops[1] of
+ '+' : Inc(A);
+ '-' : Dec(A);
+ End;
+
+ {$I-}
+ Seek (FGroupFile, A);
+ Read (FGroupFile, FGroup);
+ {$I+}
+
+ If IoResult <> 0 Then Break;
+
+ If Session.User.Access(FGroup.ACS) Then Begin
+ Session.User.ThisUser.LastFGroup := FilePos(FGroupFile);
+ Close (FGroupFile);
+ If Intro Then Session.io.OutFile ('fgroup' + strI2S(Session.User.ThisUser.LastFGroup), True, 0);
+
+ If FirstBase Then Begin
+ Session.User.ThisUser.LastFBase := 0;
+ ChangeFileArea ('+');
+ End;
+
+ Exit;
+ End;
+ Until False;
+
+ Close (FGroupFile);
+
+ FGroup := tGroup;
+ Exit;
+ End;
+
+ Data := strS2I(Ops);
+
+ Reset (FGroupFile);
+
+ If Data > 0 Then Begin
+ If Data > FileSize(FGroupFile) Then Begin
+ Close (FGroupFile);
+ Exit;
+ End;
+
+ Seek (FGroupFile, Data-1);
+ Read (FGroupFile, FGroup);
+
+ If Session.User.Access(FGroup.ACS) Then Begin
+ Session.User.ThisUser.LastFGroup := FilePos(FGroupFile);
+ If Intro Then Session.io.OutFile ('fgroup' + strI2S(Data), True, 0);
+ End Else
+ FGroup := tGroup;
+
+ Close (FGroupFile);
+
+ If FirstBase Then Begin
+ Session.User.ThisUser.LastFBase := 0;
+ ChangeFileArea ('+');
+ End;
+
+ Exit;
+ End;
+
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ Session.io.OutFullLn (Session.GetPrompt(214));
+
+ tLast := Session.User.ThisUser.LastFGroup;
+ Total := 0;
+
+ While Not Eof(FGroupFile) Do Begin
+ Read (FGroupFile, FGroup);
+ If Not FGroup.Hidden And Session.User.Access(FGroup.ACS) Then Begin
+
+ Areas := 0;
+ Session.User.ThisUser.LastFGroup := FilePos(FGroupFile);
+
+ Reset (FBaseFile);
+ While Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, tFBase);
+ If Session.User.Access(tFBase.ListACS) Then Inc(Areas);
+ End;
+ Close (FBaseFile);
+
+ Inc (Total);
+
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := FGroup.Name;
+ Session.io.PromptInfo[3] := strI2S(Areas);
+
+ Session.io.OutFullLn (Session.GetPrompt(215));
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+ End;
+
+ Session.User.ThisUser.LastFGroup := tLast;
+
+ If Total = 0 Then
+ Session.io.OutFullLn (Session.GetPrompt(216))
+ Else Begin
+ Session.io.OutFull (Session.GetPrompt(217));
+ A := strS2I(Session.io.GetInput(4, 4, 11, ''));
+ If (A > 0) and (A <= Total) Then Begin
+ Total := 0;
+ Reset (FGroupFile);
+ Repeat
+ Read (FGroupFile, FGroup);
+ If Not FGroup.Hidden And Session.User.Access(FGroup.ACS) Then Inc(Total);
+ If A = Total Then Break;
+ Until False;
+ Session.User.ThisUser.LastFGroup := FilePos(FGroupFile);
+ If Intro Then Session.io.OutFile ('fgroup' + strI2S(Session.User.ThisUser.LastFGroup), True, 0);
+
+ Session.User.ThisUser.LastFBase := 0;
+ ChangeFileArea ('+');
+ End Else FGroup := tGroup;
+ End;
+
+ Close (FGroupFile);
+End;
+
+Function TFileBase.ListFileAreas (Compress: Boolean) : Integer;
+Var
+ Total : Word = 0;
+ Listed : Word = 0;
+ tDirFile : File of FDirRec;
+Begin
+ Reset (FBaseFile);
+
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ While Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, FBase);
+ If Session.User.Access(FBase.ListACS) Then Begin
+ Inc (Listed);
+ If Listed = 1 Then Session.io.OutFullLn (Session.GetPrompt(33));
+ If Compress Then
+ Inc (Total)
+ Else
+ Total := FilePos(FBaseFile);
+
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := FBase.Name;
+ Session.io.PromptInfo[3] := '0';
+
+ Assign (TDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (TDirFile); {$I+}
+
+ If IoResult = 0 Then Begin
+ Session.io.PromptInfo[3] := strI2S(FileSize(TDirFile));
+ Close (TDirFile);
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(34));
+
+ If (Listed MOD Config.FColumns = 0) and (Listed > 0) Then Session.io.OutRawLn('');
+ End;
+ If EOF(FBaseFile) and (Listed MOD Config.FColumns <> 0) Then Session.io.OutRawLn('');
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Begin
+ Total := FileSize(FBaseFile);
+ Break;
+ End;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Close (FBaseFile);
+
+ Result := Total;
+End;
+
+Procedure TFileBase.ChangeFileArea (Data: String);
+Var
+ A : Word;
+ Total : Word;
+ Old : FBaseRec;
+ Str : String[5];
+ Compress : Boolean;
+
+ Function CheckPassword : Boolean;
+ Begin
+ CheckPassword := True;
+
+ If FBase.Password <> '' Then
+ If Not Session.io.GetPW(Session.GetPrompt(66), Session.GetPrompt(417), FBase.Password) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(67));
+ FBase := Old;
+ Close (FBaseFile);
+ CheckPassword := False;
+ Exit;
+ End;
+ End;
+
+Begin
+ Old := FBase;
+ Compress := Config.FCompress;
+
+ If (Data = '+') or (Data = '-') Then Begin
+ Reset (FBaseFile);
+
+ A := Session.User.ThisUser.LastFBase - 1;
+
+ Repeat
+ Case Data[1] of
+ '+' : Inc(A);
+ '-' : Dec(A);
+ End;
+
+ {$I-}
+ Seek (FBaseFile, A);
+ Read (FBaseFile, FBase);
+ {$I+}
+
+ If IoResult <> 0 Then Break;
+
+ If Session.User.Access(FBase.ListACS) Then Begin
+ If Not CheckPassword Then Exit;
+ Session.User.ThisUser.LastFBase := FilePos(FBaseFile);
+ Close (FBaseFile);
+ Exit;
+ End;
+ Until False;
+
+ Close (FBaseFile);
+ FBase := Old;
+ Exit;
+ End;
+
+ A := strS2I(Data);
+
+ If A > 0 Then Begin
+ Reset (FBaseFile);
+ If A <= FileSize(FBaseFile) Then Begin
+ Seek (FBaseFile, A-1);
+ Read (FBaseFile, FBase);
+
+ If Session.User.Access(FBase.ListACS) Then Begin
+ If Not CheckPassword Then Exit;
+ Session.User.ThisUser.LastFBase := FilePos(FBaseFile)
+ End Else
+ FBase := Old;
+ End;
+ Close (FBaseFile);
+ Exit;
+ End;
+
+ If Pos('NOLIST', strUpper(Data)) > 0 Then Begin
+ Reset (FBaseFile);
+ Total := FileSize(FBaseFile);
+ Close (FBaseFile);
+ End Else
+ Total := ListFileAreas(Compress);
+
+ If Total = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(37));
+ FBase := Old;
+ End Else Begin
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(36));
+
+ Str := Session.io.GetInput(5, 5, 12, '');
+
+ If Str = '?' Then Begin
+ Compress := Config.FCompress;
+ Total := ListFileAreas(Compress);
+ End Else
+ Break;
+ Until False;
+
+ A := strS2I(Str);
+
+ If (A > 0) and (A <= Total) Then Begin
+ Reset (FBaseFile);
+ If Not Compress Then Begin
+ Seek (FBaseFile, A - 1);
+ Read (FBaseFile, FBase);
+ If Not Session.User.Access(FBase.ListACS) Then Begin
+ FBase := Old;
+ Close (FBaseFile);
+ Exit;
+ End;
+ End Else Begin
+ Total := 0;
+
+ While Not Eof(FBaseFile) And (A <> Total) Do Begin
+ Read (FBaseFile, FBase);
+ If Session.User.Access(FBase.ListACS) Then Inc(Total);
+ End;
+
+ If A <> Total Then Begin
+ Close (FBaseFile);
+ FBase := OLD;
+ Exit;
+ End;
+ End;
+
+ If Not CheckPassword Then Exit;
+
+ Session.User.ThisUser.LastFBase := FilePos(FBaseFile);
+
+ Close (FBaseFile);
+ End Else
+ FBase := Old;
+ End;
+End;
+
+Function TFileBase.ListFiles (Mode : Byte; Data : String) : Byte;
+Var
+ ListType : Byte; { 0 = ascii, 1 = ansi }
+ DataFile : File;
+ Lines : Byte; { lines already displayed }
+ CurPos : Byte; { current cursor position }
+ ListSize : Byte; { number of files in this page listing }
+ CurPage : Word; { current page number }
+ TopPage : Word; { top of page file position }
+ TopDesc : Byte; { top of page description offset }
+ BotPage : Word; { bot of page file position }
+ BotDesc : Byte; { bot of page description offset }
+ PageSize : Byte; { total lines in window/page }
+ LastPage : Boolean; { is the last page displayed? }
+ Found : Boolean; { were any files found? }
+ First : Boolean; { first file on page? }
+ IsNotLast : Boolean;
+ List : Array[1..13] of Record
+ FileName : String[70];
+ RecPos : Word;
+ yPos : Byte;
+ Batch : Boolean;
+ End;
+ strListFormat,
+ strDesc,
+ strExtDesc,
+ strUploader,
+ strBarON,
+ strBarOFF : String;
+
+ Function OkFile : Boolean;
+ Var
+ T2 : Boolean;
+ A : Byte;
+ Temp : String[mysMaxFileDescLen];
+ Begin
+ OkFile := False;
+
+ If (FDir.Flags And FDirDeleted <> 0) Then Exit;
+ If (FDir.Flags And FDirInvalid <> 0) And (Not Session.User.Access(Config.AcsSeeUnvalid)) Then Exit;
+ If (FDir.Flags And FDirFailed <> 0) And (Not Session.User.Access(Config.AcsSeeFailed)) Then Exit;
+
+ Case Mode of
+ 1 : If Data <> '' Then
+ If Not WildCardMatch (Data, FDir.FileName) Then Exit;
+ 2 : If FDir.DateTime < FScan.LastNew Then Exit;
+ 3 : Begin
+ T2 := Bool_Search(Data, FDir.FileName);
+ If Not T2 Then Begin
+ Seek (DataFile, FDir.Pointer);
+ For A := 1 to FDir.Lines Do Begin
+ BlockRead (DataFile, Temp[0], 1);
+ BlockRead (DataFile, Temp[1], Length(Temp));
+ If Bool_Search(Data, Temp) Then Begin
+ T2 := True;
+ Break;
+ End;
+ End;
+ End;
+ If Not T2 Then Exit;
+ End;
+ End;
+ OkFile := True;
+ End;
+
+ Procedure ClearWindow;
+ Var
+ A : Byte;
+ Begin
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[1].Y);
+
+ Session.io.OutFull('|16');
+
+ For A := Session.io.ScreenInfo[1].Y to Session.io.ScreenInfo[2].Y Do Begin
+ Session.io.AnsiClrEOL;
+ Session.io.OutRawLn('');
+ End;
+
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[1].Y);
+ End;
+
+ Procedure SearchHighlight (Var Temp: String);
+ Var
+ Attr : Byte;
+ Begin
+ If Bool_Search(Data, Temp) Then Begin
+ Attr := Screen.TextAttr;
+
+ Screen.TextAttr := 255;
+
+ Insert (
+ Session.io.Attr2Ansi(Session.Lang.FileLo),
+ Temp,
+ Pos(Data, strUpper(Temp)) + Length(Data)
+ );
+
+ Screen.TextAttr := 255;
+
+ Insert (
+ Session.io.Attr2Ansi(Session.Lang.FileHi),
+ Temp,
+ Pos(Data, strUpper(Temp)));
+
+ Screen.TextAttr := Attr;
+
+ Session.systemlog('debug: ' + temp);
+ End;
+ End;
+
+ Procedure NextPage;
+ Begin
+ Inc (CurPage);
+
+ TopDesc := BotDesc;
+ TopPage := BotPage;
+ CurPos := 1;
+ End;
+
+ Function ShowText (Str : String) : Boolean;
+ Begin
+ If Lines = PageSize Then Begin
+ ShowText := False;
+ Exit;
+ End;
+
+ Inc (BotDesc);
+ Inc (Lines);
+
+ Session.io.OutFullLn (Str);
+
+ Found := True;
+ ShowText := True;
+ End;
+
+ Procedure PrevPage;
+ Var
+ NewPos : LongInt;
+ Count : Word;
+ Begin
+ If CurPage = 1 Then Exit;
+
+ Dec (CurPage);
+
+ NewPos := TopPage;
+ Count := 0;
+
+ If TopDesc = 0 Then Dec(NewPos);
+
+ While (NewPos >= 0) and (Count < PageSize) Do Begin
+ Seek (FDirFile, NewPos);
+ Read (FDirFile, FDir);
+
+ Dec (NewPos);
+
+ If Not OkFile Then Continue;
+
+ If TopDesc > 0 Then Begin
+ Inc (Count, FDir.Lines - (FDir.Lines - TopDesc + 1) + 1);
+ If TopDesc = FDir.Lines + 2 Then Dec(Count);
+ TopDesc := 0;
+ End Else Begin
+ Inc (Count, FDir.Lines + 1);
+ If FBase.ShowUL Then Inc(Count);
+ End;
+ End;
+
+ If NewPos < -1 Then Begin
+ CurPage := 1;
+ TopPage := 0;
+ TopDesc := 0;
+ End Else Begin
+ TopPage := NewPos + 1;
+ TopDesc := Count - PageSize;
+ End;
+ End;
+
+ Procedure PrintMessage (N : Integer);
+ Begin
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[3].Y);
+ Session.io.AnsiClrEOL;
+ Session.io.OutFull (Session.GetPrompt(N));
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[3].Y);
+ Session.io.AnsiClrEOL;
+ If Session.User.Access(FBase.SysopACS) Then
+ Session.io.OutFull (Session.GetPrompt(339))
+ Else
+ Session.io.OutFull (Session.GetPrompt(323));
+ End;
+
+ Procedure UpdateBatch;
+ Begin
+ If Session.io.ScreenInfo[4].X = 0 Then Exit;
+
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[4].X, Session.io.ScreenInfo[4].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[4].A);
+ Session.io.OutRaw (strZero(BatchNum));
+ End;
+
+ Procedure FullReDraw;
+ Begin
+ Session.io.ScreenInfo[5].Y := 0;
+ Session.io.ScreenInfo[6].Y := 0;
+
+ Session.io.OutFile ('ansiflst', True, 0);
+
+ PageSize := Session.io.ScreenInfo[2].Y - Session.io.ScreenInfo[1].Y + 1;
+
+ botdesc := topdesc;
+ botpage := toppage;
+
+ If Session.User.Access(FBase.SysopACS) Then
+ PrintMessage (339)
+ Else
+ PrintMessage (323);
+
+ UpdateBatch;
+ End;
+
+ Function GetFileListSize : String;
+ { ADD: text into prompts or lang config }
+ Var
+ A : LongInt;
+ Begin
+ If FDir.Flags And FDirOffline <> 0 Then
+ GetFileListSize := 'OFFLINE' //++lang
+ Else
+ If FDir.Flags And FDirFailed <> 0 Then
+ GetFileListSize := 'FAILED' //++lang
+ Else
+ If FDir.Flags And FDirInvalid <> 0 Then
+ GetFileListSize := 'UNVALID' //++lang
+ Else
+ If FDir.Size >= 1024000 Then Begin
+ A := FDir.Size DIV 1024;
+ GetFileListSize := strI2S(A DIV 1000) + '.' + Copy(strI2S(A MOD 1000), 1, 2) + 'MB'; //++lang
+ End Else
+ If FDir.Size >= 1024 Then
+ GetFileListSize := strI2S(FDir.Size DIV 1024) + 'KB' //++lang
+ Else
+ GetFileListSize := strI2S(FDir.Size) + 'B'; //++lang
+ End;
+
+ Procedure HeaderCheck;
+ Begin
+ Case ListType of
+ 0 : If First Then Begin
+ First := False;
+ If Config.FShowHeader or (CurPage = 1) Then Begin
+ Session.io.PausePtr := 1;
+ Session.io.OutFullLn(Session.GetPrompt(41))
+ End Else Begin
+ Session.io.OutRawLn('');
+ Session.io.PausePtr := 1;
+ End;
+
+ PageSize := Session.User.ThisUser.ScreenSize - Session.io.PausePtr - 1;
+ End;
+ 1 : If Not Found Then Begin
+ FullReDraw;
+ ClearWindow;
+ First := False;
+ End Else
+ If First Then Begin
+ ClearWindow;
+ First := False;
+ End;
+ End;
+ End;
+
+ Procedure DrawPage;
+ Var
+ OK : Boolean;
+ Str : String;
+ A : SmallInt;
+ Begin
+ ListSize := 0;
+ Lines := 0;
+
+ Seek (FDirFile, TopPage);
+ If TopDesc <> 0 Then Read (FDirFile, FDir);
+
+ BotDesc := TopDesc;
+ OK := True;
+ First := True;
+ IsNotLast := False;
+
+ Repeat
+ If BotDesc = 0 Then Begin
+ Read (FDirFile, FDir);
+
+ If Not OkFile Then Continue;
+
+ HeaderCheck;
+
+ Session.io.PromptInfo[1] := strZero(ListSize + 1);
+ Session.io.PromptInfo[2] := FDir.FileName;
+ Session.io.PromptInfo[3] := ' ';
+ Session.io.PromptInfo[4] := GetFileListSize;
+ Session.io.PromptInfo[5] := DateDos2Str(FDir.DateTime, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[6] := strI2S(FDir.DLs);
+
+ List[ListSize + 1].Batch := False;
+ For A := 1 to BatchNum Do
+ If Batch[A].FileName = FDir.FileName Then Begin
+ List[ListSize + 1].Batch := True;
+ Session.io.PromptInfo[3] := Session.Lang.TagCh;
+ Break;
+ End;
+
+ OK := ShowText(strListFormat);
+
+ If Not OK Then Begin
+ IsNotLast := True;
+ Break;
+ End;
+
+ Inc (ListSize);
+
+ List[ListSize].FileName := FDir.FileName;
+ List[ListSize].YPos := Screen.CursorY - 1;
+ List[ListSize].RecPos := FilePos(FDirFile) - 1;
+ End Else
+ HeaderCheck;
+
+ If BotDesc <= FDir.Lines + 2 Then Begin { skip if 1st line is uler }
+ Seek (DataFile, FDir.Pointer);
+
+ For A := 1 to FDir.Lines Do Begin
+ BlockRead (DataFile, Str[0], 1);
+ BlockRead (DataFile, Str[1], Ord(Str[0]));
+
+ If A < BotDesc Then Continue;
+
+ If Mode = 3 Then SearchHighlight(Str);
+
+ If A = 1 Then Begin
+ Session.io.PromptInfo[1] := GetFileListSize;
+ Session.io.PromptInfo[2] := DateDos2Str(FDir.DateTime, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[3] := strI2S(FDir.DLs);
+ Session.io.PromptInfo[4] := Str;
+ Session.io.PromptInfo[5] := FDir.Uploader;
+ OK := ShowText(strDesc);
+ End Else Begin
+ Session.io.PromptInfo[4] := Str;
+ OK := ShowText(strExtDesc);
+ End;
+
+ If Not OK Then Break;
+ End;
+ End;
+
+ If BotDesc > FDir.Lines Then Begin
+ If FBase.ShowUL Then Begin
+ OK := ShowText(strUploader);
+ If OK Then
+ BotDesc := 0
+ Else
+ Inc (BotDesc);
+ End Else
+ BotDesc := 0;
+ End;
+ Until EOF(FDirFile) Or Not OK;
+
+ BotPage := FilePos(FDirFile) - 1;
+ LastPage := Eof(FDirFile) And (BotDesc = 0) And Not IsNotLast;
+ IsNotLast := False;
+ Str := Session.io.DrawPercent(Session.Lang.FileBar, BotPage, FileSize(FDirFile), A);
+
+ If Found Then Begin
+ If (ListType = 1) and (Session.io.ScreenInfo[5].Y <> 0) Then Begin
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[5].X, Session.io.ScreenInfo[5].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[5].A);
+ Session.io.OutRaw (strPadL(strI2S(A), 3, ' '));
+ End;
+
+ If (ListType = 1) and (Session.io.ScreenInfo[6].Y <> 0) Then Begin
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[6].X, Session.io.ScreenInfo[6].Y);
+ Session.io.OutFull (Str);
+ End;
+ End;
+ End;
+
+ Procedure BarOFF;
+ Begin
+ Session.io.AnsiGotoXY (1, List[CurPos].YPos);
+
+ Session.io.PromptInfo[1] := strZero(CurPos);
+ Session.io.PromptInfo[2] := List[CurPos].FileName;
+
+ If List[CurPos].Batch Then
+ Session.io.PromptInfo[3] := Session.Lang.TagCh
+ Else
+ Session.io.PromptInfo[3] := ' ';
+
+ Session.io.OutFull(strBarOFF);
+ End;
+
+ Procedure Ansi_List;
+ Var
+ Ch : Char;
+ A : Byte;
+ B : Integer;
+ Begin
+ Session.io.AllowArrow := True;
+ ListType := 1;
+
+ strListFormat := Session.GetPrompt(431);
+ strBarON := Session.GetPrompt(432);
+ strBarOFF := Session.GetPrompt(433);
+ strDesc := Session.GetPrompt(434);
+ strExtDesc := Session.GetPrompt(435);
+ strUploader := Session.GetPrompt(436);
+
+ NextPage;
+ DrawPage;
+
+ If Found Then Begin
+ Repeat
+ If ListSize > 0 Then Begin
+ Session.io.AnsiGotoXY (1, List[CurPos].yPos);
+
+ Session.io.PromptInfo[1] := strZero(CurPos);
+ Session.io.PromptInfo[2] := List[CurPos].FileName;
+
+ If List[CurPos].Batch Then
+ Session.io.PromptInfo[3] := Session.Lang.TagCh
+ Else
+ Session.io.PromptInfo[3] := ' ';
+
+ Session.io.OutFull (strBarON);
+ End;
+
+ Session.io.PurgeInputBuffer;
+
+ Ch := UpCase(Session.io.GetKey);
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #72 : If (CurPos > 1) and (ListSize > 0) Then Begin
+ BarOFF;
+ Dec (CurPos);
+ End Else If CurPage > 1 Then Begin
+ PrevPage;
+ DrawPage;
+ CurPos := ListSize;
+ End;
+ #73,
+ #75 : If CurPage > 1 Then Begin
+ PrevPage;
+ DrawPage;
+ CurPos := ListSize;
+ End Else
+ If ListSize > 0 Then Begin
+ BarOFF;
+ CurPos := 1;
+ End;
+ #80 : If CurPos < ListSize Then Begin
+ BarOFF;
+ Inc (CurPos);
+ End Else If Not LastPage Then Begin
+ NextPage;
+ DrawPage;
+ End;
+ #77,
+ #81 : If Not LastPage Then Begin
+ NextPage;
+ DrawPage;
+ End Else If ListSize > 0 Then Begin
+ BarOFF;
+ CurPos := ListSize;
+ End;
+ End;
+ End Else Begin
+ Case Ch of
+ #13 : If LastPage Then Begin
+ Result := 2;
+ Break;
+ End Else Begin
+ NextPage;
+ DrawPage;
+ End;
+ #27 : Begin
+ Result := 1;
+ Break;
+ End;
+ #32 : If ListSize > 0 Then Begin
+ If List[CurPos].Batch Then Begin
+ For A := 1 to BatchNum Do
+ If Batch[A].FileName = List[CurPos].FileName Then Begin
+ For B := A to BatchNum Do Batch[B] := Batch[B+1];
+ Dec (BatchNum);
+ List[CurPos].Batch := False;
+ BarOFF;
+ UpdateBatch;
+ Break;
+ End;
+ End Else
+ If BatchNum < mysMaxBatchQueue Then Begin
+ Seek (FDirFile, List[CurPos].RecPos);
+ Read (FDirFile, FDir);
+
+ Case CheckFileLimits(1, FDir.Size DIV 1024) of
+ 0 : Begin
+ Inc (BatchNum);
+ Batch[BatchNum].FileName := FDir.FileName;
+ If Mode = 1 Then
+ Batch[BatchNum].Area := Session.User.ThisUser.LastFBase
+ Else
+ Batch[BatchNum].Area := FilePos(FBaseFile);
+ Batch[BatchNum].Size := FDir.Size;
+
+ List[CurPos].Batch := True;
+ BarOFF;
+ updateBatch;
+ End;
+ 1 : PrintMessage (212);
+ 2 : PrintMessage (312);
+ 3 : PrintMessage (313);
+ End;
+ End Else
+ PrintMessage (314);
+
+ If CurPos < ListSize Then Begin
+ BarOFF;
+ Inc (CurPos);
+ End Else If Not LastPage Then Begin
+ NextPage;
+ DrawPage;
+ End;
+ End;
+ '?' : Begin
+ Session.io.OutFile ('flisthlp', True, 0);
+ If Not Session.io.NoFile Then Begin
+ fullReDraw;
+ drawPage;
+ End;
+ End;
+ 'E' : If Session.User.Access(FBase.SysopACS) Then Begin
+ { Save file POS if FBaseFile is open }
+ {$I-} B := FilePos(FBaseFile); {$I+}
+ If IoResult = 0 Then
+ Close (FBaseFile)
+ Else
+ B := -1;
+
+ Close (FDirFile);
+ Close (DataFile);
+
+ DirectoryEditor(True, List[CurPos].FileName);
+
+ If B <> -1 Then Begin
+ Reset (FBaseFile);
+ Seek (FBaseFile, B);
+ End;
+
+ Reset (FDirFile);
+ Reset (DataFile, 1);
+
+ fullReDraw;
+ DrawPage;
+
+ If CurPos > ListSize Then CurPos := ListSize;
+
+ Session.io.AllowArrow := True;
+ End;
+ 'N' : If Mode > 1 Then Begin
+ Result := 2;
+ Break;
+ End;
+ 'V' : Begin
+ Session.io.AnsiGotoXY (1, 23);
+ If ArchiveView(FBase.Path + List[CurPos].FileName) Then Begin
+ fullRedraw;
+ DrawPage;
+ End Else
+ PrintMessage (324);
+
+ Session.io.AllowArrow := True;
+ End;
+ End;
+ End;
+ Until False;
+
+ Session.io.AnsiGotoXY (1, Session.User.ThisUser.ScreenSize);
+ End;
+
+ Session.io.AllowArrow := False;
+ End;
+
+ Procedure Ascii_List;
+ Var
+ A : LongInt;
+ B : LongInt;
+ okSave : Byte;
+ Begin
+ ListType := 0;
+
+ strListFormat := Session.GetPrompt(42);
+ strDesc := Session.GetPrompt(43);
+ strExtDesc := Session.GetPrompt(45);
+ strUploader := Session.GetPrompt(437);
+
+ NextPage;
+ DrawPage;
+
+ If Not Found Then Exit;
+
+ Result := 2;
+
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(44));
+ Case Session.io.OneKey(#13'FNPQV', True) of
+ #13,
+ 'N' : If LastPage Then
+ Break
+ Else Begin
+ NextPage;
+ DrawPage;
+ End;
+ 'P' : Begin
+ PrevPage;
+ DrawPage;
+ End;
+ 'Q' : Begin
+ Result := 1;
+ Break;
+ End;
+ 'V' : Begin
+ Session.io.OutFull (Session.GetPrompt(358));
+ A := strS2I(Session.io.GetInput(2, 2, 12, ''));
+ If (A > 0) and (A <= ListSize) Then
+ If Not ArchiveView (FBase.Path + List[A].FileName) Then
+ Session.io.OutFullLn(Session.GetPrompt(191));
+ DrawPage;
+ End;
+ 'F' : Begin
+ Repeat
+ If BatchNum = mysMaxBatchQueue Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(46));
+ Break;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(357));
+ A := strS2I(Session.io.GetInput(2, 2, 12, ''));
+
+ If (A < 1) or (A > ListSize) Then Break;
+
+ okSave := 0;
+
+ Seek (FDirFile, List[A].RecPos);
+ Read (FDirFile, FDir);
+
+ For A := 1 to BatchNum Do
+ If FDir.FileName = Batch[A].FileName Then Begin
+ Session.io.PromptInfo[1] := FDir.FileName;
+ Session.io.PromptInfo[2] := strComma(Batch[A].Size);
+ Session.io.OutFullLn (Session.GetPrompt(54));
+
+ For B := A to BatchNum Do
+ Batch[B] := Batch[B + 1];
+ Dec (BatchNum);
+ okSave := 2;
+ End;
+
+ If okSave = 0 Then
+ Case CheckFileLimits(1, FDir.Size DIV 1024) of
+ 0 : okSave := 1;
+ 1 : Session.io.OutFullLn (Session.GetPrompt(224));
+ 2 : Session.io.OutFullLn (Session.GetPrompt(58));
+ 3 : Session.io.OutFullLn (Session.GetPrompt(211));
+ End;
+
+ If okSave = 1 Then Begin
+ Session.io.PromptInfo[1] := FDir.FileName;
+ Session.io.PromptInfo[2] := strComma(FDir.Size);
+ Session.io.OutFullLn (Session.GetPrompt(50));
+ Inc (BatchNum);
+ Batch[BatchNum].FileName := FDir.FileName;
+ Batch[BatchNum].Size := FDir.Size;
+ If Mode = 1 Then
+ Batch[BatchNum].Area := Session.User.ThisUser.LastFBase
+ Else
+ Batch[BatchNum].Area := FilePos(FBaseFile);
+ End;
+ Until False;
+ DrawPage;
+ End;
+ End;
+ Until False;
+
+ Session.io.OutRawLn('');
+ End;
+
+Begin
+ If FBase.FileName = '' Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(38));
+ Exit;
+ End;
+
+ If Not Session.User.Access(FBase.ListACS) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(39));
+ Exit;
+ End;
+
+ If (Mode = 1) and (Data = 'SEARCH') Then Begin
+ Session.io.OutFull (Session.GetPrompt(195));
+ Data := Session.io.GetInput(70, 70, 11, '*.*');
+ If Data = '' Then Exit;
+ End;
+
+ Set_Node_Action (Session.GetPrompt(350));
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+
+ If IoResult <> 0 Then Begin
+ If Mode = 1 Then Session.io.OutFullLn (Session.GetPrompt(40));
+ Exit;
+ End;
+
+ If Eof(FDirFile) Then Begin
+ If Mode = 1 Then Session.io.OutFullLn (Session.GetPrompt(40));
+ Close (FDirFile);
+ Exit;
+ End;
+
+ Assign (DataFile, Config.DataPath + FBase.FileName + '.des');
+ {$I-} Reset (DataFile, 1); {$I+}
+ If IoResult <> 0 Then ReWrite (DataFile, 1);
+
+ Result := 0;
+
+ CurPage := 0;
+ TopPage := 0;
+ TopDesc := 0;
+ BotPage := 0;
+ BotDesc := 0;
+ Found := False;
+
+ If (Session.User.ThisUser.FileList = 1) and (Session.io.Graphics > 0) Then
+ Ansi_List
+ Else
+ Ascii_List;
+
+ Close (FDirFile);
+ Close (DataFile);
+End;
+
+Procedure TFileBase.CheckFileNameLength (FPath : String; Var FName : String);
+Var
+ D : DirStr;
+ N : NameStr;
+ E : ExtStr;
+ F : File;
+ S : String;
+Begin
+ If Length(FName) > 70 Then Begin
+ FSplit(FName, D, N, E);
+
+ S := Copy(N, 1, 70 - Length(E)) + E;
+
+ Repeat
+ Assign (F, FPath + FName);
+ {$I-} ReName(F, FPath + S); {$I+}
+
+ If IoResult = 0 Then Begin
+ FName := S;
+ Break;
+ End Else Begin
+ Session.io.OutFull (Session.GetPrompt(461));
+ S := strStripB(Session.io.GetInput(70, 70, 11, S), ' ');
+ End;
+ Until False;
+ End;
+End;
+
+Function TFileBase.IsDupeFile (FileName : String; Global : Boolean) : Boolean;
+Var
+ Res : Boolean;
+ OLD : FBaseRec;
+
+ Procedure Check_Area;
+ Var
+ TempFile : File of FDirRec;
+ Temp : FDirRec;
+ Begin
+ Assign (TempFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (TempFile); {$I+}
+ If IoResult <> 0 Then ReWrite (TempFile);
+
+ While Not Eof(TempFile) Do Begin
+ Read (TempFile, Temp);
+ {$IFDEF FS_SENSITIVE}
+ If (Temp.FileName = FileName) And (Temp.Flags And FDirDeleted = 0) Then Begin
+ {$ELSE}
+ If (strUpper(Temp.FileName) = strUpper(FileName)) And (Temp.Flags And FDirDeleted = 0) Then Begin
+ {$ENDIF}
+ Res := True;
+ Break;
+ End;
+ End;
+ Close (TempFile);
+ End;
+
+Begin
+ Res := False;
+ OLD := FBase;
+
+ If Global Then Begin
+ Reset (FBaseFile);
+ While Not Eof(FBaseFile) And Not Res Do Begin
+ Read (FBaseFile, FBase);
+ Check_Area;
+ End;
+ Close (FBaseFile);
+ End Else
+ Check_Area;
+
+ FBase := OLD;
+ Result := Res;
+End;
+
+Procedure TFileBase.GetFileDescription (FN : String);
+Var
+ A : Byte;
+Begin
+ Session.io.PromptInfo[1] := strI2S(Config.MaxFileDesc);
+ Session.io.PromptInfo[2] := FN;
+
+ Session.io.OutFullLn (Session.GetPrompt(72));
+
+ FDir.Lines := Config.MaxFileDesc;
+
+ For A := 1 to Config.MaxFileDesc Do Begin
+ Session.io.PromptInfo[1] := strZero(A);
+ Session.io.OutFull (Session.GetPrompt(207));
+ Session.Msgs.MsgText[A] := Session.io.GetInput(mysMaxFileDescLen, mysMaxFileDescLen, 11, '');
+ If Session.Msgs.MsgText[A] = '' Then Begin
+ FDir.Lines := Pred(A);
+ Break;
+ End;
+ End;
+
+ If FDir.Lines = 0 Then Begin
+ Session.Msgs.MsgText[1] := Session.GetPrompt(208);
+ FDir.Lines := 1;
+ End;
+End;
+
+Procedure TFileBase.UploadFile;
+Var
+ FileName : String;
+ A : LongInt;
+ D : DirStr;
+ N : NameStr;
+ E : ExtStr;
+ OLD : FBaseRec;
+ Blind : Boolean;
+ Temp : String;
+ FullName : String;
+ DataFile : File;
+ TempFile : File;
+ Found : Boolean;
+
+ LogFile : Text;
+ FileStatus : Boolean;
+Begin
+ OLD := FBase;
+ Found := False;
+
+ If Config.UploadBase > 0 Then Begin
+ Session.User.IgnoreGroup := True; { just in case ul area is in another group }
+
+ Reset (FBaseFile);
+ {$I-} Seek (FBaseFile, Config.UploadBase - 1); {$I+}
+
+ If IoResult = 0 Then Read (FBaseFile, FBase);
+
+ Close (FBaseFile);
+ End;
+
+ If Not Session.User.Access(FBase.ULacs) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(68));
+ FBase := OLD;
+ Exit;
+ End;
+
+ Session.User.IgnoreGroup := False;
+
+ If FBase.FileName = '' Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(38));
+ FBase := OLD;
+ Exit;
+ End;
+
+ If FBase.IsCDROM Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(80));
+ FBase := OLD;
+ Exit;
+ End;
+
+ If Config.FreeUL > 0 Then Begin
+ FSplit (FBase.Path, D, N, E);
+ If DiskFree(Ord(UpCase(D[1])) - 64) DIV 1024 < Config.FreeUL Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(81));
+ FBase := OLD;
+ Exit;
+ End;
+ End;
+
+ Blind := Session.io.GetYN(Session.GetPrompt(375), False);
+ FileName := '';
+
+ If Blind Then
+ Session.io.OutFile ('blindul', True, 0)
+ Else Begin
+ Session.io.OutFile ('upload', True, 0);
+
+ Session.io.OutFull (Session.GetPrompt(343));
+ FileName := strStripB(Session.io.GetInput(70, 70, 11, ''), ' ');
+
+ If (FileName = '') or (Pos('*', FileName) > 0) or (Pos('?', FileName) > 0) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(69));
+ FBase := OLD;
+ Exit;
+ End;
+
+ If Config.FDupeScan > 0 Then Begin
+ Session.io.OutFull (Session.GetPrompt(70));
+
+ If IsDupeFile(FileName, Config.FDupeScan = 2) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(205));
+ FBase := OLD;
+ Exit;
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(71));
+ End;
+
+ FileName := FBase.Path + FileName;
+ End;
+
+ If SelectProtocol(Blind) = 'Q' Then Begin
+ FBase := OLD;
+ Exit;
+ End;
+
+ ExecuteProtocol(False, FBase.Path);
+
+{ ++lang ADD: update node status to transferring file? }
+
+ Session.io.OutFull (Session.GetPrompt(376));
+
+ Assign (DataFile, Config.DataPath + FBase.FileName + '.des');
+ {$I-} Reset (DataFile, 1); {$I+}
+ If IoResult <> 0 Then ReWrite(DataFile, 1);
+
+ Seek (DataFile, FileSize(DataFile));
+
+ Assign (LogFile, Session.TempPath + 'xfer.log');
+ {$I-} Reset(LogFile); {$I+}
+ If IoResult = 0 Then Begin
+ While Not Eof(LogFile) Do Begin
+ dszGetFile (LogFile, FileName, FileStatus);
+
+ If FileName = '' Then Continue;
+
+ CheckFileNameLength(FBase.Path, FileName);
+
+ FullName := FBase.Path + FileName;
+
+ Session.io.PromptInfo[1] := FileName;
+
+ If Not FileStatus Then Begin
+ Session.SystemLog ('Failed Upload: ' + FileName + ' to ' + strStripMCI(FBase.Name));
+ Session.io.OutFull (Session.GetPrompt(84));
+ FileErase(FullName);
+ End Else Begin
+ Found := True;
+ Session.SystemLog ('Uploaded: ' + FileName + ' to ' + strStripMCI(FBase.Name));
+ Session.io.OutFull (Session.GetPrompt(83));
+
+ FDir.FileName := FileName;
+ FDir.DateTime := CurDateDos;
+ FDir.Uploader := Session.User.ThisUser.Handle;
+ FDir.Flags := 0;
+ FDir.DLs := 0;
+
+ If Config.FDupeScan > 0 Then Begin
+ Session.io.OutFull (Session.GetPrompt(377));
+
+ If IsDupeFile(FileName, Config.FDupeScan = 2) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(378));
+ Continue;
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(379));
+ End;
+
+ If Config.TestUploads and (Config.TestCmdLine <> '') Then Begin
+ Session.io.OutFull (Session.GetPrompt(206));
+
+ Temp := '';
+ A := 1;
+
+ While A <= Length(Config.TestCmdLine) Do Begin
+ If Config.TestCmdLine[A] = '%' Then Begin
+ Inc(A);
+ {$IFDEF UNIX}
+ If Config.TestCmdLine[A] = '0' Then Temp := Temp + '1' Else
+ {$ELSE}
+ If Config.TestCmdLine[A] = '0' Then Temp := Temp + strI2S(Session.Client.FSocketHandle) Else
+ {$ENDIF}
+ If Config.TestCmdLine[A] = '1' Then Temp := Temp + '1' Else
+ If Config.TestCmdLine[A] = '2' Then Temp := Temp + '38400' Else
+ If Config.TestCmdLine[A] = '3' Then Temp := Temp + FullName {FBase.Path + FileName};
+ End Else
+ Temp := Temp + Config.TestCmdLine[A];
+
+ Inc(A);
+ End;
+
+ If ShellDOS('', Temp) <> Config.TestPassLevel Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(35));
+ Session.SystemLog (FileName + ' has failed upload test');
+ FDir.Flags := FDir.Flags Or FDirFailed;
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(55));
+ End;
+
+ If Config.ImportDIZ Then Begin
+ Session.io.OutFull (Session.GetPrompt(380));
+
+ If ImportDIZ(FileName) Then
+ Session.io.OutFullLn (Session.GetPrompt(381))
+ Else Begin
+ Session.io.OutFullLn (Session.GetPrompt(382));
+ GetFileDescription(FileName);
+ End;
+ End Else
+ GetFileDescription(FileName);
+
+ FDir.Pointer := FileSize(DataFile);
+
+ For A := 1 to FDir.Lines Do
+ BlockWrite (DataFile, Session.Msgs.MsgText[A][0], Length(Session.Msgs.MsgText[A]) + 1);
+
+ Assign (TempFile, FBase.Path + FileName);
+ {$I-} Reset (TempFile, 1); {$I+}
+ If IoResult = 0 Then Begin
+ FDir.Size := FileSize(TempFile);
+ Close (TempFile);
+ End Else Begin
+ FDir.Flags := FDir.Flags Or FDirOffline;
+ FDir.Size := 0;
+ End;
+
+ If Not Session.User.Access(Config.AcsValidate) Then FDir.Flags := FDir.Flags Or FDirInvalid;
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult <> 0 Then ReWrite (FDirFile);
+
+ Seek (FDirFile, FileSize(FDirFile));
+ Write (FDirFile, FDir);
+ Close (FDirFile);
+
+ Inc (Session.User.ThisUser.ULs);
+ Inc (Session.User.ThisUser.ULk, FDir.Size DIV 1024);
+ Inc (Session.HistoryULs);
+ Inc (Session.HistoryULKB, FDir.Size DIV 1024);
+ End;
+ End;
+ Close (LogFile);
+ End;
+
+ Close (DataFile);
+
+ FBase := OLD;
+
+ CleanDirectory(Session.TempPath, '');
+
+ If Found Then
+ Session.io.OutFullLn (Session.GetPrompt(75))
+ Else
+ Session.io.OutFullLn (Session.GetPrompt(424));
+End;
+
+Function TFileBase.CopiedToTemp (FName: String) : Boolean;
+Var
+ Copied : Boolean;
+Begin
+ Copied := False;
+
+ If FBase.IsCDROM Then Begin
+
+ Copied := True;
+
+ If Config.FreeCDROM > 0 Then
+ Copied := DiskFree(0) DIV 1024 >= Config.FreeCDROM;
+
+ If Copied Then Copied := DiskFree(0) >= FDir.Size;
+
+ If Copied Then Begin
+ Session.io.PromptInfo[1] := FName;
+ Session.io.OutFullLn (Session.GetPrompt(82));
+
+ Copied := CopyFile(FBase.Path + FName, Session.TempPath + FName)
+ End;
+ End;
+
+ Result := Copied;
+End;
+
+Procedure TFileBase.DownloadFile;
+Var
+ FName : String[70];
+ Dir : String[40];
+ Min : Integer;
+ Sec : Byte;
+Begin
+ If FBase.FileName = '' Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(38));
+ Exit;
+ End;
+
+ If Not Session.User.Access(FBase.DLAcs) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(76));
+ Exit;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(344));
+ FName := Session.io.GetInput(70, 70, 11, '');
+
+ If FName = '' Then Exit;
+
+ Session.io.OutFullLn (Session.GetPrompt(77));
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult <> 0 Then ReWrite (FDirFile);
+
+ While Not Eof(FDirFile) Do Begin
+ Read (FDirFile, FDir);
+ {$IFDEF FS_SENSITIVE}
+ If (FDir.FileName = FName) And (FDir.Flags And FDirDeleted = 0) Then Begin
+ {$ELSE}
+ If (strUpper(FDir.FileName) = strUpper(FName)) And (FDir.Flags And FDirDeleted = 0) Then Begin
+ {$ENDIF}
+ Case CheckFileLimits (1, FDir.Size DIV 1024) of
+ 0 : Begin
+ Session.io.PromptInfo[1] := FDir.FileName;
+ Session.io.PromptInfo[2] := strComma(FDir.Size);
+ Session.io.PromptInfo[3] := FDir.Uploader;
+ Session.io.PromptInfo[4] := DateDos2Str(FDir.DateTime, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[5] := strI2S(FDir.DLs);
+
+ GetTransferTime (FDir.Size, Min, Sec);
+
+ Session.io.PromptInfo[6] := strI2S(Min);
+ Session.io.PromptInfo[7] := strI2S(Sec);
+
+ Session.io.OutFull (Session.GetPrompt(78));
+
+ If CopiedToTemp(FName) Then
+ Dir := Session.TempPath
+ Else
+ Dir := FBase.Path;
+
+ If SendFile(Dir + FName) Then Begin
+{ make tempvar which is size div 1024 or maybe 'updatefilestats' proc? }
+ Session.SystemLog ('Downloaded: ' + FDir.FileName);
+ Inc (Session.User.ThisUser.DLs);
+ Inc (Session.User.ThisUser.DLsToday);
+ Inc (Session.User.ThisUser.DLk, FDir.Size DIV 1024);
+ Inc (Session.User.ThisUser.DLkToday, FDir.Size DIV 1024);
+ Inc (FDir.DLs);
+ Inc (Session.HistoryDLs);
+ Inc (Session.HistoryDLKB, FDir.Size DIV 1024);
+ Seek (FDirFile, FilePos(FDirFile)-1);
+ Write (FDirFile, FDir);
+ End Else Begin
+ Session.SystemLog ('Download of ' + FDir.FileName + ' FAILED');
+ End;
+
+ FileErase(Session.TempPath + FName);
+ End;
+ 1 : Session.io.OutFullLn (Session.GetPrompt(224));
+ 2 : Session.io.OutFullLn (Session.GetPrompt(58));
+ 3 : Session.io.OutFullLn (Session.GetPrompt(211));
+ End;
+ Close (FDirFile);
+ Exit;
+ End;
+ End;
+ Close (FDirFile);
+
+ Session.io.OutFullLn (Session.GetPrompt(51));
+End;
+
+Procedure TFileBase.DownloadBatch;
+Var
+ A : Byte;
+ K : LongInt;
+ M : Integer;
+ Dir : String[40];
+ Old : FBaseRec;
+ FL : Text;
+Begin
+ K := 0;
+ For A := 1 to BatchNum Do Inc (K, Batch[A].Size);
+
+ GetTransferTime (K, M, A);
+
+ Session.io.PromptInfo[1] := strI2S(BatchNum);
+ Session.io.PromptInfo[2] := strComma(K);
+ Session.io.PromptInfo[3] := strI2S(M);
+ Session.io.PromptInfo[4] := strI2S(A);
+
+ Session.io.OutFullLn (Session.GetPrompt(79));
+
+ If SelectProtocol(True) = 'Q' Then Exit;
+
+ Assign (FL, Session.TempPath + 'file.lst');
+ ReWrite (FL);
+
+ Reset (FBaseFile);
+
+ For A := 1 to BatchNum Do Begin
+ Seek (FBaseFile, Batch[A].Area - 1);
+ Read (FBaseFile, Old);
+
+ FDir.Size := Batch[A].Size;
+
+ If CopiedToTemp(Batch[A].FileName) Then
+ Dir := Session.TempPath
+ Else
+ Dir := Old.Path;
+
+ WriteLn (FL, Dir + Batch[A].FileName);
+ End;
+
+ Close (FBaseFile);
+ Close (FL);
+
+ ExecuteProtocol(True, Session.TempPath + 'file.lst');
+
+ Reset (FBaseFile);
+
+ Session.io.OutRawLn ('');
+ For A := 1 to BatchNum Do Begin
+ Session.io.PromptInfo[1] := Batch[A].FileName;
+ If dszSearch (Batch[A].FileName) Then Begin
+ Session.SystemLog ('Download: ' + Batch[A].FileName);
+ Session.io.OutFullLn (Session.GetPrompt(385));
+ Inc (Session.User.ThisUser.DLs);
+ Inc (Session.User.ThisUser.DLsToday);
+ Inc (Session.User.ThisUser.DLk, Batch[A].Size DIV 1024);
+ Inc (Session.User.ThisUser.DLkToday, Batch[A].Size DIV 1024);
+ Inc (Session.HistoryDLs);
+ Inc (Session.HistoryDLKB, Batch[A].Size DIV 1024);
+ Seek (FBaseFile, Batch[A].Area - 1);
+ Read (FBaseFile, Old);
+ Assign (FDirFile, Config.DataPath + Old.FileName + '.dir');
+ Reset (FDirFile);
+ While Not Eof(FDirFile) Do Begin
+ Read (FDirFile, FDir);
+ If (FDir.FileName = Batch[A].FileName) And (FDir.Flags And FDirDeleted = 0) Then Begin
+ Inc (FDir.DLs);
+ Seek (FDirFile, FilePos(FDirFile) - 1);
+ Write (FDirFile, FDir);
+ Break;
+ End;
+ End;
+ Close (FDirFile);
+ End Else Begin
+ Session.SystemLog ('Download: ' + Batch[A].FileName + ' FAILED');
+ Session.io.OutFullLn (Session.GetPrompt(386));
+ End;
+
+ End;
+
+ Close (FBaseFile);
+
+ BatchNum := 0;
+
+ CleanDirectory (Session.TempPath, '');
+End;
+
+Procedure TFileBase.FileSearch;
+Var
+ Str : String[40];
+ Done : Boolean;
+ Found : Boolean;
+ All : Boolean;
+
+ Procedure Scan_Base;
+ Begin
+ Session.io.PromptInfo[1] := FBase.Name;
+ Session.io.OutBS (Screen.CursorX, True);
+ Session.io.OutFull (Session.GetPrompt(87));
+
+ Case ListFiles (3, Str) of
+ 0 : Found := False;
+ 1 : Begin
+ Done := True;
+ Found := True;
+ End;
+ 2 : Found := True;
+ End;
+ End;
+
+Var
+ Old : FBaseRec;
+Begin
+ Old := FBase;
+ Found := False;
+ Done := False;
+ All := False;
+
+ Session.io.OutFile ('fsearch', True, 0);
+
+ Session.io.OutFull (Session.GetPrompt(196));
+ Str := Session.io.GetInput(40, 40, 12, '');
+ If Str = '' Then Exit;
+
+ Session.SystemLog ('File search: "' + Str + '"');
+
+ All := Session.io.GetYN(Session.GetPrompt(197), True);
+ If All Then Session.User.IgnoreGroup := Session.io.GetYN(Session.GetPrompt(64), True);
+
+ If All Then Begin
+ Session.io.OutRawLn ('');
+ Reset (FBaseFile);
+ While (Not Eof(FBaseFile)) and (Not Done) Do Begin
+ Found := False;
+ Read (FBaseFile, FBase);
+ If Session.User.Access(FBase.ListACS) Then Scan_Base;
+ End;
+ Close (FBaseFile);
+ End Else Begin
+ Session.io.OutRawLn ('');
+ Reset (FBaseFile);
+ Scan_Base;
+ Close (FBaseFile);
+ End;
+ If Not Found Then Session.io.OutFullLn('|CR');
+ Session.io.OutFullLn (Session.GetPrompt(198));
+
+ FBase := Old;
+ Session.User.IgnoreGroup := False;
+End;
+
+Procedure TFileBase.NewFileScan (Mode: Char);
+Var
+ TempFBase : FBaseRec;
+ Found : Boolean;
+ Done : Boolean;
+ NewFiles : Boolean;
+
+ Procedure Scan_Current_Base;
+ Begin
+ Session.io.PromptInfo[1] := FBase.Name;
+ Session.io.OutBS (Screen.CursorX, True);
+ Session.io.OutFull (Session.GetPrompt(87));
+
+ Case ListFiles (2, '') of
+ 0 : Found := False;
+ 1 : Begin
+ Done := True;
+ Found := True;
+ NewFiles := True;
+ End;
+ 2 : Begin
+ Found := True;
+ NewFiles := True;
+ End;
+ End;
+
+ FScan.LastNew := CurDateDos;
+
+ SetFileScan;
+ End;
+
+Var
+ Global : Boolean;
+Begin
+ TempFBase := FBase;
+ Done := False;
+ Found := False;
+ NewFiles := False;
+
+ Session.SystemLog ('Scan for new files');
+
+ Case Mode of
+ 'G' : Global := True;
+ 'C' : Global := False;
+ 'A' : Begin
+ Global := True;
+ Session.User.IgnoreGroup := True;
+ End;
+ Else
+ Global := Session.io.GetYN(Session.GetPrompt(86), True);
+ End;
+
+ Session.io.OutRawLn ('');
+
+ If Global Then Begin
+ Reset (FBaseFile);
+ While (Not Eof(FBaseFile)) And (Not Done) Do Begin;
+ Read (FBaseFile, FBase);
+ GetFileScan;
+ If (FScan.NewScan > 0) and Session.User.Access(FBase.ListACS) Then Scan_Current_Base;
+ End;
+ Close (FBaseFile);
+ End Else Begin
+ If FBase.FileName = '' Then
+ Session.io.OutFullLn(Session.GetPrompt(038))
+ Else Begin
+ GetFileScan;
+ Reset (FBaseFile);
+ Scan_Current_Base;
+ Close (FBaseFile);
+ End;
+ End;
+
+ If Not Found Then Session.io.OutFullLn('|CR');
+
+ If NewFiles Then
+ Session.io.OutFullLn (Session.GetPrompt(89))
+ Else
+ Session.io.OutFullLn (Session.GetPrompt(88));
+
+ Session.User.IgnoreGroup := False;
+ FBase := TempFBase;
+End;
+
+(**************************************************************************)
+(* FILE SECTION - SYSOP FUNCTIONS *)
+(**************************************************************************)
+
+Procedure TFileBase.DirectoryEditor (Edit : Boolean; Mask: String);
+
+Function Get_Next_File (Back: Boolean): Boolean;
+Var
+ Old : FDirRec;
+ Pos : LongInt;
+Begin
+ Old := FDir;
+ Pos := FilePos(FDirFile);
+
+ Get_Next_File := True;
+ Repeat
+ If (Eof(FDirFile) And Not Back) or ((FilePos(FDirFile) = 1) and Back) Then Begin
+ FDir := Old;
+ Seek (FDirFile, Pos); (* this may need {I-} and/or Pos-1 *)
+ Get_Next_File := False;
+ Exit;
+ End;
+ If Back Then Seek (FDirFile, FilePos(FDirFile) - 2);
+ Read (FDirFile, FDir);
+ If (FDir.Flags And FDirDeleted = 0) and WildCardMatch(Mask, FDir.FileName) Then
+ Break;
+ Until False;
+End;
+
+Var
+ DataFile : File;
+ DataFile2 : File;
+ A : Integer;
+ B : Integer;
+ Temp : String;
+ Old : FBaseRec;
+ TF : Text;
+Begin
+ If FBase.FileName = '' Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(38));
+ Exit;
+ End;
+
+ If Not Session.User.Access(FBase.SysopACS) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(39));
+ Exit;
+ End;
+
+ If Mask = '' Then Begin
+ Session.io.OutFull (Session.GetPrompt(195));
+ Mask := Session.io.GetInput(70, 70, 11, '*.*');
+ End;
+
+ Session.SystemLog ('File DIR editor');
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult <> 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(40));
+ Exit;
+ End;
+
+ If Eof(FDirFile) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(40));
+ Close (FDirFile);
+ Exit;
+ End;
+
+ Assign (DataFile, Config.DataPath + FBase.FileName + '.des');
+ {$I-} Reset (DataFile, 1); {$I+}
+ If IoResult <> 0 Then ReWrite (DataFile, 1);
+
+ If Get_Next_File(False) Then Begin
+
+ If Edit Then Mask := '*.*';
+
+ Repeat
+ Session.io.OutFullLn ('|07|CLFile DIR Editor : ' + strI2S(FilePos(FDirFile)) + ' of ' + strI2S(FileSize(FDirFile)));
+ Session.io.OutFullLn ('|08|$D79Ä');
+ Session.io.OutFullLn ('|031) |14' + FDir.FileName);
+ Session.io.OutFullLn ('|08|$D79Ä');
+
+ Session.io.OutFullLn ('|032) File Size : |11' + strPadR(strComma(FDir.Size) + ' bytes', 19, ' ') +
+ '|033) Uploader : |11' + FDir.Uploader);
+
+ Session.io.OutFullLn ('|034) File Date : |11' + strPadR(DateDos2Str(FDir.DateTime, Session.User.ThisUser.DateType), 19, ' ') +
+ '|035) Downloads : |11' + strI2S(FDir.DLs));
+
+ Session.io.OutFull ('|036) Status : |11');
+
+ Temp := '';
+
+ If FDir.Flags And FDirDeleted <> 0 Then
+ Temp := '|12DELETED'
+ Else Begin
+ If FDir.Flags And FDirInvalid <> 0 Then Temp := 'Invalid ';
+ If FDir.Flags And FDirOffline <> 0 Then Temp := Temp + 'Offline ';
+ If FDir.Flags And FDirFailed <> 0 Then Temp := Temp + 'Failed ';
+ If FDir.Flags And FDirFree <> 0 Then Temp := Temp + 'Free';
+ If Temp = '' Then Temp := 'Normal';
+ End;
+
+ Session.io.OutFullLn (Temp);
+ Session.io.OutFullLn ('|08|$D79Ä');
+
+ Seek (DataFile, FDir.Pointer);
+ For A := 1 to 11 Do Begin
+ Temp := '';
+ If A <= FDir.Lines Then Begin
+ BlockRead (DataFile, Temp[0], 1);
+ BlockRead (DataFile, Temp[1], Ord(Temp[0]));
+ End;
+
+ If A = 1 Then
+ Session.io.OutFullLn ('|03!) Description : |07' + Temp)
+ Else
+ Session.io.OutFullLn (strRep(' ', 17) + Temp);
+ End;
+
+ Session.io.OutFullLn ('|08|$D79Ä');
+
+ Session.io.OutFull ('|09([) Previous (]) Next (D) Delete (I) Import DIZ (U) Update DIZ' +
+ '|CR(M) Move (V) View Archive (E) Email ULer (Q) Quit: ');
+ Case Session.io.OneKey('123456[]DEIMQUV!', True) of
+ '1' : Begin
+ Temp := Session.io.InXY (4, 3, 70, 70, 11, FDir.FileName);
+ If Not FBase.IsCDROM Then
+ If (Temp <> FDir.FileName) and (Temp <> '') Then Begin
+ If Not FileExist(FBase.Path + Temp) or (strUpper(Temp) = strUpper(FDir.FileName)) Then Begin
+ Assign(TF, FBase.Path + FDir.FileName);
+ {$I-} ReName(TF, FBase.Path + Temp); {$I+}
+ If IoResult = 0 Then FDir.FileName := Temp;
+ End;
+ End;
+ End;
+ 'D' : Begin
+ If Session.io.GetYN('|CR|12Delete this entry? |11', False) Then Begin
+ FDir.Flags := FDir.Flags Or FDirDeleted;
+ If FileExist(FBase.Path + FDir.FileName) Then
+ If Session.io.GetYN ('|12Delete ' + FBase.Path + FDir.FileName + '? |11', False) Then
+ FileErase(FBase.Path + FDir.FileName);
+ End Else
+ FDir.Flags := FDir.Flags And (Not FDirDeleted);
+
+ Seek (FDirFile, FilePos(FDirFile) - 1);
+ Write (FDirFile, FDir);
+ End;
+ 'E' : If Session.Menu.ExecuteCommand ('MW', '/TO:' + strReplace(FDir.Uploader, ' ', '_')) Then;
+ 'I' : Begin
+ Session.io.OutFullLn ('|CR|14Importing file_id.diz...');
+ If ImportDIZ(FDir.FileName) Then Begin
+ FDir.Pointer := FileSize(DataFile);
+ Seek (DataFile, FDir.Pointer);
+ For A := 1 to FDir.Lines Do
+ BlockWrite (DataFile, Session.Msgs.MsgText[A][0], Length(Session.Msgs.MsgText[A]) + 1);
+ End;
+ End;
+ 'M' : Begin
+ Session.User.IgnoreGroup := True;
+ Repeat
+ Session.io.OutFull ('|CR|09Move to which base (?/List): ');
+ Temp := Session.io.GetInput(4, 4, 12, '');
+ If Temp = '?' Then Begin
+ Old := FBase;
+ ListFileAreas(False);
+ FBase := Old;
+ End Else Begin
+ Reset (FBaseFile);
+ B := strS2I(Temp);
+ If (B > 0) and (B <= FileSize(FBaseFile)) Then Begin
+ Session.io.OutFull ('|CR|14Moving |15' + FDir.FileName + '|14: ');
+
+ Old := FBase;
+ Seek (FBaseFile, B - 1);
+ Read (FBaseFile, FBase);
+
+ If Not CopyFile (Old.Path + FDir.FileName, FBase.Path + FDir.FileName) Then Begin
+ Session.io.OutFull ('ERROR|CR|CR|PA');
+
+ FBase := Old;
+ Break;
+ End;
+
+ FileErase(Old.Path + FDir.FileName);
+
+ A := FilePos(FDirFile);
+ Close (FDirFile);
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult <> 0 Then ReWrite(FDirFile);
+
+ Assign (DataFile2, Config.DataPath + FBase.FileName + '.des');
+ {$I-} Reset (DataFile2, 1); {$I+}
+ If IoResult <> 0 Then ReWrite (DataFile2, 1);
+
+ Seek (DataFile, FDir.Pointer);
+ FDir.Pointer := FileSize(DataFile2);
+ Seek (DataFile2, FDir.Pointer);
+ For B := 1 to FDir.Lines Do Begin
+ BlockRead (DataFile, Temp[0], 1);
+ BlockRead (DataFile, Temp[1], Ord(Temp[0]));
+ BlockWrite (DataFile2, Temp[0], Length(Temp) + 1);
+ End;
+ Close (DataFile2);
+ Seek (FDirFile, FileSize(FDirFile));
+ Write (FDirFile, FDir);
+ Close (FDirFile);
+
+ FBase := Old;
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+ Reset (FDirFile);
+ Seek (FDirFile, A - 1);
+ Read (FDirFile, FDir);
+
+ FDir.Flags := FDir.Flags Or FDirDeleted;
+ End;
+ Close (FBaseFile);
+ Break;
+ End;
+ Until False;
+
+ Session.User.IgnoreGroup := False;
+ End;
+ 'Q' : Begin
+ Seek (FDirFile, FilePos(FDirFile) - 1);
+ Write (FDirFile, FDir);
+ Break;
+ End;
+ 'U' : Begin
+ Session.io.OutFullLn ('|CR|14Updating FILE_ID.DIZ...');
+
+ Assign (TF, Session.TempPath + 'file_id.diz');
+ ReWrite (TF);
+ Seek (DataFile, FDir.Pointer);
+ For B := 1 to FDir.Lines Do Begin
+ BlockRead (DataFile, Temp[0], 1);
+ BlockRead (DataFile, Temp[1], Ord(Temp[0]));
+ WriteLn (TF, Temp);
+ End;
+ Close (TF);
+
+ ExecuteArchive (FBase.Path + FDir.FileName, '', Session.TempPath + 'file_id.diz', 1);
+
+ FileErase(Session.TempPath + 'file_id.diz');
+ End;
+ 'V' : If Not ArchiveView (FBase.Path + FDir.FileName) Then Session.io.OutFullLn(Session.GetPrompt(191));
+ '[' : Begin
+ Seek (FDirFile, FilePos(FDirFile) - 1);
+ Write (FDirFile, FDir);
+ If Not Get_Next_File(True) Then;
+ End;
+ ']' : Begin
+ Seek (FDirFile, FilePos(FDirFile) - 1);
+ Write (FDirFile, FDir);
+ If Not Get_Next_File(False) Then;
+ End;
+ '!' : Begin
+ Seek (DataFile, FDir.Pointer);
+ If FDir.Lines > Config.MaxFileDesc Then FDir.Lines := Config.MaxFileDesc;
+ For A := 1 to FDir.Lines Do Begin
+ BlockRead (DataFile, Session.Msgs.MsgText[A][0], 1);
+ BlockRead (DataFile, Session.Msgs.MsgText[A][1], Ord(Session.Msgs.MsgText[A][0]));
+ End;
+
+ Temp := 'Description Editor';
+ B := FDir.Lines;
+
+ If Editor(B, mysMaxFileDescLen, Config.MaxFileDesc, True, False, Temp) Then Begin
+ FDir.Lines := B;
+ FDir.Pointer := FileSize(DataFile);
+ Seek (DataFile, FDir.Pointer);
+ For A := 1 to FDir.Lines Do
+ BlockWrite (DataFile, Session.Msgs.MsgText[A][0], Length(Session.Msgs.MsgText[A]) + 1);
+ End;
+ End;
+ '2' : Begin
+ Session.io.OutFull ('Size: ');
+ FDir.Size := strS2I(Session.io.GetInput(8, 8, 12, strI2S(FDir.Size)));
+ End;
+ '4' : FDir.DateTime := DateStr2Dos(Session.io.InXY(16, 6, 8, 8, 15, DateDos2Str(FDir.DateTime, Session.User.ThisUser.DateType)));
+ '3' : FDir.Uploader := Session.io.InXY(50, 5, 30, 30, 18, FDir.Uploader);
+ '5' : FDir.DLs := strS2I(Session.io.InXY(50, 6, 4, 4, 12, strI2S(FDir.DLs)));
+ '6' : Begin
+ Session.io.OutFull('|CRFlags: F(a)iled, (F)ree, (O)ffline, (U)nvalidated, (Q)uit: ');
+ Case Session.io.OneKey('AFOUQ', True) of
+ 'A' : FDir.Flags := FDir.Flags XOR FDirFailed;
+ 'F' : FDir.Flags := FDir.Flags XOR FDirFree;
+ 'O' : FDir.Flags := FDir.Flags XOR FDirOffline;
+ 'U' : FDir.Flags := FDir.Flags XOR FDirInvalid;
+ End;
+ End;
+ End;
+ Until False;
+ End;
+
+ Close (FDirFile);
+ Close (DataFile);
+End;
+
+Procedure TFileBase.MassUpload;
+Var
+ Done : Boolean;
+ AutoAll : Boolean;
+
+ Procedure Do_Area;
+ Var
+ A : Byte;
+ OldPos : Word;
+ Skip : Boolean;
+ DataFile : File;
+ DirInfo : SearchRec;
+ AutoArea : Boolean;
+ Temp : String;
+ Begin
+ If FBase.FileName = '' Then Exit;
+
+ AutoArea := AutoAll;
+
+ Session.io.OutFullLn ('|CR|03Processing |14|FB|03...');
+
+ Assign (DataFile, Config.DataPath + FBase.FileName + '.des');
+ {$I-} Reset (DataFile, 1); {$I+}
+ If IoResult = 0 Then
+ Seek (DataFile, FileSize(DataFile))
+ Else
+ ReWrite (DataFile, 1);
+
+ Assign (FDirFile, Config.DataPath + FBase.FileName + '.dir');
+
+ FindFirst(FBase.Path + FileMask, Archive, DirInfo);
+ While DosError = 0 Do Begin
+
+ OldPos := FilePos(FBaseFile);
+ Close (FBaseFile);
+
+ CheckFileNameLength(FBase.Path, DirInfo.Name);
+
+ Skip := IsDupeFile(DirInfo.Name, False);
+
+ Reset (FBaseFile);
+ Seek (FBaseFile, OldPos);
+
+ If Not Skip Then
+ Session.io.OutFullLn ('|CR|03File : |14' + DirInfo.Name);
+
+ If Not AutoArea And Not Skip Then Begin
+ Session.io.OutFull ('|03Cmd : |09(Y)es, (N)o, (A)uto, (G)lobal, (S)kip, (Q)uit: ');
+ Case Session.io.OneKey('AGNQSY', True) of
+ 'A' : AutoArea := True;
+ 'G' : Begin
+ AutoArea := True;
+ AutoAll := True;
+ End;
+ 'N' : Skip := True;
+ 'Q' : Begin
+ Done := True;
+ Break;
+ End;
+ 'S' : Break;
+ End;
+ End;
+
+ If Not Skip Then Begin
+ FDir.FileName := DirInfo.Name;
+ FDir.Size := DirInfo.Size;
+ FDir.DateTime := CurDateDos;
+ FDir.Uploader := Session.User.ThisUser.Handle;
+ FDir.DLs := 0;
+ FDir.Flags := 0;
+ FDir.Lines := 0;
+
+ If Config.ImportDIZ Then
+ If Not ImportDIZ(DirInfo.Name) Then
+ If Not AutoArea Then
+ GetFileDescription(DirInfo.Name);
+
+ If FDir.Lines = 0 Then Begin
+ Session.Msgs.MsgText[1] := Session.GetPrompt(208);
+ FDir.Lines := 1;
+ End;
+
+ FDir.Pointer := FileSize(DataFile);
+ For A := 1 to FDir.Lines Do
+ BlockWrite (DataFile, Session.Msgs.MsgText[A][0], Length(Session.Msgs.MsgText[A]) + 1);
+
+ If Config.TestUploads and (Config.TestCmdLine <> '') Then Begin
+ Temp := '';
+ A := 1;
+
+ While A <= Length(Config.TestCmdLine) Do Begin
+ If Config.TestCmdLine[A] = '%' Then Begin
+ Inc(A);
+ If Config.TestCmdLine[A] = '1' Then Temp := Temp + '1' Else
+ If Config.TestCmdLine[A] = '2' Then Temp := Temp + '38400' Else
+ If Config.TestCmdLine[A] = '3' Then Temp := Temp + FBase.Path + FDir.FileName;
+ End Else
+ Temp := Temp + Config.TestCmdLine[A];
+
+ Inc (A);
+ End;
+
+ If ShellDOS('', Temp) <> Config.TestPassLevel Then
+ FDir.Flags := FDir.Flags OR FDirFailed;
+ End;
+
+ {$I-} Reset (FDirFile); {$I+}
+ If IoResult <> 0 Then ReWrite(FDirFile);
+ Seek (FDirFile, FileSize(FDirFile));
+ Write (FDirFile, FDir);
+ Close (FDirFile);
+ End;
+ FindNext(DirInfo);
+ End;
+
+ FindClose(DirInfo);
+
+ Close (DataFile);
+ End;
+
+Var
+ Old : FBaseRec;
+ Pos : LongInt;
+Begin
+ Session.SystemLog ('Mass upload');
+
+ Old := FBase;
+ Done := False;
+ AutoAll := False;
+
+ Reset (FBaseFile);
+
+ If Session.io.GetYN('|CR|12Upload files in all directories? |11', True) Then Begin
+ While Not Done and Not Eof(FBaseFile) Do Begin
+ Read (FBaseFile, FBase);
+ Pos := FilePos(FBaseFile);
+ Do_Area;
+ Seek (FBaseFile, Pos);
+ End;
+ End Else
+ Do_Area;
+
+ Close (FBaseFile);
+
+ FBase := Old;
+End;
+
+End.
diff --git a/mystic/bbs_general.pas b/mystic/bbs_general.pas
new file mode 100644
index 0000000..cd90609
--- /dev/null
+++ b/mystic/bbs_general.pas
@@ -0,0 +1,1560 @@
+Unit bbs_General;
+
+{$I M_OPS.PAS}
+
+Interface
+
+// more ancient come from my teenage years lol this stuff needs to be
+// sorted out / cleaned up and passed a session pointer or relocate to
+// one of the classes
+
+Uses
+ m_Strings,
+ m_DateTime,
+ {$IFNDEF UNIX}
+ bbs_SysopChat,
+ {$ENDIF}
+ bbs_Common,
+ bbs_Edit_Full,
+ bbs_Edit_Line;
+
+Function Editor (Var Lines: SmallInt; MaxLen, MaxLine: SmallInt; TEdit, Forced : Boolean; Var Subj: String) : Boolean;
+Procedure Upgrade_User_Level (Now : Boolean; Var U : RecUser; Sec: Byte);
+Procedure View_BBS_List (Long: Boolean; Data: String);
+Procedure Add_BBS_List (Name : String);
+
+Procedure AutoSig_Edit;
+Procedure AutoSig_View;
+
+Procedure List_Users (Data: String);
+{$IFNDEF UNIX}
+ Procedure Page_For_Chat (Forced: Boolean);
+{$ENDIF}
+Procedure Last_Callers;
+Procedure Add_TimeBank;
+Procedure Get_TimeBank;
+Procedure One_Liners (Data : String);
+Procedure Display_Quote;
+Procedure Add_Booth;
+Procedure Voting_Booth (Forced: Boolean; Num: Integer);
+Procedure Voting_Result (Data : Integer);
+Procedure Voting_Booth_New;
+Procedure View_History (LastDays: Word);
+Function Check_Node_Message : Boolean;
+Procedure View_Directory (Data: String; ViewType: Byte);
+
+Implementation
+
+Uses
+ DOS,
+ m_FileIO,
+ m_QuickSort,
+ bbs_Core,
+ bbs_NodeInfo;
+
+Function Editor (Var Lines: SmallInt; MaxLen, MaxLine: SmallInt; TEdit, Forced : Boolean; Var Subj: String) : Boolean;
+Begin
+ If (Session.io.Graphics > 0) and ((Session.User.ThisUser.EditType = 1) or ((Session.User.ThisUser.EditType = 2) and Session.io.GetYN(Session.GetPrompt(106), True))) Then
+ Editor := AnsiEditor(Lines, MaxLen, MaxLine, TEdit, Forced, Subj)
+ Else
+ Editor := LineEditor(Lines, MaxLen, MaxLine, TEdit, Forced, Subj);
+End;
+
+Procedure Upgrade_User_Level (Now: Boolean; Var U: RecUser; Sec: Byte);
+Var
+ A : Char;
+ T : RecSecurity;
+Begin
+ Reset (Session.User.SecurityFile);
+ Seek (Session.User.SecurityFile, Sec - 1);
+ Read (Session.User.SecurityFile, T);
+ Close (Session.User.SecurityFile);
+
+ U.Security := Sec;
+ U.StartMenu := T.StartMenu;
+ U.TimeLeft := T.Time;
+ U.Expires := '00/00/00';
+ U.ExpiresTo := T.ExpiresTo;
+
+ If T.Expires > 0 Then
+ U.Expires := DateJulian2Str(CurDateJulian + T.Expires, 1);
+
+ For A := 'A' to 'Z' Do
+ If Ord(A) - 64 in T.AF1 Then
+ U.AF1 := U.AF1 + [Ord(A) - 64]
+ Else
+ If T.Hard Then
+ U.AF1 := U.AF1 - [Ord(A) - 64];
+
+ For A := 'A' to 'Z' Do
+ If Ord(A) - 64 in T.AF2 Then
+ U.AF2 := U.AF2 + [Ord(A) - 64]
+ Else
+ If T.Hard Then
+ U.AF2 := U.AF2 - [Ord(A) - 64];
+
+ If Now Then Session.User.Security := T;
+End;
+
+Procedure AutoSig_Edit;
+Var
+ DF : File;
+ Lines : Integer;
+ Str : String;
+Begin
+ If Config.MaxAutoSig = 0 Then Exit;
+
+ Assign (DF, Config.DataPath + 'autosig.dat');
+
+ If Session.User.ThisUser.SigLength > 0 Then Begin
+ Reset (DF, 1);
+ Seek (DF, Session.User.ThisUser.SigOffset);
+ For Lines := 1 to Session.User.ThisUser.SigLength Do Begin
+ BlockRead (DF, Session.Msgs.MsgText[Lines][0], 1);
+ BlockRead (DF, Session.Msgs.MsgText[Lines][1], Ord(Session.Msgs.MsgText[Lines][0]));
+ End;
+ Close (DF);
+ End Else
+ Lines := 0;
+
+ Str := 'Signature Editor'; {++lang}
+
+ If Editor (Lines, 78, Config.MaxAutoSig, True, False, Str) Then Begin
+ {$I-} Reset (DF, 1); {$I+}
+ If IoResult <> 0 Then ReWrite (DF, 1);
+ Session.User.ThisUser.SigLength := Lines;
+ Session.User.ThisUser.SigOffset := FileSize(DF);
+ Seek (DF, Session.User.ThisUser.SigOffset);
+ For Lines := 1 to Lines Do
+ BlockWrite (DF, Session.Msgs.MsgText[Lines][0], Length(Session.Msgs.MsgText[Lines]) + 1);
+ Close (DF);
+ End;
+End;
+
+Procedure AutoSig_View;
+Var
+ DF : File;
+ A : Byte;
+ S : String[79];
+Begin
+ If Session.User.ThisUser.SigLength > 0 Then Begin
+ Assign (DF, Config.DataPath + 'autosig.dat');
+ Reset (DF, 1);
+ Seek (DF, Session.User.ThisUser.SigOffset);
+
+ For A := 1 to Session.User.ThisUser.SigLength Do Begin
+ BlockRead (DF, S[0], 1);
+ BlockRead (DF, S[1], Ord(S[0]));
+ Session.io.OutFullLn (S);
+ End;
+
+ Close (DF);
+ End Else
+ Session.io.OutFull (Session.GetPrompt(336));
+End;
+
+Procedure Display_Quote;
+Var
+ TF : Text;
+ TxtBuf : Array[1..1024] of Char;
+ Total,
+ Count : Integer;
+ Str : String;
+Begin
+ Assign (TF, Config.DataPath + 'quotes.dat');
+ {$I-} Reset (TF); {$I+}
+ If IoResult <> 0 Then Exit;
+ SetTextBuf (TF, TxtBuf);
+
+ Total := 0;
+ While Not Eof(TF) Do Begin
+ ReadLn (TF, Str);
+ If Str[1] = '*' Then Inc(Total);
+ End;
+
+ If Total = 0 Then Begin
+ Close (TF);
+ Exit;
+ End;
+
+ Count := Random(Total) + 1;
+ Total := 0;
+
+ Reset (TF);
+ While Total <> Count Do Begin
+ ReadLn (TF, Str);
+ If Str[1] = '*' Then Inc(Total);
+ End;
+
+ While Not Eof(TF) Do Begin
+ ReadLn (TF, Str);
+ If Str[1] = '*' Then Break Else Session.io.OutFullLn (Str);
+ End;
+ Close (TF);
+End;
+
+Function SearchBBS (Str : String; Temp : BBSListRec) : Boolean;
+Begin
+ Str := strUpper(Str);
+
+ SearchBBS := Bool_Search(Str, Temp.BBSName) or
+ Bool_Search(Str, Temp.SysopName) or
+ Bool_Search(Str, Temp.Software) or
+ Bool_Search(Str, Temp.Telnet) or
+ Bool_Search(Str, Temp.Phone) or
+ Bool_Search(Str, Temp.Location);
+End;
+
+Procedure Add_BBS_List (Name : String);
+Var
+ BBSFile : File of BBSListRec;
+ BBSList : BBSListRec;
+ Temp : BBSListRec;
+Begin
+ If Name = '' Then Exit;
+
+ Session.io.OutFull (Session.GetPrompt(361));
+ Case Session.io.OneKey ('DTBQ', True) of
+ 'D' : BBSList.cType := 0;
+ 'T' : BBSList.cType := 1;
+ 'B' : BBSList.cType := 2;
+ 'Q' : Exit;
+ End;
+
+ Session.io.OutRawLn('');
+
+ If BBSList.cType in [0, 2] Then Begin
+ Session.io.OutFull (Session.GetPrompt(283));
+ BBSList.Phone := Session.io.GetInput(15, 15, 12, '');
+ If BBSList.Phone = '' Then Exit;
+ End Else
+ BBSList.Phone := 'None'; //++lang
+
+ If BBSList.cType in [1, 2] Then Begin
+ Session.io.OutFull (Session.GetPrompt(330));
+ BBSList.Telnet := Session.io.GetInput(40, 40, 11, '');
+ If BBSList.Telnet = '' Then Exit;
+ End Else
+ BBSList.Telnet := 'None'; //++lang
+
+ Assign (BBSFile, Config.DataPath + Name + '.bbi');
+ {$I-} Reset(BBSFile); {$I+}
+ If IoResult <> 0 Then ReWrite(BBSFile);
+
+ While Not Eof(BBSFile) Do Begin
+ Read (BBSFile, Temp);
+
+ If ((strUpper(BBSList.Phone) = strUpper(Temp.Phone)) and (Temp.Phone <> 'None')) or
+ ((strUpper(BBSList.Telnet) = strUpper(Temp.Telnet)) and (Temp.Telnet <> 'None')) Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(362));
+ Close (BBSFile);
+ Exit;
+ End;
+ End;
+ Close (BBSFile);
+
+ Session.io.OutFull (Session.GetPrompt(284));
+ BBSList.BBSName := Session.io.GetInput(30, 30, 11, '');
+
+ Session.io.OutFull (Session.GetPrompt(285));
+ BBSList.Location := Session.io.GetInput(25, 25, 18, '');
+
+ Session.io.OutFull (Session.GetPrompt(286));
+ BBSList.SysopName := Session.io.GetInput(30, 30, 11, '');
+
+ Session.io.OutFull (Session.GetPrompt(287));
+ BBSList.BaudRate := Session.io.GetInput(6, 6, 11, '');
+
+ Session.io.OutFull (Session.GetPrompt(288));
+ BBSList.Software := Session.io.GetInput(10, 10, 11, '');
+
+ If Session.io.GetYN(Session.GetPrompt(290), True) Then Begin
+ BBSList.Deleted := False;
+ BBSList.AddedBy := Session.User.ThisUser.Handle;
+ BBSList.Verified := CurDateDos;
+
+ Reset (BBSFile);
+ Seek (BBSFile, FileSize(BBSFile));
+ Write (BBSFile, BBSList);
+ Close (BBSFile);
+ End;
+End;
+
+Procedure View_BBS_List (Long : Boolean; Data : String);
+Var
+ BBSFile : File of BBSListRec;
+ BBSList : BBSListRec;
+ Name : String[8];
+ Str : String;
+ Search : Boolean;
+Begin
+ Search := False;
+
+ If Pos(';', Data) > 0 Then Begin
+ Name := Copy(Data, 1, Pos(';', Data) - 1);
+ Search := Pos(';SEARCH', strUpper(Data)) > 0;
+ End Else
+ Name := Data;
+
+ If Name = '' Then Exit;
+
+ Assign (BBSFile, Config.DataPath + Name + '.bbi');
+ {$I-} Reset(BBSFile); {$I+}
+ If IoResult <> 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(291));
+ Exit;
+ End;
+
+ If Search Then Begin
+ Session.io.OutFull (Session.GetPrompt(292));
+ Str := Session.io.GetInput(30, 30, 11, '');
+ End;
+
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ If Long Then
+ Session.io.OutFullLn (Session.GetPrompt(264))
+ Else
+ Session.io.OutFullLn (Session.GetPrompt(260));
+
+ While Not EOF(BBSFile) Do Begin
+ Read (BBSFile, BBSList);
+
+ If BBSList.Deleted Then Continue;
+
+ If (Search and SearchBBS(Str, BBSList)) or Not Search Then Begin
+ Session.io.PromptInfo[1] := BBSList.BBSName;
+
+ Case BBSList.cType of
+ 0 : Begin
+ Session.io.PromptInfo[3] := BBSList.Phone;
+ Session.io.PromptInfo[2] := 'DIALUP'; //++lang
+ End;
+ 1 : Begin
+ Session.io.PromptInfo[3] := BBSList.Telnet;
+ Session.io.PromptInfo[2] := 'TELNET'; //++lang
+ End;
+ 2 : Begin
+ Session.io.PromptInfo[3] := BBSList.Telnet;
+ Session.io.PromptInfo[2] := 'DU/TEL'; //++lang
+ End;
+ End;
+
+ If (BBSList.cType = 0) and Long Then Session.io.PromptInfo[3] := BBSList.Telnet;
+
+ Session.io.PromptInfo[4] := BBSList.Software;
+ Session.io.PromptInfo[5] := BBSList.Location;
+ Session.io.PromptInfo[6] := BBSList.SysopName;
+ Session.io.PromptInfo[7] := BBSList.BaudRate;
+ Session.io.PromptInfo[8] := BBSList.AddedBy;
+ Session.io.PromptInfo[9] := BBSList.Phone;
+ Session.io.PromptInfo[10] := DateDos2Str(BBSList.Verified, Session.User.ThisUser.DateType);
+
+ If Long Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(265));
+ Session.io.OutFull (Session.GetPrompt(267));
+ Case Session.io.OneKey('DQV'#13, True) of
+ 'D' : If Session.User.Access(Config.AcsSysop) or (strUpper(BBSList.AddedBy) = strUpper(Session.User.ThisUser.Handle)) Then Begin
+ If Session.io.GetYN(Session.GetPrompt(294), False) Then Begin
+ BBSList.Deleted := True;
+ Seek (BBSFile, FilePos(BBSFile) - 1);
+ Write (BBSFile, BBSList);
+ End;
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(295));
+ 'Q' : Break;
+ 'V' : If Session.io.GetYN(Session.GetPrompt(266), False) Then Begin
+ BBSList.Verified := CurDateDos;
+ Seek (BBSFile, FilePos(BBSFile) - 1);
+ Write (BBSFile, BBSList);
+ End;
+ End;
+ End Else Begin
+ Session.io.OutFullLn (Session.GetPrompt(261));
+
+ If (Session.io.PausePtr >= Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+ End;
+ End;
+ Close (BBSFile);
+
+ If Not Long Then
+ Session.io.OutFullLn (Session.GetPrompt(262));
+End;
+
+Procedure List_Users (Data : String);
+Var
+ Total : Integer;
+ tUser : RecUser;
+Begin
+ If Data = 'SEARCH' Then Begin
+ Session.io.OutFull (Session.GetPrompt(32));
+ Data := Session.io.GetInput (30, 30, 12, '');
+ End;
+
+ Reset (Session.User.UserFile);
+
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ Session.io.OutFullLn (Session.GetPrompt(29));
+
+ Total := 0;
+
+ While Not Eof(Session.User.UserFile) Do Begin
+ Read (Session.User.UserFile, tUser);
+
+ If tUser.Flags AND UserDeleted <> 0 Then Continue;
+
+ Session.io.PromptInfo[1] := tUser.Handle;
+ Session.io.PromptInfo[2] := tUser.City;
+ Session.io.PromptInfo[3] := DateDos2Str(tUser.LastOn, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[4] := tUser.Gender;
+ Session.io.PromptInfo[5] := strI2S(tUser.Security);
+ Session.io.PromptInfo[6] := tUser.Address;
+ Session.io.PromptInfo[7] := strI2S(DaysAgo(tUser.Birthday) DIV 365);
+ Session.io.PromptInfo[8] := tUser.Email;
+ Session.io.PromptInfo[9] := tUser.UserInfo;
+ Session.io.PromptInfo[10] := tUser.Optional[1];
+ Session.io.PromptInfo[11] := tUser.Optional[2];
+ Session.io.PromptInfo[12] := tUser.Optional[3];
+
+ If (Data = '') or (Pos(Data, strUpper(tUser.Handle)) > 0) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(30));
+ Inc (Total);
+
+ If (Session.io.PausePtr >= Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+ End;
+ Close (Session.User.UserFile);
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.OutFull (Session.GetPrompt(31));
+End;
+
+{$IFNDEF UNIX}
+Procedure Page_For_Chat (Forced: Boolean);
+Var
+ Temp : String;
+ A, B : Integer;
+Begin
+ If Forced or ((TimerMinutes >= Config.ChatStart) and (TimerMinutes <= Config.ChatEnd)) Then Begin
+ Session.io.OutFull (Session.GetPrompt(23));
+ Temp := Session.io.GetInput(50, 50, 11, '');
+ If Temp = '' Then Exit;
+
+ Session.SystemLog('Chat Page: ' + Temp);
+
+ Update_Status_Line (0, ' ' + strPadR(Session.User.ThisUser.Handle, 17, ' ') + ' ' + strPadR(Temp, 40, ' ') + ' ALT+(S)plit (C)Line');
+
+ Session.io.OutFull(Session.GetPrompt(24));
+
+ For A := 1 to 10 Do Begin
+ Session.io.OutFull(Session.GetPrompt(25));
+
+ For B := 0 to 6 Do Begin
+ //SysBeepEx(523, 50);
+ //SysBeepEx(659, 50);
+ End;
+ If Input.KeyPressed Then If Input.ReadKey = #0 Then Begin
+ Case Input.ReadKey of
+ #31 : OpenChat(True);
+ #46 : OpenChat(False);
+ End;
+ Exit;
+ End;
+ WaitMS(1000);
+ End;
+ End;
+
+ Update_Status_line (StatusPtr, '');
+
+ Session.io.OutFull (Session.GetPrompt(28));
+
+ If Config.ChatFeedback Then
+ If Session.io.GetYN(Session.GetPrompt(178), False) Then
+ Session.Msgs.PostMessage (True, '/TO:' + strReplace(Config.SysopName, ' ', '_') + ' /SUBJ:Chat_Feedback');
+End;
+{$ENDIF}
+
+Procedure Last_Callers;
+Begin
+ Session.io.OutFullLn (Session.GetPrompt(141));
+ Reset (LastOnFile);
+ While Not Eof(LastOnFile) Do Begin
+ Read (LastOnFile, LastOn);
+ Session.io.PromptInfo[1] := LastOn.Handle;
+ Session.io.PromptInfo[2] := strI2S(LastOn.Node);
+ Session.io.PromptInfo[3] := LastOn.City;
+ Session.io.PromptInfo[4] := DateDos2Str(LastOn.DateTime, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[5] := TimeDos2Str(LastOn.DateTime, True);
+ Session.io.PromptInfo[6] := LastOn.Baud;
+ Session.io.PromptInfo[7] := strI2S(LastOn.CallNum);
+ Session.io.PromptInfo[8] := LastOn.Address;
+ Session.io.PromptInfo[9] := LastOn.UserInfo;
+ Session.io.PromptInfo[10] := LastOn.EmailAddr;
+ Session.io.PromptInfo[11] := LastOn.Option1;
+ Session.io.PromptInfo[12] := LastOn.Option2;
+ Session.io.PromptInfo[13] := LastOn.Option3;
+ Session.io.OutFullLn (Session.GetPrompt(142));
+ End;
+ Close (LastOnFile);
+ Session.io.OutFull (Session.GetPrompt(143));
+End;
+
+Procedure Add_TimeBank;
+Var
+ A : Integer;
+Begin
+ Session.io.OutFull (Session.GetPrompt(172));
+ A := strS2I(Session.io.GetInput(4, 4, 11, ''));
+ If A > 0 Then
+ If (A < Session.TimeLeft - 4) Then Begin
+ If (Session.User.Security.MaxTB > 0) and (Session.User.ThisUser.TimeBank + A > Session.User.Security.MaxTB) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(209));
+ Exit;
+ End;
+ Inc (Session.User.ThisUser.TimeBank, A);
+ Session.SetTimeLeft (Session.TimeLeft - A);
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(210));
+End;
+
+Procedure Get_TimeBank;
+Var
+ A : Integer;
+Begin
+ Session.io.OutFull (Session.GetPrompt(173));
+ A := strS2I(Session.io.GetInput(4, 4, 11, ''));
+ If (A > 0) and (A <= Session.User.ThisUser.TimeBank) Then Begin
+ Dec (Session.User.ThisUser.TimeBank, A);
+ Session.SetTimeLeft (Session.TimeLeft + A);
+ End;
+End;
+
+Procedure One_Liners (Data : String);
+Const
+ MaxLines : Byte = 9;
+ MaxLen : Byte = 75;
+ MaxField : Byte = 75;
+Var
+ OneLineFile : File of OneLineRec;
+ OneLine : OneLineRec;
+ Str : String;
+ A : Byte;
+Begin
+ A := Pos(';', Data);
+ If A > 0 Then Begin
+ MaxLines := strS2I(Copy(Data, 1, A - 1)) - 1;
+ Delete (Data, 1, A);
+ A := Pos(';', Data);
+ MaxLen := strS2I(Copy(Data, 1, A - 1));
+ MaxField := strS2I(Copy(Data, A + 1, Length(Data)));
+ End;
+
+ Assign (OneLineFile, Config.DataPath + 'oneliner.dat');
+ {$I-} Reset (OneLineFile); {$I+}
+ If IoResult <> 0 Then ReWrite (OneLineFile);
+
+ Repeat
+ Reset (OneLineFile);
+ Session.io.OutFullLn (Session.GetPrompt(188));
+
+ While Not Eof(OneLineFile) Do Begin
+ Read (OneLineFile, OneLine);
+ Session.io.PromptInfo[1] := OneLine.Text;
+ Session.io.PromptInfo[2] := OneLine.From;
+ Session.io.PromptInfo[3] := OneLine.From[1];
+ If Pos(' ', OneLine.From) > 0 Then
+ Session.io.PromptInfo[3] := Session.io.PromptInfo[3] + OneLine.From[Pos(' ', OneLine.From) + 1];
+ Session.io.OutFullLn (Session.GetPrompt(337));
+ End;
+
+ If Session.io.GetYN(Session.GetPrompt(189), False) Then Begin
+ Session.io.OutFull (Session.GetPrompt(190));
+ Str := Session.io.GetInput (MaxField, MaxLen, 11, '');
+ If Str <> '' Then Begin
+ If FileSize(OneLineFile) > MaxLines Then
+ KillRecord (OneLineFile, 1, SizeOf(OneLineRec));
+
+ OneLine.Text := Str;
+ OneLine.From := Session.User.ThisUser.Handle;
+
+ Seek (OneLineFile, FileSize(OneLineFile));
+ Write (OneLineFile, OneLine);
+ End;
+ End Else
+ Break;
+ Until False;
+
+ Close (OneLineFile);
+End;
+
+Procedure Add_Booth;
+Var
+ A : Byte;
+Begin
+ If Not Session.io.GetYN (Session.GetPrompt(275), True) Then Exit;
+
+ Reset (VoteFile);
+ If FileSize (VoteFile) = mysMaxVoteQuestion Then Begin
+ Close (VoteFile);
+ Session.io.OutFull (Session.GetPrompt(276));
+ Exit;
+ End;
+ Close (VoteFile);
+
+ Session.io.OutFull (Session.GetPrompt(277));
+ Vote.Question := Session.io.GetInput(78, 78, 11, '');
+ If Vote.Question = '' Then Exit;
+
+ Session.io.OutFullLn (Session.GetPrompt(278));
+
+ A := 1;
+
+ While A <= 15 Do Begin
+ Session.io.PromptInfo[1] := strI2S(A);
+ Session.io.OutFull (Session.GetPrompt(279));
+ Vote.Answer[A].Text := Session.io.GetInput(40, 40, 11, '');
+ If Vote.Answer[A].Text = '' Then Begin
+ Dec (A);
+ Break;
+ End;
+ Vote.Answer[A].Votes := 0;
+ Inc(A);
+ End;
+
+ If A = 0 Then Exit;
+
+ Vote.AnsNum := A;
+ Vote.Votes := 0;
+ Vote.ACS := '';
+ Vote.AddACS := 's999';
+ Vote.ForceACS := 's999';
+
+ If Session.io.GetYN(Session.GetPrompt(280), True) Then Vote.AddACS := '';
+
+ If Session.io.GetYN(Session.GetPrompt(281), True) Then Begin
+ Reset (VoteFile);
+ Seek (VoteFile, FileSize(VoteFile));
+ Write (VoteFile, Vote);
+ Close (VoteFile);
+ End;
+End;
+
+{ VOTING BOOTH SHIT }
+
+Function Voting_List : Byte;
+Var
+ Total : Byte;
+Begin
+ Reset (VoteFile);
+
+ Session.io.OutFullLn (Session.GetPrompt(241));
+
+ Total := 0;
+ While Not Eof(VoteFile) Do Begin
+ Read (VoteFile, Vote);
+ If Session.User.Access(Vote.ACS) Then Begin
+ Inc (Total);
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := Vote.Question;
+ If Session.User.ThisUser.Vote[FilePos(VoteFile)] = 0 Then
+ Session.io.PromptInfo[3] := '*' //++lang
+ Else
+ Session.io.PromptInfo[3] := ' ';
+ Session.io.OutFullLn (Session.GetPrompt(242));
+ End;
+ End;
+ Close (VoteFile);
+
+ If Total = 0 Then Session.io.OutFullLn (Session.GetPrompt(243));
+ Voting_List := Total;
+End;
+
+Procedure Voting_Result (Data : Integer);
+Var
+ A : SmallInt;
+ P : SmallInt;
+Begin
+ Reset (VoteFile);
+
+ If (Data > 0) and (Data <= FileSize(VoteFile)) Then Begin
+ Seek (VoteFile, Data - 1);
+ Read (VoteFile, Vote);
+ Close (VoteFile);
+ End Else Begin
+ A := Voting_List;
+ If A = 0 Then Exit;
+
+ Repeat
+ Session.io.PromptInfo[1] := strI2S(A);
+ Session.io.OutFull (Session.GetPrompt(263));
+ P := strS2I(Session.io.GetInput(2, 2, 12, ''));
+ If P = 0 Then Exit;
+ If P <= A Then Break;
+ Until False;
+
+ Reset (VoteFile);
+ A := 0;
+ Repeat
+ Read (VoteFile, Vote);
+ If Session.User.Access(Vote.ACS) Then Inc(A);
+ Until A = P;
+ Close (VoteFile);
+ End;
+
+ Session.io.PromptInfo[1] := Vote.Question;
+ Session.io.PromptInfo[2] := strI2S(Vote.Votes);
+ Session.io.OutFullLn (Session.GetPrompt(249));
+ For A := 1 to Vote.AnsNum Do Begin
+ Session.io.PromptInfo[1] := strI2S(A);
+ Session.io.PromptInfo[2] := Vote.Answer[A].Text;
+ Session.io.PromptInfo[3] := strI2S(Vote.Answer[A].Votes);
+ If Vote.Votes = 0 Then Begin
+ Session.io.PromptInfo[4] := '0';
+ Session.io.PromptInfo[5] := '';
+ End Else Begin
+ Session.io.PromptInfo[5] := Session.io.DrawPercent(Session.Lang.VotingBar, Vote.Answer[A].Votes, Vote.Votes, P);
+ Session.io.PromptInfo[4] := strI2S(P);
+ End;
+ Session.io.OutFullLn (Session.GetPrompt(250));
+ End;
+ Session.io.OutFull (Session.GetPrompt(251));
+End;
+
+Procedure Voting_Booth_New;
+Var
+ NewQues : Array[1..mysMaxVoteQuestion] of Boolean;
+ Pos : Byte;
+Begin
+ Reset (VoteFile);
+ While Not Eof(VoteFile) Do Begin
+ Read (VoteFile, Vote);
+ If Session.User.Access(Vote.ACS) Then
+ NewQues[FilePos(VoteFile)] := (Session.User.ThisUser.Vote[FilePos(VoteFile)] = 0)
+ Else
+ NewQues[FilePos(VoteFile)] := False;
+ End;
+ Close (VoteFile);
+
+ For Pos := 1 to mysMaxVoteQuestion Do
+ If NewQues[Pos] Then Voting_Booth (False, Pos);
+End;
+
+Procedure Voting_Booth (Forced: Boolean; Num: Integer);
+Var
+ VPos : Byte;
+ Temp : Byte;
+ Total : Byte;
+ Str : String[40];
+Begin
+
+ If Not Forced And (Num = 0) Then Begin
+ Total := Voting_List;
+ If Total = 0 Then Exit;
+
+ Repeat
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.OutFull (Session.GetPrompt(244));
+ Temp := strS2I(Session.io.GetInput(2, 2, 12, ''));
+ If Temp = 0 Then Exit;
+ If Temp <= Total Then Break;
+ Until False;
+
+ Total := 0;
+ Reset (VoteFile);
+ Repeat
+ Read (VoteFile, Vote);
+ If Session.User.Access(Vote.ACS) Then Inc(Total);
+ Until Total = Temp;
+ End Else Begin
+ Reset (VoteFile);
+ If Num > FileSize(VoteFile) Then Begin
+ Close (VoteFile);
+ Exit;
+ End;
+ Seek (VoteFile, Num - 1);
+ Read (VoteFile, Vote);
+ End;
+
+ VPos := FilePos(VoteFile);
+
+ Repeat
+ Session.io.PromptInfo[1] := Vote.Question;
+ Session.io.OutFullLn (Session.GetPrompt(245));
+ For Temp := 1 to Vote.AnsNum Do Begin
+ Session.io.PromptInfo[1] := strI2S(Temp);
+ Session.io.PromptInfo[2] := Vote.Answer[Temp].Text;
+ If Session.User.ThisUser.Vote[VPos] = Temp Then
+ Session.io.PromptInfo[3] := '*' //++lang
+ Else
+ Session.io.PromptInfo[3] := ' ';
+ Session.io.OutFullLn (Session.GetPrompt(246));
+ End;
+
+ If Session.User.Access(Vote.AddACS) and (Vote.AnsNum < 15) Then Begin
+ Session.io.PromptInfo[1] := strI2S(Vote.AnsNum + 1);
+ Session.io.PromptInfo[2] := Session.GetPrompt(252);
+ Session.io.PromptInfo[3] := ' ';
+ Session.io.OutFullLn (Session.GetPrompt(246));
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(247));
+ Temp := strS2I(Session.io.GetInput(2, 2, 12, ''));
+
+ If (Vote.AnsNum < 15) and Session.User.Access(Vote.AddACS) and (Temp = Succ(Vote.AnsNum)) Then Begin
+ Session.io.OutFull (Session.GetPrompt(253));
+ Str := Session.io.GetInput (40, 40, 11, '');
+ If Str <> '' Then Begin
+ Inc (Vote.AnsNum);
+ Vote.Answer[Vote.AnsNum].Text := Str;
+ Vote.Answer[Vote.AnsNum].Votes := 0;
+ End;
+ End;
+
+ If (Temp > 0) and (Temp <= Vote.AnsNum) Then Begin
+ If Session.User.ThisUser.Vote[VPos] <> 0 Then Begin
+ Dec (Vote.Answer[Session.User.ThisUser.Vote[VPos]].Votes);
+ Dec (Vote.Votes);
+ End;
+ Inc(Vote.Answer[Temp].Votes);
+ Inc(Vote.Votes);
+ Session.User.ThisUser.Vote[VPos] := Temp;
+
+ Seek (VoteFile, VPos - 1);
+ Write (VoteFile, Vote);
+ Break;
+ End Else
+ If Forced Then Session.io.OutFull (Session.GetPrompt(254)) Else Break;
+ Until False;
+
+ Close (VoteFile);
+ If Session.io.GetYN (Session.GetPrompt(248), True) Then Voting_Result(VPos);
+End;
+
+Procedure View_History (LastDays: Word);
+Var
+ Temp : HistoryRec;
+ Days : Word;
+Begin
+ Assign (Session.HistoryFile, Config.DataPath + 'history.dat');
+ {$I-} Reset(Session.HistoryFile); {$I+}
+ If IoResult <> 0 Then
+ Session.io.OutFullLn (Session.GetPrompt(454))
+ Else Begin
+ If (LastDays > 0) And (FileSize(Session.HistoryFile) >= LastDays) Then
+ Seek (Session.HistoryFile, FileSize(Session.HistoryFile) - LastDays);
+
+ Session.io.AllowPause := True;
+ Session.io.PausePtr := 1;
+ Days := 0;
+
+ Session.io.OutFullLn (Session.GetPrompt(455));
+
+ While Not Eof(Session.HistoryFile) Do Begin
+ Read (Session.HistoryFile, Temp);
+
+ Session.io.PromptInfo[1] := DateDos2Str(Temp.Date, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[2] := strI2S(Temp.Calls);
+ Session.io.PromptInfo[3] := strI2S(Temp.NewUsers);
+ Session.io.PromptInfo[4] := strI2S(Temp.Posts);
+ Session.io.PromptInfo[5] := strI2S(Temp.Emails);
+ Session.io.PromptInfo[6] := strI2S(Temp.Downloads);
+ Session.io.PromptInfo[7] := strI2S(Temp.DownloadKB);
+ Session.io.PromptInfo[8] := strI2S(Temp.Uploads);
+ Session.io.PromptInfo[9] := strI2S(Temp.UploadKB);
+
+ Session.io.OutFullLn (Session.GetPrompt(456));
+
+ Inc (Days);
+
+ If (Session.io.PausePtr >= Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Close (Session.HistoryFile);
+
+ Session.io.PromptInfo[1] := strI2S(Days);
+
+ Session.io.OutFullLn (Session.GetPrompt(457));
+ End;
+End;
+
+Function Check_Node_Message : Boolean;
+Var
+ Res : Boolean;
+ Str : String;
+Begin
+ Check_Node_Message := False;
+ Res := False;
+
+ Assign (NodeMsgFile, Session.TempPath + 'chat.tmp');
+ FileMode := 66;
+ {$I-} Reset (NodeMsgFile); {$I+}
+ If IoResult <> 0 Then Exit;
+
+{ checks for non-teleconference node messages:
+ 2 = system broadcast message (ie, not from user, from mystic)
+ 3 = user to user node message }
+
+ While Not Eof(NodeMsgFile) Do Begin
+ Res := True;
+
+ Read (NodeMsgFile, NodeMsg);
+
+ Session.io.PromptInfo[1] := NodeMsg.FromWho;
+ Session.io.PromptInfo[2] := strI2S(NodeMsg.FromNode);
+
+ Case NodeMsg.MsgType of
+ 2 : Begin
+ Session.io.OutFullLn (Session.GetPrompt(179) + NodeMsg.Message);
+ Session.io.OutFullLn (Session.GetPrompt(180));
+ End;
+ 3 : Begin
+ Session.io.OutFullLn (Session.GetPrompt(144) + '|CR' + NodeMsg.Message);
+ Session.io.OutFull (Session.GetPrompt(145));
+ End;
+ End;
+ End;
+
+ Close (NodeMsgFile);
+ Erase (NodeMsgFile);
+
+ If Res And (NodeMsg.MsgType = 3) Then
+ If Session.io.OneKey(#13 + 'R', True) = 'R' Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(360));
+ Str := Session.io.GetInput(79, 79, 11, '');
+ If Str <> '' Then Send_Node_Message(3, Session.io.PromptInfo[2] + ';' + Str, 0);
+ End;
+
+ Check_Node_Message := Res;
+End;
+
+Procedure View_Directory (Data: String; ViewType: Byte);
+Const
+ vtMaxList = 1000;
+
+Type
+ RecSauceInfo = Packed Record
+ Title : String[35];
+ Author : String[20];
+ Group : String[20];
+ End;
+
+ DirRec = Record
+ Desc : String[160];
+ Size : LongInt;
+ Date : LongInt;
+ IsDir : Boolean;
+ Title : String[34];
+ Author : String[19];
+ Group : String[19];
+ End;
+
+Var
+ WinTop : Byte;
+ WinBot : Byte;
+ WinSize : Byte;
+ DirList : Array[1..vtMaxList] of ^DirRec;
+ DirCount : LongInt = 0;
+ CurTop : LongInt = 1;
+ CurBot : LongInt = 1;
+ CurPos : LongInt = 1;
+ CurPath : String;
+ Root : String;
+
+ Function ReadSauceInfo (FN: String; Var Sauce: RecSauceInfo) : Boolean;
+ Var
+ DF : File;
+ Str : String;
+ Res : LongInt;
+ Begin
+ Result := False;
+
+ Assign (DF, FN);
+
+ {$I-} Reset (DF, 1); {$I+}
+
+ If IoResult <> 0 Then Exit;
+
+ {$I-} Seek (DF, FileSize(DF) - 130); {$I+}
+
+ If IoResult <> 0 Then Begin
+ Close (DF);
+ Exit;
+ End;
+
+ BlockRead (DF, Str[1], 130);
+ Str[0] := #130;
+
+ Close (DF);
+
+ Res := Pos('SAUCE', Copy(Str, 1, 7));
+
+ If Res > 0 Then Begin
+ Result := True;
+
+ Sauce.Title := strReplace(Copy(Str, 7 + Res, 35), #0, #32);
+ Sauce.Author := strReplace(Copy(Str, 42 + Res, 20), #0, #32);
+ Sauce.Group := strReplace(Copy(Str, 62 + Res, 20), #0, #32);
+ End;
+ End;
+
+ Procedure BuildDirectory (Path: String);
+ Var
+ SR : SearchRec;
+ Count : Word;
+ Sauce : RecSauceInfo;
+ Temp : String;
+ SortLoop : Word;
+ SortPos : Word;
+ Sort : TQuickSort;
+ Begin
+ For Count := DirCount Downto 1 Do
+ Dispose(DirList[Count]);
+
+ Sort := TQuickSort.Create;
+ Temp := Session.GetPrompt(473);
+ DirCount := 0;
+
+ For Count := 1 to 2 Do Begin
+ FindFirst (Path + '*', AnyFile, SR);
+
+ While (DosError = 0) And (DirCount < vtMaxList) Do Begin
+ If (SR.Name = '.') or ((Path = Root) And (SR.Name = '..')) Then Begin
+ FindNext (SR);
+ Continue;
+ End;
+
+ If ((Count = 1) And (SR.Attr And Directory = 0)) or
+ ((Count = 2) And (SR.Attr And Directory <> 0)) Then Begin
+ FindNext(SR);
+ Continue;
+ End;
+
+ Inc (DirCount);
+
+ New (DirList[DirCount]);
+
+ DirList[DirCount]^.Desc := SR.Name;
+ DirList[DirCount]^.Size := SR.Size;
+ DirList[DirCount]^.Date := SR.Time;
+
+ If (SR.Attr And Directory) = 0 Then Begin
+ DirList[DirCount]^.IsDir := False;
+
+ If ReadSauceInfo(Path + SR.Name, Sauce) Then Begin
+ DirList[DirCount]^.Title := Sauce.Title;
+ DirList[DirCount]^.Author := Sauce.Author;
+ DirList[DirCount]^.Group := Sauce.Group;
+ End Else Begin
+ DirList[DirCount]^.Title := strWordGet(1, Temp, ';');
+ DirList[DirCount]^.Author := strWordGet(2, Temp, ';');
+ DirList[DirCount]^.Group := strWordGet(3, Temp, ';');
+ End;
+ End Else
+ DirList[DirCount]^.IsDir := True;
+
+ FindNext (SR);
+ End;
+
+ FindClose (SR);
+
+ Case Count of
+ 1 : Begin
+ SortPos := DirCount;
+
+ For SortLoop := 1 to DirCount Do
+ Sort.Add(strUpper(DirList[SortLoop]^.Desc), LongInt(@DirList[SortLoop]^));
+
+ Sort.Sort(1, DirCount, qAscending);
+
+ For SortLoop := 1 to DirCount Do
+ DirList[SortLoop] := Pointer(Sort.Data[SortLoop]^.Ptr);
+ End;
+ 2 : If SortPos <> DirCount Then Begin
+ Sort.Clear;
+
+ For SortLoop := Succ(SortPos) to DirCount Do
+ Sort.Add(strUpper(DirList[SortLoop]^.Desc), LongInt(@DirList[SortLoop]^));
+
+ Sort.Sort(1, DirCount - SortPos, qAscending);
+
+ For SortLoop := 1 to DirCount - SortPos Do
+ DirList[SortLoop + SortPos] := Pointer(Sort.Data[SortLoop]^.Ptr);
+ End;
+ End;
+ End;
+
+ Sort.Free;
+ End;
+
+ Procedure SetBarInfo (BarPos: Word);
+ Begin
+ Session.io.PromptInfo[1] := DirList[BarPos]^.Desc;
+ Session.io.PromptInfo[2] := strComma(DirList[BarPos]^.Size);
+ Session.io.PromptInfo[3] := DateDos2Str(DirList[BarPos]^.Date, Session.User.ThisUser.DateType);
+ Session.io.PromptInfo[7] := TimeDos2Str(DirList[BarPos]^.Date, True);
+
+ If DirList[BarPos]^.IsDir Then Begin
+ Session.io.PromptInfo[4] := '';
+ Session.io.PromptInfo[5] := '';
+ Session.io.PromptInfo[6] := '';
+ End Else Begin
+ Session.io.PromptInfo[4] := DirList[BarPos].Author;
+ Session.io.PromptInfo[5] := DirList[BarPos].Title;
+ Session.io.PromptInfo[6] := DirList[BarPos].Group;
+ End;
+ End;
+
+ Procedure DrawPage;
+ Var
+ Count : SmallInt;
+ Start : Word;
+ Begin
+ Start := CurTop;
+
+ For Count := WinTop to WinBot Do Begin
+ Session.io.AnsiGotoXY(1, Count);
+
+ If Start <= DirCount Then Begin
+ SetBarInfo(Start);
+
+ Case DirList[Start]^.IsDir of
+ False : Session.io.OutFull(Session.GetPrompt(467));
+ True : Session.io.OutFull(Session.GetPrompt(469));
+ End;
+ End Else Begin
+ Session.io.PromptInfo[1] := '';
+ Session.io.PromptInfo[2] := '';
+ Session.io.PromptInfo[3] := '';
+ Session.io.PromptInfo[4] := '';
+ Session.io.PromptInfo[5] := '';
+ Session.io.PromptInfo[6] := '';
+ Session.io.PromptInfo[7] := '';
+
+ Session.io.OutFull(Session.GetPrompt(467));
+ End;
+
+ Inc (Start);
+ End;
+
+ CurBot := Start - 1;
+
+ If CurPos > CurBot Then CurPos := CurBot;
+
+ Session.io.PromptInfo[1] := Session.io.DrawPercent(Session.Lang.GalleryBar, CurBot, DirCount, Count);
+ Session.io.PromptInfo[2] := strI2S(Count);
+
+ Session.io.OutFull(Session.GetPrompt(472));
+ End;
+
+ Procedure DrawBar (Selected: Boolean);
+ Begin
+ SetBarInfo(CurPos);
+
+ Session.io.AnsiGotoXY (1, CurPos - CurTop + WinTop);
+
+ If Selected Then
+ Case DirList[CurPos]^.IsDir of
+ False : Session.io.OutFull(Session.GetPrompt(468));
+ True : Session.io.OutFull(Session.GetPrompt(470));
+ End
+ Else
+ Case DirList[CurPos]^.IsDir of
+ False : Session.io.OutFull(Session.GetPrompt(467));
+ True : Session.io.OutFull(Session.GetPrompt(469));
+ End;
+ End;
+
+ Procedure UpdatePath;
+ Var
+ Temp : String;
+ Begin
+ Temp := CurPath;
+
+ Delete (Temp, 1, Length(Root) - 1);
+
+ If Length(Temp) > 70 Then
+ Session.io.PromptInfo[1] := '..' + Copy(Temp, (Length(Temp) - 68), 255)
+ Else
+ Session.io.PromptInfo[1] := Temp;
+
+ Session.io.PromptInfo[2] := strComma(DirCount);
+
+ Session.io.OutFull(Session.GetPrompt(471));
+ End;
+
+ Procedure FullReDraw;
+ Begin
+ Session.io.OutFile('ansigal', False, 0);
+
+ WinTop := Session.io.ScreenInfo[1].Y;
+ WinBot := Session.io.ScreenInfo[2].Y;
+ WinSize := WinBot - WinTop + 1;
+
+ UpdatePath;
+ DrawPage;
+ DrawBar(True);
+ End;
+
+ Function FindCharacter (Ch: Char) : Byte;
+ Var
+ Loop : Boolean;
+ StartPos : Word;
+ EndPos : Word;
+ Count : Word;
+ Begin
+ Result := 0;
+ Loop := True;
+ StartPos := CurPos + 1;
+ EndPos := DirCount;
+
+ If StartPos > DirCount Then StartPos := 1;
+
+ Count := StartPos;
+
+ While (Count <= EndPos) Do Begin
+ If UpCase(DirList[Count]^.Desc[1]) = Ch Then Begin
+ Result := 1;
+
+ While Count <> CurPos Do Begin
+ If CurPos < Count Then Begin
+ If CurPos < DirCount Then Inc (CurPos);
+ If CurPos >= CurTop + WinSize Then Begin
+ Inc (CurTop);
+ Result := 2;
+ End;
+ End Else
+ If CurPos > Count Then Begin
+ If CurPos > 1 Then Dec (CurPos);
+ If CurPos < CurTop Then Begin
+ Dec (CurTop);
+ Result := 2;
+ End;
+ End;
+ End;
+ Break;
+ End;
+
+ If (Count = DirCount) and Loop Then Begin
+ Count := 0;
+ StartPos := 1;
+ EndPos := CurPos - 1;
+ Loop := False;
+ End;
+
+ Inc (Count);
+ End;
+ End;
+
+Var
+ Ch : Char;
+ Count : Word;
+ Speed : Byte;
+Begin
+ If Session.io.Graphics = 0 Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(466));
+ Exit;
+ End;
+
+ Session.io.AllowArrow := True;
+
+ Root := DirSlash(strWordGet(1, Data, ';'));
+ Speed := strS2I(strWordGet(2, Data, ';'));
+ CurPath := Root;
+
+ BuildDirectory(CurPath);
+
+ FullReDraw;
+
+ Repeat
+ Ch := UpCase(Session.io.GetKey);
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #71 : If CurPos > 1 Then Begin
+ CurPos := 1;
+ CurTop := 1;
+
+ DrawPage;
+ DrawBar(True);
+ End;
+ #72 : If CurPos > 1 Then Begin
+ If CurPos = CurTop Then Begin
+ Dec (CurTop);
+ Dec (CurPos);
+
+ DrawPage;
+ DrawBar(True);
+ End Else Begin
+ DrawBar(False);
+ Dec(CurPos);
+ DrawBar(True);
+ End;
+ End;
+ #73,
+ #75 : If CurTop - WinSize >= 1 Then Begin
+ Dec (CurTop, WinSize);
+ Dec (CurPos, WinSize);
+
+ DrawPage;
+ DrawBar(True);
+ End Else
+ If CurPos > 1 Then Begin
+ CurPos := 1;
+ CurTop := 1;
+
+ DrawPage;
+ DrawBar(True);
+ End;
+ #79 : If CurPos < DirCount Then Begin
+ CurPos := DirCount;
+ CurTop := DirCount - WinSize + 1;
+
+ If CurTop < 1 Then CurTop := 1;
+
+ DrawPage;
+ DrawBar(True);
+ End;
+ #80 : If CurPos < DirCount Then Begin
+ If CurPos = CurBot Then Begin
+ Inc (CurTop);
+ Inc (CurPos);
+ DrawPage;
+ DrawBar(True);
+ End Else Begin
+ DrawBar(False);
+ Inc(CurPos);
+ DrawBar(True);
+ End;
+ End;
+ #77,
+ #81 : If CurTop + WinSize <= DirCount - WinSize Then Begin
+ Inc (CurPos, WinSize);
+ Inc (CurTop, WinSize);
+
+ DrawPage;
+ DrawBar(True);
+ End Else
+ If CurPos < DirCount Then Begin
+ CurPos := DirCount;
+ CurTop := DirCount - WinSize + 1;
+
+ If CurTop < 1 Then CurTop := 1;
+
+ DrawPage;
+ DrawBar(True);
+ End;
+ End;
+ End Else
+ Case Ch of
+ #08 : If CurPath <> Root Then Begin
+ Delete (CurPath, Length(CurPath), 1);
+
+ While CurPath[Length(CurPath)] <> PathChar Do
+ Delete (CurPath, Length(CurPath), 1);
+
+ BuildDirectory(CurPath);
+
+ CurPos := 1;
+ CurTop := 1;
+
+ UpdatePath;
+ DrawPage;
+ DrawBar(True);
+ End;
+ #13 : If DirList[CurPos]^.IsDir Then Begin
+ If DirList[CurPos]^.Desc = '..' Then Begin
+ Delete (CurPath, Length(CurPath), 1);
+
+ While CurPath[Length(CurPath)] <> PathChar Do
+ Delete (CurPath, Length(CurPath), 1);
+ End Else
+ CurPath := CurPath + DirList[CurPos]^.Desc + PathChar;
+
+ BuildDirectory(CurPath);
+
+ CurPos := 1;
+ CurTop := 1;
+
+ UpdatePath;
+ DrawPage;
+ DrawBar(True);
+ End Else Begin
+ Session.io.AllowMCI := True;
+ Session.io.AnsiColor(7);
+ Session.io.AnsiClear;
+ Session.io.OutFile (CurPath + DirList[CurPos]^.Desc, False, Speed);
+ Session.io.PauseScreen;
+
+ FullReDraw;
+ End;
+ #27 : Break;
+ '?' : Begin
+ Session.io.OutFile('ansigalh', False, 0);
+ FullRedraw;
+ End;
+ '!' : If Not DirList[CurPos]^.IsDir Then Begin
+ Session.io.AnsiColor(7);
+ Session.io.AnsiGotoXY(1, Session.User.ThisUser.ScreenSize);
+
+ If Session.io.GetYN(Session.GetPrompt(474), False) Then
+ Session.FileBase.SendFile(CurPath + DirList[CurPos]^.Desc);
+
+ FullReDraw;
+ End;
+ Else
+ DrawBar(False);
+
+ Case FindCharacter(Ch) of
+ 0,
+ 1 : DrawBar(True);
+ 2 : Begin
+ DrawPage;
+ DrawBar(True);
+ End;
+ End;
+ End;
+ Until Session.ShutDown;
+
+ Session.io.AnsiColor(7);
+ Session.io.AnsiGotoXY(1, 24);
+
+ For Count := DirCount DownTo 1 Do
+ Dispose (DirList[Count]);
+End;
+
+(* MYSTIC 2's ANSIVIEWER
+- needs to be intergrated with the msgbases. there should NOT be a msgtext AND this
+ class. there should only be one place where the massive message buffer exists.
+
+Procedure TBBSIO.AnsiViewer (Data: String);
+Var
+ Buf : Array[1..4096] of Char;
+ BufLen : LongInt;
+ TopLine : LongInt;
+ WinSize : LongInt;
+ Ansi : TMsgBaseAnsi;
+ AFile : File;
+ Ch : Char;
+ FN : String;
+ Template : String;
+ Str : String;
+ Sauce : RecSauceInfo;
+
+ Procedure Update;
+ Begin
+ // add percentage bar and line number here
+ Ansi.DrawPage (TBBSCore(Owner).Term.ScreenInfo[1].Y, TBBSCore(Owner).Term.ScreenInfo[2].Y, TopLine);
+ End;
+
+Begin
+ Template := strWordGet(1, Data, ';');
+ FN := strWordGet(2, Data, ';');
+
+ If Pos(mysPathSep, FN) = 0 Then
+ FN := TBBSCore(Owner).Theme.PathText + FN;
+
+ If Pos('.', FN) = 0 Then
+ FN := FN + '.ans';
+
+ If Not FileExist(FN) Then Exit;
+
+ PromptInfo['A'] := JustFile(FN);
+
+ If ReadSauceInfo(FN, Sauce) Then Begin
+ PromptInfo['B'] := strStripR(strWide2Str(Sauce.Title, 35), ' ');
+ PromptInfo['C'] := strStripR(strWide2Str(Sauce.Author, 20), ' ');
+ PromptInfo['D'] := strStripR(strWide2Str(Sauce.Group, 20), ' ');
+ Str := strWide2Str(Sauce.Date, 8);
+ PromptInfo['E'] := Copy(Str, 5, 2) + '/' + Copy(Str, 7, 2) + '/' + Copy(Str, 1, 4);
+ End Else Begin
+ PromptInfo['B'] := 'Unknown';
+ PromptInfo['C'] := PromptInfo['B'];
+ PromptInfo['D'] := PromptInfo['B'];
+ PromptInfo['E'] := '??/??/????';
+ End;
+
+ Ansi := TMsgBaseAnsi.Create(TBBSCore(Owner), False);
+
+ Assign (AFile, FN);
+ ioReset (AFile, 1, fmReadWrite + fmDenyNone);
+
+ While Not Eof(AFile) Do Begin
+ ioBlockRead (AFile, Buf, SizeOf(Buf), BufLen);
+ If Ansi.ProcessBuf (Buf, BufLen) Then Break;
+ End;
+
+ Close (AFile);
+
+ TBBSCore(Owner).Term.AllowArrow := True;
+
+ ShowTemplate(Template);
+
+ WinSize := TBBSCore(Owner).Term.ScreenInfo[2].Y - TBBSCore(Owner).Term.ScreenInfo[1].Y + 1;
+
+ If strUpper(strWordGet(3, Data, ';')) = 'END' Then Begin
+ TopLine := Ansi.Lines - WinSize + 1;
+ If TopLine < 1 Then TopLine := 1;
+ End Else
+ TopLine := 1;
+
+ Update;
+
+ While Not TBBSCore(Owner).ShutDown Do Begin
+ Ch := UpCase(GetKey(0));
+
+ If IsArrow Then Begin
+ Case Ch of
+ #71 : If TopLine > 1 Then Begin
+ TopLine := 1;
+ Update;
+ End;
+ #72 : If TopLine > 1 Then Begin
+ Dec (TopLine);
+ Update;
+ End;
+ #73,
+ #75 : If TopLine > 1 Then Begin
+ Dec (TopLine, WinSize);
+ If TopLine < 1 Then TopLine := 1;
+ Update;
+ End;
+ #79 : If TopLine + WinSize <= Ansi.Lines Then Begin
+ TopLine := Ansi.Lines - WinSize + 1;
+ Update;
+ End;
+ #80 : If TopLine + WinSize <= Ansi.Lines Then Begin
+ Inc (TopLine);
+ Update;
+ End;
+ #77,
+ #81 : If TopLine < Ansi.Lines - WinSize Then Begin
+ Inc (TopLine, WinSize);
+ If TopLine + WinSize > Ansi.Lines Then TopLine := Ansi.Lines - WinSize + 1;
+ Update;
+ End;
+ End;
+ End Else
+ If Ch = #27 Then Break;
+ End;
+
+ Ansi.Free;
+
+ OutRaw(AnsiGotoXY(1, TBBSCore(Owner).User.ThisUser.ScreenSize));
+End;
+*)
+End.
diff --git a/mystic/bbs_io.pas b/mystic/bbs_io.pas
new file mode 100644
index 0000000..5d24c49
--- /dev/null
+++ b/mystic/bbs_io.pas
@@ -0,0 +1,1824 @@
+Unit BBS_IO;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ {$IFDEF WINDOWS}
+ Windows,
+ WinSock2,
+ {$ENDIF}
+ m_Types,
+ m_DateTime,
+ m_FileIO,
+ m_Strings,
+ m_Term_Ansi,
+ bbs_Common;
+
+Const
+ TBBSIOBufferSize = 4 * 1024 - 1;
+ MaxPromptInfo = 15;
+
+Type
+ TBBSIO = Class
+ Core : Pointer;
+ Term : TTermAnsi;
+ ScreenInfo : Array[0..9] of Record X, Y, A : Byte; End;
+ PromptInfo : Array[1..MaxPromptInfo] of String[89];
+ FmtString : Boolean;
+ FmtLen : Byte;
+ FmtType : Byte;
+ InMacro : Boolean;
+ InMacroPos : Byte;
+ InMacroStr : String;
+ BaudEmulator : Byte;
+ AllowPause : Boolean;
+ AllowMCI : Boolean;
+ LocalInput : Boolean;
+ AllowArrow : Boolean;
+ IsArrow : Boolean;
+ UseInField : Boolean;
+ UseInLimit : Boolean;
+ UseInSize : Boolean;
+ InLimit : Byte;
+ InSize : Byte;
+ AllowAbort : Boolean;
+ Aborted : Boolean;
+ NoFile : Boolean;
+ Graphics : Byte;
+ PausePtr : Byte;
+ InputData : Array[1..mysMaxInputHistory] of String[255];
+ LastMCIValue : String;
+ InputPos : Byte;
+
+ {$IFDEF WINDOWS}
+ OutBuffer : Array[0..TBBSIOBufferSize] of Char;
+ OutBufPos : SmallInt;
+ SocketEvent : THandle;
+ {$ENDIF}
+
+ Constructor Create (Var Owner: Pointer);
+ Destructor Destroy; Override;
+
+ Procedure BufAddChar (Ch: Char);
+ Procedure BufAddStr (Str: String);
+ Procedure BufFlush;
+ Function ParseMCI (Display : Boolean; Code: String) : Boolean;
+ Function StrMci (Str: String) : String;
+ Function Attr2Ansi (Attr: Byte) : String;
+ Function Pipe2Ansi (Color : Byte) : String;
+ Procedure AnsiGotoXY (X : Byte; Y:Byte);
+ Procedure AnsiMoveX (X : Byte);
+ Procedure AnsiMoveY (Y : Byte);
+ Procedure AnsiColor (A : Byte);
+ Procedure AnsiClear;
+ Procedure AnsiClrEOL;
+ Procedure OutPipe (Str: String);
+ Procedure OutPipeLn (Str: String);
+ Procedure OutRaw (Str : String);
+ Procedure OutRawLn (Str: String);
+ Procedure OutBS (Num : Byte; Del: Boolean);
+ Procedure OutFull (Str : String);
+ Procedure OutFullLn (Str : String);
+ Procedure OutFile (FName : String; DoPause: Boolean; Speed: Byte);
+ Function OutYN (Y : Boolean) : String;
+ Function OutON (O : Boolean) : String;
+ Procedure PauseScreen;
+ Function MorePrompt : Char;
+ Function DrawPercent (Bar : PercentRec; Part, Whole : SmallInt; Var Percent : SmallInt) : String;
+ Function GetInput (Field, Max, Mode: Byte; Default : String) : String;
+ Function InXY (X, Y, Field, Max, Mode: Byte; Default: String) : String;
+ Function InKey : Char;
+ Function GetYNL (Str: String; Yes: Boolean) : Boolean;
+ Function GetKey : Char;
+ Function GetYN (Str: String; Yes: Boolean) : Boolean;
+ Function GetPW (Str : String; BadStr : String; PW : String) : Boolean;
+ Function OneKey (Str: String; Echo: Boolean) : Char;
+ Procedure RemoteRestore (Var Image: TConsoleImageRec);
+ Procedure PurgeInputBuffer;
+
+ {$IFDEF WINDOWS}
+ Procedure LocalScreenDisable;
+ Procedure LocalScreenEnable;
+ {$ENDIF}
+ End;
+
+Implementation
+
+Uses
+ DOS,
+ bbs_Core,
+ bbs_General;
+
+Constructor TBBSIO.Create (Var Owner: Pointer);
+Begin
+ Core := Owner;
+ FmtString := False;
+ FmtLen := 0;
+ FmtType := 0;
+ InMacro := False;
+ InMacroPos := 0;
+ InMacroStr := '';
+ AllowPause := False;
+ AllowMCI := True;
+ LocalInput := False;
+ AllowArrow := False;
+ IsArrow := False;
+ UseInField := True;
+ UseInLimit := False;
+ UseInSize := False;
+ InLimit := 0;
+ InSize := 0;
+ AllowAbort := False;
+ Aborted := False;
+ NoFile := False;
+ Graphics := 1;
+ PausePtr := 1;
+ LastMCIValue := '';
+ InputPos := 0;
+
+ {$IFDEF WINDOWS}
+ FillChar(OutBuffer, SizeOf(OutBuffer), 0);
+ OutBufPos := 0;
+
+ If Not TBBSCore(Core).LocalMode Then
+ SocketEvent := WSACreateEvent;
+ {$ENDIF}
+
+ Term := TTermAnsi.Create(Screen);
+End;
+
+Destructor TBBSIO.Destroy;
+Begin
+ {$IFDEF WINDOWS}
+ If Not TBBSCore(Core).LocalMode Then WSACloseEvent(SocketEvent);
+ {$ENDIF}
+
+ Term.Free;
+
+ Inherited Destroy;
+End;
+
+Procedure TBBSIO.BufAddChar (Ch: Char);
+Begin
+ {$IFDEF WINDOWS}
+ OutBuffer[OutBufPos] := Ch;
+
+ Inc (OutBufPos);
+
+ If OutBufPos = TBBSIOBufferSize Then BufFlush;
+ {$ENDIF}
+
+ Term.Process(Ch);
+End;
+
+Procedure TBBSIO.BufAddStr (Str: String);
+Var
+ Count : Word;
+Begin
+ For Count := 1 to Length(Str) Do
+ BufAddChar(Str[Count]);
+End;
+
+Procedure TBBSIO.BufFlush;
+Var
+ Res : LongInt;
+Begin
+ {$IFDEF WINDOWS}
+ If OutBufPos > 0 Then Begin
+ If Not TBBSCore(Core).LocalMode Then Begin
+ Res := TBBSCore(Core).Client.WriteBuf(OutBuffer, OutBufPos);
+
+ While (Res = -1) and (WSAGetLastError = EWOULDBLOCK) Do Begin
+ WaitMS(10);
+ Res := TBBSCore(Core).Client.WriteBuf(OutBuffer, OutBufPos);
+ End;
+ End;
+
+ OutBufPos := 0;
+ End;
+ {$ENDIF}
+
+ {$IFDEF UNIX}
+ Screen.BufFlush;
+ {$ENDIF}
+End;
+
+Procedure TBBSIO.AnsiMoveY (Y : Byte);
+Var
+ T : Byte;
+Begin
+ If Graphics = 0 Then Exit;
+
+ T := Screen.CursorY;
+
+ If Y > T Then BufAddStr (#27 + '[' + strI2S(Y-T) + 'B') Else
+ If Y < T Then BufAddStr (#27 + '[' + strI2S(T-Y) + 'A');
+End;
+
+Procedure TBBSIO.AnsiMoveX (X : Byte);
+Var
+ T : Byte;
+Begin
+ If Graphics = 0 Then Exit;
+
+ T := Screen.CursorX;
+
+ If X > T Then BufAddStr (#27 + '[' + strI2S(X-T) + 'C') Else
+ If X < T Then BufAddStr (#27 + '[' + strI2S(T-X) + 'D');
+End;
+
+Procedure TBBSIO.PauseScreen;
+Var
+ Attr : Byte;
+ Ch : Char;
+Begin
+ Attr := Screen.TextAttr;
+
+ OutFull (TBBSCore(Core).GetPrompt(22));
+
+ PurgeInputBuffer;
+
+ Repeat
+ Ch := GetKey;
+ Until Ch <> '';
+
+ AnsiColor(Attr);
+
+ BufAddStr(#13#10);
+End;
+
+Function TBBSIO.MorePrompt : Char;
+Var
+ SavedAttr : Byte;
+ SavedMCI : Boolean;
+ Ch : Char;
+Begin
+ SavedMCI := AllowMCI;
+ AllowMCI := True;
+ SavedAttr := Screen.TextAttr;
+
+ OutFull (TBBSCore(Core).GetPrompt(132));
+
+ PurgeInputBuffer;
+
+ Ch := OneKey('YNC' + #13, False);
+
+ OutBS(Screen.CursorX, True);
+ AnsiColor(SavedAttr);
+
+ PausePtr := 1;
+ AllowMCI := SavedMCI;
+ Result := Ch;
+End;
+
+Procedure TBBSIO.OutBS (Num: Byte; Del: Boolean);
+Var
+ A : Byte;
+ Str : String[7];
+Begin
+ If Del Then Str := #8#32#8 Else Str := #8;
+
+ For A := 1 to Num Do
+ OutRaw (Str);
+End;
+
+Procedure TBBSIO.OutPipe (Str: String);
+Var
+ Count : Byte;
+ Code : String[2];
+Begin
+ If FmtString Then Begin
+ FmtString := False;
+ Case FmtType of
+ 1 : Str := strPadR(Str, FmtLen + Length(Str) - Length(strStripPipe(Str)), ' ');
+ 2 : Str := strPadL(Str, FmtLen + Length(Str) - Length(strStripPipe(Str)), ' ');
+ 3 : Str := strPadC(Str, FmtLen + Length(Str) - Length(strStripPipe(Str)), ' ');
+ End;
+ End;
+
+ Count := 1;
+
+ While Count <= Length(Str) Do Begin
+ If (Str[Count] = '|') and (Count < Length(Str) - 1) Then Begin
+ Code := Copy(Str, Count + 1, 2);
+ If Code = '00' Then BufAddStr(Pipe2Ansi(0)) Else
+ If Code = '01' Then BufAddStr(Pipe2Ansi(1)) Else
+ If Code = '02' Then BufAddStr(Pipe2Ansi(2)) Else
+ If Code = '03' Then BufAddStr(Pipe2Ansi(3)) Else
+ If Code = '04' Then BufAddStr(Pipe2Ansi(4)) Else
+ If Code = '05' Then BufAddStr(Pipe2Ansi(5)) Else
+ If Code = '06' Then BufAddStr(Pipe2Ansi(6)) Else
+ If Code = '07' Then BufAddStr(Pipe2Ansi(7)) Else
+ If Code = '08' Then BufAddStr(Pipe2Ansi(8)) Else
+ If Code = '09' Then BufAddStr(Pipe2Ansi(9)) Else
+ If Code = '10' Then BufAddStr(Pipe2Ansi(10)) Else
+ If Code = '11' Then BufAddStr(Pipe2Ansi(11)) Else
+ If Code = '12' Then BufAddStr(Pipe2Ansi(12)) Else
+ If Code = '13' Then BufAddStr(Pipe2Ansi(13)) Else
+ If Code = '14' Then BufAddStr(Pipe2Ansi(14)) Else
+ If Code = '15' Then BufAddStr(Pipe2Ansi(15)) Else
+ If Code = '16' Then BufAddStr(Pipe2Ansi(16)) Else
+ If Code = '17' Then BufAddStr(Pipe2Ansi(17)) Else
+ If Code = '18' Then BufAddStr(Pipe2Ansi(18)) Else
+ If Code = '19' Then BufAddStr(Pipe2Ansi(19)) Else
+ If Code = '20' Then BufAddStr(Pipe2Ansi(20)) Else
+ If Code = '21' Then BufAddStr(Pipe2Ansi(21)) Else
+ If Code = '22' Then BufAddStr(Pipe2Ansi(22)) Else
+ If Code = '23' Then BufAddStr(Pipe2Ansi(23)) Else
+ BufAddStr(Str[Count] + Code);
+ Inc (Count, 2);
+ End Else
+ BufAddChar(Str[Count]);
+
+ Inc (Count);
+ End;
+End;
+
+Procedure TBBSIO.OutPipeLn (Str : String);
+Begin
+ OutPipe (Str + #13#10);
+ Inc (PausePtr);
+End;
+
+Procedure TBBSIO.OutRaw (Str: String);
+Begin
+ If FmtString Then Begin
+ FmtString := False;
+ Case FmtType of
+ 1 : Str := strPadR(Str, FmtLen, ' ');
+ 2 : Str := strPadL(Str, FmtLen, ' ');
+ 3 : Str := strPadC(Str, FmtLen, ' ');
+ End;
+ End;
+
+ BufAddStr(Str);
+End;
+
+Procedure TBBSIO.OutRawLn (Str: String);
+Begin
+ BufAddStr (Str + #13#10);
+ Inc (PausePtr);
+End;
+
+Function TBBSIO.ParseMCI (Display: Boolean; Code: String) : Boolean;
+Var
+ A : LongInt;
+Begin
+ LastMCIValue := #255;
+ Result := True;
+
+ If Not AllowMCI Then Begin
+ Result := False;
+ Exit;
+ End;
+
+ Case Code[1] of
+ '!' : Begin
+ A := strS2I(Code[2]);
+ ScreenInfo[A].X := Screen.CursorX;
+ ScreenInfo[A].Y := Screen.CursorY;
+ ScreenInfo[A].A := Screen.TextAttr;
+ End;
+ '$' : Case Code[2] of
+ 'C' : Begin
+ FmtString := True;
+ FmtType := 3;
+ End;
+ 'D' : Begin
+ FmtString := True;
+ FmtType := 4;
+ End;
+ 'L' : Begin
+ FmtString := True;
+ FmtType := 2;
+ End;
+ 'R' : Begin
+ FmtString := True;
+ FmtType := 1;
+ End;
+ End;
+ '&' : Case Code[2] of
+ '1' : LastMCIValue := PromptInfo[1];
+ '2' : LastMCIValue := PromptInfo[2];
+ '3' : LastMCIValue := PromptInfo[3];
+ '4' : LastMCIValue := PromptInfo[4];
+ '5' : LastMCIValue := PromptInfo[5];
+ '6' : LastMCIValue := PromptInfo[6];
+ '7' : LastMCIValue := PromptInfo[7];
+ '8' : LastMCIValue := PromptInfo[8];
+ '9' : LastMCIValue := PromptInfo[9];
+ '0' : LastMCIValue := PromptInfo[10];
+ 'A' : LastMCIValue := PromptInfo[11];
+ 'B' : LastMCIValue := PromptInfo[12];
+ 'C' : LastMCIValue := PromptInfo[13];
+ 'D' : LastMCIValue := PromptInfo[14];
+ 'E' : LastMCIValue := PromptInfo[15];
+ End;
+ '0' : Case Code[2] of
+ '0' : LastMCIValue := Pipe2Ansi(0);
+ '1' : LastMCIValue := Pipe2Ansi(1);
+ '2' : LastMCIValue := Pipe2Ansi(2);
+ '3' : LastMCIValue := Pipe2Ansi(3);
+ '4' : LastMCIValue := Pipe2Ansi(4);
+ '5' : LastMCIValue := Pipe2Ansi(5);
+ '6' : LastMCIValue := Pipe2Ansi(6);
+ '7' : LastMCIValue := Pipe2Ansi(7);
+ '8' : LastMCIValue := Pipe2Ansi(8);
+ '9' : LastMCIValue := Pipe2Ansi(9);
+ End;
+ '1' : Case Code[2] of
+ '0' : LastMCIValue := Pipe2Ansi(10);
+ '1' : LastMCIValue := Pipe2Ansi(11);
+ '2' : LastMCIValue := Pipe2Ansi(12);
+ '3' : LastMCIValue := Pipe2Ansi(13);
+ '4' : LastMCIValue := Pipe2Ansi(14);
+ '5' : LastMCIValue := Pipe2Ansi(15);
+ '6' : LastMCIValue := Pipe2Ansi(16);
+ '7' : LastMCIValue := Pipe2Ansi(17);
+ '8' : LastMCIValue := Pipe2Ansi(18);
+ '9' : LastMCIValue := Pipe2Ansi(19);
+ End;
+ '2' : Case Code[2] of
+ '0' : LastMCIValue := Pipe2Ansi(20);
+ '1' : LastMCIValue := Pipe2Ansi(21);
+ '2' : LastMCIValue := Pipe2Ansi(22);
+ '3' : LastMCIValue := Pipe2Ansi(23);
+ End;
+ 'A' : Case Code[2] of
+ 'G' : LastMCIValue := strI2S(DaysAgo(TBBSCore(Core).User.ThisUser.Birthday) DIV 365);
+ 'S' : LastMCIValue := OutON(TBBSCore(Core).User.ThisUser.SigUse);
+ 'V' : LastMCIValue := OutYN(Chat.Available);
+ End;
+ 'B' : Case Code[2] of
+ 'D' : If TBBSCore(Core).LocalMode Then
+ LastMCIValue := 'LOCAL' {++lang add these to lang file }
+ Else
+ LastMCIValue := 'TELNET'; {++lang }
+ 'E' : LastMCIValue := ^G;
+ 'I' : LastMCIValue := DateJulian2Str(TBBSCore(Core).User.ThisUser.Birthday, TBBSCore(Core).User.ThisUser.DateType);
+ 'N' : LastMCIValue := Config.BBSName;
+ 'S' : OutBS(1, True);
+ End;
+ 'C' : Case Code[2] of
+ 'L' : AnsiClear;
+ 'M' : LastMCIValue := OutON(TBBSCore(Core).User.ThisUser.UseFullChat);
+ 'R' : OutRawLn ('');
+ 'S' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.Calls);
+ 'T' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.CallsToday);
+ End;
+ 'D' : Case Code[2] of
+ 'A' : LastMCIValue := DateDos2Str(CurDateDos, TBBSCore(Core).User.ThisUser.DateType);
+ 'E' : Begin
+ BufFlush;
+ WaitMS(500);
+ End;
+ 'F' : Begin
+ FmtString := True;
+ FmtType := 5;
+ End;
+ 'I' : Begin
+ FmtString := True;
+ FmtType := 16;
+ End;
+ 'K' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.DLk);
+ 'L' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.DLs);
+ 'T' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.DLsToday);
+ End;
+ 'F' : Case Code[2] of
+ 'B' : LastMCIValue := TBBSCore(Core).FileBase.FBase.Name;
+ 'G' : LastMCIValue := TBBSCore(Core).FileBase.FGroup.Name;
+ 'K' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.ULk);
+ 'O' : LastMCIValue := DateDos2Str(TBBSCore(Core).User.ThisUser.FirstOn, TBBSCore(Core).User.ThisUser.DateType);
+ 'U' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.ULs);
+ End;
+ 'H' : Case Code[2] of
+ 'K' : LastMCIValue := OutON(TBBSCore(Core).User.ThisUser.HotKeys);
+ End;
+ 'I' : Case Code[2] of
+ 'F' : UseInField := False;
+ 'N' : Begin
+ FmtString := True;
+ FmtType := 12;
+ End;
+ 'L' : LastMCIValue := OutON(Chat.Invisible);
+ 'S' : Begin
+ FmtString := True;
+ FmtType := 14;
+ End;
+ End;
+ 'K' : Case Code[2] of
+ 'T' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.DLkToday);
+ End;
+ 'L' : Case Code[2] of
+ 'O' : LastMCIValue := DateDos2Str(TBBSCore(Core).User.ThisUser.LastOn, TBBSCore(Core).User.ThisUser.DateType);
+ End;
+ 'M' : Case Code[2] of
+ 'B' : LastMCIValue := TBBSCore(Core).Msgs.MBase.Name;
+ 'E' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.Emails);
+ 'G' : LastMCIValue := TBBSCore(Core).Msgs.Group.Name;
+ 'L' : LastMCIValue := OutON(TBBSCore(Core).User.ThisUser.UseLBIndex);
+ 'N' : LastMCIValue := Config.NetDesc[TBBSCore(Core).Msgs.MBase.NetAddr];
+ 'P' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.Posts);
+ 'T' : LastMCIValue := strI2S(TBBSCore(Core).Msgs.GetTotalMessages(TBBSCore(Core).Msgs.MBase));
+ End;
+ 'N' : Case Code[2] of
+ 'D' : LastMCIValue := strI2S(TBBSCore(Core).NodeNum);
+ 'E' : LastMCIValue := strI2S(TBBSCore(Core).MinutesUntilEvent(TBBSCore(Core).NextEvent.ExecTime));
+ End;
+ 'O' : Case Code[2] of
+ 'S' : LastMCIValue := OSID;
+ End;
+ 'P' : Case Code[2] of
+ 'A' : PauseScreen;
+ 'B' : PurgeInputBuffer;
+ 'C' : Begin
+ A := 0;
+ If TBBSCore(Core).User.ThisUser.Calls > 0 Then
+ A := Round(TBBSCore(Core).User.ThisUser.Posts / TBBSCore(Core).User.ThisUser.Calls * 100);
+ LastMCIValue := strI2S(A);
+ End;
+ 'I' : BufAddChar('|');
+ 'N' : Repeat Until GetKey <> '';
+ 'O' : AllowPause := False;
+ 'W' : LastMCIValue := strI2S(Config.PWChange);
+ End;
+ 'Q' : Case Code[2] of
+ 'A' : LastMCIValue := TBBSCore(Core).User.ThisUser.Archive;
+ 'L' : LastMCIValue := OutYN (TBBSCore(Core).User.ThisUser.QwkFiles);
+ 'O' : Display_Quote;
+ End;
+ 'R' : Case Code[2] of
+ 'D' : LastMCIValue := strI2S(TBBSCore(Core).User.Security.DLRatio);
+ 'K' : LastMCIValue := strI2S(TBBSCore(Core).User.Security.DLkRatio);
+ 'P' : Begin
+ FmtString := True;
+ FmtType := 13;
+ End;
+ End;
+ 'S' : Case Code[2] of
+ 'B' : LastMCIValue := strI2S(TBBSCore(Core).User.Security.MaxTB);
+ 'C' : LastMCIValue := strI2S(TBBSCore(Core).User.Security.MaxCalls);
+ 'D' : LastMCIValue := TBBSCore(Core).User.Security.Desc;
+ 'K' : LastMCIValue := strI2S(TBBSCore(Core).User.Security.MaxDLK);
+ 'L' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.Security);
+ 'N' : LastMCIValue := Config.SysopName;
+ 'P' : Begin
+ A := Round(TBBSCore(Core).User.Security.PCRatio / 100 * 100);
+ LastMCIValue := strI2S(A);
+ End;
+ 'T' : LastMCIValue := strI2S(TBBSCore(Core).User.Security.Time);
+ 'X' : LastMCIValue := strI2S(TBBSCore(Core).User.Security.MaxDLs);
+ End;
+ 'T' : Case Code[2] of
+ 'B' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.TimeBank);
+ 'C' : LastMCIValue := strI2S(Config.SystemCalls);
+ 'E' : If Graphics = 1 Then LastMCIValue := 'Ansi' Else LastMCIValue := 'Ascii'; //++lang
+ 'I' : LastMCIValue := TimeDos2Str(CurDateDos, True);
+ 'L' : LastMCIValue := strI2S(TBBSCore(Core).TimeLeft);
+ 'O' : LastMCIValue := strI2S(TBBSCore(Core).ElapsedTime);
+ End;
+ 'U' : Case Code[2] of
+ '#' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.PermIdx);
+ '1' : LastMCIValue := TBBSCore(Core).User.ThisUser.Optional[1];
+ '2' : LastMCIValue := TBBSCore(Core).User.ThisUser.Optional[2];
+ '3' : LastMCIValue := TBBSCore(Core).User.ThisUser.Optional[3];
+ 'A' : LastMCIValue := TBBSCore(Core).User.ThisUser.Address;
+ 'B' : Case TBBSCore(Core).User.ThisUser.FileList of
+ 0 : LastMCIValue := 'Normal';
+ 1 : LastMCIValue := 'Lightbar'; {++lang}
+ End;
+ 'C' : LastMCIValue := TBBSCore(Core).User.ThisUser.City;
+ 'D' : LastMCIValue := TBBSCore(Core).User.ThisUser.DataPhone;
+ 'E' : Case TBBSCore(Core).User.ThisUser.EditType of
+ 0 : LastMCIValue := 'Line'; {++lang}
+ 1 : LastMCIValue := 'Full';
+ 2 : LastMCIValue := 'Ask';
+ End;
+ 'F' : LastMCIValue := DateTypeStr[TBBSCore(Core).User.ThisUser.DateType];
+ 'G' : If TBBSCore(Core).User.ThisUser.Gender = 'M' Then
+ LastMCIValue := 'Male'
+ Else
+ LastMCIValue := 'Female'; {++lang}
+ 'H' : LastMCIValue := TBBSCore(Core).User.ThisUser.Handle;
+ 'I' : LastMCIValue := TBBSCore(Core).User.ThisUser.UserInfo;
+ 'J' : Case TBBSCore(Core).User.ThisUser.MReadType of
+ 0 : LastMCIValue := 'Normal';
+ 1 : LastMCIValue := 'Lightbar'; {++lang}
+ End;
+ 'K' : LastMCIValue := TBBSCore(Core).User.ThisUser.Email;
+ 'L' : LastMCIValue := TBBSCore(Core).Lang.Desc;
+ 'M' : LastMCIValue := OutON(TBBSCore(Core).User.ThisUser.UseLBMIdx);
+ 'N' : LastMCIValue := TBBSCore(Core).User.ThisUser.RealName;
+ 'P' : LastMCIValue := TBBSCore(Core).User.ThisUser.HomePhone;
+ 'Q' : Case TBBSCore(Core).User.ThisUser.UseLBQuote of
+ False : LastMCIValue := 'Standard';
+ True : LastMCIValue := 'Lightbar'; {++langfile++}
+ End;
+ 'S' : LastMCIValue := strI2S(TBBSCore(Core).User.ThisUser.ScreenSize);
+ 'X' : LastMCIValue := TBBSCore(Core).UserHostInfo;
+ 'Y' : LastMCIValue := TBBSCore(Core).UserIPInfo;
+ 'Z' : LastMCIValue := TBBSCore(Core).User.ThisUser.ZipCode;
+ End;
+ 'V' : Case Code[2] of
+ 'R' : LastMCIValue := mysVersion;
+ End;
+ 'X' : Case Code[2] of
+ 'D' : If DateValid(Session.User.ThisUser.Expires) Then
+ LastMCIValue := strI2S(Abs(CurDateJulian - DateStr2Julian(Session.User.ThisUser.Expires)))
+ Else
+ LastMCIValue := '0';
+ 'S' : LastMCIValue := strI2S(Session.User.ThisUser.ExpiresTo);
+ 'X' : LastMCIValue := '';
+ End;
+ '[' : Case Code[2] of
+ 'A' : Begin
+ FmtString := True;
+ FmtType := 8;
+ End;
+ 'B' : Begin
+ FmtString := True;
+ FmtType := 9;
+ End;
+ 'C' : Begin
+ FmtString := True;
+ FmtType := 10;
+ End;
+ 'D' : Begin
+ FmtString := True;
+ FmtType := 11;
+ End;
+ 'K' : AnsiClrEOL;
+ 'L' : Begin
+ FmtString := True;
+ FmtType := 15;
+ End;
+ 'X' : Begin
+ FmtString := True;
+ FmtType := 6;
+ End;
+ 'Y' : Begin
+ FmtString := True;
+ FmtType := 7;
+ End;
+ End;
+ Else
+ Result := False;
+ End;
+
+ If Display And (LastMCIValue <> #255) Then
+ OutPipe(LastMCIValue);
+End;
+
+Procedure TBBSIO.OutFull (Str : String);
+Var
+ A : Byte;
+ B : Byte;
+ D : DirStr;
+ N : NameStr;
+ E : ExtStr;
+Begin
+ A := 1;
+
+ While A <= Length(Str) Do Begin
+ If (Str[A] = '|') and (A < Length(Str) - 1) Then Begin
+
+ If Not ParseMCI (True, Copy(Str, A + 1, 2)) Then Begin
+ BufAddChar(Str[A]);
+ Inc(A);
+ Continue;
+ End;
+
+ Inc (A, 2);
+
+ If FmtString Then Begin
+ If FmtType = 5 Then Begin
+ FmtString := False;
+
+ B := A + 1;
+
+ While (Str[B] <> ' ') and (Str[B] <> '|') and (B <= Length(Str)) Do
+ Inc (B);
+
+ FSplit (strStripLOW(Copy(Str, A + 1, B - A - 1)), D, N, E);
+ OutFile (TBBSCore(Core).Lang.TextPath + N + E, True, 0);
+
+ A := B;
+
+ Continue;
+ End;
+
+ FmtLen := strS2I(Copy(Str, A + 1, 2));
+ Inc (A, 2);
+
+ Case FmtType of
+ 4 : Begin
+ Inc (A);
+ FmtString := False;
+ BufAddStr (strRep(Str[A], FmtLen));
+ End;
+ 6 : Begin
+ AnsiMoveX (FmtLen);
+ FmtString := False;
+ End;
+ 7 : Begin
+ AnsiMoveY (FmtLen);
+ FmtString := False;
+ End;
+ 8 : Begin
+ AnsiMoveY (Screen.CursorY - FmtLen);
+ FmtString := False;
+ End;
+ 9 : Begin
+ AnsiMoveY (Screen.CursorY + FmtLen);
+ FmtString := False;
+ End;
+ 10: Begin
+ AnsiMoveX (Screen.CursorX + FmtLen);
+ FmtString := False;
+ End;
+ 11: Begin
+ AnsiMoveX (Screen.CursorX - FmtLen);
+ FmtString := False;
+ End;
+ 12: Begin
+ UseInLimit := True;
+ InLimit := FmtLen;
+ FmtString := False;
+ End;
+ 13: Begin
+ PausePtr := FmtLen;
+ FmtString := False;
+ End;
+ 14: Begin
+ UseInSize := True;
+ InSize := FmtLen;
+ FmtString := False;
+ End;
+ 15: Begin
+ While Screen.CursorX > FmtLen Do
+ OutBS(1, True);
+
+ FmtString := False;
+ End;
+ End;
+ End;
+ End Else
+ BufAddChar (Str[A]);
+
+ Inc(A);
+ End;
+End;
+
+Procedure TBBSIO.OutFullLn (Str : String);
+Begin
+ OutFull (Str + #13#10);
+ Inc (PausePtr);
+End;
+
+Procedure TBBSIO.AnsiClrEOL;
+Begin
+ BufAddStr (#27 + '[K');
+End;
+
+Function TBBSIO.Pipe2Ansi (Color: Byte) : String;
+Var
+ CurFG : Byte;
+ CurBG : Byte;
+ Prefix : String[2];
+Begin
+ Result := '';
+
+ If Graphics = 0 Then Exit;
+
+ CurBG := (Screen.TextAttr SHR 4) AND 7;
+ CurFG := Screen.TextAttr AND $F;
+ Prefix := '';
+
+ If Color < 16 Then Begin
+ If Color = CurFG Then Exit;
+
+// Screen.TextAttr := Color + CurBG * 16;
+
+ If (Color < 8) and (CurFG > 7) Then Prefix := '0;';
+ If (Color > 7) and (CurFG < 8) Then Prefix := '1;';
+ If Color > 7 Then Dec(Color, 8);
+
+ Case Color of
+ 00: Result := #27 + '[' + Prefix + '30';
+ 01: Result := #27 + '[' + Prefix + '34';
+ 02: Result := #27 + '[' + Prefix + '32';
+ 03: Result := #27 + '[' + Prefix + '36';
+ 04: Result := #27 + '[' + Prefix + '31';
+ 05: Result := #27 + '[' + Prefix + '35';
+ 06: Result := #27 + '[' + Prefix + '33';
+ 07: Result := #27 + '[' + Prefix + '37';
+ End;
+
+ If Prefix <> '0;' Then
+ Result := Result + 'm'
+ Else
+ Case CurBG of
+ 00: Result := Result + ';40m';
+ 01: Result := Result + ';44m';
+ 02: Result := Result + ';42m';
+ 03: Result := Result + ';46m';
+ 04: Result := Result + ';41m';
+ 05: Result := Result + ';45m';
+ 06: Result := Result + ';43m';
+ 07: Result := Result + ';47m';
+ End;
+ End Else Begin
+ If (Color - 16) = CurBG Then Exit;
+
+// Screen.TextAttr := CurFG + (Color - 16) * 16;
+
+ Case Color of
+ 16: Result := #27 + '[40m';
+ 17: Result := #27 + '[44m';
+ 18: Result := #27 + '[42m';
+ 19: Result := #27 + '[46m';
+ 20: Result := #27 + '[41m';
+ 21: Result := #27 + '[45m';
+ 22: Result := #27 + '[43m';
+ 23: Result := #27 + '[47m';
+ End;
+ End;
+End;
+
+Function TBBSIO.Attr2Ansi (Attr: Byte) : String;
+Begin
+ Result := '';
+
+ If Graphics = 0 Then Exit;
+
+ Result := Pipe2Ansi(Attr AND $F) + Pipe2Ansi(((Attr SHR 4) AND 7) + 16);
+End;
+
+Procedure TBBSIO.AnsiColor (A : Byte);
+Begin
+ If Graphics = 0 Then Exit;
+
+ BufAddStr(Attr2Ansi(A));
+End;
+
+Procedure TBBSIO.AnsiGotoXY (X: Byte; Y: Byte);
+Begin
+ If Graphics = 0 Then Exit;
+
+ If X = 0 Then X := Screen.CursorX;
+ If Y = 0 Then Y := Screen.CursorY;
+
+ BufAddStr (#27'[' + strI2S(Y) + ';' + strI2S(X) + 'H');
+End;
+
+Procedure TBBSIO.AnsiClear;
+Begin
+ If Graphics > 0 Then
+ BufAddStr (#27 + '[2J')
+ Else
+ BufAddChar (#12);
+
+ PausePtr := 1;
+End;
+
+Function TBBSIO.OutYN (Y: Boolean) : String;
+Begin
+ If Y Then OutYN := 'Yes' Else OutYN := 'No'; {++lang?}
+End;
+
+Function TBBSIO.OutON (O : Boolean) : String;
+Begin
+ If O Then OutON := 'On' Else OutON := 'Off'; {++lang}
+End;
+
+Procedure TBBSIO.OutFile (FName : String; DoPause: Boolean; Speed: Byte);
+Var
+ Buffer : Array[1..4096] of Char;
+ BufPos : LongInt;
+ BufSize : LongInt;
+ dFile : File;
+ Ext : String[4];
+ Code : String[2];
+ Old : Boolean;
+ Str : String;
+ Ch : Char;
+ Done : Boolean;
+
+ Function GetChar : Char;
+ Begin
+ If BufPos = BufSize Then Begin
+ BlockRead (dFile, Buffer, SizeOf(Buffer), BufSize);
+
+ BufPos := 0;
+
+ If BufSize = 0 Then Begin
+ Done := True;
+ Buffer[1] := #26;
+ End;
+ End;
+
+ Inc (BufPos);
+
+ Result := Buffer[BufPos];
+ End;
+
+Begin
+ If Pos(PathChar, FName) = 0 Then
+ FName := TBBSCore(Core).Lang.TextPath + FName;
+
+ If Pos('.', FName) > 0 Then
+ Ext := ''
+ Else
+ If (Graphics = 1) and (FileExist(FName + '.ans')) Then
+ Ext := '.ans'
+ Else
+ Ext := '.asc';
+
+ If FileExist(FName + Copy(Ext, 1, 3) + '1') Then Begin
+ Repeat
+ BufPos := Random(9);
+ If BufPos = 0 Then
+ Code := Ext[Length(Ext)]
+ Else
+ Code := strI2S(BufPos);
+ Until FileExist(FName + Copy(Ext, 1, 3) + Code);
+
+ Ext := Copy(Ext, 1, 3) + Code;
+ End;
+
+ Assign (dFile, FName + Ext);
+ {$I-} Reset(dFile, 1); {$I+}
+ If IoResult <> 0 Then Begin
+ NoFile := True;
+ Exit;
+ End;
+
+ NoFile := False;
+ Old := AllowPause;
+ AllowPause := DoPause;
+ PausePtr := 1;
+ Done := False;
+ BufPos := 0;
+ BufSize := 0;
+ Ch := #0;
+ BaudEmulator := Speed;
+
+ While Not Done Do Begin
+ Ch := GetChar;
+
+ If BaudEmulator > 0 Then Begin
+ BufFlush;
+
+ If BufPos MOD BaudEmulator = 0 Then WaitMS(6);
+ End;
+
+ Case Ch of
+ #10 : Begin
+ BufAddChar (#10);
+ Inc (PausePtr);
+
+ If (PausePtr = TBBSCore(Core).User.ThisUser.ScreenSize) and (AllowPause) Then
+ Case MorePrompt of
+ 'N' : Break;
+ 'C' : AllowPause := False;
+ End;
+ End;
+ #26 : Break;
+ '|' : Begin
+ Code := GetChar;
+ Code := Code + GetChar;
+
+ If Not ParseMCI(True, Code) Then Begin
+ BufAddStr('|' + Code);
+ Continue;
+ End;
+
+ If FmtString Then Begin
+ If FmtType = 5 Then Begin
+ FmtString := False;
+ Str := '';
+
+ While Not Done Do Begin
+ Ch := GetChar;
+ If Ch in [#10, '|'] Then Break;
+ Str := Str + GetChar;
+ End;
+
+ OutFile (TBBSCore(Core).Lang.TextPath + strStripLOW(Str), True, 0);
+
+ Continue;
+ End;
+
+ Code := GetChar;
+ Code := Code + GetChar;
+ FmtLen := strS2I(Code);
+
+ Case FmtType of
+ 4 : Begin
+ BufAddStr (strRep(GetChar, FmtLen));
+ FmtString := False;
+ End;
+ 6 : Begin
+ AnsiMoveX (FmtLen);
+ FmtString := False;
+ End;
+ 7 : Begin
+ AnsiMoveY (FmtLen);
+ FmtString := False;
+ End;
+ 8 : Begin
+ AnsiMoveY (Screen.CursorY - FmtLen);
+ FmtString := False;
+ End;
+ 9 : Begin
+ AnsiMoveY (Screen.CursorY + FmtLen);
+ FmtString := False;
+ End;
+ 10: Begin
+ AnsiMoveX (Screen.CursorX + FmtLen);
+ FmtString := False;
+ End;
+ 11: Begin
+ AnsiMoveX (Screen.CursorX - FmtLen);
+ FmtString := False;
+ End;
+ 12: Begin
+ UseInLimit := True;
+ InLimit := FmtLen;
+ FmtString := False;
+ End;
+ 13: Begin
+ PausePtr := FmtLen;
+ FmtString := True;
+ End;
+ 14: Begin
+ UseInSize := True;
+ InSize := FmtLen;
+ FmtString := False;
+ End;
+ 15: Begin
+ While Screen.CursorX > FmtLen Do
+ OutBS(1, True);
+
+ FmtString := False;
+ End;
+ 16: Begin
+ BaudEmulator := FmtLen;
+ FmtString := False;
+ End;
+ End;
+ End;
+ End;
+ Else
+ BufAddChar(Ch);
+ End;
+ End;
+
+ AllowPause := Old;
+ Close (dFile);
+
+ BufFlush;
+End;
+
+{$IFDEF UNIX}
+Function TBBSIO.InKey : Char;
+Begin
+ Result := #1;
+ IsArrow := False;
+
+ If Input.KeyWait(1000) Then Begin
+ Result := Input.ReadKey;
+ LocalInput := True;
+
+ If Result = #0 Then Begin
+ Result := Input.ReadKey;
+
+ If (AllowArrow) and (Result in [#71..#73, #75, #77, #79..#83]) Then Begin
+ IsArrow := True;
+ Exit;
+ End;
+
+ Result := #1;
+ End;
+ End;
+End;
+{$ENDIF}
+
+{$IFDEF WINDOWS}
+Function TBBSIO.InKey : Char;
+Var
+ Handles : Array[0..1] of THandle;
+ InType : Byte;
+Begin
+ Result := #1;
+
+ Handles[0] := Input.ConIn;
+
+ If Not TBBSCore(Core).LocalMode Then Begin
+ Handles[1] := SocketEvent;
+
+ WSAResetEvent (Handles[1]);
+ WSAEventSelect (TBBSCore(Core).Client.FSocketHandle, Handles[1], FD_READ OR FD_CLOSE);
+
+ Case WaitForMultipleObjects(2, @Handles, False, 1000) of
+ WAIT_OBJECT_0 : InType := 1;
+ WAIT_OBJECT_0 + 1 : InType := 2;
+ Else
+ Exit;
+ End;
+ End Else
+ Case WaitForSingleObject (Handles[0], 1000) of
+ WAIT_OBJECT_0 : InType := 1;
+ Else
+ Exit;
+ End;
+
+ Case InType of
+ 1 : Begin // LOCAL input event
+ If Not Input.ProcessQueue Then Exit;
+
+ Result := Input.ReadKey;
+ LocalInput := True;
+ IsArrow := False;
+
+ If Result = #0 Then Begin
+ Result := Input.ReadKey;
+
+ If (AllowArrow) and (Result in [#71..#73, #75, #77, #79..#83]) and (Screen.Active) Then Begin
+ IsArrow := True;
+ Exit;
+ End;
+
+ Process_Sysop_Cmd (Result);
+
+ Result := #1;
+ End;
+
+ If Not Screen.Active Then Result := #1;
+ End;
+ 2 : Begin // SOCKET read event
+ If TBBSCore(Core).Client.ReadBuf(Result, 1) < 0 Then Begin
+ TBBSCore(Core).SystemLog ('User dropped carrier');
+ Halt(0);
+ End;
+
+ LocalInput := False;
+
+ If AllowArrow Then Begin
+ IsArrow := True;
+
+ Case Result of
+ #03 : Result := #81; { pgdn }
+ #04 : Result := #77; { right }
+ #05 : Result := #72; { up }
+ #18 : Result := #73; { pgup }
+ #19 : Result := #75; { left }
+ #24 : Result := #80; { down }
+ #27 : Begin
+ If Not TBBSCore(Core).Client.DataWaiting Then WaitMS(25);
+ If Not TBBSCore(Core).Client.DataWaiting Then WaitMS(25);
+ If TBBSCore(Core).Client.DataWaiting Then Begin
+ If TBBSCore(Core).Client.ReadChar = '[' Then
+ Case TBBSCore(Core).Client.ReadChar of
+ 'A' : Result := #72; { ansi up }
+ 'B' : Result := #80; { ansi down }
+ 'C' : Result := #77; { ansi right }
+ 'D' : Result := #75; { ansi left }
+ 'H' : Result := #71; { ansi home }
+ 'K' : Result := #79; { ansi end }
+ 'V' : Result := #73; { ansi pageup }
+ 'U' : Result := #81; { ansi pgdown }
+ End;
+ End Else
+ IsArrow := False;
+ End;
+ #127: Result := #83; { delete }
+ Else
+ IsArrow := False;
+ End;
+ End;
+ End;
+ End;
+End;
+{$ENDIF}
+
+Function TBBSIO.GetKey : Char;
+Var
+ TimeCount : LongInt;
+ LastSec : LongInt;
+Begin
+ Result := #1;
+
+ TBBSCore(Core).TimeOut := TimerSeconds;
+
+ BufFlush;
+
+ Repeat
+ If LastSec <> TimerSeconds Then Begin
+
+ If GetKeyFunc(False) Then Begin
+ Result := #02;
+ Exit;
+ End;
+
+ LastSec := TimerSeconds;
+
+ If InMacro Then
+ If InMacroPos <= Length(InMacroStr) Then Begin
+ Result := InMacroStr[InMacroPos];
+ Inc (InMacroPos);
+ Exit;
+ End Else
+ InMacro := False;
+
+ If TBBSCore(Core).CheckTimeOut Then
+ If TimerSeconds - TBBSCore(Core).TimeOut >= Config.Inactivity Then Begin
+ TBBSCore(Core).SystemLog('Inactivity timeout');
+ OutFullLn (TBBSCore(Core).GetPrompt(136));
+ Halt(0);
+ End;
+
+ TimeCount := TBBSCore(Core).TimeLeft;
+
+ If TimeCount <> Session.LastTimeLeft Then Begin
+ Session.LastTimeLeft := TimeCount;
+
+ {$IFNDEF UNIX}
+ Update_Status_Line(StatusPtr, '');
+ {$ENDIF}
+
+ If TBBSCore(Core).TimerOn Then Begin
+ If TimeCount = 5 Then Begin
+ If Not TBBSCore(Core).TimeChecked Then Begin
+ TBBSCore(Core).TimeChecked := True;
+ OutFullLn (TBBSCore(Core).GetPrompt(134));
+ End;
+ End Else
+ If TimeCount < 1 Then Begin
+ If Not TBBSCore(Core).TimeChecked Then Begin
+ TBBSCore(Core).TimeChecked := True;
+ OutFullLn (TBBSCore(Core).GetPrompt(135));
+ TBBSCore(Core).SystemLog ('User ran out of time');
+ Halt(0);
+ End;
+ End Else
+ TBBSCore(Core).TimeChecked := False;
+ End;
+
+ If TBBSCore(Core).NextEvent.Active Then
+ If (TBBSCore(Core).MinutesUntilEvent(TBBSCore(Core).NextEvent.ExecTime) = TBBSCore(Core).NextEvent.Warning) And
+ (Not TBBSCore(Core).EventWarn) And (TBBSCore(Core).NextEvent.Forced) Then Begin
+ TBBSCore(Core).EventWarn := True;
+ OutFullLn (TBBSCore(Core).GetPrompt(133));
+ End;
+ End;
+ End;
+
+ Result := InKey;
+ Until Result <> #1;
+End;
+
+Function TBBSIO.GetYNL (Str: String; Yes: Boolean) : Boolean;
+Var
+ Ch : Char;
+ X : Byte;
+ Temp : Boolean;
+Begin
+ PurgeInputBuffer;
+
+ OutFull (Str);
+
+ Temp := AllowArrow;
+ AllowArrow := True;
+ X := Screen.CursorX;
+
+ Repeat
+ AnsiMoveX (X);
+ If Yes Then OutFull (TBBSCore(Core).GetPrompt(316)) Else OutFull (TBBSCore(Core).GetPrompt(317));
+ Ch := UpCase(GetKey);
+ If IsArrow Then Begin
+ If Ch = #77 Then Yes := False;
+ If Ch = #75 Then Yes := True;
+ End Else
+ If Ch = #13 Then Break Else
+ If Ch = #32 Then Yes := Not Yes Else
+ If Ch = 'Y' Then Begin
+ Yes := True;
+ AnsiMoveX(X);
+ OutFull (TBBSCore(Core).GetPrompt(316));
+ Break;
+ End Else
+ If Ch = 'N' Then Begin
+ Yes := False;
+ AnsiMoveX (X);
+ OutFull (TBBSCore(Core).GetPrompt(317));
+ Break;
+ End;
+ Until False;
+
+ OutRawLn('');
+
+ AllowArrow := Temp;
+ GetYNL := Yes;
+End;
+
+Function TBBSIO.GetYN (Str: String; Yes: Boolean) : Boolean;
+Begin
+ If TBBSCore(Core).Lang.BarYN and (Graphics = 1) Then Begin
+ GetYN := GetYNL(Str, Yes);
+ Exit;
+ End;
+
+ OutFull (Str);
+
+ Case OneKey(#13'YN', False) of
+ 'Y' : Yes := True;
+ 'N' : Yes := False;
+ End;
+
+ OutFullLn (OutYN(Yes));
+
+ GetYN := Yes;
+End;
+
+Function TBBSIO.GetPW (Str: String; BadStr: String; PW: String) : Boolean;
+Var
+ Loop : Byte;
+ Temp : String[15];
+Begin
+ Result := True;
+
+ If PW = '' Then Exit;
+
+ Loop := 0;
+
+ Repeat
+ OutFull (Str);
+ Temp := GetInput(15, 15, 16, '');
+ If Temp = PW Then
+ Exit
+ Else Begin
+ OutFullLn(BadStr);
+ Inc (Loop);
+
+ If (TBBSCore(Core).User.ThisUser.Handle <> '') and (Loop = 1) Then
+ TBBSCore(Core).SystemLog ('User: ' + TBBSCore(Core).User.ThisUser.Handle);
+
+ TBBSCore(Core).SystemLog ('Bad PW: ' + Temp);
+ End;
+ Until Loop = Config.PWAttempts;
+
+ Result := False;
+End;
+
+Function TBBSIO.OneKey (Str: String; Echo: Boolean): Char;
+Var
+ Ch : Char;
+Begin
+ PurgeInputBuffer;
+
+ Repeat
+ Ch := UpCase(GetKey);
+ Until Pos (Ch, Str) > 0;
+
+ If Echo Then OutRawLn (Ch);
+
+ Result := Ch;
+End;
+
+Function TBBSIO.GetInput (Field, Max, Mode: Byte; Default: String) : String;
+(*
+{ input modes: }
+{ 1 = standard input
+{ 2 = upper case }
+{ 3 = proper }
+{ 4 = usa phone number }
+{ 5 = date }
+{ 6 = password }
+{ 7 = lower cased }
+{ 8 = user defined input }
+{ 9 = standard input with no CRLF }
+*)
+Var
+ FieldCh : Char;
+ Ch : Char;
+ S : String;
+ StrPos : Integer;
+ xPos : Byte;
+ Junk : Integer;
+ CurPos : Integer;
+ ArrowSave : Boolean;
+ BackPos : Byte;
+ BackSaved : String;
+
+ Procedure pWrite (Str : String);
+ Begin
+ If (Mode = 6) and (S <> '') Then
+ BufAddStr (strRep(TBBSCore(Core).Lang.EchoCh, Length(Str)))
+ Else
+ BufAddStr (Str);
+ End;
+
+ Procedure ReDraw;
+ Begin
+ AnsiMoveX (xPos);
+
+ pWrite (Copy(S, Junk, Field));
+ If UseInField Then AnsiColor(TBBSCore(Core).Lang.FieldCol2);
+ pWrite (strRep(FieldCh, Field - Length(Copy(S, Junk, Field))));
+ If UseInField Then AnsiColor(TBBSCore(Core).Lang.FieldCol1);
+
+ AnsiMoveX (xPos + CurPos - 1);
+ End;
+
+ Procedure ReDrawPart;
+ Begin
+ pWrite (Copy(S, StrPos, Field - CurPos + 1));
+ If UseInField Then AnsiColor(TBBSCore(Core).Lang.FieldCol2);
+ pWrite (strRep(FieldCh, (Field - CurPos + 1) - Length(Copy(S, StrPos, Field - CurPos + 1))));
+ If UseInField Then AnsiColor(TBBSCore(Core).Lang.FieldCol1);
+
+ AnsiMoveX (xPos + CurPos - 1);
+ End;
+
+ Procedure ScrollRight;
+ Begin
+ Inc (Junk, Field DIV 2); {scroll size}
+ If Junk > Length(S) Then Junk := Length(S);
+ If Junk > Max Then Junk := Max;
+ CurPos := StrPos - Junk + 1;
+ ReDraw;
+ End;
+
+ Procedure ScrollLeft;
+ Begin
+ Dec (Junk, Field DIV 2); {scroll size}
+ If Junk < 1 Then Junk := 1;
+ CurPos := StrPos - Junk + 1;
+ ReDraw;
+ End;
+
+ Procedure Add_Char (Ch : Char);
+ Begin
+ If CurPos > Field then ScrollRight;
+
+ Insert (Ch, S, StrPos);
+ If StrPos < Length(S) Then ReDrawPart;
+
+ Inc (StrPos);
+ Inc (CurPos);
+
+ pWrite (Ch);
+ End;
+
+Begin
+ If UseInLimit Then Begin
+ Field := InLimit;
+ UseInLimit := False;
+ End;
+
+ If UseInSize Then Begin
+ UseInSize := False;
+ If InSize <= Max Then Max := InSize;
+ End;
+
+ xPos := Screen.CursorX;
+ FieldCh := ' ';
+
+ If Mode > 10 Then Begin
+ Dec (Mode, 10);
+ If UseInField and (Graphics = 1) Then Begin
+ FieldCh := TBBSCore(Core).Lang.FieldChar;
+ AnsiColor (TBBSCore(Core).Lang.FieldCol2);
+ BufAddStr (strRep(FieldCh, Field));
+ AnsiColor (TBBSCore(Core).Lang.FieldCol1);
+ AnsiMoveX (xPos);
+ End Else
+ UseInField := False;
+ End Else
+ UseInField := False;
+
+ If Mode = 8 Then
+ Case Config.UserNameFormat of
+ 0 : Mode := 1;
+ 1 : Mode := 2;
+ 2 : Mode := 7;
+ 3 : Mode := 3;
+ End;
+
+ ArrowSave := AllowArrow;
+ AllowArrow := (Mode in [1..3, 7..9]) and (Graphics > 0);
+
+ BackPos := 0;
+ S := Default;
+ StrPos := Length(S) + 1;
+ Junk := StrPos - Field;
+ If Junk < 1 Then Junk := 1;
+ CurPos := StrPos - Junk + 1;
+ pWrite (Copy(S, Junk, Field));
+
+ PurgeInputBuffer;
+
+ Repeat
+ Ch := GetKey;
+ If IsArrow Then Begin
+ Case Ch of
+ #71 : If StrPos > 1 Then Begin
+ StrPos := 1;
+ Junk := 1;
+ CurPos := 1;
+ ReDraw;
+ End;
+ #72 : If (BackPos < mysMaxInputHistory) And (BackPos < InputPos) Then Begin
+ Inc (BackPos);
+
+ If BackPos = 1 Then BackSaved := S;
+
+ S := InputData[BackPos];
+ StrPos := Length(S) + 1;
+ Junk := StrPos - Field;
+ If Junk < 1 Then Junk := 1;
+ CurPos := StrPos - Junk + 1;
+ ReDraw;
+ End;
+ #75 : If StrPos > 1 Then Begin
+ If CurPos = 1 Then ScrollLeft;
+ Dec (StrPos);
+ Dec (CurPos);
+ If CurPos < 1 then CurPos := 1;
+ AnsiMoveX (Screen.CursorX - 1);
+ End;
+ #77 : If StrPos < Length(S) + 1 Then Begin
+ If (CurPos = Field) and (StrPos < Length(S)) Then ScrollRight;
+ Inc (CurPos);
+ Inc (StrPos);
+ AnsiMoveX (Screen.CursorX + 1);
+ End;
+ #79 : Begin
+ StrPos := Length(S) + 1;
+ Junk := StrPos - Field;
+ If Junk < 1 Then Junk := 1;
+ CurPos := StrPos - Junk + 1;
+ ReDraw;
+ End;
+ #80 : If (BackPos > 0) Then Begin
+ Dec (BackPos);
+
+ If BackPos = 0 Then
+ S := BackSaved
+ Else
+ S := InputData[BackPos];
+
+ StrPos := Length(S) + 1;
+ Junk := StrPos - Field;
+ If Junk < 1 Then Junk := 1;
+ CurPos := StrPos - Junk + 1;
+ ReDraw;
+ End;
+ #83 : If (StrPos <= Length(S)) and (Length(S) > 0) Then Begin
+ Delete(S, StrPos, 1);
+ ReDrawPart;
+ End;
+ End;
+ End Else
+ Case Ch of
+ #02 : ReDraw;
+ #08 : If StrPos > 1 Then Begin
+ Dec (StrPos);
+ Delete (S, StrPos, 1);
+ If CurPos = 1 Then
+ ScrollLeft
+ Else
+ If StrPos = Length(S) + 1 Then Begin
+ If UseInField Then AnsiColor(TBBSCore(Core).Lang.FieldCol2);
+ BufAddStr (#8 + FieldCh + #8);
+ If UseInField Then AnsiColor(TBBSCore(Core).Lang.FieldCol1);
+ Dec (CurPos);
+ End Else Begin
+ BufAddChar (#8);
+ Dec (CurPos);
+ ReDrawPart;
+ End;
+ End;
+ #13 : Break;
+ ^Y : Begin
+ S := '';
+ StrPos := 1;
+ Junk := 1;
+ CurPos := 1;
+ ReDraw;
+ End;
+ #32..
+ #254: If Length(S) < Max Then
+ Case Mode of
+ 1 : Add_Char (Ch);
+ 2 : Add_Char (UpCase(Ch));
+ 3 : Begin
+ If (CurPos = 1) or (S[StrPos-1] in [' ', '.']) Then
+ Ch := UpCase(Ch)
+ Else
+ Ch := LoCase(Ch);
+ Add_Char(Ch);
+ End;
+ 4 : If (Ord(Ch) > 47) and (Ord(Ch) < 58) Then
+ Case StrPos of
+ 4,8 : Begin
+ Add_Char ('-');
+ Add_Char (Ch);
+ End;
+ 3,7 : Begin
+ Add_Char (Ch);
+ Add_Char ('-');
+ End;
+ Else
+ Add_Char(Ch);
+ End;
+ 5 : If (Ord(Ch) > 47) and (Ord(Ch) < 58) Then
+ Case StrPos of
+ 2,5 : Begin
+ Add_Char (Ch);
+ Add_Char ('/');
+ End;
+ 3,6 : Begin
+ Add_Char ('/');
+ Add_Char (Ch);
+ End;
+ Else
+ Add_Char (Ch);
+ End;
+ 6 : Add_Char(UpCase(Ch));
+ 7 : Add_Char(LoCase(Ch));
+ 9 : Add_Char(Ch);
+ End;
+ End;
+ Until False;
+
+ If Mode <> 6 Then Begin
+ For Junk := 4 DownTo 2 Do
+ InputData[Junk] := InputData[Junk - 1];
+
+ InputData[1] := S;
+
+ If InputPos < mysMaxInputHistory Then Inc(InputPos);
+ End;
+
+ If Mode = 9 Then
+ OutFull ('|16')
+ Else
+ OutFullLn ('|16');
+
+ Case Mode of
+ 5 : Case TBBSCore(Core).User.ThisUser.DateType of { Convert to MM/DD/YY }
+ {DD/MM/YY}
+ 2 : S := Copy(S, 4, 2) + '/' + Copy(S, 1, 2) + '/' + Copy(S, 7, 2);
+ {YY/DD/MM}
+ 3 : S := Copy(S, 7, 2) + '/' + Copy(S, 4, 2) + '/' + Copy(S, 1, 2);
+ End;
+ End;
+
+ UseInField := True;
+ AllowArrow := ArrowSave;
+ GetInput := S;
+End;
+
+Function TBBSIO.InXY (X, Y, Field, Max, Mode: Byte; Default: String) : String;
+Begin
+ If Graphics = 0 Then
+ OutFull ('|CR: ')
+ Else
+ AnsiGotoXY (X, Y);
+
+ InXY := GetInput (Field, Max, Mode, Default);
+End;
+
+Function TBBSIO.DrawPercent (Bar : PercentRec; Part, Whole : SmallInt; Var Percent : SmallInt) : String;
+Var
+ FillSize : Byte;
+Begin
+ Screen.TextAttr := 0; // kludge to force it to return full ansi codes
+
+ If (Part = 0) or (Whole = 0) or (Part > Whole) Then Begin
+ FillSize := 0;
+ Percent := 0;
+// FillSize := Bar.BarLen;
+// Percent := 100;
+// this needs work...
+ End Else Begin
+ FillSize := Round(Part / Whole * Bar.BarLen);
+ Percent := Round(Part / Whole * 100);
+ End;
+
+ DrawPercent := Attr2Ansi(Bar.HiAttr) + strRep(Bar.HiChar, FillSize) +
+ Attr2Ansi(Bar.LoAttr) + strRep(Bar.LoChar, Bar.BarLen - FillSize);
+End;
+
+{$IFDEF UNIX}
+Procedure TBBSIO.RemoteRestore (Var Image: TConsoleImageRec);
+Var
+ CountX : Byte;
+ CountY : Byte;
+Begin
+ For CountY := Image.Y1 to Image.Y2 Do Begin
+ Session.io.AnsiGotoXY (Image.X1, CountY);
+
+ For CountX := Image.X1 to Image.X2 Do Begin
+ Session.io.AnsiColor(Image.Data[CountY][CountX].Attributes);
+ Session.io.BufAddChar(Image.Data[CountY][CountX].UnicodeChar);
+ End;
+ End;
+
+ Session.io.AnsiColor(Image.CursorA);
+ Session.io.AnsiGotoXY(Image.CursorX, Image.CursorY);
+
+ Session.io.BufFlush;
+End;
+{$ELSE}
+Procedure TBBSIO.RemoteRestore (Var Image: TConsoleImageRec);
+Var
+ CountX : Byte;
+ CountY : Byte;
+ BufPos : Integer;
+ Buffer : Array[1..SizeOf(TConsoleScreenRec) DIV 2] of Word Absolute Image.Data;
+ TempChar : Char;
+Begin
+ BufPos := 1;
+
+ For CountY := Image.Y1 to Image.Y2 Do Begin
+ Session.io.AnsiGotoXY (Image.X1, CountY);
+
+ For CountX := Image.X1 to Image.X2 Do Begin
+
+ Session.io.AnsiColor(Buffer[BufPos+1]);
+
+ TempChar := Char(Buffer[BufPos]);
+
+ If TempChar = #0 Then TempChar := ' ';
+
+ Session.io.BufAddChar(TempChar);
+ Inc (BufPos, 2);
+ End;
+ End;
+
+ Session.io.AnsiColor(Image.CursorA);
+ Session.io.AnsiGotoXY(Image.CursorX, Image.CursorY);
+
+ Session.io.BufFlush;
+End;
+{$ENDIF}
+
+Function TBBSIO.StrMci (Str: String) : String;
+Var
+ Count : Byte;
+ Code : String[2];
+Begin
+ Result := '';
+ Count := 1;
+
+ While Count <= Length(Str) Do Begin
+ If (Str[Count] = '|') and (Count < Length(Str) - 1) Then Begin
+ Code := Copy(Str, Count + 1, 2);
+ Inc (Count, 2);
+ Case Code[1] of
+ '0' : Result := Result + '|' + Code;
+ '1' : Result := Result + '|' + Code;
+ '2' : Result := Result + '|' + Code;
+ Else
+ If ParseMCI(False, Code) Then
+ Result := Result + LastMCIValue
+ Else
+ Result := Result + '|' + Code;
+ End;
+ End Else
+ Result := Result + Str[Count];
+
+ Inc(Count);
+ End;
+End;
+
+Procedure TBBSIO.PurgeInputBuffer;
+Begin
+ While Input.KeyPressed Do Input.ReadKey;
+ {$IFDEF WINDOWS}
+ If Not TBBSCore(Core).LocalMode Then TBBSCore(Core).Client.PurgeInputData;
+ {$ENDIF}
+End;
+
+{$IFDEF WINDOWS}
+Procedure TBBSIO.LocalScreenDisable;
+Begin
+ Screen.ClearScreenNoUpdate;
+ Screen.WriteXYNoUpdate(1, 1, 7, 'Screen disabled. Press ALT-V to view user');
+ Screen.Active := False;
+End;
+
+Procedure TBBSIO.LocalScreenEnable;
+Begin
+ Screen.Active := True;
+ Screen.ShowBuffer;
+ Update_Status_Line(StatusPtr, '');
+End;
+{$ENDIF}
+
+End.
diff --git a/mystic/bbs_msgbase.pas b/mystic/bbs_msgbase.pas
new file mode 100644
index 0000000..ac18a10
--- /dev/null
+++ b/mystic/bbs_msgbase.pas
@@ -0,0 +1,3664 @@
+Unit bbs_MsgBase;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ m_FileIO,
+ m_DateTime,
+ bbs_Common,
+ bbs_General,
+ bbs_MsgBase_ABS,
+ bbs_MsgBase_JAM,
+ bbs_MsgBase_Squish;
+
+Type
+ TMsgBase = Class
+ MBaseFile : File of MBaseRec;
+ MScanFile : File of MScanRec;
+ GroupFile : File of RecGroup;
+ TotalMsgs : Integer;
+ TotalConf : Integer;
+ MsgBase : PMsgBaseABS;
+ MBase : MBaseRec;
+ MScan : MScanRec;
+ Group : RecGroup;
+ MsgText : RecMessageText;
+ WereMsgs : Boolean;
+ Reading : Boolean;
+
+ Constructor Create (Var Owner: Pointer);
+ Destructor Destroy; Override;
+
+ Function OpenCreateBase (Var Msg: PMsgBaseABS; Var Area: MBaseRec) : Boolean;
+ Procedure AppendMessageText (Var Msg: PMsgBaseABS; Lines: Integer; ReplyID: String);
+ Procedure AssignMessageData (Var Msg: PMsgBaseABS);
+ Function GetTotalMessages (Var TempBase: MBaseRec) : LongInt;
+ Procedure PostTextFile (Data: String; AllowCodes: Boolean);
+ Function SaveMessage (mArea: MBaseRec; mFrom, mTo, mSubj: String; mAddr: RecEchoMailAddr; mLines: Integer) : Boolean;
+ Function ListAreas (Compress: Boolean) : Integer;
+ Procedure ChangeArea (Data: String);
+ Procedure SetMessageScan;
+ Procedure GetMessageScan;
+ Procedure SendMassEmail;
+ Procedure MessageUpload (Var CurLine: SmallInt);
+ Procedure ReplyMessage (Email: Boolean; ListMode: Byte; ReplyID: String);
+ Procedure EditMessage;
+ Function ReadMessages (Mode : Char; SearchStr: String) : Boolean;
+ Procedure ToggleNewScan (QWK: Boolean);
+ Procedure MessageGroupChange (Ops: String; FirstBase, Intro : Boolean);
+ Procedure PostMessage (Email: Boolean; Data: String);
+ Procedure CheckEMail;
+ Procedure MessageNewScan (Data: String);
+ Procedure GlobalMessageSearch (Mode: Char);
+ Procedure SetMessagePointers;
+ Procedure ViewSentEmail;
+ Procedure DownloadQWK (Data: String);
+ Procedure UploadREP;
+ Procedure WriteCONTROLDAT;
+ Function WriteMSGDAT : LongInt;
+ Function ResolveOrigin (var mArea: MBaseRec) : String;
+ End;
+
+Implementation
+
+Uses
+ m_Strings,
+ bbs_Core,
+ bbs_User,
+ bbs_NodeInfo,
+ bbs_cfg_UserEdit;
+
+Type
+ BSingle = Array [0..3] of Byte;
+
+ QwkNdxHdr = Record
+ MsgPos : BSingle;
+ Junk : Byte;
+ End;
+
+ QwkDATHdr = Record {128 bytes}
+ 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;
+ Active : Char; {225 active, 226 killed}
+ ConfNum : Word;
+ Junk : Word;
+ NetTag : Char;
+ End;
+
+Constructor TMsgBase.Create (Var Owner: Pointer);
+Begin
+ Inherited Create;
+
+ MBase.Name := 'None';
+ Group.Name := 'None';
+ WereMsgs := False;
+ Reading := False;
+End;
+
+Destructor TMsgBase.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Function TMsgBase.OpenCreateBase (Var Msg: PMsgBaseABS; Var Area: MBaseRec) : Boolean;
+Begin
+ Result := False;
+
+ Case Area.BaseType of
+ 0 : Msg := New(PMsgBaseJAM, Init);
+ 1 : Msg := New(PMsgBaseSquish, Init);
+ End;
+
+ Msg^.SetMsgPath (Area.Path + Area.FileName);
+ Msg^.SetTempFile (Session.TempPath + 'msgbuf.');
+
+ If Not Msg^.OpenMsgBase Then
+ If Not Msg^.CreateMsgBase (Area.MaxMsgs, Area.MaxAge) Then Begin
+ Dispose (Msg, Done);
+ Exit;
+ End Else
+ If Not Msg^.OpenMsgBase Then Begin
+ Dispose (Msg, Done);
+ Exit;
+ End;
+
+ Result := True;
+End;
+
+Function TMsgBase.GetTotalMessages (Var TempBase: MBaseRec) : LongInt;
+Var
+ TempMsg : PMsgBaseABS;
+Begin
+ Result := 0;
+
+ If TempBase.Name = 'None' Then Exit;
+
+ If OpenCreateBase(TempMsg, TempBase) Then Begin
+ Result := TempMsg^.NumberOfMsgs;
+ TempMsg^.CloseMsgBase;
+ Dispose (TempMsg, Done);
+ End;
+End;
+
+Procedure TMsgBase.SetMessageScan;
+Var
+ Count : Integer;
+ Temp : MScanRec;
+Begin
+ Temp.NewScan := MBase.DefNScan;
+ Temp.QwkScan := MBase.DefQScan;
+
+ Assign (MScanFile, MBase.Path + MBase.FileName + '.scn');
+ {$I-} Reset (MScanFile); {$I+}
+ If IoResult <> 0 Then ReWrite (MScanFile);
+
+ If FileSize(MScanFile) < Session.User.UserNum - 1 Then Begin
+ Seek (MScanFile, FileSize(MScanFile));
+ For Count := FileSize(MScanFile) to Session.User.UserNum - 1 Do
+ Write (MScanFile, Temp);
+ End;
+
+ Seek (MScanFile, Session.User.UserNum - 1);
+ Write (MScanFile, MScan);
+ Close (MScanFile);
+End;
+
+Procedure TMsgBase.GetMessageScan;
+Begin
+ MScan.NewScan := MBase.DefNScan;
+ MScan.QwkScan := MBase.DefQScan;
+
+ Assign (MScanFile, MBase.Path + MBase.FileName + '.scn');
+ {$I-} Reset (MScanFile); {$I+}
+ If IoResult <> 0 Then Exit;
+
+ If FileSize(MScanFile) >= Session.User.UserNum Then Begin {filesize and usernum are }
+ Seek (MScanFile, Session.User.UserNum - 1); {not zero based }
+ Read (MScanFile, MScan);
+
+ { added security measure for forced reading bases }
+
+ If MBase.DefNScan = 2 Then MScan.NewScan := 2;
+ If MBase.DefQScan = 2 Then MScan.QwkScan := 2;
+ End;
+
+ Close (MScanFile);
+End;
+
+Procedure TMsgBase.AppendMessageText (Var Msg: PMsgBaseABS; Lines: Integer; ReplyID: String);
+Var
+ DF : File;
+ S : String;
+ A : SmallInt;
+Begin
+ If MBase.NetType > 0 Then Begin
+ Msg^.DoStringLn (#1 + 'MSGID: ' + strAddr2Str(Config.NetAddress[MBase.NetAddr]) + ' ' + strI2H(CurDateDos));
+
+ If ReplyID <> '' Then
+ Msg^.DoStringLn (#1 + 'REPLY: ' + ReplyID);
+ End;
+
+ For A := 1 to Lines Do
+ Msg^.DoStringLn(MsgText[A]);
+
+ If Session.User.ThisUser.SigUse and (Session.User.ThisUser.SigLength > 0) Then Begin
+
+ Assign (DF, Config.DataPath + 'autosig.dat');
+ Reset (DF, 1);
+ Seek (DF, Session.User.ThisUser.SigOffset);
+
+ Msg^.DoStringLn('');
+
+ For A := 1 to Session.User.ThisUser.SigLength Do Begin
+ BlockRead (DF, S[0], 1);
+ BlockRead (DF, S[1], Ord(S[0]));
+ Msg^.DoStringLn(S);
+ End;
+
+ Close (DF);
+ End;
+
+ If MBase.NetType > 0 Then Begin
+ Msg^.DoStringLn (#13 + '--- ' + mysSoftwareID + ' BBS v' + mysVersion + ' (' + OSID + ')');
+ Msg^.DoStringLn (' * Origin: ' + ResolveOrigin(MBase) + ' (' + strAddr2Str(Config.NetAddress[MBase.NetAddr]) + ')');
+ End;
+End;
+
+Procedure TMsgBase.AssignMessageData (Var Msg: PMsgBaseABS);
+Var
+ Addr : RecEchoMailAddr;
+ SemFile : Text;
+Begin
+ Msg^.StartNewMsg;
+
+ If MBase.UseReal Then
+ Msg^.SetFrom(Session.User.ThisUser.RealName)
+ Else
+ Msg^.SetFrom(Session.User.ThisUser.Handle);
+
+ Msg^.SetLocal (True);
+
+ If MBase.NetType > 0 Then Begin
+ If MBase.NetType = 3 Then
+ Msg^.SetMailType(mmtNetMail)
+ Else
+ Msg^.SetMailType(mmtEchoMail);
+
+ Addr := Config.NetAddress[MBase.NetAddr];
+
+ Msg^.SetOrig(Addr);
+
+ Case MBase.NetType of
+ 1 : Begin
+ Assign (SemFile, Config.SemaPath + 'echomail.now');
+ If Session.ExitLevel > 5 Then Session.ExitLevel := 7 Else Session.ExitLevel := 5;
+ End;
+ 2 : Begin
+ Assign (SemFile, Config.SemaPath + 'netmail.now');
+ If Session.ExitLevel = 5 Then Session.ExitLevel := 7 Else Session.ExitLevel := 6;
+ End;
+ 3 : Begin
+ Assign (SemFile, Config.SemaPath + 'newsmail.now');
+ If Session.ExitLevel > 5 Then Session.ExitLevel := 7 Else Session.ExitLevel := 5;
+ End;
+ End;
+
+ ReWrite (SemFile);
+ Close (SemFile);
+
+ End Else
+ Msg^.SetMailType(mmtNormal);
+
+ Msg^.SetPriv(MBase.PostType = 1);
+ Msg^.SetDate(DateDos2Str(CurDateDos, 1));
+ Msg^.SetTime(TimeDos2Str(CurDateDos, False));
+End;
+
+Procedure TMsgBase.ChangeArea (Data: String);
+Var
+ A,
+ Total : Word;
+ Old : MBaseRec;
+ Str : String[5];
+ Compress : Boolean;
+
+ Function CheckPassword : Boolean;
+ Begin
+ CheckPassword := True;
+
+ If MBase.Password <> '' Then
+ If Not Session.io.GetPW(Session.GetPrompt(103), Session.GetPrompt(417), MBase.Password) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(67));
+ MBase := Old;
+ Close (MBaseFile);
+ CheckPassword := False;
+ Exit;
+ End;
+ End;
+
+Begin
+ Compress := Config.MCompress;
+ Old := MBase;
+
+ If (Data = '+') or (Data = '-') Then Begin
+ Reset (MBaseFile);
+
+ A := Session.User.ThisUser.LastMBase - 1;
+
+ Repeat
+ Case Data[1] of
+ '+' : Inc(A);
+ '-' : Dec(A);
+ End;
+
+ {$I-}
+ Seek (MBaseFile, A);
+ Read (MBaseFile, MBase);
+ {$I+}
+
+ If IoResult <> 0 Then Break;
+
+ If Session.User.Access(MBase.ACS) Then Begin
+ If Not CheckPassword Then Break;
+ Session.User.ThisUser.LastMBase := FilePos(MBaseFile);
+ Close (MBaseFile);
+ Exit;
+ End;
+ Until False;
+
+ Close (MBaseFile);
+ MBase := Old;
+ Exit;
+ End;
+
+ A := strS2I(Data);
+
+ If A > 0 Then Begin
+ Inc (A);
+ Reset (MBaseFile);
+ If A <= FileSize(MBaseFile) Then Begin
+ Seek (MBaseFile, A-1);
+ Read (MBaseFile, MBase);
+ If Session.User.Access(MBase.ACS) Then Begin
+ If Not CheckPassword Then Exit;
+ Session.User.ThisUser.LastMBase := FilePos(MBaseFile)
+ End Else
+ MBase := Old;
+ End;
+ Close (MBaseFile);
+ Exit;
+ End;
+
+ If Pos('NOLIST', strUpper(Data)) > 0 Then Begin
+ Reset (MBaseFile);
+ Total := FileSize(MBaseFile);
+ Close (MBaseFile);
+ End Else
+ Total := ListAreas(Compress);
+
+ If Total = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(94));
+ MBase := Old;
+ End Else Begin
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(102));
+
+ Str := Session.io.GetInput(5, 5, 12, '');
+
+ If Str = '?' Then Begin
+ Compress := Config.MCompress;
+ Total := ListAreas(Compress);
+ End Else
+ Break;
+ Until False;
+
+ A := strS2I(Str);
+
+ If (A > 0) and (A <= Total) Then Begin
+ Reset (MBaseFile);
+ If Not Compress Then Begin
+ Seek (MBaseFile, A - 1);
+ Read (MBaseFile, MBase);
+ If Not Session.User.Access(MBase.ACS) Then Begin
+ MBase := Old;
+ Close (MBaseFile);
+ Exit;
+ End;
+ End Else Begin
+ Total := 0;
+
+ While Not Eof(MBaseFile) And (A <> Total) Do Begin
+ Read (MBaseFile, MBase);
+ If Session.User.Access(MBase.ACS) Then Inc(Total);
+ End;
+
+ If A <> Total Then Begin
+ Close (MBaseFile);
+ MBase := OLD;
+ Exit;
+ End;
+ End;
+
+ If Not CheckPassword Then Exit;
+
+ Session.User.ThisUser.LastMBase := FilePos(MBaseFile);
+
+ Close (MBaseFile);
+ End Else
+ MBase := Old;
+ End;
+End;
+
+Procedure TMsgBase.ToggleNewScan (QWK: Boolean);
+Var
+ Total: LongInt;
+
+ Procedure List_Bases;
+ Begin
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ If QWK Then
+ Session.io.OutFullLn (Session.GetPrompt(90))
+ Else
+ Session.io.OutFullLn (Session.GetPrompt(91));
+
+ Session.io.OutFullLn (Session.GetPrompt(92));
+
+ Total := 0;
+ Reset (MBaseFile);
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, MBase);
+ If Session.User.Access(MBase.ACS) Then Begin
+ Inc (Total);
+
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := MBase.Name;
+
+ GetMessageScan;
+
+ If ((MScan.NewScan > 0) And Not QWK) or ((MScan.QwkScan > 0) And QWK) Then
+ Session.io.PromptInfo[3] := 'Yes'
+ Else
+ Session.io.PromptInfo[3] := 'No';
+
+ Session.io.OutFull (Session.GetPrompt(93));
+
+ If (Total MOD 2 = 0) And (Total > 0) Then Session.io.OutRawLn('');
+ End;
+
+ If EOF(MBaseFile) and (Total MOD 2 <> 0) Then Session.io.OutRawLn('');
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(430));
+ End;
+
+ Procedure ToggleBase (A : Word);
+ Var
+ B : Word;
+ Begin
+ B := 0;
+ Reset (MBaseFile);
+ Repeat
+ Read (MBaseFile, MBase);
+ If Session.User.Access(MBase.ACS) Then Inc(B);
+ If A = B Then Break;
+ Until False;
+
+ GetMessageScan;
+
+ Session.io.PromptInfo[1] := MBase.Name;
+
+ If QWK Then Begin
+ Case MScan.QwkScan of
+ 0 : Begin
+ MScan.QwkScan := 1;
+ Session.io.OutFullLn (Session.GetPrompt(97));
+ End;
+ 1 : Begin
+ MScan.QwkScan := 0;
+ Session.io.OutFullLn (Session.GetPrompt(96));
+ End;
+ 2 : Session.io.OutFullLn (Session.GetPrompt(302));
+ End;
+ End Else Begin
+ Case MScan.NewScan of
+ 0 : Begin
+ MScan.NewScan := 1;
+ Session.io.OutFullLn (Session.GetPrompt(99));
+ End;
+ 1 : Begin
+ MScan.NewScan := 0;
+ Session.io.OutFullLn (Session.GetPrompt(98));
+ End;
+ 2 : Session.io.OutFullLn (Session.GetPrompt(302));
+ End;
+ End;
+
+ SetMessageScan;
+ End;
+
+Var
+ Old : MBaseRec;
+ Temp : String[11];
+ A : Word;
+ N1 : Word;
+ N2 : Word;
+Begin
+ Old := MBase;
+
+ List_Bases;
+
+ If Total = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(94));
+ MBase := Old;
+ Exit;
+ End;
+
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(95));
+ Temp := Session.io.GetInput(11, 11, 12, '');
+ If (Temp = '') or (Temp = 'Q') Then Break;
+ If Temp = '?' Then
+ List_Bases
+ Else Begin
+ If Pos('-', Temp) > 0 Then Begin
+ N1 := strS2I(Copy(Temp, 1, Pos('-', Temp) - 1));
+ N2 := strS2I(Copy(Temp, Pos('-', Temp) + 1, Length(Temp)));
+ End Else Begin
+ N1 := strS2I(Temp);
+ N2 := N1;
+ End;
+
+ For A := N1 to N2 Do
+ If (A > 0) and (A <= Total) Then ToggleBase(A);
+ End;
+ Until False;
+
+ Close (MBaseFile);
+ MBase := Old;
+End;
+
+Procedure TMsgBase.MessageGroupChange (Ops : String; FirstBase, Intro : Boolean);
+Var
+ A : Word;
+ Total : Word;
+ tGroup : RecGroup;
+ tMBase : MBaseRec;
+ tLast : Word;
+ Areas : Word;
+ Data : Word;
+Begin
+ tGroup := Group;
+
+ If (Ops = '+') or (Ops = '-') Then Begin
+ Reset (GroupFile);
+
+ A := Session.User.ThisUser.LastMGroup - 1;
+
+ Repeat
+ Case Ops[1] of
+ '+' : Inc(A);
+ '-' : Dec(A);
+ End;
+
+ {$I-}
+ Seek (GroupFile, A);
+ Read (GroupFile, Group);
+ {$I+}
+
+ If IoResult <> 0 Then Break;
+
+ If Session.User.Access(Group.ACS) Then Begin
+ Session.User.ThisUser.LastMGroup := FilePos(GroupFile);
+ Close (GroupFile);
+
+ If Intro Then Session.io.OutFile ('group' + strI2S(Session.User.ThisUser.LastMGroup), True, 0);
+
+ If FirstBase Then Begin
+ Session.User.ThisUser.LastMBase := 0;
+ ChangeArea('+');
+ End;
+
+ Exit;
+ End;
+ Until False;
+
+ Close (GroupFile);
+
+ Group := tGroup;
+ Exit;
+ End;
+
+ Data := strS2I(Ops);
+
+ Reset (GroupFile);
+
+ If Data > 0 Then Begin
+ If Data > FileSize(GroupFile) Then Begin
+ Close (GroupFile);
+ Exit;
+ End;
+
+ Seek (GroupFile, Data-1);
+ Read (GroupFile, Group);
+
+ If Session.User.Access(Group.ACS) Then Begin
+ Session.User.ThisUser.LastMGroup := FilePos(GroupFile);
+ If Intro Then Session.io.OutFile ('group' + strI2S(Data), True, 0);
+ End Else
+ Group := tGroup;
+
+ Close (GroupFile);
+
+ If FirstBase Then Begin
+ Session.User.ThisUser.LastMBase := 1;
+ ChangeArea('+');
+ End;
+
+ Exit;
+ End;
+
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ Session.io.OutFullLn (Session.GetPrompt(174)); { was after reset(groupfile) }
+
+ tLast := Session.User.ThisUser.LastMGroup;
+ Total := 0;
+
+ While Not Eof(GroupFile) Do Begin
+ Read (GroupFile, Group);
+
+ If Not Group.Hidden And Session.User.Access(Group.ACS) Then Begin
+
+ Areas := 0;
+ Session.User.ThisUser.LastMGroup := FilePos(GroupFile);
+
+ If Config.MShowBases Then Begin
+ Reset (MBaseFile);
+ Read (MBaseFile, tMBase); { Skip EMAIL base }
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, tMBase);
+ If Session.User.Access(tMBase.ACS) Then Inc(Areas);
+ End;
+ Close (MBaseFile);
+ End;
+
+ Inc (Total);
+
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := Group.Name;
+ Session.io.PromptInfo[3] := strI2S(Areas);
+
+ Session.io.OutFullLn (Session.GetPrompt(175));
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+ End;
+
+ Session.User.ThisUser.LastMGroup := tLast;
+
+ If Total = 0 Then
+ Session.io.OutFullLn (Session.GetPrompt(176))
+ Else Begin
+ Session.io.OutFull (Session.GetPrompt(177));
+
+ A := strS2I(Session.io.GetInput(5, 5, 11, ''));
+
+ If (A > 0) and (A <= Total) Then Begin
+ Total := 0;
+
+ Reset (GroupFile);
+ Repeat
+ Read (GroupFile, Group);
+ If Not Group.Hidden And Session.User.Access(Group.ACS) Then Inc(Total);
+ If A = Total Then Break;
+ Until False;
+
+ Session.User.ThisUser.LastMGroup := FilePos(GroupFile);
+
+ If Intro Then Session.io.OutFile ('group' + strI2S(Session.User.ThisUser.LastMGroup), True, 0);
+
+ Session.User.ThisUser.LastMBase := 1;
+ ChangeArea('+');
+
+ End Else
+ Group := tGroup;
+ End;
+
+ Close (GroupFile);
+End;
+
+Function TMsgBase.ListAreas (Compress: Boolean) : Integer;
+Var
+ Total : Word = 0;
+ Listed : Word = 0;
+ TempBase : MBaseRec;
+Begin
+ Reset (MBaseFile);
+
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, TempBase);
+
+ If Session.User.Access(TempBase.ACS) Then Begin
+ Inc (Listed);
+
+ If Listed = 1 Then
+ Session.io.OutFullLn(Session.GetPrompt(100));
+
+ If Compress Then
+ Inc (Total)
+ Else
+ Total := FilePos(MBaseFile);
+
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := TempBase.Name;
+ Session.io.PromptInfo[3] := strI2S(GetTotalMessages(TempBase));
+
+ Session.io.OutFull (Session.GetPrompt(101));
+
+ If (Listed MOD Config.MColumns = 0) and (Listed > 0) Then Session.io.OutRawLn('');
+ End;
+
+ If Eof(MBaseFile) and (Listed MOD Config.MColumns <> 0) Then Session.io.OutRawLn('');
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Begin
+ Total := FileSize(MBaseFile);
+ Break;
+ End;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Close (MBaseFile);
+
+ Result := Total;
+End;
+
+Procedure TMsgBase.ReplyMessage (Email: Boolean; ListMode : Byte; ReplyID : String);
+Var
+ ToWho : String[30]; {to field}
+ Subj : String[60]; {subject field}
+ Addr : RecEchomailAddr; {netmail to addr}
+ MsgNew : PMsgBaseABS;
+ Temp1 : String; {quote text}
+ Temp2 : String[2]; {Initials}
+ Temp3 : String[80]; {Text}
+ tFile : Text;
+ Lines : SmallInt;
+Begin
+ If Not Session.User.Access(MBase.PostACS) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(105));
+ Exit;
+ End;
+
+ Set_Node_Action (Session.GetPrompt(349));
+
+ If ListMode = 0 Then
+ Session.io.OutFull (Session.GetPrompt(407))
+ Else
+ Session.io.OutFull (Session.GetPrompt(408));
+
+ Repeat
+ ToWho := Session.io.GetInput(30, 30, 18, MsgBase^.GetFrom);
+
+ If ToWho = '' Then Exit;
+
+ If Not Email Then Break;
+
+ If Not Session.User.FindUser(ToWho, False) Then Begin
+ Session.io.PromptInfo[1] := ToWho;
+
+ Session.io.OutFullLn (Session.GetPrompt(161));
+
+ ToWho := MsgBase^.GetFrom;
+ End Else
+ Break;
+ Until False;
+
+ If MBase.NetType = 3 Then Begin
+ Session.io.OutFull (Session.GetPrompt(342));
+
+ MsgBase^.GetOrig(Addr);
+
+ Temp3 := Session.io.GetInput(20, 20, 12, strAddr2Str(Addr));
+
+ If Not strStr2Addr (Temp3, Addr) Then Exit;
+ End;
+
+ Subj := MsgBase^.GetSubj;
+
+ If Pos ('Re:', Subj) = 0 Then Subj := 'Re: ' + Subj;
+
+ Session.io.OutFull (Session.GetPrompt(451));
+
+ Subj := Session.io.GetInput (60, 60, 11, Subj);
+
+ If Subj = '' Then Exit;
+
+ Assign (tFile, Session.TempPath + 'msgtmp');
+ {$I-} ReWrite (tFile); {$I+}
+ If IoResult = 0 Then Begin
+ Temp3 := MsgBase^.GetFrom;
+ Temp2 := Temp3[1];
+
+ If Pos(' ', Temp3) > 0 Then
+ Temp2 := Temp2 + Temp3[Succ(Pos(' ', Temp3))];
+
+ Temp1 := Session.GetPrompt(464);
+
+ Temp1 := strReplace(Temp1, '|&1', MsgBase^.GetDate);
+ Temp1 := strReplace(Temp1, '|&2', MsgBase^.GetFrom);
+ Temp1 := strReplace(Temp1, '|&3', Temp2);
+
+ WriteLn (tFile, Temp1);
+ WriteLn (tFile, ' ');
+
+ Lines := 0;
+
+ MsgBase^.MsgTxtStartUp;
+
+ While Not MsgBase^.EOM and (Lines < mysMaxMsgLines - 2) Do Begin
+ Inc (Lines);
+
+ Temp3 := MsgBase^.GetString(79);
+
+ If Temp3[1] <> #1 Then
+ WriteLn (tFile, Temp2 + '> ' + Copy(Temp3, 1, 74));
+ End;
+
+ Close (tFile);
+ End;
+
+ Lines := 0;
+
+ Session.io.PromptInfo[1] := ToWho;
+ Session.io.PromptInfo[2] := Subj;
+
+ If Editor(Lines, 78, mysMaxMsgLines, False, False, Subj) Then Begin
+
+ Session.io.OutFull (Session.GetPrompt(107));
+
+ Case MBase.BaseType of
+ 0 : MsgNew := New(PMsgBaseJAM, Init);
+ 1 : MsgNew := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgNew^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If Not MsgNew^.OpenMsgBase Then Begin
+ Dispose (MsgNew, Done);
+ Exit;
+ End;
+
+ AssignMessageData(MsgNew);
+
+ Case MBase.NetType of
+ 2 : MsgNew^.SetTo('All');
+ 3 : Begin
+ MsgNew^.SetDest (Addr);
+ MsgNew^.SetCrash (Config.netCrash);
+ MsgNew^.SetHold (Config.netHold);
+ MsgNew^.SetKillSent (Config.netKillSent);
+ MsgNew^.SetTo (ToWho);
+
+ Addr := Config.NetAddress[MBase.NetAddr];
+ MsgNew^.SetOrig (Addr);
+ End;
+ Else
+ MsgNew^.SetTo(ToWho);
+ End;
+
+ MsgNew^.SetSubj(Subj);
+ MsgNew^.SetRefer(MsgBase^.GetMsgNum);
+
+ AppendMessageText (MsgNew, Lines, ReplyID);
+
+ MsgNew^.WriteMsg;
+ MsgNew^.CloseMsgBase;
+
+ If MsgBase^.GetSeeAlso = 0 Then Begin
+ MsgBase^.MsgStartUp;
+ MsgBase^.SetSeeAlso(MsgNew^.GetMsgNum);
+ MsgBase^.ReWriteHdr;
+ End;
+
+ If Email Then Begin
+ Session.SystemLog ('Sent Email to ' + MsgNew^.GetTo);
+
+ Inc (Session.User.ThisUser.Emails);
+ Inc (Session.HistoryEmails);
+ End Else Begin
+ Session.SystemLog ('Posted #' + strI2S(MsgNew^.GetMsgNum) + ': "' + Subj + '" to ' + strStripMCI(MBase.Name));
+
+ Inc (Session.User.ThisUser.Posts);
+ Inc (Session.HistoryPosts);
+ End;
+
+ Dispose (MsgNew, Done);
+
+ Session.io.OutFullLn (Session.GetPrompt(122));
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(109));
+
+ FileErase(Session.TempPath + 'msgtmp');
+End;
+
+Procedure TMsgBase.EditMessage;
+Var
+ A : Integer;
+ Lines : Integer;
+ Temp1 : String;
+ DestAddr : RecEchoMailAddr;
+
+ Procedure ReadText;
+ Begin
+ MsgBase^.MsgTxtStartUp;
+ Lines := 0;
+ While Not MsgBase^.EOM and (Lines < mysMaxMsgLines) Do Begin
+ Inc (Lines);
+ MsgText[Lines] := MsgBase^.GetString(79);
+ End;
+
+ If Lines < mysMaxMsgLines Then Begin
+ Inc (Lines);
+ MsgText[Lines] := '';
+ End;
+ End;
+
+Begin
+ ReadText;
+
+ Repeat
+ Session.io.PromptInfo[1] := MsgBase^.GetTo;
+ Session.io.PromptInfo[2] := MsgBase^.GetSubj;
+
+ If MBase.NetType = 3 Then Begin
+ MsgBase^.GetDest(DestAddr);
+ Session.io.PromptInfo[1] := Session.io.PromptInfo[1] + ' (' + strAddr2Str(DestAddr) + ')';
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(296));
+
+ Case Session.io.OneKey('ABQ!', True) of
+ 'A' : Begin
+ Session.io.OutFull (Session.GetPrompt(297));
+ If MBase.NetType = 3 Then Begin
+ Temp1 := Session.io.GetInput(30, 30, 11, MsgBase^.GetTo);
+ Session.io.OutFull (Session.GetPrompt(298));
+ If strStr2Addr(Session.io.GetInput(20, 20, 12, strAddr2Str(DestAddr)), DestAddr) Then Begin
+ MsgBase^.SetTo(Temp1);
+ MsgBase^.SetDest(DestAddr)
+ End;
+ End Else
+ If MBase.PostType = 1 Then Begin
+ Temp1 := Session.io.GetInput (30, 30, 11, MsgBase^.GetTo);
+ If Session.User.SearchUser(Temp1, MBase.UseReal) Then
+ MsgBase^.SetTo(Temp1);
+ End Else
+ MsgBase^.SetTo(Session.io.GetInput(30, 30, 11, MsgBase^.GetTo));
+ End;
+ 'B' : Begin
+ Session.io.OutFull (Session.GetPrompt(299));
+ MsgBase^.SetSubj(Session.io.GetInput(50, 50, 11, MsgBase^.GetSubj));
+ End;
+ '!' : Begin
+ Temp1 := MsgBase^.GetSubj;
+ If Editor(Lines, 78, mysMaxMsgLines, False, False, Temp1) Then
+ MsgBase^.SetSubj(Temp1)
+ Else
+ ReadText;
+ End;
+ 'Q' : Begin
+ If Session.io.GetYN(Session.GetPrompt(300), False) Then Begin
+ MsgBase^.EditMsgInit;
+
+ For A := 1 to Lines Do
+ MsgBase^.DoStringLn(MsgText[A]);
+
+ MsgBase^.EditMsgSave;
+ End;
+ Break;
+ End;
+
+ End;
+ Until False;
+End;
+
+Procedure TMsgBase.MessageUpload (Var CurLine: SmallInt);
+Var
+ FN : String[100]; {was string}
+ TF : Text;
+ T1 : String[30]; { Saved TO: }
+ T2 : String[60]; { Saved SUBJ: }
+ OK : Boolean;
+Begin
+ OK := False;
+
+ T1 := Session.io.PromptInfo[1];
+ T2 := Session.io.PromptInfo[2];
+
+ Session.io.OutFull (Session.GetPrompt(352));
+
+ If Session.LocalMode Then Begin
+ FN := Session.io.GetInput(70, 70, 11, '');
+
+ If FN = '' Then Exit;
+
+ OK := FileExist(FN);
+ End Else Begin
+ FN := Session.TempPath + Session.io.GetInput(70, 70, 11, '');
+
+ If Session.FileBase.SelectProtocol(False) = 'Q' Then Exit;
+
+ Session.FileBase.ExecuteProtocol(False, FN);
+
+ OK := Session.FileBase.dszSearch(JustFile(FN));
+ End;
+
+ If OK Then Begin
+ Assign (TF, FN);
+ Reset (TF);
+ While Not Eof(TF) and (CurLine < mysMaxMsgLines) Do Begin
+ ReadLn (TF, MsgText[CurLine]);
+ Inc (CurLine);
+ End;
+ Close (TF);
+ End;
+
+ If Not Session.LocalMode Then FileErase(FN);
+
+ CleanDirectory(Session.TempPath, 'msgtmp');
+
+ Session.io.PromptInfo[1] := T1;
+ Session.io.PromptInfo[2] := T2;
+End;
+
+Function TMsgBase.ReadMessages (Mode : Char; SearchStr : String) : Boolean;
+Var
+ ReadRes : Boolean;
+ ScanMode : Byte;
+ ValidKeys : String;
+ HelpFile : String[8];
+ LastRead : LongInt;
+ ListMode : Byte;
+ ReplyID : String[31];
+
+ Procedure Set_Message_Security;
+ Begin
+ If Mode = 'E' Then Begin
+ ValidKeys := 'ADJLNPQRX?'#13;
+ HelpFile := 'emailhlp';
+ End Else
+ If Session.User.Access(MBase.SysopACS) or Session.User.IsThisUser(MsgBase^.GetFrom) Then Begin
+ ValidKeys := 'ADEGHIJLMNPQRTX[]?'#13;
+ HelpFile := 'readshlp';
+ End Else Begin
+ ValidKeys := 'AGHIJLNPQRTX[]?'#13;
+ HelpFile := 'readhlp';
+ End;
+ End;
+
+ Function Move_Message : Boolean;
+ Var
+ MsgNew : PMsgBaseABS;
+ Str : String;
+ TempBase : MBaseRec;
+ Area : Integer;
+ Addr : RecEchoMailAddr;
+ Begin
+ Result := False;
+ Session.User.IgnoreGroup := True;
+
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(282));
+
+ Str := Session.io.GetInput(4, 4, 12, '');
+
+ If Str = '?' Then
+ ListAreas(False)
+ Else Begin
+ Reset (MBaseFile);
+
+ Area := strS2I(Str) - 1;
+
+ If (Area > 0) and (Area < FileSize(MBaseFile) - 1) Then Begin
+ Seek (MBaseFile, Area);
+ Read (MBaseFile, TempBase);
+ Close (MBaseFile);
+
+ If Not Session.User.Access(TempBase.PostACS) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(105));
+ Break;
+ End;
+
+ Session.io.PromptInfo[1] := TempBase.Name;
+
+ Session.io.OutFullLn (Session.GetPrompt(318));
+
+ If Not OpenCreateBase(MsgNew, TempBase) Then Break;
+
+ MsgNew^.StartNewMsg;
+ MsgNew^.SetFrom (MsgBase^.GetFrom);
+ MsgNew^.SetLocal (True);
+
+ Case TempBase.NetType of
+ 0 : MsgNew^.SetMailType(mmtNormal);
+ 3 : MsgNew^.SetMailType(mmtNetMail);
+ Else
+ MsgNew^.SetMailType(mmtEchoMail);
+ End;
+
+ MsgBase^.GetOrig(Addr);
+ MsgNew^.SetOrig(Addr);
+ MsgNew^.SetPriv(MsgBase^.IsPriv);
+ MsgNew^.SetDate(MsgBase^.GetDate);
+ MsgNew^.SetTime(MsgBase^.GetTime);
+ MsgNew^.SetTo(MsgBase^.GetTo);
+ MsgNew^.SetSubj(MsgBase^.GetSubj);
+
+ MsgBase^.MsgTxtStartUp;
+
+ While Not MsgBase^.EOM Do Begin
+ Str := MsgBase^.GetString(79);
+ MsgNew^.DoStringLn(Str);
+ End;
+
+ If MsgNew^.WriteMsg <> 0 Then;
+
+ MsgNew^.CloseMsgBase;
+
+ Session.SystemLog('Moved msg to ' + strStripMCI(TempBase.Name));
+
+ Dispose (MsgNew, Done);
+
+ MsgBase^.DeleteMsg;
+
+ Move_Message := True;
+ Break;
+ End Else Begin
+ Close (MBaseFile);
+ Break;
+ End;
+ End;
+ Until False;
+
+ Session.User.IgnoreGroup := False;
+ End;
+
+ Procedure Export_Message;
+ Var
+ FN : String;
+ Temp : String;
+ TF : Text;
+ Begin
+ If Session.LocalMode Then Begin
+ If ListMode = 0 Then
+ Session.io.OutFull (Session.GetPrompt(363))
+ Else
+ Session.io.OutFull (Session.GetPrompt(415));
+
+ FN := Session.io.GetInput(70, 70, 11, '');
+ End Else Begin
+ If ListMode = 0 Then
+ Session.io.OutFull (Session.GetPrompt(326))
+ Else
+ Session.io.OutFull (Session.GetPrompt(414));
+
+ FN := Session.TempPath + Session.io.GetInput(70, 70, 11, '');
+ End;
+
+ If FN = '' Then Exit;
+
+ Session.io.PromptInfo[1] := JustFileName(FN);
+
+ Assign (TF, FN);
+ {$I-} ReWrite (TF); {$I+}
+ If IoResult = 0 Then Begin
+ WriteLn (TF, 'From: ' + MsgBase^.GetFrom);
+ WriteLn (TF, ' To: ' + MsgBase^.GetTo);
+ WriteLn (TF, 'Subj: ' + MsgBase^.GetSubj);
+ WriteLn (TF, 'Date: ' + MsgBase^.GetDate + ' ' + MsgBase^.GetTime);
+ WriteLn (TF, 'Base: ' + MBase.Name);
+ WriteLn (TF, '');
+ MsgBase^.MsgTxtStartUp;
+ While Not MsgBase^.EOM Do Begin
+ Temp := MsgBase^.GetString(79);
+ If Temp[1] <> #1 Then WriteLn (TF, Temp);
+ End;
+ Close (TF);
+ Session.io.OutFullLn (Session.GetPrompt(327));
+ If Not Session.LocalMode Then Begin
+ Session.FileBase.SendFile(FN);
+ FileErase(FN);
+ End;
+ End;
+ End;
+
+ Function SeekNextMsg (First, Back: Boolean): Boolean;
+ Var
+ Res : Boolean;
+ Str : String;
+ Begin
+ Res := False;
+
+ If (ScanMode = 3) and First Then Begin
+ If Mode = 'S' Then Session.io.OutRawLn('');
+ Session.io.OutFull (Session.GetPrompt(130));
+ End;
+
+ If Not First Then
+ If Back Then
+ MsgBase^.SeekPrior
+ Else
+ MsgBase^.SeekNext;
+
+ While Not Res And MsgBase^.SeekFound Do Begin
+ MsgBase^.MsgStartUp;
+
+ Case ScanMode of
+ 0 : Res := True;
+ 1 : Res := Session.User.IsThisUser(MsgBase^.GetTo);
+ 2 : Res := Session.User.IsThisUser(MsgBase^.GetTo) or Session.User.IsThisUser(MsgBase^.GetFrom);
+ 3 : Begin
+ Res := (Pos(SearchStr, strUpper(MsgBase^.GetTo)) > 0) or (Pos(SearchStr, strUpper(MsgBase^.GetFrom)) > 0) or
+ (Pos(SearchStr, strUpper(MsgBase^.GetSubj)) > 0);
+
+ If Not Res Then Begin
+ MsgBase^.MsgTxtStartUp;
+
+ While Not Res And Not MsgBase^.EOM Do Begin
+ Str := strUpper(MsgBase^.GetString(79));
+ Res := Pos(SearchStr, Str) > 0;
+ End;
+ End;
+ End;
+ 4 : Res := Session.User.IsThisUser(MsgBase^.GetFrom);
+ End;
+
+ If Not Res Then
+ If Back Then
+ MsgBase^.SeekPrior
+ Else
+ MsgBase^.SeekNext;
+ End;
+
+ If (ScanMode = 3) And First Then
+ Session.io.OutBS (Screen.CursorX, True);
+
+ If Not WereMsgs Then WereMsgs := Res;
+
+ SeekNextMsg := Res;
+ End;
+
+ Procedure Assign_Header_Info;
+ Var
+ NetAddr : RecEchoMailAddr;
+ Begin
+ Session.io.PromptInfo[1] := MsgBase^.GetFrom;
+
+ If MBase.NetType = 3 Then Begin
+ MsgBase^.GetOrig(NetAddr);
+ Session.io.PromptInfo[1] := Session.io.PromptInfo[1] + ' (' + strAddr2Str(NetAddr) + ')';
+ End;
+
+ Session.io.PromptInfo[2] := MsgBase^.GetTo;
+
+ If MBase.NetType = 3 Then Begin
+ MsgBase^.GetDest(NetAddr);
+ Session.io.PromptInfo[2] := Session.io.PromptInfo[2] + ' (' + strAddr2Str(NetAddr) + ')';
+ End;
+
+ Session.io.PromptInfo[3] := MsgBase^.GetSubj;
+ Session.io.PromptInfo[4] := MsgBase^.GetDate;
+ Session.io.PromptInfo[10] := MsgBase^.GetTime;
+ Session.io.PromptInfo[5] := strI2S(MsgBase^.GetMsgNum);
+ Session.io.PromptInfo[6] := strI2S(MsgBase^.GetHighMsgNum);
+ Session.io.PromptInfo[7] := strI2S(MsgBase^.GetRefer);
+ Session.io.PromptInfo[8] := strI2S(MsgBase^.GetSeeAlso);
+
+ If MsgBase^.IsLocal Then Session.io.PromptInfo[9] := 'Local' Else Session.io.PromptInfo[9] := 'Echo'; //++lang
+ If MsgBase^.IsPriv Then Session.io.PromptInfo[9] := Session.io.PromptInfo[9] + ' Private'; //++lang
+ If MsgBase^.IsSent Then Session.io.PromptInfo[9] := Session.io.PromptInfo[9] + ' Sent'; //++lang
+ If MsgBase^.IsDeleted Then Session.io.PromptInfo[9] := Session.io.PromptInfo[9] + ' Deleted'; //++lang
+ End;
+
+ Procedure Send_Msg_Text (Str : String);
+ Var
+ B : Byte;
+ Begin
+ B := Pos('>', strStripL(Str, ' '));
+ If (B > 0) and (B < 5) Then Begin
+ Session.io.AnsiColor(MBase.ColQuote);
+ Session.io.OutPipe (Str);
+ Session.io.AnsiColor(MBase.ColText);
+ End Else
+ If Copy(Str, 1, 4) = '--- ' Then Begin
+ Session.io.AnsiColor(MBase.ColTear);
+ Session.io.OutPipe (Str);
+ Session.io.AnsiColor(MBase.ColText);
+ End Else
+ If Copy(Str, 1, 2) = ' *' Then Begin
+ Session.io.AnsiColor(MBase.ColOrigin);
+ Session.io.OutPipe (Str);
+ Session.io.AnsiColor(MBase.ColText);
+ End Else
+ Session.io.OutPipe (Str);
+
+ If ListMode = 1 Then
+ Session.io.AnsiClrEOL;
+
+ Session.io.OutRawLn('');
+ End;
+
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+
+ Function Ansi_View_Message : Boolean;
+ Var
+ Lines : SmallInt;
+ PageSize : SmallInt;
+ PageStart : SmallInt;
+ PageEnd : SmallInt;
+
+ Procedure Draw_Msg_Text;
+ Var
+ A : SmallInt;
+ Temp : String;
+ Begin
+ PageEnd := PageStart;
+
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[1].Y);
+ Session.io.AnsiColor (MBase.ColText);
+
+ For A := 1 to PageSize Do
+ If PageEnd <= Lines Then Begin
+ Send_Msg_Text(MsgText[PageEnd]);
+ Inc (PageEnd);
+ End Else Begin
+ Session.io.AnsiClrEOL;
+ Session.io.OutRawLn ('');
+ End;
+
+ Temp := Session.io.DrawPercent(Session.Lang.MsgBar, PageEnd - 1, Lines, A);
+
+ If Session.io.ScreenInfo[4].Y <> 0 Then Begin
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[4].X, Session.io.ScreenInfo[4].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[4].A);
+ Session.io.OutRaw (strPadL(strI2S(A), 3, ' '));
+ End;
+
+ If Session.io.ScreenInfo[5].Y <> 0 Then Begin
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[5].X, Session.io.ScreenInfo[5].Y);
+ Session.io.OutFull (Temp);
+ End;
+ End;
+
+ Var
+ Ch : Char;
+ A : LongInt;
+ CurMsg : LongInt;
+ Begin
+ Ansi_View_Message := False;
+
+ Repeat
+ If Check_Node_Message Then;
+
+ Set_Node_Action (Session.GetPrompt(348));
+
+ Set_Message_Security;
+
+ If MsgBase^.GetMsgNum > LastRead Then LastRead := MsgBase^.GetMsgNum;
+
+ CurMsg := MsgBase^.GetMsgNum;
+ Session.io.AllowArrow := True;
+ Lines := 0;
+ PageStart := 1;
+
+ MsgBase^.MsgTxtStartUp;
+
+ While Not MsgBase^.EOM And (Lines < mysMaxMsgLines) Do Begin
+ Inc (Lines);
+ MsgText[Lines] := MsgBase^.GetString(79);//HERE
+
+ If MsgText[Lines][1] = #1 Then Begin
+ If Copy(MsgText[Lines], 1, 6) = #1 + 'MSGID' Then
+ ReplyID := Copy(MsgText[Lines], 9, Length(MsgText[Lines]));
+
+ Dec (Lines);
+ End;
+ End;
+
+ Assign_Header_Info;
+
+ Session.io.ScreenInfo[4].Y := 0;
+ Session.io.ScreenInfo[5].Y := 0;
+
+ Session.io.OutFile ('ansimrd', True, 0);
+
+ PageSize := Session.io.ScreenInfo[2].Y - Session.io.ScreenInfo[1].Y + 1;
+
+ Draw_Msg_Text;
+
+ Repeat
+ Session.io.PurgeInputBuffer;
+
+ Repeat
+ Ch := UpCase(Session.io.GetKey);
+ Until (Pos(Ch, #27 + ValidKeys) > 0) or Session.io.IsArrow;
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #71 : If PageStart > 1 Then Begin
+ PageStart := 1;
+ Draw_Msg_Text;
+ End;
+ #72 : If PageStart > 1 Then Begin
+ Dec (PageStart);
+ Draw_Msg_Text;
+ End;
+ #73 : If PageStart > 1 Then Begin
+ If PageStart - PageSize > 0 Then
+ Dec (PageStart, PageSize)
+ Else
+ PageStart := 1;
+ Draw_Msg_Text;
+ End;
+ #75 : If SeekNextMsg(False, True) Then
+ Break
+ Else Begin
+ MsgBase^.SeekFirst(CurMsg);
+ SeekNextMsg(True, False);
+ End;
+ #77 : If SeekNextMsg(False, False) Then
+ Break
+ Else Begin
+ MsgBase^.SeekFirst(CurMsg);
+ SeekNextMsg(True, False);
+ End;
+ #79 : Begin
+ PageStart := Lines - PageSize + 1;
+
+ If PageStart < 1 Then PageStart := 1;
+
+ Draw_Msg_Text;
+ End;
+ #80 : If PageEnd <= Lines Then Begin
+ Inc (PageStart);
+ Draw_Msg_Text;
+ End;
+ #81 : If (Lines > PageSize) and (PageEnd <= Lines) Then Begin
+ If PageStart + PageSize <= Lines - PageSize Then
+ Inc (PageStart, PageSize)
+ Else
+ PageStart := Lines - PageSize + 1;
+
+ Draw_Msg_Text;
+ End;
+ End;
+ End Else
+ Case Ch of
+ 'A' : Break;
+ 'D' : Begin
+ If Session.io.GetYN(Session.GetPrompt(402), True) Then Begin
+ MsgBase^.DeleteMsg;
+ If Not SeekNextMsg(False, False) Then Begin
+ Ansi_View_Message := True;
+ Exit;
+ End;
+ End Else
+ MsgBase^.SeekFirst(CurMsg);
+ Break;
+ End;
+ 'E' : Begin
+ EditMessage;
+ Break;
+ End;
+ 'G' : Begin
+ Ansi_View_Message := True;
+ Exit;
+ End;
+ 'H' : Begin
+ LastRead := CurMsg - 1;
+ End;
+ 'I' : Begin
+ LastRead := MsgBase^.GetHighMsgNum;
+ Ansi_View_Message := True;
+ Exit;
+ End;
+ 'J' : Begin
+ Session.io.PromptInfo[1] := strI2S(CurMsg);
+ Session.io.PromptInfo[2] := strI2S(MsgBase^.GetHighMsgNum);
+
+ Session.io.OutFull (Session.GetPrompt(403));
+ A := strS2I(Session.io.GetInput(9, 9, 12, ''));
+ If (A > 0) and (A <= MsgBase^.GetHighMsgNum) Then Begin
+ MsgBase^.SeekFirst(A);
+ If Not SeekNextMsg(True, False) Then Begin
+ MsgBase^.SeekFirst(CurMsg);
+ If SeekNextMsg(True, False) Then;
+ End;
+ End;
+ Break;
+ End;
+ 'L' : Exit;
+ 'M' : Begin
+ If Move_Message Then
+ If Not SeekNextMsg(False, False) Then Begin
+ Ansi_View_Message := True;
+ Exit;
+ End;
+ Break;
+ End;
+ #13 : If (Lines > PageSize) and (PageEnd <= Lines) Then Begin
+ If PageStart + PageSize <= Lines - PageSize Then
+ Inc (PageStart, PageSize)
+ Else
+ PageStart := Lines - PageSize + 1;
+
+ Draw_Msg_Text;
+ End Else Begin
+ If SeekNextMsg(False, False) Then
+ Break
+ Else Begin
+ Ansi_View_Message := True;
+ Exit;
+ End;
+ End;
+ 'N' : If SeekNextMsg(False, False) Then
+ Break
+ Else Begin
+ Ansi_View_Message := True;
+ Exit;
+ End;
+ 'P' : If SeekNextMsg(False, True) Then
+ Break
+ Else Begin
+ MsgBase^.SeekFirst(CurMsg);
+ SeekNextMsg(True, False);
+ End;
+ #27,
+ 'Q' : Begin
+ GetMessageScan;
+ If MScan.NewScan = 2 Then
+ Session.io.OutFullLn(Session.GetPrompt(406))
+ Else Begin
+ ReadRes := False;
+ Ansi_View_Message := True;
+ Exit;
+ End;
+ End;
+ 'R' : Begin
+ ReplyMessage (Mode = 'E', ListMode, ReplyID);
+ Break;
+ End;
+ 'T' : Begin
+ Session.io.PromptInfo[1] := MBase.Name;
+ GetMessageScan;
+ Case MScan.NewScan of
+ 0 : Begin
+ MScan.NewScan := 1;
+ Session.io.OutFull (Session.GetPrompt(405));
+ End;
+ 1 : Begin
+ MScan.NewScan := 0;
+ Session.io.OutFull (Session.GetPrompt(404));
+ End;
+ 2 : Session.io.OutFull (Session.GetPrompt(406));
+ End;
+
+ SetMessageScan;
+ Break;
+ End;
+ 'X' : Begin
+ Export_Message;
+ Break;
+ End;
+ '[' : If MsgBase^.GetRefer > 0 Then Begin
+ MsgBase^.SeekFirst(MsgBase^.GetRefer);
+ MsgBase^.MsgStartUp;
+ Break;
+ End;
+ ']' : If MsgBase^.GetSeeAlso > 0 Then Begin
+ MsgBase^.SeekFirst(MsgBase^.GetSeeAlso);
+ MsgBase^.MsgStartUp;
+ Break;
+ End;
+ '?' : Begin
+ Session.io.OutFile ('amsghlp2', True, 0);
+ Break;
+ End;
+ End;
+ Until False;
+ Until False;
+ End;
+
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+
+ Procedure Ansi_Read_Messages;
+ Type
+ MsgInfoRec = Record
+ Num : LongInt;
+ MsgFrom : String[30];
+ MsgTo : String[30];
+ Subj : String[60];
+ NewMsgs : Boolean;
+ End;
+
+ Var
+ PageSize : SmallInt;
+ PagePos : SmallInt;
+ PageTotal : SmallInt;
+ CurPage : Word;
+ MsgInfo : Array[1..24] of MsgInfoRec;
+ FirstPage : Boolean;
+
+ Procedure DrawPage;
+ Var
+ A : SmallInt;
+ Begin
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[1].Y);
+
+ For A := 1 to PageSize Do
+ If A <= PageTotal Then Begin
+ With MsgInfo[A] Do Begin
+ Session.io.PromptInfo[1] := strI2S(Num);
+ Session.io.PromptInfo[2] := Subj;
+ Session.io.PromptInfo[3] := MsgFrom;
+ Session.io.PromptInfo[4] := MsgTo;
+ If NewMsgs Then
+ Session.io.PromptInfo[5] := Session.Lang.NewMsgChar
+ Else
+ Session.io.PromptInfo[5] := ' ';
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(399));
+ Session.io.AnsiClrEOL;
+ Session.io.OutRawLn('');
+ End Else Begin
+ Session.io.AnsiClrEOL;
+ Session.io.OutRawLn('');
+ End;
+ End;
+
+ Procedure FullReDraw;
+ Begin
+ If Check_Node_Message Then;
+ Session.io.OutFile ('ansimlst', True, 0);
+
+ PageSize := Session.io.ScreenInfo[2].Y - Session.io.ScreenInfo[1].Y + 1;
+ End;
+
+ Function Read_Page (First, Back, NoDraw : Boolean) : Boolean;
+ Var
+ A : SmallInt;
+ B : SmallInt;
+ Temp : MsgInfoRec;
+ Begin
+ Read_Page := False;
+ FirstPage := False;
+
+ If SeekNextMsg(First, Back) Then Begin
+
+ If First Then Begin
+ FullReDraw;
+ CurPage := 0;
+ End;
+
+{ add scanning prompt here }
+(*
+if (scanmode=3) then begin
+ Session.io.AnsiGotoXY(32, 11);
+ Session.io.OutFull ('|08.---------------.');
+ Session.io.AnsiGotoXY(32, 12);
+ Session.io.OutFull ('| |07searching ... |08|');
+ Session.io.AnsiGotoXY(32, 13);
+ Session.io.OutFull ('`---------------''');
+end;
+*)
+ PageTotal := 0;
+ Read_Page := True;
+
+ Repeat
+ Inc (PageTotal);
+ MsgInfo[PageTotal].Num := MsgBase^.GetMsgNum;
+ MsgInfo[PageTotal].MsgFrom := MsgBase^.GetFrom;
+ MsgInfo[PageTotal].MsgTo := MsgBase^.GetTo;
+ MsgInfo[PageTotal].Subj := MsgBase^.GetSubj;
+ MsgInfo[PageTotal].NewMsgs := MsgBase^.GetMsgNum > LastRead;
+ Until (PageTotal = PageSize) or (Not SeekNextMsg(False, Back));
+
+ If Back Then Begin { reverse message order }
+ Dec (CurPage);
+
+ B := PageTotal;
+
+ For A := 1 to PageTotal DIV 2 Do Begin
+ Temp := MsgInfo[A];
+ MsgInfo[A] := MsgInfo[B];
+ MsgInfo[B] := Temp;
+ Dec (B);
+ End;
+
+ // if backwards and page is not filled, fill it going foward.
+
+ If PageTotal < PageSize Then Begin
+ FirstPage := True;
+
+ MsgBase.SeekFirst(MsgInfo[PageTotal].Num);
+
+ While SeekNextMsg(False, False) and (PageTotal < PageSize) Do Begin
+ Inc (PageTotal);
+ MsgInfo[PageTotal].Num := MsgBase^.GetMsgNum;
+ MsgInfo[PageTotal].MsgFrom := MsgBase^.GetFrom;
+ MsgInfo[PageTotal].MsgTo := MsgBase^.GetTo;
+ MsgInfo[PageTotal].Subj := MsgBase^.GetSubj;
+ MsgInfo[PageTotal].NewMsgs := MsgBase^.GetMsgNum > LastRead;
+ End;
+
+ Read_Page := False;
+ End;
+ End Else Begin
+ Inc (CurPage);
+ Read_Page := True;
+ End;
+
+ If Not NoDraw Then DrawPage;
+ End;
+ End;
+
+ Procedure UpdateBar (On : Boolean);
+ Begin
+ If PageTotal = 0 Then Exit;
+
+ Session.io.PromptInfo[1] := strI2S(MsgInfo[PagePos].Num);
+ Session.io.PromptInfo[2] := MsgInfo[PagePos].Subj;
+ Session.io.PromptInfo[3] := MsgInfo[PagePos].MsgFrom;
+ Session.io.PromptInfo[4] := MsgInfo[PagePos].MsgTo;
+
+ If MsgInfo[PagePos].NewMsgs Then
+ Session.io.PromptInfo[5] := Session.Lang.NewMsgChar
+ Else
+ Session.io.PromptInfo[5] := ' ';
+
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[1].Y + PagePos - 1);
+
+ If On Then
+ Session.io.OutFull (Session.GetPrompt(400))
+ Else
+ Session.io.OutFull (Session.GetPrompt(401));
+ End;
+
+ Procedure Ansi_Message_Index;
+ Var
+ Ch : Char;
+ SN : LongInt;
+ A : Byte;
+ Begin
+ If Read_Page (True, False, False) Then Begin
+ WereMsgs := True;
+ PagePos := 1;
+
+ Repeat
+ Session.io.AllowArrow := True;
+
+ UpdateBar(True);
+
+ Session.io.PurgeInputBuffer;
+
+ Ch := UpCase(Session.io.GetKey);
+
+ If Session.io.IsArrow Then Begin
+ Case Ch of
+ #71 : Begin
+ UpdateBar(False);
+
+ While Read_Page(False, True, True) Do;
+
+ PagePos := 1;
+
+ DrawPage;
+ End;
+ #72 : Begin
+ UpdateBar(False);
+
+ If PagePos > 1 Then
+ Dec (PagePos)
+ Else Begin
+ SN := MsgInfo[PagePos].Num;
+
+ MsgBase^.SeekFirst(MsgInfo[1].Num);
+
+ If Not Read_Page(False, True, False) Then
+ PagePos := 1
+ Else
+ If Not FirstPage Then
+ PagePos := PageTotal
+ Else Begin
+ For A := 1 to PageTotal Do
+ If MsgInfo[A].Num = SN Then Begin
+ PagePos := A - 1;
+ Break;
+ End;
+
+ If PagePos < 1 Then PagePos := 1;
+ End;
+ End;
+ End;
+ #73,
+ #75 : Begin
+ UpdateBar(False);
+
+ MsgBase^.SeekFirst(MsgInfo[1].Num);
+
+ If Not Read_Page(False, True, False) Then
+ PagePos := 1
+ Else
+ If PagePos > PageTotal Then PagePos := PageTotal;
+ End;
+ #80 : Begin
+ UpdateBar(False);
+
+ If PagePos < PageTotal Then
+ Inc (PagePos)
+ Else Begin
+ MsgBase^.SeekFirst(MsgInfo[PageTotal].Num);
+ If Read_Page(False, False, False) Then PagePos := 1;
+ End;
+ End;
+ #77,
+ #81 : Begin
+ MsgBase^.SeekFirst(MsgInfo[PageTotal].Num);
+ If Read_Page(False, False, False) Then Begin
+ If PagePos > PageTotal Then PagePos := PageTotal;
+ End Else Begin
+ UpdateBar(False);
+ PagePos := PageTotal;
+ End;
+ End;
+ #79 : Begin
+ UpdateBar(False);
+ While Read_Page(False, False, True) Do ;
+ PagePos := PageTotal;
+ DrawPage;
+ End;
+ End;
+ End Else
+ Case Ch of
+ #13 : Begin
+ MsgBase^.SeekFirst(MsgInfo[PagePos].Num);
+ If SeekNextMsg (True, False) Then;
+ If Ansi_View_Message Then Break;
+
+ MsgBase^.SeekFirst(MsgInfo[1].Num);
+
+ If Not Read_Page(True, False, False) Then Begin
+ PageTotal := 0;
+ FullReDraw;
+ DrawPage;
+ End;
+ End;
+ 'Q',
+ #27 : Begin
+ GetMessageScan;
+
+ If MScan.NewScan = 2 Then
+ Session.io.OutFullLn(Session.GetPrompt(406))
+ Else Begin
+ ReadRes := False;
+ Break;
+ End;
+ End;
+ 'G' : Break;
+ 'I' : Begin
+ LastRead := MsgBase^.GetHighMsgNum;
+ Break;
+ End;
+ '?' : Begin
+ Session.io.OutFile('amsghlp1', True, 0);
+ FullReDraw;
+ DrawPage;
+ End;
+ End;
+ Until False;
+ End;
+
+ Session.io.AllowArrow := False;
+
+ If WereMsgs Then Begin
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[3].Y);
+ Session.io.OutRawLn('');
+ End;
+ End;
+
+ Begin
+ If ((Mode = 'E') and Session.User.ThisUser.UseLBMIdx) or ((Mode <> 'E') and Session.User.ThisUser.UseLBIndex) Then
+ Ansi_Message_Index
+ Else Begin
+ If SeekNextMsg(True, False) Then
+ If Not Ansi_View_Message Then
+ Ansi_Message_Index;
+ End;
+ End;
+
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+
+ Procedure Ascii_Read_Messages;
+
+ Procedure Display_Header;
+ Begin
+ Session.io.PausePtr := 1;
+
+ Session.io.OutFile (MBase.Header, True, 0);
+
+ If Session.io.NoFile Then Begin
+ Session.io.OutFullLn ('|CL|03From : |14|$R40|&1 |03Msg # : |14|&5 |03of |14|&6');
+ Session.io.OutFullLn ('|03To : |10|$R40|&2 |03Refer to : |10|&7');
+ Session.io.OutFullLn ('|03Subj : |12|$R40|&3 |03See Also : |12|&8');
+ Session.io.OutFullLn ('|03Date : |11|&4 |$R31|&0 |03Status : |13|&9');
+ Session.io.OutFullLn ('|03Base : |14|MB|CR');
+ End;
+
+ Session.io.AnsiColor (MBase.ColText);
+ End;
+
+ Var
+ Str : String;
+ A : LongInt;
+ B : LongInt;
+ Begin
+ If SeekNextMsg(True, False) Then
+ Repeat
+// Set_Node_Action (Session.GetPrompt(348));
+
+ If MsgBase^.GetMsgNum > LastRead Then LastRead := MsgBase^.GetMsgNum;
+
+ Set_Message_Security;
+ Assign_Header_Info;
+ Display_Header;
+
+ MsgBase^.MsgTxtStartUp;
+
+ WereMsgs := True;
+ Session.io.AllowPause := True;
+
+ While Not MsgBase^.EOM Do Begin
+ Str := MsgBase^.GetString(79);
+
+ If Str[1] = #1 Then Begin
+ If Copy(Str, 1, 6) = #1 + 'MSGID' Then
+ ReplyID := Copy(Str, 9, Length(Str));
+ End Else
+ Send_Msg_Text (Str);
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then Begin
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+
+ If Config.MShowHeader Then Display_Header;
+ End;
+ End;
+
+ Session.io.AllowPause := False;
+
+ Repeat
+ Check_Node_Message;
+
+ Session.io.PromptInfo[1] := strI2S(MsgBase^.GetMsgNum);
+ Session.io.PromptInfo[2] := strI2S(MsgBase^.GetHighMsgNum);
+
+ If Mode = 'E' Then
+ Session.io.OutFull (Session.GetPrompt(115))
+ Else
+ If Session.User.Access(MBase.SysopACS) or Session.User.IsThisUser(MsgBase^.GetFrom) Then
+ Session.io.OutFull (Session.GetPrompt(213))
+ Else
+ Session.io.OutFull (Session.GetPrompt(116));
+
+ Str := Session.io.OneKey(ValidKeys, True);
+ Case Str[1] of
+ 'A' : Break;
+ 'D' : If Session.io.GetYN (Session.GetPrompt(117), True) Then Begin {Delete E-mail}
+ MsgBase^.DeleteMsg;
+ If Not SeekNextMsg(False, False) Then Exit;
+ Break;
+ End;
+ 'E' : Begin
+ EditMessage;
+ Break;
+ End;
+ 'G' : Exit;
+ 'H' : LastRead := MsgBase^.GetMsgNum - 1;
+ 'I' : Begin
+ LastRead := MsgBase^.GetHighMsgNum;
+ Exit;
+ End;
+ 'J' : Begin
+ B := MsgBase^.GetMsgNum;
+ Session.io.OutFull (Session.GetPrompt(334));
+ A := strS2I(Session.io.GetInput(9, 9, 12, ''));
+ If (A > 0) and (A <= MsgBase^.GetHighMsgNum) Then Begin
+ MsgBase^.SeekFirst(A);
+ If Not SeekNextMsg(True, False) Then Begin
+ MsgBase^.SeekFirst(B);
+ If Not SeekNextMsg(True, False) Then;
+ End;
+ End;
+ Break;
+ End;
+ 'L' : Begin
+ Session.io.PausePtr := 1;
+ Session.io.AllowPause := True;
+ A := MsgBase^.GetMsgNum;
+
+ Session.io.OutFullLn(Session.GetPrompt(411));
+
+ While SeekNextMsg(False, False) Do Begin
+ Assign_Header_Info;
+ Session.io.OutFullLn (Session.GetPrompt(412));
+
+ If (Session.io.PausePtr = Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
+ Case Session.io.MorePrompt of
+ 'N' : Break;
+ 'C' : Session.io.AllowPause := False;
+ End;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(413));
+
+ MsgBase^.SeekFirst(A);
+ MsgBase^.MsgStartup;
+ End;
+ 'M' : Begin
+ If Move_Message Then
+ If Not SeekNextMsg(False, False) Then Exit;
+ Break;
+ End;
+ #13,
+ 'N' : If SeekNextMsg(False, False) Then Break Else Exit;
+ 'P' : Begin
+ If Not SeekNextMsg(False, True) Then Begin
+ MsgBase^.SeekFirst(MsgBase^.GetMsgNum);
+ SeekNextMsg(True, False);
+ End;
+ Break;
+ End;
+ 'Q' : Begin
+ GetMessageScan;
+ If MScan.NewScan = 2 Then
+ Session.io.OutFullLn(Session.GetPrompt(302))
+ Else Begin
+ ReadRes := False;
+ Exit;
+ End;
+ End;
+ 'R' : Begin
+ ReplyMessage (Mode = 'E', ListMode, ReplyID);
+ Break;
+ End;
+ 'T' : Begin
+ Session.io.PromptInfo[1] := MBase.Name;
+ GetMessageScan;
+ Case MScan.NewScan of
+ 0 : Begin
+ MScan.NewScan := 1;
+ Session.io.OutFull (Session.GetPrompt(99));
+ End;
+ 1 : Begin
+ MScan.NewScan := 0;
+ Session.io.OutFull (Session.GetPrompt(98));
+ End;
+ 2 : Session.io.OutFull (Session.GetPrompt(302));
+ End;
+
+ SetMessageScan;
+ End;
+ 'X' : Export_Message;
+ '?' : Session.io.OutFile(HelpFile, True, 0);
+ '[' : If MsgBase^.GetRefer > 0 Then Begin
+ MsgBase^.SeekFirst(MsgBase^.GetRefer);
+ MsgBase^.MsgStartUp;
+ Break;
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(128));
+ ']' : If MsgBase^.GetSeeAlso > 0 Then Begin
+ MsgBase^.SeekFirst(MsgBase^.GetSeeAlso);
+ MsgBase^.MsgStartUp;
+ Break;
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(199));
+ End;
+ Until False;
+ Until False;
+ End;
+
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+(**************************************************************************)
+{ F = Forward S = Search E = Electronic Mail
+ N = New messages Y = Your messages G = Global scan
+ P = Global personal scan B = By You T = Global text search }
+Var
+ MsgNum : LongInt;
+Begin
+ ReadMessages := True;
+ ReadRes := True;
+ WereMsgs := False;
+ ReplyID := '';
+
+ If MBase.FileName = '' Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(110));
+ Exit;
+ End;
+
+ If Not Session.User.Access(MBase.ReadACS) Then Begin
+ If Not (Mode in ['G', 'P', 'T']) Then Session.io.OutFullLn (Session.GetPrompt(111));
+ Exit;
+ End;
+
+ If Not (Mode in ['B', 'T', 'S', 'E', 'F', 'G', 'N', 'P', 'Y']) Then Begin
+ Session.io.OutFull (Session.GetPrompt(112));
+ Mode := Session.io.OneKey('BFNSYQ', True);
+ End;
+
+ Case Mode of
+ 'Q' : Exit;
+ 'S' : If SearchStr = '' Then Begin
+ Session.io.OutFull (Session.GetPrompt(396));
+ SearchStr := Session.io.GetInput(50, 50, 12, '');
+ If SearchStr = '' Then Exit;
+ End;
+ End;
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgbaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If Not MsgBase^.OpenMsgBase Then Begin
+ If Mode = 'E' Then
+ Session.io.OutFullLn (Session.GetPrompt(124))
+ Else
+ If Not (Mode in ['G', 'P', 'T']) Then Session.io.OutFullLn (Session.GetPrompt(114));
+ Dispose (MsgBase, Done);
+ Exit;
+ End;
+
+ If Mode = 'E' Then
+ ScanMode := 1
+ Else
+ If (MBase.PostType = 1) or (Mode = 'Y') or (Mode = 'P') Then
+ ScanMode := 2
+ Else
+ If (Mode = 'S') or (Mode = 'T') Then
+ ScanMode := 3
+ Else
+ If Mode = 'B' Then
+ ScanMode := 4
+ Else
+ ScanMode := 0;
+
+ LastRead := MsgBase^.GetLastRead(Session.User.UserNum);
+ MsgNum := 1;
+
+ If Mode = 'F' Then Begin
+ Session.io.PromptInfo[1] := strI2S(MsgBase^.GetHighMsgNum);
+ Session.io.OutFull (Session.GetPrompt(338));
+ MsgNum := strS2I(Session.io.GetInput(6, 6, 12, ''));
+ End;
+
+ Set_Node_Action (Session.GetPrompt(348));
+
+ If Mode in ['B', 'S', 'T', 'Y', 'E', 'F'] Then
+ MsgBase^.SeekFirst(MsgNum)
+ Else
+ MsgBase^.SeekFirst(LastRead + 1);
+
+ Set_Message_Security;
+
+ Reading := True;
+
+ If (Session.User.ThisUser.MReadType = 1) and (Session.io.Graphics > 0) Then Begin
+ ListMode := 1;
+ Ansi_Read_Messages;
+ End Else Begin
+ ListMode := 0;
+ Ascii_Read_Messages;
+ End;
+
+ If Not (Mode in ['E', 'S', 'T']) Then MsgBase^.SetLastRead (Session.User.UserNum, LastRead);
+
+ MsgBase^.CloseMsgBase;
+ Dispose (MsgBase, Done);
+
+ Reading := False;
+
+ If WereMsgs Then Begin
+ If Not (Mode in ['B', 'E', 'P']) And ReadRes Then
+ If ListMode = 0 Then Begin
+ Session.io.OutFull('|CR');
+ If Session.io.GetYN(Session.GetPrompt(383), False) Then
+ PostMessage (False, '');
+ End Else
+ If Session.io.GetYN(Session.GetPrompt(438), False) Then
+ PostMessage (False, '');
+ End Else
+ Case Mode of
+ 'S' : Session.io.OutFullLn (Session.GetPrompt(113));
+ 'B',
+ 'Y',
+ 'N' : Session.io.OutFullLn ('|CR' + Session.GetPrompt(113));
+ End;
+
+ Result := ReadRes;
+End;
+
+Procedure TMsgBase.PostMessage (Email: Boolean; Data: String);
+Var
+ MsgTo : String[30];
+ MsgSubj : String[60];
+ MsgAddr : String[20];
+ TempStr : String;
+ DestAddr : RecEchoMailAddr;
+ A : Integer;
+ Lines : Integer;
+ Forced : Boolean;
+ Old : MBaseRec;
+Begin
+ Old := MBase;
+
+ If Email Then Begin
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase);
+ Close (MBaseFile);
+ End;
+
+ If MBase.FileName = '' Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(110));
+ MBase := Old;
+ Exit;
+ End;
+
+ If Not Session.User.Access(MBase.PostACS) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(105));
+ MBase := Old;
+ Exit;
+ End;
+
+ Set_Node_Action (Session.GetPrompt(349));
+
+ MsgTo := '';
+ MsgSubj := '';
+ MsgAddr := '';
+ Forced := False;
+
+ For A := 1 to strWordCount(Data, ' ') Do Begin
+ TempStr := strWordGet(A, Data, ' ');
+
+ If Pos ('/F', strUpper(TempStr)) > 0 Then
+ Forced := True
+ Else
+ If Pos ('/TO:', strUpper(TempStr)) > 0 Then
+ MsgTo := strReplace(Copy(TempStr, Pos('/TO:', strUpper(TempStr)) + 4, Length(TempStr)), '_', ' ')
+ Else
+ If Pos ('/SUBJ:', strUpper(TempStr)) > 0 Then
+ MsgSubj := strReplace(Copy(TempStr, Pos('/SUBJ:', strUpper(TempStr)) + 6, Length(TempStr)), '_', ' ')
+ Else
+ If Pos('/ADDR:', strUpper(TempStr)) > 0 Then
+ MsgAddr := strReplace(Copy(TempStr, Pos('/ADDR:', strUpper(TempStr)) + 6, Length(TempStr)), '_', ' ');
+ End;
+
+ If MBase.NetType = 2 Then { UseNet Base: To = "All" }
+ MsgTo := 'All'
+ Else
+ If MBase.NetType = 3 Then Begin { NetMail Base: Get To *and* Address }
+ If MsgTo = '' Then Begin
+ Session.io.OutFull (Session.GetPrompt(119));
+ MsgTo := Session.io.GetInput (30, 30, 18, '');
+ End;
+
+ If MsgAddr = '' Then Begin
+ Session.io.OutFull (Session.GetPrompt(342));
+ MsgAddr := Session.io.GetInput (20, 20, 12, '');
+ If Not strStr2Addr(MsgAddr, DestAddr) Then MsgTo := '';
+ End;
+ End Else
+ If MBase.PostType = 1 Then Begin { if the base is flagged private }
+ If MsgTo = '' Then Begin
+ Session.io.OutFull (Session.GetPrompt(450));
+ MsgTo := Session.io.GetInput (30, 30, 18, '');
+ If Not Session.User.SearchUser(MsgTo, MBase.UseReal) Then MsgTo := '';
+ End Else
+ If strUpper(MsgTo) = 'SYSOP' Then MsgTo := Config.SysopName;
+
+ If Session.User.FindUser(MsgTo, False) Then Begin
+ Session.io.PromptInfo[1] := MsgTo;
+ Session.io.OutFullLn (Session.GetPrompt(108));
+ End Else
+ MsgTo := '';
+ End Else Begin
+ Session.io.OutFull (Session.GetPrompt(119));
+ MsgTo := Session.io.GetInput (30, 30, 18, 'All');
+ End;
+
+ If MsgTo = '' Then Begin
+ MBase := Old;
+ Exit;
+ End;
+
+ If MsgSubj = '' Then
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(120));
+ MsgSubj := Session.io.GetInput (60, 60, 11, '');
+ If MsgSubj = '' Then
+ If Forced Then
+ Session.io.OutFull (Session.GetPrompt(307))
+ Else Begin
+ MBase := Old;
+ Exit;
+ End;
+ Until MsgSubj <> '';
+
+ Lines := 0;
+
+ Session.io.PromptInfo[1] := MsgTo;
+ Session.io.PromptInfo[2] := MsgSubj;
+
+ If Editor(Lines, 78, mysMaxMsgLines, False, Forced, MsgSubj) Then Begin
+ Session.io.OutFull (Session.GetPrompt(107));
+
+ { all of this below should be replaced with a SaveMessage function }
+ { the same should be used for Replying and also for TextFile post }
+ { and then the automated e-mails can be added where mystic will send }
+ { notifications out to the sysop for various things (configurable) }
+ { also could be used in mass email rewrite and qwk .REP rewrite }
+
+ If Not OpenCreateBase(MsgBase, MBase) Then Begin
+ MBase := Old;
+ Exit;
+ End;
+
+ AssignMessageData(MsgBase);
+
+ MsgBase^.SetTo (MsgTo);
+ MsgBase^.SetSubj (MsgSubj);
+
+ If MBase.NetType = 3 Then Begin
+ MsgBase^.SetDest (DestAddr);
+ MsgBase^.SetCrash (Config.netCrash);
+ MsgBase^.SetHold (Config.netHold);
+ MsgBase^.SetKillSent (Config.netKillSent);
+
+ DestAddr := Config.NetAddress[MBase.NetAddr];
+ MsgBase^.SetOrig (DestAddr);
+ End;
+
+ AppendMessageText (MsgBase, Lines, '');
+
+ If MsgBase^.WriteMsg <> 0 Then;
+
+ MsgBase^.CloseMsgBase;
+
+ If Email Then Begin
+ Session.SystemLog ('Sent Email to ' + MsgTo);
+
+ Inc (Session.User.ThisUser.Emails);
+ Inc (Session.HistoryEmails);
+
+ A := Is_User_Online(MsgTo);
+
+ If A <> 0 Then Begin
+ TempStr := Session.GetPrompt(465);
+ TempStr := strReplace(TempStr, '|&1', Session.User.ThisUser.Handle);
+ TempStr := strReplace(TempStr, '|&2', MsgSubj);
+
+ Send_Node_Message(2, strI2S(A) + ';' + TempStr, 0);
+ End;
+ End Else Begin
+ Session.SystemLog ('Posted #' + strI2S(MsgBase^.GetMsgNum) + ': "' + MsgSubj + '" to ' + strStripMCI(MBase.Name));
+
+ Inc (Session.User.ThisUser.Posts);
+ Inc (Session.HistoryPosts);
+ End;
+
+ Dispose (MsgBase, Done);
+ Session.io.OutFullLn (Session.GetPrompt(122));
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(109));
+
+ MBase := Old;
+End;
+
+Procedure TMsgBase.CheckEMail;
+Var
+ Old : MBaseRec;
+ Total : Integer;
+Begin
+ Session.io.OutFull (Session.GetPrompt(123));
+
+ Session.io.BufFlush;
+
+ Old := MBase;
+
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase);
+ Close (MBaseFile);
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If Not MsgBase^.OpenMsgBase Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(124));
+ Dispose (MsgBase, Done);
+ MBase := Old;
+ Exit;
+ End;
+
+ Total := 0;
+
+ MsgBase^.YoursFirst(Session.User.ThisUser.RealName, Session.User.ThisUser.Handle);
+
+ If MsgBase^.YoursFound Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(125));
+
+ Total := 0;
+
+ While MsgBase^.YoursFound Do Begin
+ MsgBase^.MsgStartUp;
+
+ Inc (Total);
+
+ Session.io.PromptInfo[1] := strI2S(Total);
+ Session.io.PromptInfo[2] := MsgBase^.GetFrom;
+ Session.io.PromptInfo[3] := MsgBase^.GetSubj;
+ Session.io.PromptInfo[4] := MsgBase^.GetDate;
+
+ Session.io.OutFullLn (Session.GetPrompt(126));
+
+ MsgBase^.YoursNext;
+ End;
+
+ If Session.io.GetYN (Session.GetPrompt(127), True) Then Begin
+ MsgBase^.CloseMsgBase;
+ Dispose (MsgBase, Done);
+
+ ReadMessages('E', '');
+
+ Session.io.OutFullLn (Session.GetPrompt(118));
+
+ MBase := Old;
+ Exit;
+ End;
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(124));
+
+ MsgBase^.CloseMsgBase;
+ Dispose (MsgBase, Done);
+ MBase := Old;
+End;
+
+Procedure TMsgBase.SetMessagePointers;
+Var
+ NewDate : LongInt;
+
+ Procedure UpdateBase;
+ Var
+ Found : Boolean;
+ Begin
+ Found := False;
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If MsgBase^.OpenMsgBase Then Begin
+ MsgBase^.SeekFirst(1);
+
+ While MsgBase^.SeekFound Do Begin
+ MsgBase^.MsgStartUp;
+ If DateStr2Dos(MsgBase^.GetDate) >= NewDate Then Begin
+ MsgBase^.SetLastRead(Session.User.UserNum, MsgBase^.GetMsgNum - 1);
+ Found := True;
+ Break;
+ End;
+ MsgBase^.SeekNext;
+ End;
+
+ If Not Found Then
+ MsgBase^.SetLastRead(Session.User.UserNum, MsgBase^.GetHighMsgNum);
+ End;
+
+ Dispose (MsgBase, Done);
+ End;
+
+Var
+ Global : Boolean;
+ InDate : String[8];
+Begin
+ Session.io.OutFull (Session.GetPrompt(458));
+
+ InDate := Session.io.GetInput(8, 8, 15, '');
+
+ If Not DateValid(InDate) Then Exit;
+
+ NewDate := DateStr2Dos(InDate);
+ Global := Session.io.GetYN(Session.GetPrompt(459), False);
+
+ Session.io.OutFullLn (Session.GetPrompt(460));
+
+ If Global Then Begin
+ ioReset (MBaseFile, SizeOf(MBaseRec), fmRWDN);
+ ioRead (MBaseFile, MBase);
+
+ While Not Eof(MBaseFile) Do Begin
+ ioRead (MBaseFile, MBase);
+ UpdateBase;
+ End;
+ End Else
+ UpdateBase;
+End;
+
+Procedure TMsgBase.MessageNewScan (Data : String);
+{ menu data commands: }
+{ /P : scan for personal mail in all bases }
+{ /M : scan only mandatory bases }
+{ /G : scan all bases in all groups }
+Var
+ Old : MBaseRec;
+ Mode : Char;
+ Mand : Boolean;
+Begin
+ Old := MBase;
+ Mand := False;
+
+ Reset (MBaseFile);
+
+ If Pos ('/P', Data) > 0 Then Begin
+ Mode := 'P';
+
+ Session.SystemLog ('Scan for personal messages');
+ End Else Begin
+ Mand := Pos('/M', Data) > 0;
+ Mode := 'G';
+
+ Read (MBaseFile, MBase);
+
+ Session.SystemLog ('Scan for new messages');
+ End;
+
+ Session.User.IgnoreGroup := Pos('/G', Data) > 0;
+ WereMsgs := False;
+
+ Session.io.OutRawLn ('');
+
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, MBase);
+
+ If Session.User.Access(MBase.ReadACS) Then Begin
+ GetMessageScan;
+
+ If ((Mand) and (MBase.DefNScan = 2)) or ((Not Mand) and (MScan.NewScan > 0)) Then Begin
+ Session.io.OutBS (Screen.CursorX, True);
+ Session.io.OutFull (Session.GetPrompt(130));
+
+ If Not ReadMessages(Mode, '') Then Begin
+ Session.io.OutRawLn('');
+ Break;
+ End;
+
+ If WereMsgs Then Session.io.OutRawLn('');
+ End;
+ End;
+ End;
+
+ If Not WereMsgs Then Session.io.OutFullLn('|CR');
+
+ Session.io.OutFull (Session.GetPrompt(131));
+
+ Close (MBaseFile);
+
+ Session.User.IgnoreGroup := False;
+ MBase := OLD;
+End;
+
+Procedure TMsgBase.GlobalMessageSearch (Mode : Char);
+{ C = current area }
+{ G = all areas in group }
+{ A = all areas in all groups }
+Var
+ SearchStr : String;
+ Old : MBaseRec;
+Begin
+ If Not (Mode in ['A', 'C', 'G']) Then Mode := 'G';
+
+ Session.io.OutFull (Session.GetPrompt(310));
+
+ SearchStr := Session.io.GetInput(50, 50, 12, '');
+
+ If SearchStr = '' Then Exit;
+
+ OLD := MBase;
+ WereMsgs := False;
+ Session.User.IgnoreGroup := Mode = 'A';
+
+ If Mode = 'C' Then
+ ReadMessages('S', SearchStr)
+ Else Begin
+ Session.io.OutRawLn ('');
+
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase); {skip email base}
+
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, MBase);
+
+ If Session.User.Access(MBase.ReadACS) Then Begin
+ GetMessageScan;
+
+ If MScan.NewScan > 0 Then Begin
+ If Not ReadMessages('T', SearchStr) Then Begin
+ Session.io.OutRawLn('');
+ Break;
+ End;
+
+ If WereMsgs Then Session.io.OutRawLn('');
+ End;
+ End;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(311));
+ Close (MBaseFile);
+ End;
+
+ Session.User.IgnoreGroup := False;
+ MBase := OLD;
+End;
+
+Procedure TMsgBase.SendMassEmail;
+Var
+ Mode : Char;
+ Names : Array[1..25] of String[35];
+ NamePos : SmallInt;
+ ACS : String[20];
+ Str : String[30];
+ A : SmallInt;
+ MsgFrom : String[30];
+ MsgTo : String[30];
+ MsgSubj : String[60];
+ Lines : Integer;
+ Old : MBaseRec;
+ OldUser : RecUser;
+
+ Procedure Write_Mass_Msg;
+ Begin
+ Session.SystemLog ('Sending mass mail to ' + MsgTo);
+
+ AssignMessageData(MsgBase);
+
+ MsgBase^.SetFrom (MsgFrom);
+ MsgBase^.SetTo (MsgTo);
+ MsgBase^.SetSubj (MsgSubj);
+
+ AppendMessageText (MsgBase, Lines, '');
+
+ If MsgBase^.WriteMsg <> 0 Then;
+ End;
+
+Begin
+ MsgFrom := Session.User.ThisUser.Handle;
+
+ Session.io.OutFull (Session.GetPrompt(387));
+
+ Mode := Session.io.OneKey('123Q', True);
+
+ Case Mode of
+ '1' : Begin
+ Session.io.OutFull (Session.GetPrompt(388));
+ ACS := Session.io.GetInput(20, 20, 11, '');
+
+ If ACS = '' Then Exit;
+
+ Session.io.OutFullLn (Session.GetPrompt(391));
+
+ OldUser := Session.User.ThisUser;
+
+ Reset (Session.User.UserFile);
+ While Not Eof(Session.User.UserFile) Do Begin
+ If (Session.User.ThisUser.Flags AND UserDeleted = 0) and Session.User.Access(ACS) Then Begin
+ Read (Session.User.UserFile, Session.User.ThisUser);
+ Session.io.PromptInfo[1] := Session.User.ThisUser.Handle;
+ Session.io.OutFullLn (Session.GetPrompt(392));
+ End;
+ End;
+ Close (Session.User.UserFile);
+
+ Session.User.ThisUser := OldUser;
+
+ If Not Session.io.GetYN(Session.GetPrompt(393), True) Then
+ Exit;
+ End;
+ '2' : Begin
+ NamePos := 0;
+
+ Session.io.OutFull (Session.GetPrompt(389));
+
+ While NamePos < 25 Do Begin
+ Session.io.PromptInfo[1] := strI2S(NamePos);
+ Session.io.OutFull (Session.GetPrompt(390));
+ Str := Session.io.GetInput (30, 30, 18, '');
+ If Str <> '' Then Begin
+ If Session.User.SearchUser(Str, MBase.UseReal) Then Begin
+ Inc (NamePos);
+ Names[NamePos] := Str;
+ End;
+ End Else
+ If NamePos = 0 Then
+ Exit
+ Else
+ Break;
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(391));
+
+ For A := 1 to NamePos Do Begin
+ Session.io.PromptInfo[1] := Names[A];
+ Session.io.OutFullLn (Session.GetPrompt(392));
+ End;
+
+ If Not Session.io.GetYN(Session.GetPrompt(393), True) Then
+ Exit;
+ End;
+ '3' : Begin
+ Mode := '1';
+ ACS := '^';
+ End;
+ 'Q' : Exit;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(416));
+
+ MsgSubj := Session.io.GetInput (60, 60, 11, '');
+
+ If MsgSubj = '' Then Exit;
+
+ Session.io.PromptInfo[1] := 'Mass Mail';
+ Session.io.PromptInfo[2] := MsgSubj;
+
+ Lines := 0;
+
+ If Editor(Lines, 78, mysMaxMsgLines, False, False, MsgSubj) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(394));
+
+ OLD := MBase;
+
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase);
+ Close (MBaseFile);
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If Not MsgBase^.OpenMsgBase Then
+ If Not MsgBase^.CreateMsgBase (MBase.MaxMsgs, MBase.MaxAge) Then Begin
+ Dispose (MsgBase, Done);
+ MBase := Old;
+ Exit;
+ End Else
+ If Not MsgBase^.OpenMsgBase Then Begin
+ Dispose (MsgBase, Done);
+ MBase := Old;
+ Exit;
+ End;
+
+ Case Mode of
+ '1' : Begin
+ OldUser := Session.User.ThisUser;
+
+ Reset (Session.User.UserFile);
+ While Not Eof(Session.User.UserFile) Do Begin
+ Read (Session.User.UserFile, Session.User.ThisUser);
+ If (Session.User.ThisUser.Flags AND UserDeleted = 0) and Session.User.Access(ACS) Then Begin
+ MsgTo := Session.User.ThisUser.Handle;
+ Session.User.ThisUser := OldUser;
+ Write_Mass_Msg;
+ {// appends wrong autosig so we add thisuser := olduser?}
+ // shitty kludge all of these var swaps should be
+ // rewritten.. probably do away with global MBAse records
+ End;
+ End;
+ Close (Session.User.UserFile);
+
+ Session.User.ThisUser := OldUser;
+ End;
+ '2' : For A := 1 to NamePos Do Begin
+ MsgTo := Names[A];
+ Write_Mass_Msg;
+ End;
+ End;
+
+ MsgBase^.CloseMsgBase;
+
+ Dispose (MsgBase, Done);
+ End;
+End;
+
+Procedure TMsgBase.ViewSentEmail;
+Var
+ Old : MBaseRec;
+Begin
+ Old := MBase;
+
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase);
+ Close (MBaseFile);
+
+ ReadMessages('B', '');
+
+ MBase := Old;
+End;
+
+{ QWK OPTIONS }
+
+// this unbuffered foulness should be rewritten... if only people actually
+// used QWK... low priority. also it doesnt copy the welcome, etc files.
+
+Procedure TMsgBase.WriteCONTROLDAT;
+Const
+ CRLF = #13#10; { for eventually having option for linux OR dos text files }
+Var
+ tFile : Text;
+Begin
+ Assign (tFile, Session.TempPath + 'control.dat');
+ ReWrite (tFile);
+
+ Write (tFile, Config.BBSName + CRLF);
+ Write (tFile, CRLF); {bbs City/State}
+ Write (tFile, CRLF); {bbs Phone number}
+ Write (tFile, Config.SysopName + CRLF);
+ Write (tFile, '0,' + Config.qwkBBSID + CRLF);
+ Write (tFile, DateDos2Str(CurDateDos, 1), ',', TimeDos2Str(CurDateDos, False) + CRLF);
+ Write (tFile, strUpper(Session.User.ThisUser.Handle) + CRLF);
+ Write (tFile, CRLF);
+ Write (tFile, '0' + CRLF); {What is this line?}
+ Write (tFile, TotalMsgs, CRLF); {TOTAL MSG IN PACKET}
+ Write (tFile, TotalConf - 1, CRLF); {TOTAL CONF - 1}
+
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase); {SKIP EMAIL BASE}
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, MBase);
+ If Session.User.Access(MBase.ReadACS) Then Begin
+ GetMessageScan;
+ If MScan.QwkScan > 0 Then Begin
+ Write (tFile, MBase.Index, CRLF); {conf #}
+ Write (tFile, MBase.QwkName, CRLF); {conf name}
+ End;
+ End;
+ End;
+
+ Write (tFile, Config.qwkWelcome + CRLF);
+ Write (tFile, Config.qwkNews + CRLF);
+ Write (tFile, Config.qwkGoodbye + CRLF);
+ Close (tFile);
+End;
+
+{ converts TP real to Microsoft 4 bytes single }
+{ what kind of stupid standard uses this var type!? }
+
+Procedure Long2msb (Index : LongInt; Var MS : BSingle);
+Var
+ Exp : Byte;
+Begin
+ If Index <> 0 Then Begin
+ Exp := 0;
+
+ While Index And $800000 = 0 Do Begin
+ Inc (Exp);
+ Index := Index SHL 1
+ End;
+
+ Index := Index And $7FFFFF;
+ End Else
+ Exp := 152;
+
+ MS[0] := Index AND $FF;
+ MS[1] := (Index SHR 8) AND $FF;
+ MS[2] := (Index SHR 16) AND $FF;
+ MS[3] := 152 - Exp;
+End;
+
+Function TMsgBase.WriteMSGDAT : LongInt;
+{ returns last message added to qwk packet }
+Var
+ DataFile : File;
+ NdxFile : File of QwkNdxHdr;
+ NdxHdr : QwkNdxHdr;
+ QwkHdr : QwkDATHdr;
+ Temp : String;
+ MsgAdded : Integer; {# of message added in packet}
+ LastRead : LongInt;
+ BufStr : String[128];
+ Blocks : Word;
+ Index : LongInt;
+ Count : SmallInt;
+Begin
+ Inc (TotalConf);
+
+ MsgAdded := 0;
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If Not MsgBase^.OpenMsgBase Then Begin
+ Dispose (MsgBase, Done);
+ Exit;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(231));
+
+ Assign (DataFile, Session.TempPath + 'messages.dat');
+ Reset (DataFile, 1);
+ Seek (DataFile, FileSize(DataFile));
+
+ LastRead := MsgBase^.GetLastRead(Session.User.UserNum) + 1;
+
+ MsgBase^.SeekFirst(LastRead);
+ While MsgBase^.SeekFound Do Begin
+ If ((Config.qwkMaxBase > 0) and (MsgAdded = Config.qwkMaxBase)) or
+ ((Config.qwkMaxPacket > 0) and (TotalMsgs = Config.qwkMaxPacket)) Then Break;
+
+ FillChar (QwkHdr, 128, ' ');
+
+ MsgBase^.MsgStartUp;
+
+ If MsgBase^.IsPriv Then
+ If Not ((MsgBase^.GetTo = Session.User.ThisUser.RealName) or (MsgBase^.GetTo = Session.User.ThisUser.Handle)) Then Begin
+ MsgBase^.SeekNext;
+ Continue;
+ End;
+
+ Inc (MsgAdded);
+ Inc (TotalMsgs);
+
+ LastRead := MsgBase^.GetMsgNum;
+
+ Temp := strPadR(strUpper(MsgBase^.GetFrom), 25, ' ');
+ Move (Temp[1], QwkHdr.UPFrom, 25);
+ Temp := strPadR(strUpper(MsgBase^.GetTo), 25, ' ');
+ Move (Temp[1], QwkHdr.UPTo, 25);
+ Temp := strPadR(MsgBase^.GetSubj, 25, ' ');
+ Move (Temp[1], QwkHdr.Subject, 25);
+ Temp := MsgBase^.GetDate;
+ Move (Temp[1], QwkHdr.Date, 8);
+ Temp := MsgBase^.GetTime;
+ Move (Temp[1], QwkHdr.Time, 5);
+ Temp := strPadR(strI2S(MsgBase^.GetMsgNum), 7, ' ');
+ Move (Temp[1], QwkHdr.MSGNum, 7);
+ Temp := strPadR(strI2S(MsgBase^.GetRefer), 8, ' ');
+ Move (Temp[1], QwkHdr.ReferNum, 8);
+
+ QwkHdr.Active := #225;
+ QwkHdr.ConfNum := MBase.Index;
+ QwkHdr.Status := ' ';
+
+ MsgBase^.MsgTxtStartUp;
+
+ Blocks := MsgBase^.GetTextLen DIV 128;
+ If MsgBase^.GetTextLen MOD 128 > 0 Then Inc(Blocks, 2) Else Inc(Blocks);
+ Temp := strPadR(strI2S(Blocks), 6, ' ');
+ Move (Temp[1], QwkHdr.NumChunk, 6);
+
+ If MsgAdded = 1 Then Begin
+ Assign (NdxFile, Session.TempPath + strPadL(strI2S(MBase.Index), 3, '0') + '.ndx');
+ ReWrite (NdxFile);
+ End;
+
+ Index := FileSize(DataFile) DIV 128 + 1;
+
+ long2msb (Index, NdxHdr.MsgPos);
+
+ Write (NdxFile, NdxHdr);
+
+ BlockWrite (DataFile, QwkHdr, 128);
+
+ BufStr := '';
+
+ While Not MsgBase^.EOM Do Begin
+ Temp := MsgBase^.GetString(79) + #227;
+
+ If Temp[1] = #1 Then Continue;
+
+ For Count := 1 to Length(Temp) Do Begin
+ BufStr := BufStr + Temp[Count];
+
+ If BufStr[0] = #128 Then Begin
+ BlockWrite (DataFile, BufStr[1], 128);
+ BufStr := '';
+ End;
+ End;
+ End;
+
+ If BufStr <> '' Then Begin
+ BufStr := strPadR(BufStr, 128, ' ');
+ BlockWrite (DataFile, BufStr[1], 128);
+ End;
+
+ MsgBase^.SeekNext;
+ End;
+
+ Close (DataFile);
+
+ If MsgAdded > 0 Then Close (NdxFile);
+
+ Session.io.PromptInfo[1] := strI2S(MBase.Index);
+ Session.io.PromptInfo[2] := MBase.Name;
+ Session.io.PromptInfo[3] := MBase.QwkName;
+ Session.io.PromptInfo[4] := strI2S(MsgBase^.NumberOfMsgs);
+ Session.io.PromptInfo[5] := strI2S(MsgAdded);
+
+ MsgBase^.CloseMsgBase;
+ Dispose (MsgBase, Done);
+
+ Session.io.OutBS (Screen.CursorX, True);
+ Session.io.OutFullLn (Session.GetPrompt(232));
+
+ Result := LastRead;
+End;
+
+Procedure TMsgBase.UploadREP;
+Var
+ DataFile : File;
+ OldMBase : MBaseRec;
+ QwkHdr : QwkDATHdr;
+ Temp : String[128];
+ A : SmallInt;
+ B : SmallInt;
+ Chunks : SmallInt;
+Begin
+ If Session.LocalMode Then
+ Session.FileBase.ExecuteArchive (Config.QWKPath + Config.qwkBBSID + '.rep', Session.User.ThisUser.Archive, FileMask{TempPath}, 2)
+ Else Begin
+ If Session.FileBase.SelectProtocol(False) = 'Q' Then Exit;
+
+ Session.FileBase.ExecuteProtocol(False, Session.TempPath + Config.qwkBBSID + '.rep');
+
+ If Not Session.FileBase.dszSearch(Config.qwkBBSID + '.rep') Then Begin
+ Session.io.PromptInfo[1] := Config.qwkBBSID + '.rep';
+ Session.io.OutFullLn (Session.GetPrompt(84));
+ Exit;
+ End;
+
+ Session.FileBase.ExecuteArchive (Session.TempPath + Config.qwkBBSID + '.rep', Session.User.ThisUser.Archive, FileMask, 2)
+ End;
+
+ Assign (DataFile, Session.TempPath + Config.qwkBBSID + '.msg');
+ {$I-} Reset (DataFile, 1); {$I+}
+ If IoResult <> 0 Then Begin
+ Session.io.OutFull (Session.GetPrompt(238));
+ CleanDirectory(Session.TempPath, '');
+ Exit;
+ End;
+
+ BlockRead (DataFile, Temp[1], 128);
+ Temp[0] := #128;
+ If Pos(strUpper(Config.qwkBBSID), strUpper(Temp)) = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(239));
+ Close (DataFile);
+ CleanDirectory(Session.TempPath, '');
+ Exit;
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(240));
+
+ OldMBase := MBase;
+
+ While Not Eof(DataFile) Do Begin
+ BlockRead (DataFile, QwkHdr, SizeOf(QwkHdr));
+ Move (QwkHdr.MsgNum, Temp[1], 7);
+ Temp[0] := #7;
+
+ Reset (MBaseFile);
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, MBase);
+ If (strS2I(Temp) = MBase.Index) and (Session.User.Access(MBase.PostACS)) Then Begin
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If MsgBase^.OpenMsgBase Then Begin
+
+ AssignMessageData(MsgBase);
+
+ Temp[0] := #25;
+ Move (QwkHdr.UpTo, Temp[1], 25);
+ MsgBase^.SetTo(strStripR(Temp, ' '));
+ Move (QwkHdr.Subject, Temp[1], 25);
+ MsgBase^.SetSubj(strStripR(Temp, ' '));
+ Move (QwkHdr.ReferNum, Temp[1], 6);
+ Temp[0] := #6;
+ MsgBase^.SetRefer(strS2I(strStripR(Temp, ' ')));
+
+ Move(QwkHdr.NumChunk, Temp[1], 6);
+ Chunks := strS2I(Temp) - 1;
+
+ For A := 1 to Chunks Do Begin
+ BlockRead(DataFile, Temp[1], 128);
+ Temp[0] := #128;
+ Temp := strStripR(Temp, ' ');
+ For B := 1 to Length(Temp) Do Begin
+ If Temp[B] = #227 Then Temp[B] := #13;
+ MsgBase^.DoChar(Temp[B]);
+ End;
+ End;
+
+ If MBase.NetType > 0 Then Begin
+ MsgBase^.DoStringLn(#13 + '--- ' + mysSoftwareID + ' BBS v' + mysVersion + ' (' + OSID + ')');
+ MsgBase^.DoStringLn(' * Origin: ' + ResolveOrigin(MBase) + ' (' + strAddr2Str(Config.NetAddress[MBase.NetAddr]) + ')');
+ End;
+
+ If MsgBase^.WriteMsg <> 0 Then;
+ MsgBase^.CloseMsgBase;
+
+ Inc (Session.User.ThisUser.Posts);
+ End;
+ Dispose (MsgBase, Done);
+ Break;
+ End;
+ End;
+ Close (MBaseFile);
+ End;
+
+ Close (DataFile);
+ CleanDirectory (Session.TempPath, '');
+
+ MBase := OldMBase;
+End;
+
+Procedure TMsgBase.DownloadQWK (Data: String);
+Type
+ QwkLRRec = Record
+ Base : Word;
+ Pos : LongInt;
+ End;
+Var
+ Old : MBaseRec;
+ DataFile : File;
+ Temp : String;
+ QwkLR : QwkLRRec;
+ QwkLRFile : File of QwkLRRec;
+Begin
+ If Session.User.ThisUser.QwkFiles Then
+ Session.FileBase.ExportFileList(True, True);
+
+ Old := MBase;
+ Temp := strPadR('Produced By ' + mysSoftwareID + ' BBS v' + mysVersion + '. ' + CopyID, 128, ' ');
+
+ Assign (DataFile, Session.TempPath + 'messages.dat');
+ ReWrite (DataFile, 1);
+ BlockWrite (DataFile, Temp[1], 128);
+ Close (DataFile);
+
+ Assign (QwkLRFile, Session.TempPath + 'qlr.dat');
+ ReWrite (QwkLRFile);
+
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase); {Skip Email base}
+
+ Session.io.OutFullLn (Session.GetPrompt(230));
+
+ TotalMsgs := 0;
+ TotalConf := 0;
+ Session.User.IgnoreGroup := Pos('/ALLGROUP', strUpper(Data)) > 0;
+
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, MBase);
+ If Session.User.Access(MBase.ReadACS) Then Begin
+ GetMessageScan;
+ If MScan.QwkScan > 0 Then Begin
+ QwkLR.Base := FilePos(MBaseFile);
+ QwkLR.Pos := WriteMsgDAT;
+ Write (QwkLRFile, QwkLR);
+ End;
+ End;
+ End;
+
+ WriteControlDAT;
+
+ Close (QwkLRFile);
+
+ If TotalMsgs > 0 Then Begin
+ Session.io.PromptInfo[1] := strI2S(TotalMsgs);
+ Session.io.PromptInfo[2] := strI2S(TotalConf);
+ Session.io.OutFullLn (Session.GetPrompt(233));
+
+ Temp := Config.qwkBBSID + '.qwk';
+
+ Session.io.OutFullLn (Session.GetPrompt(234));
+
+ Session.io.PromptInfo[1] := Temp;
+
+ If Session.LocalMode Then Begin
+ Session.FileBase.ExecuteArchive (Config.QWKPath + Temp, Session.User.ThisUser.Archive, Session.TempPath + FileMask, 1);
+ Session.io.OutFullLn (Session.GetPrompt(235));
+ End Else Begin
+ Session.FileBase.ExecuteArchive (Session.TempPath + Temp, Session.User.ThisUser.Archive, Session.TempPath + FileMask, 1);
+ Session.FileBase.SendFile (Session.TempPath + Temp);
+ End;
+
+ If Session.io.GetYN (Session.GetPrompt(236), True) Then Begin
+ Reset (MBaseFile);
+ Reset (QwkLRFile);
+
+ While Not Eof(QwkLRFile) Do Begin
+ Read (QwkLRFile, QwkLR);
+ Seek (MBaseFile, QwkLR.Base - 1);
+ Read (MBaseFile, MBase);
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If MsgBase^.OpenMsgBase Then Begin
+ MsgBase^.SetLastRead (Session.User.UserNum, QwkLR.Pos);
+ MsgBase^.CloseMsgBase;
+ End;
+
+ Dispose(MsgBase, Done);
+ End;
+ Close (QwkLRFile);
+ End;
+ End Else
+ Session.io.OutFullLn (Session.GetPrompt(228));
+
+ Session.User.IgnoreGroup := False;
+
+ Close (MBaseFile);
+ MBase := Old;
+
+ CleanDirectory (Session.TempPath, '');
+End;
+
+(*
+// not completed or documented. is this worth bothering with? pcboard style
+Procedure TMsgBase.Message_QuickScan (Mode: Char);
+{ C = Current G = Group A = All Areas/Groups }
+{ ADD: /NEW show only if new }
+{ ADD: /YOU show only if new to you }
+{ ADD for prompts: /NOSCAN }{ ADD: /NOFOOT }{ ADD: /NOHEAD }
+Const
+ Global_CurBase : LongInt = 1;
+ Global_TotalBases : LongInt = 1;
+ Global_TotalMsgs : LongInt = 0;
+ Global_NewMsgs : LongInt = 0;
+ Global_YourMsgs : LongInt = 0;
+
+ Procedure ScanBase;
+ Var
+ MsgBase : PMsgBaseABS;
+ NewMsgs : LongInt;
+ YourMsgs : LongInt;
+ TotalMsgs : LongInt;
+ MsgTo : String;
+ Begin
+ Session.io.PromptInfo[1] := MBase.Name;
+ Session.io.PromptInfo[2] := strI2S(Global_CurBase);
+ Session.io.PromptInfo[3] := strI2S(Global_TotalBases);
+
+ NewMsgs := 0;
+ YourMsgs := 0;
+ TotalMsgs := 0;
+
+ Session.io.OutFull('Scanning |&1 [|&2 of |&3]...');
+
+ Case MBase.BaseType of
+ 0 : MsgBase := New(PMsgBaseJAM, Init);
+ 1 : MsgBase := New(PMsgBaseSquish, Init);
+ End;
+
+ MsgBase^.SetMsgPath (MBase.Path + MBase.FileName);
+
+ If MsgBase^.OpenMsgBase Then Begin
+ TotalMsgs := MsgBase^.NumberOfMsgs;
+
+ MsgBase^.SeekFirst(MsgBase^.GetLastRead(Session.User.UserNum) + 1);
+
+ While MsgBase^.SeekFound Do Begin
+ Inc (NewMsgs);
+
+ MsgBase^.MsgStartUp;
+
+ MsgTo := strUpper(MsgBase^.GetTo);
+
+ If (MsgTo = strUpper(Session.User.ThisUser.Handle)) or (MsgTo = strUpper(Session.User.ThisUser.RealName)) Then
+ Inc(YourMsgs);
+
+ MsgBase^.SeekNext;
+ End;
+
+ MsgBase^.CloseMsgBase;
+ End;
+
+ Inc (Global_TotalMsgs, TotalMsgs);
+ Inc (Global_NewMsgs, NewMsgs);
+ Inc (Global_YourMsgs, YourMsgs);
+
+ Session.io.PromptInfo[4] := strI2S(TotalMsgs);
+ Session.io.PromptInfo[5] := strI2S(NewMsgs);
+ Session.io.PromptInfo[6] := strI2S(YourMsgs);
+ Session.io.PromptInfo[7] := strI2S(Global_TotalMsgs);
+ Session.io.PromptInfo[8] := strI2S(Global_NewMsgs);
+ Session.io.PromptInfo[9] := strI2S(Global_YourMsgs);
+
+ Session.io.OutBS(Screen.CursorX, True);
+ Session.io.OutFullLn('|03Base: |14|$R40|&1 |03Total: |09|$L04|&4|03 New: |11|$L04|&5 |03Yours: |12|$L03|&6');
+
+ Dispose (MsgBase, Done);
+ End;
+
+Var
+ Old : MBaseRec;
+Begin
+ If Not (Mode in ['A', 'C', 'G']) Then Mode := 'G';
+
+ Old := MBase;
+ Session.User.IgnoreGroup := Mode = 'A';
+
+ Session.io.OutFullLn ('|CRStarting Quick Scan|CR');
+
+ If Mode = 'C' Then
+ ScanBase
+ Else Begin
+ Reset (MBaseFile);
+ Read (MBaseFile, MBase); {skip email base}
+
+ Global_TotalBases := FileSize(MBaseFile);
+
+ While Not Eof(MBaseFile) Do Begin
+ Read (MBaseFile, MBase);
+
+ Global_CurBase := FilePos(MBaseFile);
+
+ If Session.User.Access(MBase.ReadACS) Then Begin
+ GetMessageScan;
+ If MScan.NewScan > 0 Then ScanBase;
+ End;
+ End;
+
+ Close (MBaseFile);
+ End;
+
+ Session.io.OutFullLn('|CRQuick Scan complete. |PA');
+
+ Session.User.IgnoreGroup := False;
+ MBase := Old;
+End;
+*)
+
+Function TMsgBase.SaveMessage (mArea: MBaseRec; mFrom, mTo, mSubj: String; mAddr: RecEchoMailAddr; mLines: Integer) : Boolean;
+Var
+ SemFile : File;
+ Count : SmallInt;
+ Msg : PMsgBaseABS;
+Begin
+// things to do:
+// 2) see if we can use assignmessagedata, etc
+// 3) add autosig? if we cannot use the assignmsgdata things
+ Result := False;
+
+ If Not OpenCreateBase(Msg, mArea) Then Exit;
+
+ Msg^.StartNewMsg;
+ Msg^.SetLocal (True);
+
+ If mArea.NetType > 0 Then Begin
+ If mArea.NetType = 2 Then Begin
+ Msg^.SetMailType (mmtNetMail);
+ Msg^.SetCrash (Config.netCrash);
+ Msg^.SetHold (Config.netHold);
+ Msg^.SetKillSent (Config.netKillSent);
+ Msg^.SetDest (mAddr);
+ End Else
+ Msg^.SetMailType (mmtEchoMail);
+
+ Msg^.SetOrig(Config.NetAddress[mArea.NetAddr]);
+
+ Case mArea.NetType of
+ 1 : Assign (SemFile, Config.SemaPath + 'echomail.now');
+ 2 : Assign (SemFile, Config.SemaPath + 'netmail.now');
+ 3 : Assign (SemFile, Config.SemaPath + 'newsmail.now');
+ End;
+
+ ReWrite (SemFile);
+ Close (SemFile);
+ End Else
+ Msg^.SetMailType (mmtNormal);
+
+ Msg^.SetPriv (mArea.PostType = 1);
+ Msg^.SetDate (DateDos2Str(CurDateDos, 1));
+ Msg^.SetTime (TimeDos2Str(CurDateDos, False));
+ Msg^.SetFrom (mFrom);
+ Msg^.SetTo (mTo);
+ Msg^.SetSubj (mSubj);
+
+ For Count := 1 to mLines Do
+ Msg^.DoStringLn(MsgText[Count]);
+
+ If mArea.NetType > 0 Then Begin
+ Msg^.DoStringLn (#13 + '--- ' + mysSoftwareID + ' BBS v' + mysVersion + ' (' + OSID + ')');
+ Msg^.DoStringLn (' * Origin: ' + ResolveOrigin(mArea) + ' (' + strAddr2Str(Config.NetAddress[mArea.NetAddr]) + ')');
+ End;
+
+ Msg^.WriteMsg;
+ Msg^.CloseMsgBase;
+
+ Dispose (Msg, Done);
+
+ Result := True;
+End;
+
+Procedure TMsgBase.PostTextFile (Data: String; AllowCodes: Boolean);
+Const
+ MaxLines = 10000;
+Var
+ MBaseFile : File;
+ mName : String;
+ mArea : Word;
+ mFrom : String;
+ mTo : String;
+ mSubj : String;
+ mAddr : RecEchoMailAddr;
+ mLines : Integer;
+ InFile : Text;
+ TextBuf : Array[1..2048] of Char;
+ Buffer : Array[1..MaxLines] of ^String;
+ Str : String[79];
+ Lines : Integer;
+ Pages : Integer;
+ Count : Integer;
+ Offset : Integer;
+ TempBase : MBaseRec;
+Begin
+ mName := strWordGet(1, Data, ';');
+ mArea := strS2I(strWordGet(2, Data, ';'));
+ mFrom := strWordGet(3, Data, ';');
+ mTo := strWordGet(4, Data, ';');
+ mSubj := strWordGet(5, Data, ';');
+
+ Str := strWordGet(6, Data, ';');
+ If (Str = '') Then Str := '0:0/0';
+ strStr2Addr (Str, mAddr);
+
+ If FileExist(Config.DataPath + mName) Then
+ mName := Config.DataPath + mName
+ Else
+ If Not FileExist(mName) Then Begin
+ Session.SystemLog('AutoPost: ' + mName + ' not found');
+ Exit;
+ End;
+
+ Assign (MBaseFile, Config.DataPath + 'mbases.dat');
+ ioReset (MBaseFile, SizeOf(MBaseRec), fmReadWrite + fmDenyNone);
+
+ If Not ioSeek (MBaseFile, mArea) Then Begin
+ Close (MBaseFile);
+ Exit;
+ End;
+
+ If Not ioRead (MBaseFile, TempBase) Then Begin
+ Close (MBaseFile);
+ Exit;
+ End;
+
+ Close (MBaseFile);
+
+ Assign (InFile, mName);
+ SetTextBuf (InFile, TextBuf, SizeOf(TextBuf));
+ Reset (InFile);
+
+ Lines := 0;
+
+ While Not Eof(InFile) And (Lines < MaxLines) Do Begin
+ ReadLn (InFile, Str);
+
+ If AllowCodes Then Str := Session.io.StrMci(Str);
+
+ Inc (Lines);
+ New (Buffer[Lines]);
+
+ Buffer[Lines]^ := Str;
+ End;
+
+ Close (InFile);
+
+ Pages := Lines DIV mysMaxMsgLines + 1;
+
+ If (Lines MOD mysMaxMsgLines = 0) Then Dec(Pages);
+
+ For Count := 1 to Pages Do Begin
+ Offset := mysMaxMsgLines * Pred(Count);
+ mLines := 0;
+
+ While (Offset < Lines) and (mLines < mysMaxMsgLines) Do Begin
+ Inc (mLines);
+ Inc (Offset);
+
+ MsgText[mLines] := Buffer[Offset]^;
+ End;
+
+ If Pages > 1 Then
+ Str := mSubj + ' (' + strI2S(Count) + '/' + strI2S(Pages) + ')'
+ Else
+ Str := mSubj;
+
+ If Not SaveMessage (TempBase, mFrom, mTo, Str, mAddr, mLines) Then Break;
+ End;
+
+ While Lines > 0 Do Begin
+ Dispose (Buffer[Lines]);
+ Dec (Lines);
+ End;
+End;
+
+Function TMsgBase.ResolveOrigin (Var mArea: MBaseRec) : String;
+Var
+ Loc : Byte;
+ FN : String;
+ TF : Text;
+ Buf : Array[1..2048] of Char;
+ Str : String;
+ Count : LongInt;
+ Pick : LongInt;
+Begin
+ Result := '';
+ Loc := Pos('@RANDOM=', mArea.Origin);
+
+ If Loc > 0 Then Begin
+ FN := strStripB(Copy(mArea.Origin, Loc + 8, 255), ' ');
+
+ If Pos(PathChar, FN) = 0 Then FN := Config.DataPath + FN;
+
+ FileMode := 66;
+
+ Assign (TF, FN);
+ SetTextBuf (TF, Buf, SizeOf(Buf));
+ Reset (TF);
+
+ If IoResult <> 0 Then Exit;
+
+ Count := 0;
+
+ While Not Eof(TF) Do Begin
+ ReadLn (TF, Str);
+
+ If strStripB(Str, ' ') = '' Then Continue;
+
+ Inc (Count);
+ End;
+
+ If Count = 0 Then Begin
+ Close (TF);
+ Exit;
+ End;
+
+ Pick := Random(Count) + 1;
+
+ Reset (TF);
+
+ Count := 0;
+
+ While Not Eof(TF) Do Begin
+ ReadLn (TF, Str);
+
+ If strStripB(Str, ' ') = '' Then Continue;
+
+ Inc (Count);
+
+ If Count = Pick Then Begin
+ Result := Str;
+ Break;
+ End;
+ End;
+
+ Close (TF);
+ End Else
+ Result := mArea.Origin;
+End;
+
+End.
diff --git a/mystic/bbs_msgbase_abs.pas b/mystic/bbs_msgbase_abs.pas
new file mode 100644
index 0000000..3083cba
--- /dev/null
+++ b/mystic/bbs_msgbase_abs.pas
@@ -0,0 +1,608 @@
+{$I M_OPS.PAS}
+{$WARNINGS OFF}
+
+Unit BBS_MsgBase_ABS;
+
+Interface
+
+Uses
+ BBS_Common;
+
+Type
+ MsgMailType = (mmtNormal, mmtEchoMail, mmtNetMail);
+
+ PMsgBaseABS = ^TMsgBaseABS;
+ TMsgBaseABS = Object
+ LastSoft : Boolean;
+ TempFile : String;
+
+ Procedure EditMsgInit; Virtual;
+ Procedure EditMsgSave; Virtual;
+
+ Constructor Init; {Initialize}
+ Destructor Done; Virtual; {Done}
+ Procedure SetMsgPath(MP: String); Virtual; {Set msg path/other info}
+ Function OpenMsgBase : Boolean; Virtual; {Open the message base}
+ Procedure CloseMsgBase; Virtual; {Close the message base}
+ Function CreateMsgBase(MaxMsg: Word; MaxDays: Word): Boolean; Virtual;
+ Function MsgBaseExists: Boolean; Virtual; {Does msg base exist}
+ Function LockMsgBase: Boolean; Virtual; {Lock the message base}
+ Function UnLockMsgBase: Boolean; Virtual; {Unlock the message base}
+ Procedure SetDest(Var Addr: RecEchoMailAddr); Virtual; {Set Zone/Net/Node/Point for Dest}
+ Procedure SetOrig(Var Addr: RecEchoMailAddr); Virtual; {Set Zone/Net/Node/Point for Orig}
+ Procedure SetFrom(Name: String); Virtual; {Set message from}
+ Procedure SetTo(Name: String); Virtual; {Set message to}
+ Procedure SetSubj(Str: String); Virtual; {Set message subject}
+ Procedure SetCost(SCost: Word); Virtual; {Set message cost}
+ Procedure SetRefer(SRefer: LongInt); Virtual; {Set message reference}
+ Procedure SetSeeAlso(SAlso: LongInt); Virtual; {Set message see also}
+ Procedure SetDate(SDate: String); Virtual; {Set message date}
+ Procedure SetTime(STime: String); Virtual; {Set message time}
+ Procedure SetLocal(LS: Boolean); Virtual; {Set local status}
+ Procedure SetRcvd(RS: Boolean); Virtual; {Set received status}
+ Procedure SetPriv(PS: Boolean); Virtual; {Set priveledge vs public status}
+ Procedure SetCrash(SS: Boolean); Virtual; {Set crash netmail status}
+ Procedure SetHold(SS: Boolean); Virtual; {Set hold netmail status}
+ Procedure SetKillSent(SS: Boolean); Virtual; {Set kill/sent netmail status}
+ Procedure SetSent(SS: Boolean); Virtual; {Set sent netmail status}
+ Procedure SetFAttach(SS: Boolean); Virtual; {Set file attach status}
+ Procedure SetReqRct(SS: Boolean); Virtual; {Set request receipt status}
+ Procedure SetReqAud(SS: Boolean); Virtual; {Set request audit status}
+ Procedure SetRetRct(SS: Boolean); Virtual; {Set return receipt status}
+ Procedure SetFileReq(SS: Boolean); Virtual; {Set file request status}
+ Procedure DoString(Str: String); Virtual; {Add string to message text}
+ Procedure DoChar(Ch: Char); Virtual; {Add character to message text}
+ Procedure DoStringLn(Str: String); Virtual; {Add string and newline to msg text}
+ Procedure DoKludgeLn(Str: String); Virtual; {Add ^A kludge line to msg}
+ Function WriteMsg: Word; Virtual; {Write msg to msg base}
+ Function GetChar: Char; Virtual; {Get msg text character}
+ Function EOM: Boolean; Virtual; {No more msg text}
+ Function GetString(MaxLen: Word): String; Virtual; {Get wordwrapped string}
+ Function GetNoKludgeStr(MaxLen: Word): String; Virtual; {Get ww str no ^A lines}
+ Function GetFrom: String; Virtual; {Get from name on current msg}
+ Function GetTo: String; Virtual; {Get to name on current msg}
+ Function GetSubj: String; Virtual; {Get subject on current msg}
+ Function GetCost: Word; Virtual; {Get cost of current msg}
+ Function GetDate: String; Virtual; {Get date of current msg}
+ Function GetTime: String; Virtual; {Get time of current msg}
+ Function GetRefer: LongInt; Virtual; {Get reply to of current msg}
+ Function GetSeeAlso: LongInt; Virtual; {Get see also of current msg}
+ Function GetNextSeeAlso: LongInt; Virtual;
+ Procedure SetNextSeeAlso(SAlso: LongInt); Virtual;
+ Function GetMsgNum: LongInt; Virtual; {Get message number}
+ Function GetTextLen: LongInt; Virtual; {Get text length}
+ Procedure GetOrig (Var Addr : RecEchoMailAddr); Virtual; {Get origin address}
+ Procedure GetDest (Var Addr : RecEchoMailAddr); Virtual; {Get destination address}
+ Function IsLocal: Boolean; Virtual; {Is current msg local}
+ Function IsCrash: Boolean; Virtual; {Is current msg crash}
+ Function IsKillSent: Boolean; Virtual; {Is current msg kill sent}
+ Function IsSent: Boolean; Virtual; {Is current msg sent}
+ Function IsFAttach: Boolean; Virtual; {Is current msg file attach}
+ Function IsReqRct: Boolean; Virtual; {Is current msg request receipt}
+ Function IsReqAud: Boolean; Virtual; {Is current msg request audit}
+ Function IsRetRct: Boolean; Virtual; {Is current msg a return receipt}
+ Function IsFileReq: Boolean; Virtual; {Is current msg a file request}
+ Function IsRcvd: Boolean; Virtual; {Is current msg received}
+ Function IsPriv: Boolean; Virtual; {Is current msg priviledged/private}
+ Function IsDeleted: Boolean; Virtual; {Is current msg deleted}
+ Function IsEchoed: Boolean; Virtual; {Is current msg unmoved echomail msg}
+ Function GetMsgLoc: LongInt; Virtual; {To allow reseeking to message}
+ Procedure SetMsgLoc(ML: LongInt); Virtual; {Reseek to message}
+ Procedure MsgStartUp; Virtual; {Do message set-up tasks}
+ Procedure MsgTxtStartUp; Virtual; {Do message text start up tasks}
+ Procedure StartNewMsg; Virtual; {Initialize for adding message}
+ Procedure SeekFirst(MsgNum: LongInt); Virtual; {Start msg seek}
+ Procedure SeekNext; Virtual; {Find next matching msg}
+ Procedure SeekPrior; Virtual; {Prior msg}
+ Function SeekFound: Boolean; Virtual; {Msg was found}
+ Procedure YoursFirst(Name: String; Handle: String); Virtual; {Seek your mail}
+ Procedure YoursNext; Virtual; {Seek next your mail}
+ Function YoursFound: Boolean; Virtual; {Message found}
+ Function GetHighMsgNum: LongInt; Virtual; {Get highest msg number}
+ Procedure SetMailType(MT: MsgMailType); Virtual; {Set message base type}
+ Function GetSubArea: Word; Virtual; {Get sub area number}
+ Procedure ReWriteHdr; Virtual; {Rewrite msg header after changes}
+ Procedure DeleteMsg; Virtual; {Delete current message}
+ Procedure SetEcho(ES: Boolean); Virtual; {Set echo status}
+ Function NumberOfMsgs: LongInt; Virtual; {Number of messages}
+ Function GetLastRead(UNum: LongInt): LongInt; Virtual; {Get last read for user num}
+ Procedure SetLastRead(UNum: LongInt; LR: LongInt); Virtual; {Set last read}
+ Function GetMsgDisplayNum: LongInt; Virtual; {Get msg number to display}
+ Function GetTxtPos: LongInt; Virtual; {Get indicator of msg text position}
+ Procedure SetTxtPos(TP: LongInt); Virtual; {Set text position}
+ Function GetHighActiveMsgNum: LongInt; Virtual; {Get highest active msg num}
+ Procedure SetTempFile (TF: String);
+ End;
+
+Implementation
+
+Procedure TMsgBaseABS.SetTempFile (TF: String);
+Begin
+ TempFile := TF;
+End;
+
+Constructor TMsgBaseABS.Init;
+Begin
+End;
+
+Destructor TMsgBaseABS.Done;
+Begin
+End;
+
+Procedure TMsgBaseABS.SetMsgPath(MP: String);
+Begin
+End;
+
+Function TMsgBaseABS.OpenMsgBase: Boolean;
+Begin
+End;
+
+Procedure TMsgBaseABS.CloseMsgBase;
+Begin
+End;
+
+Function TMsgBaseABS.LockMsgBase: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.UnLockMsgBase: Boolean;
+Begin
+End;
+
+Procedure TMsgBaseABS.SetDest(Var Addr: RecEchoMailAddr);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetOrig(Var Addr: RecEchoMailAddr);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetFrom(Name: String);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetTo(Name: String);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetSubj(Str: String);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetCost(SCost: Word);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetRefer(SRefer: LongInt);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetSeeAlso(SAlso: LongInt);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetDate(SDate: String);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetTime(STime: String);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetLocal(LS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetRcvd(RS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetPriv(PS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetHold (SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetCrash(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetKillSent(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetSent(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetFAttach(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetReqRct(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetReqAud(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetRetRct(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetFileReq(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.DoString (Str: String);
+Var
+ Count : SmallWord;
+Begin
+ For Count := 1 to Length(Str) Do
+ DoChar(Str[Count]);
+End;
+
+Procedure TMsgBaseABS.DoChar(Ch: Char);
+Begin
+End;
+
+Procedure TMsgBaseABS.DoStringLn(Str: String);
+Begin
+ DoString(Str + #13);
+// DoChar(#13);
+End;
+
+Procedure TMsgBaseABS.DoKludgeLn(Str: String);
+Begin
+ DoStringLn(Str);
+End;
+
+Function TMsgBaseABS.WriteMsg: Word;
+Begin
+End;
+
+Function TMsgBaseABS.GetChar: Char;
+Begin
+End;
+
+Function TMsgBaseABS.EOM: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.GetString(MaxLen: Word): String;
+(*
+ Var
+ WPos: LongInt;
+ WLen: Byte;
+ StrDone: Boolean;
+ TxtOver: Boolean;
+ StartSoft: Boolean;
+ CurrLen: Word;
+ PPos: LongInt;
+ TmpCh: Char;
+ OldPos: LongInt;
+
+ Begin
+ If EOM Then
+ GetString := ''
+ Else
+ Begin
+ StrDone := False;
+ CurrLen := 0;
+ PPos := GetTxtPos;
+ WPos := GetTxtPos;
+ WLen := 0;
+ StartSoft := LastSoft;
+ LastSoft := True;
+ OldPos := GetTxtPos;
+ TmpCh := GetChar;
+ While ((Not StrDone) And (CurrLen < MaxLen) And (Not EOM)) Do
+ Begin
+ Case TmpCh of
+ #$00:;
+ #$0d: Begin
+ StrDone := True;
+ LastSoft := False;
+ End;
+ #$8d:;
+ #$0a:;
+ #$20: Begin
+ If ((CurrLen <> 0) or (Not StartSoft)) Then
+ Begin
+ Inc(CurrLen);
+ WLen := CurrLen;
+ GetString[CurrLen] := TmpCh;
+ WPos := GetTxtPos;
+ End
+ Else
+ StartSoft := False;
+ End;
+ Else
+ Begin
+ Inc(CurrLen);
+ GetString[CurrLen] := TmpCh;
+ End;
+ End;
+ If Not StrDone Then
+ Begin
+ OldPos := GetTxtPos;
+ TmpCh := GetChar;
+ End;
+ End;
+ If StrDone Then
+ Begin
+ GetString[0] := Chr(CurrLen);
+ End
+ Else
+ If EOM Then
+ Begin
+ GetString[0] := Chr(CurrLen);
+ End
+ Else
+ Begin
+ If WLen = 0 Then
+ Begin
+ GetString[0] := Chr(CurrLen);
+ SetTxtPos(OldPos);
+ End
+ Else
+ Begin
+ GetString[0] := Chr(WLen);
+ SetTxtPos(WPos);
+ End;
+ End;
+ End;
+*)
+{ the above stuff could be used to write universal GETSTRING and GETCHAR }
+{ functions for ANY message base format. }
+Begin
+End;
+
+Procedure TMsgBaseABS.SeekFirst(MsgNum: LongInt);
+Begin
+End;
+
+Procedure TMsgBaseABS.SeekNext;
+Begin
+End;
+
+Function TMsgBaseABS.GetFrom: String;
+Begin
+End;
+
+Function TMsgBaseABS.GetTo: String;
+Begin
+End;
+
+Function TMsgBaseABS.GetSubj: String;
+Begin
+End;
+
+Function TMsgBaseABS.GetCost: Word;
+Begin
+End;
+
+Function TMsgBaseABS.GetDate: String;
+Begin
+End;
+
+Function TMsgBaseABS.GetTime: String;
+Begin
+End;
+
+Function TMsgBaseABS.GetRefer: LongInt;
+Begin
+End;
+
+Function TMsgBaseABS.GetSeeAlso: LongInt;
+Begin
+End;
+
+Function TMsgBaseABS.GetMsgNum: LongInt;
+Begin
+End;
+
+Function TMsgBaseABS.GetTextLen : LongInt;
+Begin
+End;
+
+Procedure TMsgBaseABS.GetOrig(Var Addr: RecEchoMailAddr);
+Begin
+End;
+
+Procedure TMsgBaseABS.GetDest(Var Addr: RecEchoMailAddr);
+Begin
+End;
+
+Function TMsgBaseABS.IsLocal: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsCrash: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsKillSent: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsSent: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsFAttach: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsReqRct: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsReqAud: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsRetRct: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsFileReq: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsRcvd: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsPriv: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsDeleted: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.IsEchoed: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.GetMsgLoc: LongInt;
+Begin
+End;
+
+Procedure TMsgBaseABS.SetMsgLoc(ML: LongInt);
+Begin
+End;
+
+Procedure TMsgBaseABS.MsgStartUp;
+Begin
+End;
+
+Procedure TMsgBaseABS.MsgTxtStartUp;
+Begin
+End;
+
+Procedure TMsgBaseABS.YoursFirst(Name: String; Handle: String);
+Begin
+End;
+
+Procedure TMsgBaseABS.YoursNext;
+Begin
+End;
+
+Function TMsgBaseABS.YoursFound: Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.CreateMsgBase(MaxMsg: Word; MaxDays: Word): Boolean;
+Begin
+End;
+
+Function TMsgBaseABS.MsgBaseExists: Boolean;
+Begin
+End;
+
+Procedure TMsgBaseABS.StartNewMsg;
+Begin
+End;
+
+Function TMsgBaseABS.GetHighMsgNum: LongInt;
+Begin
+End;
+
+Function TMsgBaseABS.SeekFound: Boolean;
+Begin
+End;
+
+Procedure TMsgBaseABS.SetMailType(MT: MsgMailType);
+Begin
+End;
+
+Function TMsgBaseABS.GetSubArea: Word;
+Begin
+ GetSubArea := 0;
+End;
+
+Procedure TMsgBaseABS.ReWriteHdr;
+Begin
+End;
+
+Procedure TMsgBaseABS.DeleteMsg;
+Begin
+End;
+
+Procedure TMsgBaseABS.SetEcho(ES: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseABS.SeekPrior;
+Begin
+End;
+
+Function TMsgBaseABS.NumberOfMsgs: LongInt;
+Begin
+End;
+
+Function TMsgBaseABS.GetLastRead(UNum: LongInt): LongInt;
+Begin
+End;
+
+Procedure TMsgBaseABS.SetLastRead(UNum: LongInt; LR: LongInt);
+Begin
+End;
+
+Function TMsgBaseABS.GetMsgDisplayNum: LongInt;
+Begin
+ GetMsgDisplayNum := GetMsgNum;
+End;
+
+Function TMsgBaseABS.GetTxtPos: LongInt;
+Begin
+ GetTxtPos := 0;
+End;
+
+Procedure TMsgBaseABS.SetTxtPos(TP: LongInt);
+Begin
+End;
+
+Procedure TMsgBaseABS.SetNextSeeAlso(SAlso: LongInt);
+Begin
+End;
+
+Function TMsgBaseABS.GetNextSeeAlso: LongInt;
+Begin
+ GetNextSeeAlso:=0;
+End;
+
+Function TMsgBaseABS.GetNoKludgeStr(MaxLen: Word): String;
+Begin
+ Result := GetString(MaxLen);
+ While ((Length(Result) > 0) and (Result[1] = #1) and (Not EOM)) Do
+ Result := GetString(MaxLen);
+End;
+
+Function TMsgBaseABS.GetHighActiveMsgNum: LongInt;
+Begin
+ SeekFirst(GetHighMsgNum);
+
+ If Not SeekFound Then
+ SeekPrior;
+
+ If SeekFound Then
+ GetHighActiveMsgNum := GetMsgNum
+ Else
+ GetHighActiveMsgNum := 0;
+End;
+
+Procedure TMsgBaseABS.EditMsgInit;
+Begin
+End;
+
+Procedure TMsgBaseABS.EditMsgSave;
+Begin
+End;
+
+End.
diff --git a/mystic/bbs_msgbase_ansi.pas b/mystic/bbs_msgbase_ansi.pas
new file mode 100644
index 0000000..c2ea0cf
--- /dev/null
+++ b/mystic/bbs_msgbase_ansi.pas
@@ -0,0 +1,486 @@
+{$I M_OPS.PAS}
+
+Unit BBS_MsgBase_Ansi;
+
+// mystic 2 ansi reader
+
+Interface
+
+Uses
+ m_Strings,
+ BBS_Common;
+
+Type
+ PtrMessageLine = ^RecMessageLine;
+ RecMessageLine = Array[1..80] of Record
+ Ch : Char;
+ Attr : Byte;
+ End;
+
+ RecMessageAnsi = Array[1..mysMaxMsgLines] of RecMessageLine;
+ // make this a pointer?
+
+ TMsgBaseAnsi = Class
+ GotAnsi : Boolean;
+ GotPipe : Boolean;
+ PipeCode : String[2];
+ Owner : Pointer;
+ Data : RecMessageAnsi;
+ Code : String;
+ Lines : Word;
+ CurY : Word;
+ Escape : Byte;
+ SavedX : Byte;
+ SavedY : Byte;
+ CurX : Byte;
+ Attr : Byte;
+
+ Procedure SetFore (Color: Byte);
+ Procedure SetBack (Color: Byte);
+ Procedure ResetControlCode;
+ Function ParseNumber (Var Line: String) : Integer;
+ Function AddChar (Ch: Char) : Boolean;
+ Procedure MoveXY (X, Y: Word);
+ Procedure MoveUP;
+ Procedure MoveDOWN;
+ Procedure MoveLEFT;
+ Procedure MoveRIGHT;
+ Procedure MoveCursor;
+ Procedure CheckCode (Ch: Char);
+ Procedure ProcessChar (Ch: Char);
+
+ Constructor Create (O: Pointer; Msg: Boolean);
+ Destructor Destroy; Override;
+ Function ProcessBuf (Var Buf; BufLen: Word) : Boolean;
+ Procedure WriteLine (Line: Word; Flush: Boolean);
+ Procedure DrawLine (Y, Line: Word; Flush: Boolean);
+ Procedure DrawPage (pStart, pEnd, pLine: Word);
+ Procedure Clear;
+ Function GetLineText (Line: Word) : String;
+ Procedure SetLineColor (Attr, Line: Word);
+ Procedure RemoveLine (Line: Word);
+ End;
+
+Implementation
+
+Uses
+ BBS_Core;
+
+Constructor TMsgBaseAnsi.Create (O: Pointer; Msg: Boolean);
+Begin
+ Inherited Create;
+
+ Owner := O;
+
+ Clear;
+End;
+
+Destructor TMsgBaseAnsi.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Procedure TMsgBaseAnsi.Clear;
+Begin
+ Lines := 1;
+ CurX := 1;
+ CurY := 1;
+ Attr := 7;
+ GotAnsi := False;
+ GotPipe := False;
+ PipeCode := '';
+
+ FillChar (Data, SizeOf(Data), 0);
+
+ ResetControlCode;
+End;
+
+Procedure TMsgBaseAnsi.ResetControlCode;
+Begin
+ Escape := 0;
+ Code := '';
+End;
+
+Procedure TMsgBaseAnsi.SetFore (Color: Byte);
+Begin
+ Attr := Color + ((Attr SHR 4) AND 7) * 16;
+End;
+
+Procedure TMsgBaseAnsi.SetBack (Color: Byte);
+Begin
+ Attr := (Attr AND $F) + Color * 16;
+End;
+
+Function TMsgBaseAnsi.AddChar (Ch: Char) : Boolean;
+Begin
+ AddChar := False;
+
+ Data[CurY][CurX].Ch := Ch;
+ Data[CurY][CurX].Attr := Attr;
+
+ If CurX < 80 Then
+ Inc (CurX)
+ Else Begin
+ If CurY = mysMaxMsgLines Then Begin
+ AddChar := True;
+ Exit;
+ End Else Begin
+ CurX := 1;
+ Inc (CurY);
+ End;
+ End;
+End;
+
+Function TMsgBaseAnsi.ParseNumber (Var Line: String) : Integer;
+Var
+ A : Integer;
+ B : LongInt;
+ Str1 : String;
+ Str2 : String;
+Begin
+ Str1 := Line;
+
+ Val(Str1, A, B);
+
+ If B = 0 Then
+ Str1 := ''
+ Else Begin
+ Str2 := Copy(Str1, 1, B - 1);
+
+ Delete (Str1, 1, B);
+ Val (Str2, A, B);
+ End;
+
+ Line := Str1;
+ ParseNumber := A;
+End;
+
+Procedure TMsgBaseAnsi.MoveXY (X, Y: Word);
+Begin
+ If X > 80 Then X := 80;
+ If Y > mysMaxMsgLines Then Y := mysMaxMsgLines;
+
+ CurX := X;
+ CurY := Y;
+End;
+
+Procedure TMsgBaseAnsi.MoveCursor;
+Var
+ X : Byte;
+ Y : Byte;
+Begin
+ X := ParseNumber(Code);
+ Y := ParseNumber(Code);
+
+ If X = 0 Then X := 1;
+ If Y = 0 Then Y := 1;
+
+ MoveXY (X, Y);
+
+ ResetControlCode;
+End;
+
+Procedure TMsgBaseAnsi.MoveUP;
+Var
+ NewPos : Integer;
+ Offset : Integer;
+Begin
+ Offset := ParseNumber (Code);
+
+ If Offset = 0 Then Offset := 1;
+
+ If (CurY - Offset) < 1 Then
+ NewPos := 1
+ Else
+ NewPos := CurY - Offset;
+
+ MoveXY (CurX, NewPos);
+
+ ResetControlCode;
+End;
+
+Procedure TMsgBaseAnsi.MoveDOWN;
+Var
+ NewPos : Byte;
+Begin
+ NewPos := ParseNumber (Code);
+
+ If NewPos = 0 Then NewPos := 1;
+
+ MoveXY (CurX, CurY + NewPos);
+
+ ResetControlCode;
+End;
+
+Procedure TMsgBaseAnsi.MoveLEFT;
+Var
+ NewPos : Integer;
+ Offset : Integer;
+Begin
+ Offset := ParseNumber (Code);
+
+ If Offset = 0 Then Offset := 1;
+
+ If CurX - Offset < 1 Then
+ NewPos := 1
+ Else
+ NewPos := CurX - Offset;
+
+ MoveXY (NewPos, CurY);
+
+ ResetControlCode;
+End;
+
+Procedure TMsgBaseAnsi.MoveRIGHT;
+Var
+ NewPos : Integer;
+ Offset : Integer;
+Begin
+ Offset := ParseNumber(Code);
+
+ If Offset = 0 Then Offset := 1;
+
+ If CurX + Offset > 80 Then Begin
+ NewPos := (CurX + Offset) - 80;
+ Inc (CurY);
+ End Else
+ NewPos := CurX + Offset;
+
+ MoveXY (NewPos, CurY);
+
+ ResetControlCode;
+End;
+
+Procedure TMsgBaseAnsi.CheckCode (Ch: Char);
+Var
+ Temp1 : Byte;
+ Temp2 : Byte;
+Begin
+ Case Ch of
+ '0'..'9', ';', '?' : Code := Code + Ch;
+ 'H', 'f' : MoveCursor;
+ 'A' : MoveUP;
+ 'B' : MoveDOWN;
+ 'C' : MoveRIGHT;
+ 'D' : MoveLEFT;
+ 'J' : Begin
+ {ClearScreenData;}
+ ResetControlCode;
+ End;
+ 'K' : Begin
+ Temp1 := CurX;
+ For Temp2 := CurX To 80 Do
+ AddChar(' ');
+ MoveXY (Temp1, CurY);
+ ResetControlCode;
+ End;
+ 'h' : ResetControlCode;
+ 'm' : Begin
+ While Length(Code) > 0 Do Begin
+ Case ParseNumber(Code) of
+ 0 : Attr := 7;
+ 1 : Attr := Attr OR $08;
+ 5 : Attr := Attr OR $80;
+ 7 : Begin
+ Attr := Attr AND $F7;
+ Attr := ((Attr AND $70) SHR 4) + ((Attr AND $7) SHL 4) + Attr AND $80;
+ End;
+ 30: Attr := (Attr AND $F8) + 0;
+ 31: Attr := (Attr AND $F8) + 4;
+ 32: Attr := (Attr AND $F8) + 2;
+ 33: Attr := (Attr AND $F8) + 6;
+ 34: Attr := (Attr AND $F8) + 1;
+ 35: Attr := (Attr AND $F8) + 5;
+ 36: Attr := (Attr AND $F8) + 3;
+ 37: Attr := (Attr AND $F8) + 7;
+ 40: SetBack (0);
+ 41: SetBack (4);
+ 42: SetBack (2);
+ 43: SetBack (6);
+ 44: SetBack (1);
+ 45: SetBack (5);
+ 46: SetBack (3);
+ 47: SetBack (7);
+ End;
+ End;
+
+ ResetControlCode;
+ End;
+ 's' : Begin
+ SavedX := CurX;
+ SavedY := CurY;
+ ResetControlCode;
+ End;
+ 'u' : Begin
+ MoveXY (SavedX, SavedY);
+ ResetControlCode;
+ End;
+ Else
+ ResetControlCode;
+ End;
+End;
+
+Procedure TMsgBaseAnsi.ProcessChar (Ch: Char);
+Begin
+ If GotPipe Then Begin
+ PipeCode := PipeCode + Ch;
+
+ If Length(PipeCode) = 2 Then Begin
+
+ Case strS2I(PipeCode) of
+ 00..
+ 15 : SetFore(strS2I(PipeCode));
+ 16..
+ 23 : SetBack(strS2I(PipeCode) - 16);
+ Else
+ AddChar('|');
+ AddChar(PipeCode[1]);
+ AddChar(PipeCode[2]);
+ End;
+
+ GotPipe := False;
+ PipeCode := '';
+ End;
+
+ Exit;
+ End;
+
+ Case Escape of
+ 0 : Begin
+ Case Ch of
+ #27 : Escape := 1;
+ #9 : MoveXY (CurX + 8, CurY);
+ #12 : {Edit.ClearScreenData};
+ Else
+ If Ch = '|' Then
+ GotPipe := True
+ Else
+ AddChar (Ch);
+
+ ResetControlCode;
+ End;
+ End;
+ 1 : If Ch = '[' Then Begin
+ Escape := 2;
+ Code := '';
+ GotAnsi := True;
+ End Else
+ Escape := 0;
+ 2 : CheckCode(Ch);
+ Else
+ ResetControlCode;
+ End;
+End;
+
+Function TMsgBaseAnsi.ProcessBuf (Var Buf; BufLen: Word) : Boolean;
+Var
+ Count : Word;
+ Buffer : Array[1..4096] of Char Absolute Buf;
+Begin
+ Result := False;
+
+ For Count := 1 to BufLen Do Begin
+ If CurY > Lines Then Lines := CurY;
+ Case Buffer[Count] of
+ #10 : If CurY = mysMaxMsgLines Then Begin
+ Result := True;
+ GotAnsi := False;
+ Break;
+ End Else
+ Inc (CurY);
+ #13 : CurX := 1;
+ #26 : Begin
+ Result := True;
+ Break;
+ End;
+ Else
+ ProcessChar(Buffer[Count]);
+ End;
+ End;
+End;
+
+Procedure TMsgBaseAnsi.WriteLine (Line: Word; Flush: Boolean);
+Var
+ Count : Byte;
+Begin
+ If Line > Lines Then Exit;
+
+ For Count := 1 to 79 Do Begin
+ Session.io.BufAddStr (Session.io.Attr2Ansi(Data[Line][Count].Attr));
+ If Data[Line][Count].Ch in [#0, #255] Then
+ Session.io.BufAddStr(' ')
+ Else
+ Session.io.BufAddStr (Data[Line][Count].Ch);
+ End;
+
+ Session.io.BufAddStr(#13#10);
+
+ If Flush Then Session.io.BufFlush;
+
+ Inc (Session.io.PausePtr);
+End;
+
+Procedure TMsgBaseAnsi.DrawLine (Y, Line: Word; Flush: Boolean);
+Var
+ Count : Byte;
+Begin
+ Session.io.AnsiGotoXY(1, Y);
+
+ If Line > Lines Then Begin
+ Session.io.BufAddStr(Session.io.Attr2Ansi(Session.io.ScreenInfo[1].A));
+ Session.io.AnsiClrEOL;
+ End Else
+ For Count := 1 to 80 Do Begin
+ Session.io.BufAddStr (Session.io.Attr2Ansi(Data[Line][Count].Attr));
+ If Data[Line][Count].Ch in [#0, #255] Then
+ Session.io.BufAddStr(' ')
+ Else
+ Session.io.BufAddStr (Data[Line][Count].Ch);
+ End;
+
+ If Flush Then Session.io.BufFlush;
+End;
+
+Procedure TMsgBaseAnsi.DrawPage (pStart, pEnd, pLine: Word);
+Var
+ Count : Word;
+Begin
+ For Count := pStart to pEnd Do Begin
+ DrawLine (Count, pLine, False);
+ Inc (pLine);
+ End;
+
+ Session.io.BufFlush;
+End;
+
+Function TMsgBaseAnsi.GetLineText (Line: Word) : String;
+Var
+ Count : Word;
+Begin
+ Result := '';
+
+ If Line > Lines Then Exit;
+
+ For Count := 1 to 80 Do
+ Result := Result + Data[Line][Count].Ch;
+End;
+
+Procedure TMsgBaseAnsi.SetLineColor (Attr, Line: Word);
+Var
+ Count : Word;
+Begin
+ For Count := 1 to 80 Do
+ Data[Line][Count].Attr := Attr;
+End;
+
+Procedure TMsgBaseAnsi.RemoveLine (Line: Word);
+Var
+ Count : Word;
+Begin
+ For Count := Line to Lines - 1 Do
+ Data[Count] := Data[Count + 1];
+
+ Dec (Lines);
+End;
+
+End.
diff --git a/mystic/bbs_msgbase_jam.pas b/mystic/bbs_msgbase_jam.pas
new file mode 100644
index 0000000..b1486a0
--- /dev/null
+++ b/mystic/bbs_msgbase_jam.pas
@@ -0,0 +1,1831 @@
+{$I M_OPS.PAS}
+
+// things to do:
+// 2. make readmsgtext read directly into msgtext record?
+// 3. make squish use temporary buffer file
+// 4. remove all mkcrap stuff... basically rewrite everything 1 by 1
+
+Unit BBS_MsgBase_JAM;
+
+Interface
+
+Uses
+ m_Strings,
+ bbs_Common,
+ bbs_MsgBase_ABS;
+
+Const
+ JamIdxBufSize = 200;
+ JamSubBufSize = 4000;
+ JamTxtBufSize = 4000;
+ TxtSubBufSize = 2000;
+
+ Jam_Local = $00000001;
+ Jam_InTransit = $00000002;
+ Jam_Priv = $00000004;
+ Jam_Rcvd = $00000008;
+ Jam_Sent = $00000010;
+ Jam_KillSent = $00000020;
+ Jam_AchvSent = $00000040;
+ Jam_Hold = $00000080;
+ Jam_Crash = $00000100;
+ Jam_Imm = $00000200;
+ Jam_Direct = $00000400;
+ Jam_Gate = $00000800;
+ Jam_Freq = $00001000;
+ Jam_FAttch = $00002000;
+ Jam_TruncFile = $00004000;
+ Jam_KillFile = $00008000;
+ Jam_RcptReq = $00010000;
+ Jam_ConfmReq = $00020000;
+ Jam_Orphan = $00040000;
+ Jam_Encrypt = $00080000;
+ Jam_Compress = $00100000;
+ Jam_Escaped = $00200000;
+ Jam_FPU = $00400000;
+ Jam_TypeLocal = $00800000;
+ Jam_TypeEcho = $01000000;
+ Jam_TypeNet = $02000000;
+ Jam_NoDisp = $20000000;
+ Jam_Locked = $40000000;
+ Jam_Deleted = $80000000;
+
+Type
+ JamHdrType = Record
+ Signature : Array[1..4] of Char;
+ Created : LongInt;
+ ModCounter : LongInt;
+ ActiveMsgs : LongInt;
+ PwdCRC : LongInt;
+ BaseMsgNum : LongInt;
+ Extra : Array[1..1000] of Char;
+ End;
+
+ JamMsgHdrType = Record
+ Signature : Array[1..4] of Char;
+ Rev : Word;
+ Resvd : Word;
+ SubFieldLen : LongInt;
+ TimesRead : LongInt;
+ MsgIdCrc : LongInt;
+ ReplyCrc : LongInt;
+ ReplyTo : LongInt;
+ ReplyFirst : LongInt;
+ ReplyNext : LongInt;
+ DateWritten : LongInt;
+ DateRcvd : LongInt;
+ DateArrived : LongInt;
+ MsgNum : LongInt;
+ Attr1 : LongInt;
+ Attr2 : LongInt;
+ TextOfs : LongInt;
+ TextLen : LongInt;
+ PwdCrc : LongInt;
+ Cost : LongInt;
+ End;
+
+ JamIdxType = Record
+ MsgToCrc : LongInt;
+ HdrLoc : LongInt;
+ End;
+
+ JamLastType = Record
+ NameCrc : LongInt;
+ UserNum : LongInt;
+ LastRead : LongInt;
+ HighRead : LongInt;
+ End;
+
+ JamIdxArrayType = Array[0..JamIdxBufSize] of JamIdxType;
+ JamSubBuffer = Array[1..JamSubBufSize] of Char;
+ JamTxtBufType = Array[0..JamTxtBufSize] Of Char;
+
+ HdrType = Record
+ JamHdr : JamMsgHdrType;
+ SubBuf : JamSubBuffer;
+ End;
+
+ JamMsgType = Record
+ HdrFile : File;
+ TxtFile : File;
+ IdxFile : File;
+ MsgPath : String[128];
+ BaseHdr : JamHdrType;
+ Dest : RecEchoMailAddr;
+ Orig : RecEchoMailAddr;
+ MsgFrom : String[65];
+ MsgTo : String[65];
+ MsgSubj : String[100];
+ MsgDate : String[8];
+ MsgTime : String[5];
+ CurrMsgNum : LongInt;
+ YourName : String[35];
+ YourHdl : String[35];
+ NameCrc : LongInt;
+ HdlCrc : LongInt;
+ TxtPos : LongInt;
+ TxtEnd : LongInt;
+ TxtBufStart : LongInt;
+ TxtRead : {$IFDEF FPC} Word {$ELSE} LongInt {$ENDIF};
+ IdxRead : {$IFDEF FPC} Word {$ELSE} LongInt {$ENDIF};
+ MailType : MsgMailType;
+ BufFile : File;
+ LockCount : LongInt;
+ IdxStart : LongInt;
+ TxtSubBuf : Array[0..TxtSubBufSize] of Char;
+ TxtSubChars : Integer;
+ End;
+
+ PMsgBaseJAM = ^TMsgBaseJAM;
+ TMsgBaseJAM = Object(TMsgBaseABS)
+ JM : ^JamMsgType;
+ MsgHdr : ^HdrType;
+ JamIdx : ^JamIdxArrayType;
+ TxtBuf : ^JamTxtBufType;
+ Error : Word;
+
+ Procedure EditMsgInit; Virtual;
+ Procedure EditMsgSave; Virtual;
+ Constructor Init; {Initialize}
+ Destructor Done; Virtual; {Done}
+ Procedure SetMsgPath(St: String); Virtual; {Set netmail path}
+ Function GetHighMsgNum: LongInt; Virtual; {Get highest netmail msg number in area}
+ Function LockMsgBase: Boolean; Virtual; {Lock the message base}
+ Function UnLockMsgBase: Boolean; Virtual; {Unlock the message base}
+ Procedure SetDest(Var Addr: RecEchoMailAddr); Virtual; {Set Zone/Net/Node/Point for Dest}
+ Procedure SetOrig(Var Addr: RecEchoMailAddr); Virtual; {Set Zone/Net/Node/Point for Orig}
+ Procedure SetFrom(Name: String); Virtual; {Set message from}
+ Procedure SetTo(Name: String); Virtual; {Set message to}
+ Procedure SetSubj(Str: String); Virtual; {Set message subject}
+ Procedure SetCost(SCost: Word); Virtual; {Set message cost}
+ Procedure SetRefer(SRefer: LongInt); Virtual; {Set message reference}
+ Procedure SetSeeAlso(SAlso: LongInt); Virtual; {Set message see also}
+ Function GetNextSeeAlso: LongInt; Virtual;
+ Procedure SetNextSeeAlso(SAlso: LongInt); Virtual;
+ Procedure SetDate(SDate: String); Virtual; {Set message date}
+ Procedure SetTime(STime: String); Virtual; {Set message time}
+ Procedure SetLocal(LS: Boolean); Virtual; {Set local status}
+ Procedure SetRcvd(RS: Boolean); Virtual; {Set received status}
+ Procedure SetPriv(PS: Boolean); Virtual; {Set priveledge vs public status}
+ Procedure SetHold(SS: Boolean); Virtual; {set hold netmail status}
+ Procedure SetCrash(SS: Boolean); Virtual; {Set crash netmail status}
+ Procedure SetKillSent(SS: Boolean); Virtual; {Set kill/sent netmail status}
+ Procedure SetSent(SS: Boolean); Virtual; {Set sent netmail status}
+ Procedure SetFAttach(SS: Boolean); Virtual; {Set file attach status}
+ Procedure SetReqRct(SS: Boolean); Virtual; {Set request receipt status}
+ Procedure SetReqAud(SS: Boolean); Virtual; {Set request audit status}
+ Procedure SetRetRct(SS: Boolean); Virtual; {Set return receipt status}
+ Procedure SetFileReq(SS: Boolean); Virtual; {Set file request status}
+ Procedure DoString(Str: String); Virtual; {Add string to message text}
+ Procedure DoChar(Ch: Char); Virtual; {Add character to message text}
+ Procedure DoStringLn(Str: String); Virtual; {Add string and newline to msg text}
+ Procedure DoKludgeLn(Str: String); Virtual; {Add ^AKludge line to msg}
+ Function WriteMsg: Word; Virtual;
+ Function GetChar: Char; Virtual;
+ Procedure MsgStartUp; Virtual; {set up msg for reading}
+ Function EOM: Boolean; Virtual; {No more msg text}
+ Function GetString(MaxLen: Word): String; Virtual; {Get wordwrapped string}
+ Procedure SeekFirst(MsgNum: LongInt); Virtual; {Seek msg number}
+ Procedure SeekNext; Virtual; {Find next matching msg}
+ Procedure SeekPrior; Virtual; {Seek prior matching msg}
+ Function GetFrom: String; Virtual; {Get from name on current msg}
+ Function GetTo: String; Virtual; {Get to name on current msg}
+ Function GetSubj: String; Virtual; {Get subject on current msg}
+ Function GetCost: Word; Virtual; {Get cost of current msg}
+ Function GetDate: String; Virtual; {Get date of current msg}
+ Function GetTime: String; Virtual; {Get time of current msg}
+ Function GetRefer: LongInt; Virtual; {Get reply to of current msg}
+ Function GetSeeAlso: LongInt; Virtual; {Get see also of current msg}
+ Function GetMsgNum: LongInt; Virtual; {Get message number}
+ Procedure GetOrig(Var Addr: RecEchoMailAddr); Virtual; {Get origin address}
+ Procedure GetDest(Var Addr: RecEchoMailAddr); Virtual; {Get destination address}
+ Function GetTextLen : LongInt; Virtual; {returns length of text in msg}
+ Function IsLocal: Boolean; Virtual; {Is current msg local}
+ Function IsCrash: Boolean; Virtual; {Is current msg crash}
+ Function IsKillSent: Boolean; Virtual; {Is current msg kill sent}
+ Function IsSent: Boolean; Virtual; {Is current msg sent}
+ Function IsFAttach: Boolean; Virtual; {Is current msg file attach}
+ Function IsReqRct: Boolean; Virtual; {Is current msg request receipt}
+ Function IsReqAud: Boolean; Virtual; {Is current msg request audit}
+ Function IsRetRct: Boolean; Virtual; {Is current msg a return receipt}
+ Function IsFileReq: Boolean; Virtual; {Is current msg a file request}
+ Function IsRcvd: Boolean; Virtual; {Is current msg received}
+ Function IsPriv: Boolean; Virtual; {Is current msg priviledged/private}
+ Function IsDeleted: Boolean; Virtual; {Is current msg deleted}
+ Function IsEchoed: Boolean; Virtual; {Msg should be echoed}
+ Function GetMsgLoc: LongInt; Virtual; {Msg location}
+ Procedure SetMsgLoc(ML: LongInt); Virtual; {Msg location}
+ Procedure YoursFirst(Name: String; Handle: String); Virtual; {Seek your mail}
+ Procedure YoursNext; Virtual; {Seek next your mail}
+ Function YoursFound: Boolean; Virtual; {Message found}
+ Procedure StartNewMsg; Virtual;
+ Function OpenMsgBase : Boolean; Virtual;
+ Procedure CloseMsgBase; Virtual;
+ Function MsgBaseExists: Boolean; Virtual; {Does msg base exist}
+ Function CreateMsgBase(MaxMsg: Word; MaxDays: Word): Boolean; Virtual;
+ Function SeekFound: Boolean; Virtual;
+ Procedure SetMailType(MT: MsgMailType); Virtual; {Set message base type}
+ Function GetSubArea: Word; Virtual; {Get sub area number}
+ Procedure ReWriteHdr; Virtual; {Rewrite msg header after changes}
+ Procedure DeleteMsg; Virtual; {Delete current message}
+ Function NumberOfMsgs: LongInt; Virtual; {Number of messages}
+ Function GetLastRead(UNum: LongInt): LongInt; Virtual; {Get last read for user num}
+ Procedure SetLastRead(UNum: LongInt; LR: LongInt); Virtual; {Set last read}
+ Procedure MsgTxtStartUp; Virtual; {Do message text start up tasks}
+ Function GetTxtPos: LongInt; Virtual; {Get indicator of msg text position}
+ Procedure SetTxtPos(TP: LongInt); Virtual; {Set text position}
+ Procedure SetAttr1(Mask: LongInt; St: Boolean); {Set attribute 1}
+ Function ReadIdx: Word;
+ Function WriteIdx: Word;
+ Procedure AddSubField(id: Word; Data: String);
+ Function FindLastRead(Var LastFile: File; UNum: LongInt): LongInt;
+ Function ReReadIdx(Var IdxLoc : LongInt) : Word;
+ End;
+
+Function JamStrCrc(St: String): LongInt;
+
+Implementation
+
+Uses
+ DOS,
+ m_CRC,
+ m_FileIO,
+ m_DateTime,
+ MKCRAP; // REMOVE THIS ASAP.
+
+Type
+ SubFieldPTR = ^SubFieldType;
+ SubFieldType = Record
+ LoId : Word;
+ HiId : Word;
+ DataLen : LongInt;
+ Data : Array[1..1000] of Char;
+ End;
+
+Constructor TMsgBaseJAM.Init;
+Begin
+ New(JM);
+ New(JamIdx);
+ New(MsgHdr); { this new here messes everything up }
+ New(TxtBuf);
+ If ((JM = Nil) Or (JamIdx = Nil) or (MsgHdr = Nil) or (TxtBuf = Nil)) Then Begin
+ If JM <> Nil Then Dispose(JM);
+ If JamIdx <> Nil Then Dispose(JamIdx);
+ If MsgHdr <> Nil Then Dispose(MsgHdr);
+ If TxtBuf <> Nil Then Dispose(TxtBuf);
+ Fail;
+ Exit;
+ End Else Begin
+ FillChar(JM^, SizeOf(JM^), #0);
+ JM^.MsgPath := '';
+ JM^.IdxStart := -30;
+ JM^.IdxRead := 0;
+ Error := 0;
+ End;
+End;
+
+Destructor TMsgBaseJAM.Done;
+Begin
+ If JM <> Nil Then Dispose(JM);
+ If JamIdx <> Nil Then Dispose(JamIdx);
+ If MsgHdr <> Nil Then Dispose(MsgHdr);
+ If TxtBuf <> Nil Then Dispose(TxtBuf);
+End;
+
+Function JamStrCrc(St: String): LongInt;
+ Var
+ i: Word;
+ crc: LongInt;
+
+ Begin
+ Crc := -1;
+ For i := 1 to Length(St) Do
+ Crc := Crc32(Ord(LoCase(St[i])), Crc);
+ JamStrCrc := Crc;
+ End;
+
+
+Procedure TMsgBaseJAM.SetMsgPath(St: String);
+ Begin
+ JM^.MsgPath := Copy(St, 1, 124);
+ End;
+
+
+Function TMsgBaseJAM.GetHighMsgNum: LongInt;
+ Begin
+ GetHighMsgNum := JM^.BaseHdr.BaseMsgNum + FileSize(JM^.IdxFile) - 1;
+ End;
+
+
+Procedure TMsgBaseJAM.SetDest(Var Addr: RecEchoMailAddr);
+ Begin
+ JM^.Dest := Addr;
+ End;
+
+
+Procedure TMsgBaseJAM.SetOrig(Var Addr: RecEchoMailAddr);
+ Begin
+ JM^.Orig := Addr;
+ End;
+
+
+Procedure TMsgBaseJAM.SetFrom(Name: String);
+ Begin
+ JM^.MsgFrom := Name;
+ End;
+
+
+Procedure TMsgBaseJAM.SetTo(Name: String);
+ Begin
+ JM^.MsgTo := Name;
+ End;
+
+
+Procedure TMsgBaseJAM.SetSubj(Str: String);
+ Begin
+ JM^.MsgSubj := Str;
+ End;
+
+
+Procedure TMsgBaseJAM.SetCost(SCost: Word);
+ Begin
+ MsgHdr^.JamHdr.Cost := SCost;
+ End;
+
+
+Procedure TMsgBaseJAM.SetRefer(SRefer: LongInt);
+ Begin
+ MsgHdr^.JamHdr.ReplyTo := SRefer;
+ End;
+
+
+Procedure TMsgBaseJAM.SetSeeAlso(SAlso: LongInt);
+ Begin
+ MsgHdr^.JamHdr.ReplyFirst := SAlso;
+ End;
+
+
+Procedure TMsgBaseJAM.SetDate(SDate: String);
+ Begin
+ JM^.MsgDate := SDate;
+ End;
+
+
+Procedure TMsgBaseJAM.SetTime(STime: String);
+ Begin
+ JM^.MsgTime := STime;
+ End;
+
+
+Procedure TMsgBaseJAM.SetAttr1(Mask: LongInt; St: Boolean);
+ Begin
+ If St Then
+ MsgHdr^.JamHdr.Attr1 := MsgHdr^.JamHdr.Attr1 Or Mask
+ Else
+ MsgHdr^.JamHdr.Attr1 := MsgHdr^.JamHdr.Attr1 And (Not Mask);
+ End;
+
+Procedure TMsgBaseJAM.SetLocal(LS: Boolean);
+Begin
+ SetAttr1(Jam_Local, LS);
+End;
+
+Procedure TMsgBaseJAM.SetRcvd(RS: Boolean);
+Begin
+ SetAttr1(Jam_Rcvd, RS);
+End;
+
+Procedure TMsgBaseJAM.SetPriv(PS: Boolean);
+Begin
+ SetAttr1(Jam_Priv, PS);
+End;
+
+Procedure TMsgBaseJAM.SetHold(SS: Boolean);
+Begin
+ SetAttr1 (Jam_Hold, SS);
+End;
+
+Procedure TMsgBaseJAM.SetCrash(SS: Boolean);
+Begin
+ SetAttr1(Jam_Crash, SS);
+End;
+
+Procedure TMsgBaseJAM.SetKillSent(SS: Boolean);
+Begin
+ SetAttr1(Jam_KillSent, SS);
+End;
+
+Procedure TMsgBaseJAM.SetSent(SS: Boolean);
+Begin
+ SetAttr1(Jam_Sent, SS);
+End;
+
+Procedure TMsgBaseJAM.SetFAttach(SS: Boolean);
+Begin
+ SetAttr1(Jam_FAttch, SS);
+End;
+
+Procedure TMsgBaseJAM.SetReqRct(SS: Boolean);
+Begin
+ SetAttr1(Jam_RcptReq, SS);
+End;
+
+Procedure TMsgBaseJAM.SetReqAud(SS: Boolean);
+Begin
+ SetAttr1(Jam_ConfmReq, SS);
+End;
+
+Procedure TMsgBaseJAM.SetRetRct(SS: Boolean);
+Begin
+End;
+
+Procedure TMsgBaseJAM.SetFileReq(SS: Boolean);
+Begin
+ SetAttr1(Jam_Freq, SS);
+End;
+
+
+Procedure TMsgBaseJAM.DoString(Str: String);
+Var
+ i: Word;
+Begin
+ i := 1;
+ While i <= Length(Str) Do Begin
+ DoChar(Str[i]);
+ Inc(i);
+ End;
+End;
+
+Procedure TMsgBaseJAM.DoChar(Ch: Char);
+Var
+// TmpStr: String;
+ NumWrite: Word;
+Begin
+ Case ch of
+ #13: LastSoft := False;
+ #10:;
+ Else
+ LastSoft := True;
+ End;
+ If (JM^.TxtPos - JM^.TxtBufStart) >= JamTxtBufSize Then Begin
+ If JM^.TxtBufStart = 0 Then Begin
+// GetDir(0, TmpStr);
+// TmpStr := GetTempName(TmpStr);
+ Assign(JM^.BufFile, TempFile);
+ FileMode := fmReadWrite + fmDenyNone;
+ ReWrite(JM^.BufFile, 1);
+ End;
+ NumWrite := JM^.TxtPos - JM^.TxtBufStart;
+ BlockWrite(JM^.BufFile, TxtBuf^, NumWrite);
+ Error := IoResult;
+ JM^.TxtBufStart := FileSize(JM^.BufFile);
+ End;
+ TxtBuf^[JM^.TxtPos - JM^.TxtBufStart] := Ch;
+ Inc(JM^.TxtPos);
+End;
+
+Procedure TMsgBaseJAM.DoStringLn(Str: String);
+Begin
+ DoString(Str);
+ DoChar(#13);
+End;
+
+Procedure TMsgBaseJAM.DoKludgeLn(Str: String);
+Var
+ TmpStr: String;
+
+Begin
+ If Str[1] = #1 Then
+ Str := Copy(Str,2,255);
+
+ If Copy(Str,1,3) = 'PID' Then Begin
+ TmpStr := strStripL(Copy(Str,4,255),':');
+ TmpStr := Copy(strStripB(TmpStr, ' '),1,40);
+ AddSubField(7, TmpStr);
+ End Else
+ If Copy(Str,1,5) = 'MSGID' Then Begin
+ TmpStr := strStripL(Copy(Str,6,255),':');
+ TmpStr := Copy(strStripB(TmpStr,' '),1,100);
+ AddSubField(4, TmpStr);
+ MsgHdr^.JamHdr.MsgIdCrc := JamStrCrc(TmpStr);
+ End Else
+ If Copy(Str,1,4) = 'INTL' Then Begin {ignore}
+ End Else
+ If Copy(Str,1,4) = 'TOPT' Then Begin {ignore}
+ End Else
+ If Copy(Str,1,4) = 'FMPT' Then Begin {ignore}
+ End Else
+ If Copy(Str,1,5) = 'REPLY' Then Begin
+ TmpStr := strStripL(Copy(Str,8,255),':');
+ TmpStr := Copy(strStripB(TmpStr,' '),1,100);
+ AddSubField(5, TmpStr);
+ MsgHdr^.JamHdr.ReplyCrc := JamStrCrc(TmpStr);
+ End Else
+ If Copy(Str,1,4) = 'PATH' Then Begin
+ TmpStr := strStripL(Copy(Str,5,255),':');
+ TmpStr := strStripB(TmpStr,' ');
+ AddSubField(2002, TmpStr);
+ End Else Begin
+ AddSubField(2000, strStripB(Str,' '));
+ End;
+End;
+
+Procedure TMsgBaseJAM.AddSubField(id: Word; Data: String);
+Type
+ SubFieldPTR = ^SubFieldType;
+ SubFieldType = Record
+ LoID : Word;
+ HiID : Word;
+ DataLen : LongInt;
+ Data : Array[1..256] of Char;
+ End;
+
+ Var
+ SubField: SubFieldPTR;
+
+ Begin
+ SubField := SubFieldPTR(@MsgHdr^.SubBuf[MsgHdr^.JamHdr.SubFieldLen + 1]);
+ If (MsgHdr^.JamHdr.SubFieldLen + 8 + Length(Data) < JamSubBufSize) Then
+ Begin
+ Inc(MsgHdr^.JamHdr.SubFieldLen, 8 + Length(Data));
+ SubField^.LoId := Id;
+ SubField^.HiId := 0;
+ SubField^.DataLen := Length(Data);
+ Move(Data[1], SubField^.Data[1], Length(Data));
+ End;
+ End;
+
+Procedure TMsgBaseJAM.EditMsgInit;
+Begin
+ JM^.TxtBufStart := 0;
+ JM^.TxtPos := 0;
+
+ MsgHdr^.JamHdr.SubFieldLen := 0;
+ FillChar(MsgHdr^.SubBuf, SizeOf(MsgHdr^.SubBuf), #0);
+End;
+
+(*
+Procedure TMsgBaseJAM.EditMsgSave;
+Var
+{$IFDEF MSDOS}
+ iPos : Word;
+{$ELSE}
+ iPos : LongInt;
+{$ENDIF}
+ TmpIdx : JamIdxType;
+ TmpHdr : JamMsgHdrType;
+ IdxLoc : LongInt;
+Begin
+ MsgHdr^.JamHdr.TextOfs := FileSize(JM^.TxtFile);
+ MsgHdr^.JamHdr.TextLen := JM^.TxtPos;
+
+ If JM^.TxtBufStart > 0 Then Begin
+ iPos := JM^.TxtPos - JM^.TxtBufStart;
+
+ BlockWrite(JM^.BufFile, TxtBuf^, iPos);
+
+ Seek (JM^.BufFile, 0);
+ Seek (JM^.TxtFile, FileSize(JM^.TxtFile));
+
+ While Not Eof(JM^.BufFile) Do Begin
+ BlockRead (JM^.BufFile, TxtBuf^, SizeOf(TxtBuf^), iPos);
+
+ JM^.TxtBufStart := FilePos(JM^.TxtFile);
+ JM^.TxtRead := iPos;
+
+ BlockWrite(JM^.TxtFile, TxtBuf^, iPos);
+ End;
+
+ Close(JM^.BufFile);
+ Erase(JM^.BufFile);
+ End Else Begin {Write text using TxtBuf only}
+ Seek (JM^.Txtfile, FileSize(JM^.TxtFile));
+ BlockWrite (JM^.TxtFile, TxtBuf^, JM^.TxtPos);
+ JM^.TxtRead := JM^.TxtPos;
+ End;
+
+ If ReReadIdx(IdxLoc) = 0 Then;
+
+ TmpHdr := MsgHdr^.JamHdr;
+ TmpHdr.Attr1 := TmpHdr.Attr1 AND Jam_Deleted;
+ TmpHdr.TextLen := 0;
+
+ Seek (JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
+ BlockWrite (JM^.HdrFile, TmpHdr, SizeOf(TmpHdr));
+
+ JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc := FileSize(JM^.HdrFile);
+
+ If Length(JM^.MsgTo) > 0 Then AddSubField(3, JM^.MsgTo);
+ If Length(JM^.MsgFrom) > 0 Then AddSubField(2, JM^.MsgFrom);
+ If Length(JM^.MsgSubj) > 0 Then Begin
+ If IsFileReq Then
+ AddSubField(11, JM^.MsgSubj)
+ Else
+ AddSubField(6, JM^.MsgSubj);
+ End;
+
+ If ((JM^.Dest.Zone <> 0) or (JM^.Dest.Net <> 0) or
+ (JM^.Dest.Node <> 0) or (JM^.Dest.Point <> 0)) Then
+ AddSubField(1, AddrType2Str(JM^.Dest));
+
+ If ((JM^.Orig.Zone <> 0) or (JM^.Orig.Net <> 0) or
+ (JM^.Orig.Node <> 0) or (JM^.Orig.Point <> 0)) Then
+ AddSubField(0, AddrType2Str(JM^.Orig));
+
+ JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc := JamStrCrc(JM^.MsgTo);
+
+ Seek (JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
+ BlockWrite (JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^.JamHdr) + MsgHdr^.JamHdr.SubFieldLen);
+
+ If WriteIdx = 0 Then;
+ If UnLockMsgBase Then;
+End;
+*)
+
+Procedure TMsgBaseJAM.EditMsgSave;
+Var
+ iPos : LongInt;
+ WriteError : Word;
+// TmpIdx : JamIdxType;
+ TmpHdr : JamMsgHdrType;
+ IdxLoc : LongInt;
+Begin
+ WriteError := 0;
+
+ MsgHdr^.JamHdr.TextOfs := FileSize(JM^.TxtFile);
+ MsgHdr^.JamHdr.TextLen := JM^.TxtPos;
+
+ If JM^.TxtBufStart > 0 Then Begin {Write text using buffer file}
+ iPos := JM^.TxtPos - JM^.TxtBufStart;
+ BlockWrite(JM^.BufFile, TxtBuf^, iPos);
+ WriteError := IoResult;
+ If WriteError = 0 Then Begin
+ Seek(JM^.BufFile, 0);
+ WriteError := IoResult;
+ End;
+ If WriteError = 0 Then Begin
+ Seek(JM^.TxtFile, FileSize(JM^.TxtFile));
+ WriteError := IoResult;
+ End;
+ While ((Not Eof(JM^.BufFile)) and (WriteError = 0)) Do Begin {copy buffer file to text file}
+ BlockRead(JM^.BufFile, TxtBuf^, SizeOf(TxtBuf^), iPos);
+ WriteError := IoResult;
+ If WriteError = 0 Then Begin
+ JM^.TxtBufStart := FilePos(JM^.TxtFile);
+ JM^.TxtRead := iPos;
+ BlockWrite(JM^.TxtFile, TxtBuf^, iPos);
+ Error := IoResult;
+ End;
+ End;
+ Close(JM^.BufFile);
+ Error := IoResult;
+ Erase(JM^.BufFile);
+ Error := IoResult;
+ End Else Begin {Write text using TxtBuf only}
+ Seek(JM^.Txtfile, FileSize(JM^.TxtFile));
+ WriteError := IoResult;
+ If WriteError = 0 Then Begin
+ BlockWrite(JM^.TxtFile, TxtBuf^, JM^.TxtPos);
+ WriteError := IoResult;
+ JM^.TxtRead := JM^.TxtPos;
+ End;
+ End;
+
+ If WriteError = 0 Then Begin {Add subfields as needed}
+ If Length(JM^.MsgTo) > 0 Then AddSubField(3, JM^.MsgTo);
+ If Length(JM^.MsgFrom) > 0 Then AddSubField(2, JM^.MsgFrom);
+ If Length(JM^.MsgSubj) > 0 Then Begin
+ If IsFileReq Then
+ AddSubField(11, JM^.MsgSubj)
+ Else
+ AddSubField(6, JM^.MsgSubj);
+ End;
+ If ((JM^.Dest.Zone <> 0) or (JM^.Dest.Net <> 0) or
+ (JM^.Dest.Node <> 0) or (JM^.Dest.Point <> 0)) Then
+ AddSubField(1, strAddr2Str(JM^.Dest));
+ If ((JM^.Orig.Zone <> 0) or (JM^.Orig.Net <> 0) or
+ (JM^.Orig.Node <> 0) or (JM^.Orig.Point <> 0)) Then
+ AddSubField(0, strAddr2Str(JM^.Orig));
+ WriteError := IoResult;
+ End;
+
+ If ReReadIdx(IdxLoc) = 0 Then;
+
+(* new shit begin *)
+ TmpHdr := MsgHdr^.JamHdr;
+ TmpHdr.TextLen := 0;
+ TmpHdr.Attr1 := Jam_Deleted;
+
+ Seek (JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
+ BlockWrite (JM^.HdrFile, TmpHdr, SizeOf(TmpHdr));
+
+(* new shit end *)
+
+ JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc := FileSize(JM^.HdrFile);
+ JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc := JamStrCrc(JM^.MsgTo);
+
+ Seek (JM^.HdrFile, FileSize(JM^.HdrFile));
+ BlockWrite (JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^.JamHdr) + MsgHdr^.JamHdr.SubFieldLen);
+
+ If WriteIdx = 0 Then;
+
+ If UnLockMsgBase Then; {unlock msg base}
+End;
+
+Function TMsgBaseJAM.WriteMsg: Word;
+ Var
+ DT: DateTime;
+ WriteError: Word;
+ i: longint;
+ TmpIdx: JamIdxType;
+
+Begin
+ WriteError := 0;
+ If LastSoft Then Begin
+ DoChar(#13);
+ DoChar(#10);
+ End;
+ If WriteError = 0 Then Begin
+ MsgHdr^.JamHdr.Signature[1] := 'J';{Set signature}
+ MsgHdr^.JamHdr.Signature[2] := 'A';
+ MsgHdr^.JamHdr.Signature[3] := 'M';
+ MsgHdr^.JamHdr.Signature[4] := #0;
+ Case JM^.MailType of
+ mmtNormal : SetAttr1 (Jam_TypeLocal, True);
+ mmtEchoMail : SetAttr1 (Jam_TypeEcho, True);
+ mmtNetMail : SetAttr1 (Jam_TypeNet, True);
+ End;
+ MsgHdr^.JamHdr.Rev := 1;
+ MsgHdr^.JamHdr.DateArrived := ToUnixDate(CurDateDos); {Get date processed}
+
+ DT.Year := strS2I(Copy(JM^.MsgDate, 7, 2)); {Convert date written}
+ DT.Month := strS2I(Copy(JM^.MsgDate, 1, 2));
+ DT.Day := strS2I(Copy(JM^.MsgDate, 4, 2));
+
+ If DT.Year < 80 Then
+ Inc(DT.Year, 2000)
+ Else
+ Inc(DT.Year, 1900);
+
+ DT.Sec := 0;
+ DT.Hour := strS2I(Copy(JM^.MsgTime, 1, 2));
+ DT.Min := strS2I(Copy(JM^.MsgTime, 4, 2));
+
+ MsgHdr^.JamHdr.DateWritten := DTToUnixDate(DT);
+ End;
+
+ If WriteError = 0 Then Begin {Lock message base for update}
+ If Not LockMsgBase Then
+ WriteError := 5;
+ End;
+ If WriteError = 0 Then Begin {Handle message text}
+ MsgHdr^.JamHdr.TextOfs := FileSize(JM^.TxtFile);
+ MsgHdr^.JamHdr.MsgNum := GetHighMsgNum + 1;
+ MsgHdr^.Jamhdr.TextLen := JM^.TxtPos;
+ If JM^.TxtBufStart > 0 Then Begin {Write text using buffer file}
+ i := JM^.TxtPos - JM^.TxtBufStart;
+ BlockWrite(JM^.BufFile, TxtBuf^, i); {write buffer to file}
+ WriteError := IoResult;
+ If WriteError = 0 Then Begin {seek start of buffer file}
+ Seek(JM^.BufFile, 0);
+ WriteError := IoResult;
+ End;
+ If WriteError = 0 Then Begin {seek end of text file}
+ Seek(JM^.TxtFile, FileSize(JM^.TxtFile));
+ WriteError := IoResult;
+ End;
+ While ((Not Eof(JM^.BufFile)) and (WriteError = 0)) Do Begin {copy buffer file to text file}
+ BlockRead(JM^.BufFile, TxtBuf^, SizeOf(TxtBuf^), i);
+ WriteError := IoResult;
+ If WriteError = 0 Then Begin
+ JM^.TxtBufStart := FilePos(JM^.TxtFile);
+ JM^.TxtRead := i;
+ BlockWrite(JM^.TxtFile, TxtBuf^, i);
+ Error := IoResult;
+ End;
+ End;
+ Close(JM^.BufFile);
+ Error := IoResult;
+ Erase(JM^.BufFile);
+ Error := IoResult;
+ End Else Begin {Write text using TxtBuf only}
+ Seek(JM^.Txtfile, FileSize(JM^.TxtFile));
+ WriteError := IoResult;
+ If WriteError = 0 Then
+ Begin
+ BlockWrite(JM^.TxtFile, TxtBuf^, JM^.TxtPos);
+ WriteError := IoResult;
+ JM^.TxtRead := JM^.TxtPos;
+ End;
+ End;
+ If WriteError = 0 Then {Add index record}
+ Begin
+ TmpIdx.HdrLoc := FileSize(JM^.HdrFile);
+ TmpIdx.MsgToCrc := JamStrCrc(JM^.MsgTo);
+ Seek(JM^.IdxFile, FileSize(JM^.IdxFile));
+ WriteError := IoResult;
+ End;
+ If WriteError = 0 Then {write index record}
+ Begin
+ BlockWrite(JM^.IdxFile, TmpIdx, 1);
+ WriteError := IoResult;
+ End;
+ If WriteError = 0 Then Begin {Add subfields as needed}
+ If Length(JM^.MsgTo) > 0 Then AddSubField(3, JM^.MsgTo);
+ If Length(JM^.MsgFrom) > 0 Then AddSubField(2, JM^.MsgFrom);
+ If Length(JM^.MsgSubj) > 0 Then Begin
+ If IsFileReq Then
+ AddSubField(11, JM^.MsgSubj)
+ Else
+ AddSubField(6, JM^.MsgSubj);
+ End;
+ If ((JM^.Dest.Zone <> 0) or (JM^.Dest.Net <> 0) or
+ (JM^.Dest.Node <> 0) or (JM^.Dest.Point <> 0)) Then
+ AddSubField(1, strAddr2Str(JM^.Dest));
+ If ((JM^.Orig.Zone <> 0) or (JM^.Orig.Net <> 0) or
+ (JM^.Orig.Node <> 0) or (JM^.Orig.Point <> 0)) Then
+ AddSubField(0, strAddr2Str(JM^.Orig));
+ Seek(JM^.HdrFile, FileSize(JM^.HdrFile)); {Seek to end of .jhr file}
+ WriteError := IoResult;
+ End;
+ If WriteError = 0 Then Begin {write msg header}
+ BlockWrite(JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^.JamHdr) +
+ MsgHdr^.JamHdr.SubFieldLen);
+ WriteError := IoResult;
+ End;
+ If WriteError = 0 Then Begin {update msg base header}
+ Inc(JM^.BaseHdr.ActiveMsgs);
+ Inc(JM^.BaseHdr.ModCounter);
+ End;
+ If UnLockMsgBase Then; {unlock msg base}
+ End;
+ WriteMsg := WriteError; {return result of writing msg}
+ End;
+
+
+Function TMsgBaseJAM.GetChar: Char;
+Begin
+ If JM^.TxtPos < 0 Then Begin
+ GetChar := JM^.TxtSubBuf[JM^.TxtSubChars + JM^.TxtPos];
+ Inc(JM^.TxtPos);
+ If JM^.TxtPos >= 0 Then
+ JM^.TxtPos := MsgHdr^.JamHdr.TextOfs;
+ End Else Begin
+ If ((JM^.TxtPos < JM^.TxtBufStart) Or
+ (JM^.TxtPos >= JM^.TxtBufStart + JM^.TxtRead)) Then Begin
+ JM^.TxtBufStart := JM^.TxtPos - 80;
+ If JM^.TxtBufStart < 0 Then JM^.TxtBufStart := 0;
+ Seek(JM^.TxtFile, JM^.TxtBufStart);
+ Error := IoResult;
+ If Error = 0 Then Begin
+ BlockRead(JM^.TxtFile, TxtBuf^, SizeOf(TxtBuf^), JM^.TxtRead);
+ Error := IoResult;
+ End;
+ End;
+ GetChar := TxtBuf^[JM^.TxtPos - JM^.TxtBufStart];
+ Inc(JM^.TxtPos);
+ End;
+End;
+
+(*
+Procedure TMsgBaseJAM.AddTxtSub(St: String);
+ Var
+ i: Word;
+
+ Begin
+ For i := 1 to Length(St) Do
+ Begin
+ If JM^.TxtSubChars <= TxtSubBufSize Then
+ Begin
+ JM^.TxtSubBuf[JM^.TxtSubChars] := St[i];
+ Inc(JM^.TxtSubChars);
+ End;
+ End;
+ If JM^.TxtSubChars <= TxtSubBufSize Then
+ Begin
+ JM^.TxtSubBuf[JM^.TxtSubChars] := #13;
+ Inc(JM^.TxtSubChars);
+ End;
+ End;
+*)
+
+Procedure TMsgBaseJAM.MsgStartUp;
+ Var
+ SubCtr: LongInt;
+ SubPtr: SubFieldPTR;
+ NumRead: LongInt;
+ DT: DateTime;
+ IdxLoc: LongInt;
+ TmpStr: String;
+{ TmpAddr: AddrType;}
+
+ Begin
+ Error := 0;
+ LastSoft := False;
+ JM^.MsgFrom := '';
+ JM^.MsgTo := '';
+ JM^.MsgSubj := '';
+ JM^.TxtSubChars := 0;
+ If SeekFound Then Begin
+ Error := ReReadIdx(IdxLoc);
+ If Error = 0 Then Begin
+ Seek(JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
+ Error := IoResult;
+ End;
+ If Error = 0 Then Begin
+ BlockRead(JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^), NumRead);
+ Error := IoResult;
+ End;
+ If Error = 0 Then Begin
+ UnixToDt(MsgHdr^.JamHdr.DateWritten, DT);
+
+ JM^.MsgDate := strZero(DT.Month) + '-' + strZero(DT.Day) + '-' + Copy(strI2S(DT.Year), 3, 2);
+ JM^.MsgTime := strZero(DT.Hour) + ':' + strZero(DT.Min);
+
+ SubCtr := 1;
+ While ((SubCtr <= MsgHdr^.JamHdr.SubFieldLen) and
+ (SubCtr < JamSubBufSize)) Do
+ Begin
+ SubPtr := SubFieldPTR(@MsgHdr^.SubBuf[SubCtr]);
+ Inc(SubCtr, SubPtr^.DataLen + 8);
+ Case(SubPtr^.LoId) Of
+ 0: Begin {Orig}
+ FillChar(JM^.Orig, SizeOf(JM^.Orig), #0);
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 128 Then TmpStr[0] := #128;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ If strStr2Addr(TmpStr, JM^.Orig) Then;
+ End;
+ 1: Begin {Dest}
+ FillChar(JM^.Dest, SizeOf(JM^.Dest), #0);
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 128 Then
+ TmpStr[0] := #128;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ If strStr2Addr(TmpStr, JM^.Dest) Then;
+ End;
+ 2: Begin {MsgFrom}
+ JM^.MsgFrom[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(JM^.MsgFrom[0]) > 65 Then
+ JM^.MsgFrom[0] := #65;
+ Move(SubPtr^.Data, JM^.MsgFrom[1], Ord(JM^.MsgFrom[0]));
+ End;
+ 3: Begin {MsgTo}
+ JM^.MsgTo[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(JM^.MsgTo[0]) > 65 Then JM^.MsgTo[0] := #65;
+ Move(SubPtr^.Data, JM^.MsgTo[1], Ord(JM^.MsgTo[0]));
+ End;
+(*
+ 4: Begin {MsgId}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1'MSGID: ' + TmpStr);
+ End;
+*)
+(*
+ 5: Begin {Reply}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1'REPLY: ' + TmpStr);
+ End;
+*)
+ 6: Begin {MsgSubj}
+ JM^.MsgSubj[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(JM^.MsgSubj[0]) > 100 Then
+ JM^.MsgSubj[0] := #100;
+ Move(SubPtr^.Data, JM^.MsgSubj[1], Ord(JM^.MsgSubj[0]));
+ End;
+(*
+ 7: Begin {PID}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1'PID: ' + TmpStr);
+ End;
+*)
+(*
+ 8: Begin {VIA}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1'Via ' + TmpStr);
+ End;
+*)
+ 9: Begin {File attached}
+ If IsFAttach Then Begin
+ JM^.MsgSubj[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(JM^.MsgSubj[0]) > 100 Then
+ JM^.MsgSubj[0] := #100;
+ Move(SubPtr^.Data, JM^.MsgSubj[1], Ord(JM^.MsgSubj[0]));
+ End
+ End;
+ 11: Begin {File request}
+ If IsFileReq Then
+ Begin
+ JM^.MsgSubj[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(JM^.MsgSubj[0]) > 100 Then
+ JM^.MsgSubj[0] := #100;
+ Move(SubPtr^.Data, JM^.MsgSubj[1], Ord(JM^.MsgSubj[0]));
+ End
+ End;
+(*
+ 2000: Begin {Unknown kludge}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1 + TmpStr);
+ End;
+*)
+(*
+ 2001: Begin {SEEN-BY}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1'SEEN-BY: ' + TmpStr);
+ End;
+*)
+(*
+ 2002: Begin {PATH}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1'PATH: ' + TmpStr);
+ End;
+*)
+(*
+ 2003: Begin {FLAGS}
+ TmpStr[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(TmpStr[0]) > 240 Then
+ TmpSTr[0] := #240;
+ Move(SubPtr^.Data, TmpStr[1], Ord(TmpStr[0]));
+ AddTxtSub(#1'FLAGS: ' + TmpStr);
+ End;
+*)
+ End;
+ End;
+ End;
+ End;
+ End;
+
+Procedure TMsgBaseJAM.MsgTxtStartUp;
+Begin
+ LastSoft := False;
+ JM^.TxtEnd := MsgHdr^.JamHdr.TextOfs + MsgHdr^.JamHdr.TextLen - 1;
+
+ If JM^.TxtSubChars > 0 Then
+ JM^.TxtPos := - JM^.TxtSubChars
+ Else
+ JM^.TxtPos := MsgHdr^.JamHdr.TextOfs;
+End;
+
+Function TMsgBaseJAM.GetString(MaxLen: Word): String;
+ Var
+ WPos: LongInt;
+ WLen: Byte;
+ StrDone: Boolean;
+// TxtOver: Boolean;
+ StartSoft: Boolean;
+ CurrLen: Word;
+// PPos: LongInt;
+ TmpCh: Char;
+
+ Begin
+ StrDone := False;
+ CurrLen := 0;
+// PPos := JM^.TxtPos;
+ WPos := 0;
+ WLen := 0;
+ StartSoft := LastSoft;
+ LastSoft := True;
+ TmpCh := GetChar;
+ While ((Not StrDone) And (CurrLen <= MaxLen) And (Not EOM)) Do Begin
+ Case TmpCh of
+ #$00:;
+ #$0d: Begin
+ StrDone := True;
+ LastSoft := False;
+ End;
+ #$8d:;
+ #$0a:;
+ #$20: Begin
+ If ((CurrLen <> 0) or (Not StartSoft)) Then
+ Begin
+ Inc(CurrLen);
+ WLen := CurrLen;
+ GetString[CurrLen] := TmpCh;
+ WPos := JM^.TxtPos;
+ End
+ Else
+ StartSoft := False;
+ End;
+ Else
+ Begin
+ Inc(CurrLen);
+ GetString[CurrLen] := TmpCh;
+ End;
+ End;
+ If Not StrDone Then
+ TmpCh := GetChar;
+ End;
+ If StrDone Then
+ Begin
+ GetString[0] := Chr(CurrLen);
+ End
+ Else
+ If EOM Then
+ Begin
+ GetString[0] := Chr(CurrLen);
+ End
+ Else
+ Begin
+ If WLen = 0 Then
+ Begin
+ GetString[0] := Chr(CurrLen);
+ Dec(JM^.TxtPos);
+ End
+ Else
+ Begin
+ GetString[0] := Chr(WLen);
+ JM^.TxtPos := WPos;
+ End;
+ End;
+ End;
+
+
+Function TMsgBaseJAM.EOM: Boolean;
+ Begin
+ EOM := (((JM^.TxtPos < MsgHdr^.JamHdr.TextOfs) Or
+ (JM^.TxtPos > JM^.TxtEnd)) And (JM^.TxtPos >= 0));
+ End;
+
+(*
+Function TMsgBaseJAM.WasWrap: Boolean;
+ Begin
+ WasWrap := LastSoft;
+ End;
+*)
+
+Function TMsgBaseJAM.GetFrom: String; {Get from name on current msg}
+ Begin
+ GetFrom := JM^.MsgFrom;
+ End;
+
+
+Function TMsgBaseJAM.GetTo: String; {Get to name on current msg}
+ Begin
+ GetTo := JM^.MsgTo;
+ End;
+
+
+Function TMsgBaseJAM.GetSubj: String; {Get subject on current msg}
+ Begin
+ GetSubj := JM^.MsgSubj;
+ End;
+
+
+Function TMsgBaseJAM.GetCost: Word; {Get cost of current msg}
+ Begin
+ GetCost := MsgHdr^.JamHdr.Cost;
+ End;
+
+
+Function TMsgBaseJAM.GetDate: String; {Get date of current msg}
+ Begin
+ GetDate := JM^.MsgDate;
+ End;
+
+
+Function TMsgBaseJAM.GetTime: String; {Get time of current msg}
+ Begin
+ GetTime := JM^.MsgTime;
+ End;
+
+
+Function TMsgBaseJAM.GetRefer: LongInt; {Get reply to of current msg}
+ Begin
+ GetRefer := MsgHdr^.JamHdr.ReplyTo;
+ End;
+
+
+Function TMsgBaseJAM.GetSeeAlso: LongInt; {Get see also of current msg}
+ Begin
+ GetSeeAlso := MsgHdr^.JamHdr.ReplyFirst;
+ End;
+
+
+Function TMsgBaseJAM.GetMsgNum: LongInt; {Get message number}
+ Begin
+ GetMsgNum := MsgHdr^.JamHdr.MsgNum;
+ End;
+
+
+Procedure TMsgBaseJAM.GetOrig(Var Addr: RecEchoMailAddr); {Get origin address}
+ Begin
+ Addr := JM^.Orig;
+ End;
+
+
+Procedure TMsgBaseJAM.GetDest(Var Addr: RecEchoMailAddr); {Get destination address}
+ Begin
+ Addr := JM^.Dest;
+ End;
+
+Function TMsgBaseJAM.GetTextLen : LongInt; {returns length of text in msg}
+Begin
+ GetTextLen := MsgHdr^.JamHdr.TextLen;
+End;
+
+Function TMsgBaseJAM.IsLocal: Boolean; {Is current msg local}
+ Begin
+ IsLocal := (MsgHdr^.JamHdr.Attr1 and Jam_Local) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsCrash: Boolean; {Is current msg crash}
+ Begin
+ IsCrash := (MsgHdr^.JamHdr.Attr1 and Jam_Crash) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsKillSent: Boolean; {Is current msg kill sent}
+ Begin
+ IsKillSent := (MsgHdr^.JamHdr.Attr1 and Jam_KillSent) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsSent: Boolean; {Is current msg sent}
+ Begin
+ IsSent := (MsgHdr^.JamHdr.Attr1 and Jam_Sent) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsFAttach: Boolean; {Is current msg file attach}
+ Begin
+ IsFAttach := (MsgHdr^.JamHdr.Attr1 and Jam_FAttch) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsReqRct: Boolean; {Is current msg request receipt}
+ Begin
+ IsReqRct := (MsgHdr^.JamHdr.Attr1 and Jam_RcptReq) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsReqAud: Boolean; {Is current msg request audit}
+ Begin
+ IsReqAud := (MsgHdr^.JamHdr.Attr1 and Jam_ConfmReq) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsRetRct: Boolean; {Is current msg a return receipt}
+ Begin
+ IsRetRct := False;
+ End;
+
+
+Function TMsgBaseJAM.IsFileReq: Boolean; {Is current msg a file request}
+ Begin
+ IsFileReq := (MsgHdr^.JamHdr.Attr1 and Jam_Freq) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsRcvd: Boolean; {Is current msg received}
+ Begin
+ IsRcvd := (MsgHdr^.JamHdr.Attr1 and Jam_Rcvd) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsPriv: Boolean; {Is current msg priviledged/private}
+ Begin
+ IsPriv := (MsgHdr^.JamHdr.Attr1 and Jam_Priv) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsDeleted: Boolean; {Is current msg deleted}
+ Begin
+ IsDeleted := (MsgHdr^.JamHdr.Attr1 and Jam_Deleted) <> 0;
+ End;
+
+
+Function TMsgBaseJAM.IsEchoed: Boolean; {Is current msg echoed}
+ Begin
+ IsEchoed := True;
+ End;
+
+
+Procedure TMsgBaseJAM.SeekFirst(MsgNum: LongInt); {Start msg seek}
+ Begin
+ JM^.CurrMsgNum := MsgNum - 1;
+ If JM^.CurrMsgNum < (JM^.BaseHdr.BaseMsgNum - 1) Then
+ JM^.CurrMsgNum := JM^.BaseHdr.BaseMsgNum - 1;
+ SeekNext;
+ End;
+
+
+Procedure TMsgBaseJAM.SeekNext; {Find next matching msg}
+Var
+ IdxLoc: LongInt;
+Begin
+ If JM^.CurrMsgNum <= GetHighMsgNum Then
+ Inc(JM^.CurrMsgNum);
+
+ Error := ReReadIdx(IdxLoc);
+
+ While (((JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc < 0) or (JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = -1)) And (JM^.CurrMsgNum <= GetHighMsgNum)) Do Begin
+ Inc(JM^.CurrMsgNum);
+ Error := ReReadIdx(IdxLoc);
+ End;
+End;
+
+Procedure TMsgBaseJAM.SeekPrior;
+Var
+ IdxLoc: LongInt;
+Begin
+ If JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum Then
+ Dec(JM^.CurrMsgNum);
+
+ Error := ReReadIdx(IdxLoc);
+
+ If JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum Then
+ While (IdxLoc >= 0) And (((JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc < 0) or (JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = -1)) And (JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum)) Do Begin
+ Dec(JM^.CurrMsgNum);
+ Error := ReReadIdx(IdxLoc);
+ End;
+End;
+
+Function TMsgBaseJAM.SeekFound: Boolean;
+Begin
+ SeekFound := ((JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum) and (JM^.CurrMsgNum <= GetHighMsgNum));
+End;
+
+
+Function TMsgBaseJAM.GetMsgLoc: LongInt; {Msg location}
+Begin
+ GetMsgLoc := GetMsgNum;
+End;
+
+
+Procedure TMsgBaseJAM.SetMsgLoc(ML: LongInt); {Msg location}
+ Begin
+ JM^.CurrMsgNum := ML;
+ End;
+
+
+Procedure TMsgBaseJAM.YoursFirst(Name: String; Handle: String);
+Begin
+ JM^.YourName := Name;
+ JM^.YourHdl := Handle;
+ JM^.NameCrc := JamStrCrc(Name);
+ JM^.HdlCrc := JamStrCrc(Handle);
+ JM^.CurrMsgNum := JM^.BaseHdr.BaseMsgNum - 1;
+ YoursNext;
+End;
+
+
+Procedure TMsgBaseJAM.YoursNext;
+Var
+ Found : Boolean;
+ IdxLoc : LongInt;
+ NumRead : LongInt;
+ SubCtr : LongInt;
+ SubPtr : SubFieldPTR;
+Begin
+ Error := 0;
+ Found := False;
+
+ Inc(JM^.CurrMsgNum);
+ While ((Not Found) and (JM^.CurrMsgNum <= GetHighMsgNum) And (Error = 0)) Do Begin
+ Error := ReReadIdx(IdxLoc);
+ If Error = 0 Then Begin {Check CRC values}
+ If ((JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = JM^.NameCrc) or
+ (JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc = JM^.HdlCrc)) Then Begin
+ Seek(JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
+ Error := IoResult;
+ If Error = 0 Then {Read message header}
+ Begin
+ BlockRead(JM^.HdrFile, MsgHdr^, SizeOf(MsgHdr^), NumRead);
+ Error := IoResult;
+ End;
+ If ((Error = 0) and (Not IsRcvd)) Then Begin
+ SubCtr := 1;
+ While ((SubCtr <= MsgHdr^.JamHdr.SubFieldLen) and
+ (SubCtr < JamSubBufSize)) Do Begin
+ SubPtr := SubFieldPTR(@MsgHdr^.SubBuf[SubCtr]);
+ Inc(SubCtr, SubPtr^.DataLen + 8);
+ Case(SubPtr^.LoId) Of
+ 3: Begin {MsgTo}
+ JM^.MsgTo[0] := Chr(SubPtr^.DataLen and $ff);
+ If Ord(JM^.MsgTo[0]) > 65 Then
+ JM^.MsgTo[0] := #65;
+ Move(SubPtr^.Data, JM^.MsgTo[1], Ord(JM^.MsgTo[0]));
+ If ((strUpper(JM^.MsgTo) = strUpper(JM^.YourName)) Or
+ (strUpper(JM^.MsgTo) = strUpper(JM^.YourHdl))) Then
+ Found := True;
+ End;
+ End;
+ End;
+ End;
+ End;
+ End;
+ If (Not Found) Then
+ Inc(JM^.CurrMsgNum);
+ End;
+ End;
+
+
+Function TMsgBaseJAM.YoursFound: Boolean;
+ Begin
+ YoursFound := ((JM^.CurrMsgNum >= JM^.BaseHdr.BaseMsgNum) and
+ (JM^.CurrMsgNum <= GetHighMsgNum));
+ End;
+
+
+Procedure TMsgBaseJAM.StartNewMsg;
+ Begin
+ JM^.TxtBufStart := 0;
+ JM^.TxtPos := 0;
+ FillChar(MsgHdr^, SizeOf(MsgHdr^), #0);
+ MsgHdr^.JamHdr.SubFieldLen := 0;
+ MsgHdr^.JamHdr.MsgIdCrc := -1;
+ MsgHdr^.JamHdr.ReplyCrc := -1;
+ MsgHdr^.JamHdr.PwdCrc := -1;
+ JM^.MsgTo := '';
+ JM^.MsgFrom := '';
+ JM^.MsgSubj := '';
+ FillChar(JM^.Orig, SizeOf(JM^.Orig), #0);
+ FillChar(JM^.Dest, SizeOf(JM^.Dest), #0);
+ JM^.MsgDate := DateDos2Str(CurDateDos, 1);
+ JM^.MsgTime := TimeDos2Str(CurDateDos, False);
+// writeln(jm^.msgdate);
+ End;
+
+
+Function TMsgBaseJAM.MsgBaseExists: Boolean;
+ Begin
+ MsgBaseExists := (FileExist(JM^.MsgPath + '.jhr'));
+ End;
+
+
+Function TMsgBaseJAM.ReadIdx: Word;
+ Begin
+ If JM^.IdxStart < 0 Then JM^.IdxStart := 0;
+ Seek(JM^.IdxFile, JM^.IdxStart);
+ BlockRead(JM^.IdxFile, JamIdx^, JamIdxBufSize, JM^.IdxRead);
+ ReadIdx := IoResult;
+ End;
+
+
+Function TMsgBaseJAM.WriteIdx: Word;
+ Begin
+ Seek(JM^.IdxFile, JM^.IdxStart);
+ BlockWrite(JM^.IdxFile, JamIdx^, JM^.IdxRead);
+ WriteIdx := IoResult;
+ End;
+
+
+Function TMsgBaseJAM.OpenMsgBase: Boolean;
+Var
+ OpenError : Word;
+Begin
+ OpenError := 0;
+ JM^.LockCount := 0;
+
+ Assign(JM^.HdrFile, JM^.MsgPath + '.jhr');
+ Assign(JM^.TxtFile, JM^.MsgPath + '.jdt');
+ Assign(JM^.IdxFile, JM^.MsgPath + '.jdx');
+
+ FileMode := fmReadWrite + fmDenyNone;
+
+ Reset(JM^.HdrFile, 1);
+
+ OpenError := IoResult;
+
+ If OpenError = 0 Then Begin
+ Seek(JM^.HdrFile, 1);
+ BlockRead(JM^.HdrFile, JM^.BaseHdr.Signature[2] , SizeOf(JM^.BaseHdr) - 1);
+ OpenError := IoResult;
+ End;
+
+ If OpenError = 0 Then Begin
+ FileMode := fmReadWrite + fmDenyNone;
+ Reset(JM^.TxtFile, 1);
+ OpenError := IoResult;
+ End;
+
+ If OpenError = 0 Then Begin
+ FileMode := fmReadWrite + fmDenyNone;
+ Reset(JM^.IdxFile, SizeOf(JamIdxType));
+ OpenError := IoResult;
+ End;
+
+ JM^.IdxStart := -10;
+ JM^.IdxRead := 0;
+ JM^.TxtBufStart := - 10;
+ JM^.TxtRead := 0;
+ OpenMsgBase := (OpenError = 0);
+End;
+
+Procedure TMsgBaseJAM.CloseMsgBase;
+Begin
+ Close(JM^.HdrFile);
+ Close(JM^.TxtFile);
+ Close(JM^.IdxFile);
+End;
+
+Function TMsgBaseJAM.CreateMsgBase(MaxMsg: Word; MaxDays: Word): Boolean;
+Var
+ TmpHdr: ^JamHdrType;
+ CreateError: Word;
+// i: Word;
+
+Begin
+ CreateError := 0;
+
+ New(TmpHdr);
+ If TmpHdr = Nil Then
+ CreateError := 500
+ Else Begin;
+ FillChar(TmpHdr^, SizeOf(TmpHdr^), #0);
+ TmpHdr^.Signature[1] := 'J';
+ TmpHdr^.Signature[2] := 'A';
+ TmpHdr^.Signature[3] := 'M';
+ TmpHdr^.BaseMsgNum := 1;
+ TmpHdr^.Created := ToUnixDate(CurDateDos);
+ TmpHdr^.PwdCrc := -1;
+ CreateError := SaveFile(JM^.MsgPath + '.jhr', TmpHdr^, SizeOf(TmpHdr^));
+ Dispose(TmpHdr);
+ If CreateError = 0 Then
+ CreateError := SaveFile(JM^.MsgPath + '.jlr', CreateError, 0);
+ If CreateError = 0 Then
+ CreateError := SaveFile(JM^.MsgPath + '.jdt', CreateError, 0);
+ If CreateError = 0 Then
+ CreateError := SaveFile(JM^.MsgPath + '.jdx', CreateError , 0);
+ If IoResult <> 0 Then;
+ End;
+ CreateMsgBase := CreateError = 0;
+End;
+
+
+Procedure TMsgBaseJAM.SetMailType(MT: MsgMailType);
+ Begin
+ JM^.MailType := MT;
+ End;
+
+
+Function TMsgBaseJAM.GetSubArea: Word;
+ Begin
+ GetSubArea := 0;
+ End;
+
+
+Procedure TMsgBaseJAM.ReWriteHdr;
+Var
+ IdxLoc: LongInt;
+
+Begin
+ If LockMsgBase Then
+ Error := 0
+ Else
+ Error := 5;
+ Error := ReReadIdx(IdxLoc);
+ If Error = 0 Then Begin
+ Seek(JM^.HdrFile, JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc);
+ Error := IoResult;
+ End;
+ If Error = 0 Then Begin
+ BlockWrite(JM^.HdrFile, MsgHdr^.JamHdr, SizeOf(MsgHdr^.JamHdr));
+ Error := IoResult;
+ End;
+ If UnLockMsgBase Then;
+End;
+
+
+Procedure TMsgBaseJAM.DeleteMsg;
+Var
+ DelError : Word;
+ IdxLoc : LongInt;
+Begin
+ If Not IsDeleted Then Begin
+ If LockMsgBase Then
+ DelError := 0
+ Else
+ DelError := 5;
+ If DelError = 0 Then Begin
+ SetAttr1(Jam_Deleted, True);
+ Dec(JM^.BaseHdr.ActiveMsgs);
+ DelError := ReReadIdx(IdxLoc);
+ End;
+ If DelError = 0 Then ReWriteHdr;
+ If DelError = 0 Then Begin
+ Inc(JM^.BaseHdr.ModCounter);
+{these three were commented out for some reason }
+ JamIdx^[IdxLoc - JM^.IdxStart].MsgToCrc := -1;
+ JamIdx^[IdxLoc - JM^.IdxStart].HdrLoc := -1;
+ If WriteIdx = 0 Then;
+ End;
+ If UnLockMsgBase Then;
+ End;
+End;
+
+Function TMsgBaseJAM.NumberOfMsgs: LongInt;
+Begin
+ NumberOfMsgs := JM^.BaseHdr.ActiveMsgs;
+End;
+
+
+Function TMsgBaseJAM.FindLastRead(Var LastFile: File; UNum: LongInt): LongInt;
+ Const
+ LastSize = 100;
+
+ Type LastArray = Array[1..LastSize] of JamLastType;
+
+ Var
+ LastBuf: ^LastArray;
+ LastError: Word;
+ NumRead: LongInt;
+ Found: Boolean;
+ i: Word;
+ LastStart: LongInt;
+
+ Begin
+ FindLastRead := -1;
+ Found := False;
+ New(LastBuf);
+ Seek(LastFile, 0);
+ LastError := IoResult;
+ While ((Not Eof(LastFile)) and (LastError = 0) And (Not Found)) Do
+ Begin
+ LastStart := FilePos(LastFile);
+ BlockRead(LastFile, LastBuf^, LastSize, NumRead);
+ LastError := IoResult;
+ For i := 1 to NumRead Do Begin
+ If LastBuf^[i].UserNum = UNum Then
+ Begin
+ Found := True;
+ FindLastRead := LastStart + i - 1;
+ End;
+ End;
+ End;
+ Dispose(LastBuf);
+ End;
+
+
+Function TMsgBaseJAM.GetLastRead(UNum: LongInt): LongInt;
+Var
+ RecNum: LongInt;
+ LastFile: File;
+ TmpLast: JamLastType;
+Begin
+ Assign(LastFile, JM^.MsgPath + '.jlr');
+ FileMode := fmReadWrite + fmDenyNone;
+ Reset(LastFile, SizeOf(JamLastType));
+ Error := IoResult;
+ RecNum := FindLastRead(LastFile, UNum);
+ If RecNum >= 0 Then Begin
+ Seek(LastFile, RecNum);
+ If Error = 0 Then Begin
+ BlockRead(LastFile, TmpLast, 1);
+ Error := IoResult;
+ GetLastRead := TmpLast.HighRead;
+ End;
+ End Else
+ GetLastRead := 0;
+
+ Close(LastFile);
+ Error := IoResult;
+End;
+
+Procedure TMsgBaseJAM.SetLastRead(UNum: LongInt; LR: LongInt);
+ Var
+ RecNum: LongInt;
+ LastFile: File;
+ TmpLast: JamLastType;
+
+ Begin
+ Assign(LastFile, JM^.MsgPath + '.jlr');
+ FileMode := fmReadWrite + fmDenyNone;
+ Reset(LastFile, SizeOf(JamLastType));
+ Error := IoResult;
+ If Error <> 0 Then ReWrite(LastFile, SizeOf(JamLastType));
+ Error := IoResult;
+ RecNum := FindLastRead(LastFile, UNum);
+ If RecNum >= 0 Then Begin
+ Seek(LastFile, RecNum);
+ If Error = 0 Then Begin
+ BlockRead(LastFile, TmpLast, 1);
+ Error := IoResult;
+ TmpLast.HighRead := LR;
+ TmpLast.LastRead := LR;
+ If Error = 0 Then Begin
+ Seek(LastFile, RecNum);
+ Error := IoResult;
+ End;
+ If Error = 0 Then Begin
+ BlockWrite(LastFile, TmpLast, 1);
+ Error := IoResult;
+ End;
+ End;
+ End Else Begin
+ TmpLast.UserNum := UNum;
+ TmpLast.HighRead := Lr;
+ TmpLast.NameCrc := UNum;
+ TmpLast.LastRead := Lr;
+ Seek(LastFile, FileSize(LastFile));
+ Error := IoResult;
+ If Error = 0 Then Begin
+ BlockWrite(LastFile, TmpLast, 1);
+ Error := IoResult;
+ End;
+ End;
+ Close(LastFile);
+ Error := IoResult;
+End;
+
+
+Function TMsgBaseJAM.GetTxtPos: LongInt;
+Begin
+ GetTxtPos := JM^.TxtPos;
+End;
+
+Procedure TMsgBaseJAM.SetTxtPos(TP: LongInt);
+Begin
+ JM^.TxtPos := TP;
+End;
+
+Function TMsgBaseJAM.LockMsgBase: Boolean;
+Var
+ LockError: Word;
+Begin
+ LockError := 0;
+ If JM^.LockCount = 0 Then Begin
+ If LockError = 0 Then Begin
+// LockError := shLock(JM^.HdrFile, 0, 1);
+ End;
+ If LockError = 0 Then Begin
+ Seek(JM^.HdrFile, 0);
+ LockError := IoResult;
+ End;
+ If LockError = 0 Then Begin
+ BlockRead(JM^.HdrFile, JM^.BaseHdr , SizeOf(JM^.BaseHdr));
+ LockError := IoResult;
+ End;
+ End;
+ Inc(JM^.LockCount);
+ LockMsgBase := (LockError = 0);
+End;
+
+Function TMsgBaseJAM.UnLockMsgBase: Boolean;
+Var
+ LockError: Word;
+Begin
+ LockError := 0;
+ If JM^.LockCount > 0 Then Dec(JM^.LockCount);
+ If JM^.LockCount = 0 Then Begin
+ If LockError = 0 Then Begin
+// LockError := UnLockFile(JM^.HdrFile, 0, 1);
+ End;
+ If LockError = 0 Then Begin
+ Seek(JM^.HdrFile, 0);
+ LockError := IoResult;
+ End;
+ If LockError = 0 Then Begin
+ BlockWrite(JM^.HdrFile, JM^.BaseHdr, SizeOf(JM^.BaseHdr));
+ LockError := IoResult;
+ End;
+ End;
+ UnLockMsgBase := (LockError = 0);
+End;
+
+{SetSeeAlso/GetSeeAlso provided by 2:201/623@FidoNet Jonas@iis.bbs.bad.se}
+
+Procedure TMsgBaseJAM.SetNextSeeAlso(SAlso: LongInt);
+Begin
+ MsgHdr^.JamHdr.ReplyNext := SAlso;
+End;
+
+Function TMsgBaseJAM.GetNextSeeAlso: LongInt; {Get next see also of current msg}
+Begin
+ GetNextSeeAlso := MsgHdr^.JamHdr.ReplyNext;
+End;
+
+Function TMsgBaseJAM.ReReadIdx(Var IdxLoc : LongInt) : Word;
+Begin
+ ReReadIdx := 0;
+ IdxLoc := JM^.CurrMsgNum - JM^.BaseHdr.BaseMsgNum;
+ If ((IdxLoc < JM^.IdxStart) OR (IdxLoc >= (JM^.IdxStart+JM^.IdxRead))) Then Begin
+ JM^.IdxStart := IdxLoc - 30;
+ If JM^.IdxStart < 0 Then JM^.IdxStart := 0;
+ ReReadIdx := ReadIdx;
+ End;
+End;
+
+End.
diff --git a/mystic/bbs_msgbase_squish.pas b/mystic/bbs_msgbase_squish.pas
new file mode 100644
index 0000000..1a8907c
--- /dev/null
+++ b/mystic/bbs_msgbase_squish.pas
@@ -0,0 +1,1582 @@
+{$I M_OPS.PAS}
+
+Unit BBS_MsgBase_Squish;
+
+Interface
+
+Uses
+ BBS_MsgBase_Abs,
+ BBS_Common,
+ DOS;
+
+Const
+ SqHdrId = $AFAE4453;
+ SqLinkNext = 0;
+ SqLinkPrev = 1;
+ SqNullFrame = 0;
+ SqFrameMsg = 0;
+ SqFrameFree = 1;
+ SqFrameRLE = 2;
+ SqFrameLZW = 3;
+ SqFromSize = 36;
+ SqToSize = 36;
+ SqSubjSize = 72;
+ SqMaxReply = 10;
+
+Type
+ SqBaseType = Record
+ Len : Word; { Length of this record }
+ Rsvd1 : Word; { Future use }
+ NumMsg : LongInt; { Number of messages }
+ HighMsg : LongInt; { Highest msg }
+ SkipMsg : LongInt; { # of msgs to keep in beginning of area }
+ HighWater : LongInt; { High water UMsgId }
+ Uid : LongInt; { Next UMsgId }
+ Base : String[79]; { Base name of Squish file }
+ BeginFrame : LongInt; { Offset of first frame in file }
+ LastFrame : LongInt; { Offset of last frame in file }
+ FirstFree : LongInt; { Offset of first free frame in file }
+ LastFree : LongInt; { Offset of last free frame in file }
+ EndFrame : LongInt; { Pointer to end of file }
+ MaxMsg : LongInt; { Maximum number of messages }
+ KeepDays : Word; { Maximum age of messages }
+ SqHdrSize : Word; { Size of frame header }
+ Rsvd2 : Array[1..124] of Byte;
+ End;
+
+ SqFrameHdrType = Record
+ Id : LongInt; { Must equal SqHdrId }
+ NextFrame : LongInt; { Next msg frame }
+ PrevFrame : LongInt; { Prior msg frame }
+ FrameLength : LongInt; { Length of this frame not counting header }
+ MsgLength : LongInt; { Length of message }
+ ControlLength : LongInt; { Length of control information }
+ FrameType : Word; { Type of message frame }
+ Rsvd : Word; { Future use }
+ End;
+
+ SqMsgHdrType = Record
+ Attr : LongInt; { Msg attribute }
+ MsgFrom : String[SqFromSize - 1]; { Nul Term from name }
+ MsgTo : String[SqToSize - 1]; { Nul term to name }
+ Subj : String[SqSubjSize - 1]; { Nul term subject }
+ Orig : RecEchoMailAddr; { Origin address }
+ Dest : RecEchoMailAddr; { Destination address }
+ DateWritten : LongInt; { Date msg written }
+ DateArrived : LongInt; { Date msg arrived here }
+ UtcOffset : Word; { Minutes offset from UTC }
+ ReplyTo : LongInt; { Original msg }
+ Replies : Array[1..SqMaxReply] of LongInt; { Replies }
+ AzDate : String[19]; { AsciiZ "Fido" style date }
+ End;
+
+ SqIdxType = Record
+ Ofs : LongInt; { Offset of frame header }
+ UMsgId : LongInt; { Unique message id }
+ Hash : LongInt; { Hash of MsgTo name }
+ End;
+
+Const
+ SqIdxArraySize = 5200; {5200}
+
+Type
+ SqIdxArrayType = Array[1..SqIdxArraySize] of SqIdxType;
+ SqIdxPtrType = ^SqIdxArrayType;
+
+ FreeListType = Record
+ FreePos : LongInt;
+ FreeSize : LongInt;
+ End;
+
+Const
+ MaxFree = 500;
+
+Type
+ FreeArrayType = Array[1..MaxFree] of FreeListType;
+
+Const
+ SqBSize : Word = SizeOf(SqBaseType);
+ SqFSize : Word = SizeOf(SqFrameHdrType);
+ SqMSize : Word = SizeOf(SqMsgHdrType);
+ SqISize : Word = SizeOf(SqIdxType);
+
+Const
+ SqTxtBufferSize = 16000; {reduced from 34000 to 16000. this should }
+ {handle 200 lines x 80 chars EASILY }
+Type
+ SqInfoType = Record
+ FN : String[80];
+ MsgChars : Array[1..SqTxtBufferSize] of Char;
+ Error : Word;
+ SqdFile : File;
+ SqIFile : File;
+ SqBase : SqBaseType;
+ SqBaseExtra : Array[1..100] of Char;
+ SqdOpened : Boolean;
+ SqiOpened : Boolean;
+ SqiAlloc : Word;
+ Locked : Boolean;
+ FreeLoaded : Boolean;
+ HighestFree : Word;
+ Frame : SqFrameHdrType;
+ MsgHdr : SqMsgHdrType;
+ Extra : Array[1..100] of Char;
+ TxtCtr : Word;
+ MsgDone : Boolean;
+ CurrIdx : Word;
+ StrDate : String[8];
+ StrTime : String[8];
+ CurrentFramePos : LongInt;
+ CurrentUID : LongInt;
+ SName : String[35];
+ SHandle : String[35];
+ HName : LongInt;
+ HHandle : LongInt;
+ End;
+
+Type
+
+ PMsgBaseSquish = ^TMsgBaseSquish;
+ TMsgBaseSquish = Object(TMsgBaseAbs)
+ SqInfo : ^SqInfoType;
+ SqIdx : ^SqIdxArrayType;
+ FreeArray : ^FreeArrayType;
+
+ Procedure EditMsgInit; Virtual;
+ Procedure EditMsgSave; Virtual;
+
+ Constructor Init; {Initialize}
+ Destructor Done; Virtual; {Done cleanup and dispose}
+ Function OpenMsgBase: Boolean; Virtual; {Open message base}
+ Procedure CloseMsgBase; Virtual; {Close message base}
+ Function CreateMsgBase(MaxMsg: Word; MaxDays: Word): Boolean; Virtual;
+ Function MsgBaseExists: Boolean; Virtual;
+ Procedure SetMsgPath(FN: String); Virtual; {Set filepath and name - no extension}
+ Function SqdOpen: Boolean; Virtual; {Open squish data file}
+ Function SqiOpen: Boolean; Virtual; {Open squish index file}
+ Procedure SqdClose; Virtual; {Close squish data file}
+ Procedure SqiClose; Virtual; {Close squish index file}
+ Function LockMsgBase: Boolean; Virtual; {Lock msg base}
+ Function UnLockMsgBase: Boolean; Virtual; {Unlock msg base}
+ Procedure ReadBase; Virtual; {Read base data record}
+ Procedure WriteBase; Virtual; {Write base data record}
+ Function GetBeginFrame: LongInt; Virtual; {Get beginning frame pos}
+ Function GetHighWater: LongInt; Virtual; {Get high water umsgid}
+ Function GetHighMsgNum: LongInt; Virtual; {Get highest msg number}
+ Procedure ReadFrame(FPos: LongInt); Virtual; {Read frame at FPos}
+ Procedure ReadVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); Virtual; {Read frame at FPos into Frame}
+ Procedure WriteFrame(FPos: LongInt); Virtual; {Write frame at FPos}
+ Procedure WriteVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); Virtual;
+ Procedure UnlinkFrame(Var Frame: SqFrameHdrType); Virtual; {Unlink frame from linked list}
+ Procedure LinkFrameNext(Var Frame: SqFrameHdrType; OtherFrame: LongInt;
+ FramePos: LongInt); Virtual; {Link frame after other frame}
+ Procedure KillMsg(MsgNum: LongInt); {Kill msg msgnum}
+ Procedure KillExcess; {Kill msg in excess of limit}
+ Procedure FindFrame(Var FL: LongInt; Var FramePos: LongInt); Virtual;
+ Function GetNextFrame: LongInt; Virtual; {Get next frame pos}
+ Procedure ReadMsgHdr(FPos: LongInt); Virtual; {Read msg hdr for frame at FPos}
+ Procedure WriteMsgHdr(FPos: LongInt); Virtual; {Read msg hdr for frame at FPos}
+ Procedure WriteText(FPos: LongInt); Virtual; {Write text buffer for frame at Fpos}
+ Function SqHashName(Name: String): LongInt; Virtual; {Convert name to hash value}
+ Procedure StartNewMsg; Virtual; {Initialize msg header}
+ Function GetFrom: String; Virtual; {Get message from}
+ Function GetTo: String; Virtual; {Get message to}
+ Function GetSubj: String; Virtual; {Get message subject}
+ Function GetTextLen: LongInt; Virtual; {Get text length}
+ Procedure SetFrom(Str: String); Virtual; {Set message from}
+ Procedure SetTo(Str: String); Virtual; {Set message to}
+ Procedure SetSubj(Str: String); Virtual; {Set message subject}
+ Procedure SetDate(Str: String); Virtual; {Set message date}
+ Procedure SetTime(Str: String); Virtual; {Set message time}
+ Function GetDate: String; Virtual; {Get message date mm-dd-yy}
+ Function GetTime: String; Virtual; {Get message time hh:mm}
+ Function GetRefer: LongInt; Virtual; {Get reply to of current msg}
+ Procedure SetRefer(Num: LongInt); Virtual; {Set reply to of current msg}
+ Function GetSeeAlso: LongInt; Virtual; {Get see also msg}
+ Procedure SetSeeAlso(Num: LongInt); Virtual; {Set see also msg}
+ Procedure ReadText(FPos: LongInt); Virtual;
+ Function GetChar: Char; Virtual;
+ Function GetString(MaxLen: Word): String; Virtual;
+ Procedure GetOrig(Var Addr: RecEchoMailAddr); Virtual;
+ Procedure SetOrig(Var Addr: RecEchoMailAddr); Virtual;
+ Procedure GetDest(Var Addr: RecEchoMailAddr); Virtual;
+ Procedure SetDest(Var Addr: RecEchoMailAddr); Virtual;
+ Function EOM: Boolean; Virtual;
+(*
+ Function WasWrap: Boolean; Virtual;
+*)
+ Procedure InitText; Virtual;
+ Procedure DoString(Str: String); Virtual; {Add string to message text}
+ Procedure DoChar(Ch: Char); Virtual; {Add character to message text}
+ Procedure DoStringLn(Str: String); Virtual; {Add string and newline to msg text}
+ Function WriteMsg: Word; Virtual; {Write msg to msg base}
+ Procedure ReadIdx; Virtual;
+ Procedure WriteIdx; Virtual;
+ Procedure SeekFirst(MsgNum: LongInt); Virtual; {Seeks to 1st msg >= MsgNum}
+ Function GetMsgNum: LongInt; Virtual;
+ Procedure SeekNext; Virtual;
+ Procedure SeekPrior; Virtual;
+ Function SeekFound: Boolean; Virtual;
+ Function GetIdxFramePos: LongInt; Virtual;
+ Function GetIdxHash: LongInt; Virtual;
+ Function IsLocal: Boolean; Virtual; {Is current msg local}
+ Function IsCrash: Boolean; Virtual; {Is current msg crash}
+ Function IsKillSent: Boolean; Virtual; {Is current msg kill sent}
+ Function IsSent: Boolean; Virtual; {Is current msg sent}
+ Function IsFAttach: Boolean; Virtual; {Is current msg file attach}
+ Function IsReqRct: Boolean; Virtual; {Is current msg request receipt}
+ Function IsReqAud: Boolean; Virtual; {Is current msg request audit}
+ Function IsRetRct: Boolean; Virtual; {Is current msg a return receipt}
+ Function IsFileReq: Boolean; Virtual; {Is current msg a file request}
+ Function IsRcvd: Boolean; Virtual; {Is current msg received}
+ Function IsPriv: Boolean; Virtual; {Is current msg priviledged/private}
+ Function IsDeleted: Boolean; Virtual; {Is current msg deleted}
+ Procedure SetAttr(St: Boolean; Mask: LongInt); Virtual; {Set attribute}
+ Procedure SetLocal(St: Boolean); Virtual; {Set local status}
+ Procedure SetRcvd(St: Boolean); Virtual; {Set received status}
+ Procedure SetPriv(St: Boolean); Virtual; {Set priveledge vs public status}
+ Procedure SetCrash(St: Boolean); Virtual; {Set crash netmail status}
+ Procedure SetHold (ST: Boolean); Virtual;
+ Procedure SetKillSent(St: Boolean); Virtual; {Set kill/sent netmail status}
+ Procedure SetSent(St: Boolean); Virtual; {Set sent netmail status}
+ Procedure SetFAttach(St: Boolean); Virtual; {Set file attach status}
+ Procedure SetReqRct(St: Boolean); Virtual; {Set request receipt status}
+ Procedure SetReqAud(St: Boolean); Virtual; {Set request audit status}
+ Procedure SetRetRct(St: Boolean); Virtual; {Set return receipt status}
+ Procedure SetFileReq(St: Boolean); Virtual; {Set file request status}
+ Procedure MsgStartUp; Virtual; {Set up message}
+ Procedure MsgTxtStartUp; Virtual; {Set up for msg text}
+ Procedure SetMailType(MT: MsgMailType); Virtual; {Set message base type}
+ Function GetSubArea: Word; Virtual; {Get sub area number}
+ Procedure ReWriteHdr; Virtual; {Rewrite msg header after changes}
+ Procedure DeleteMsg; Virtual; {Delete current message}
+ Procedure LoadFree; Virtual; {Load freelist into memory}
+ Function NumberOfMsgs: LongInt; Virtual; {Number of messages}
+ Procedure SetEcho(ES: Boolean); Virtual; {Set echo status}
+ Function IsEchoed: Boolean; Virtual; {Is current msg unmoved echomail msg}
+ Function GetLastRead(UNum: LongInt): LongInt; Virtual; {Get last read for user num}
+ Procedure SetLastRead(UNum: LongInt; LR: LongInt); Virtual; {Set last read}
+ Function GetMsgLoc: LongInt; Virtual; {To allow reseeking to message}
+ Procedure SetMsgLoc(ML: LongInt); Virtual; {Reseek to message}
+ Function IdxHighest: LongInt; Virtual; { *** }
+ Procedure YoursFirst(Name: String; Handle: String); Virtual; {Seek your mail}
+ Procedure YoursNext; Virtual; {Seek next your mail}
+ Function YoursFound: Boolean; Virtual; {Message found}
+ Function GetMsgDisplayNum: LongInt; Virtual; {Get msg number to display}
+ Function GetTxtPos: LongInt; Virtual; {Get indicator of msg text position}
+ Procedure SetTxtPos(TP: LongInt); Virtual; {Set text position}
+ End;
+
+Implementation
+
+Uses
+ mkcrap,
+ m_Strings,
+ m_DateTime,
+ m_FileIO;
+
+Const
+ SqMsgPriv = $00001;
+ SqMsgCrash = $00002;
+ SqMsgRcvd = $00004;
+ SqMsgSent = $00008;
+ SqMsgFile = $00010;
+ SqMsgFwd = $00020;
+ SqMsgOrphan = $00040;
+ SqMsgKill = $00080;
+ SqMsgLocal = $00100;
+ SqMsgHold = $00200;
+ SqMsgXX2 = $00400;
+ SqMsgFreq = $00800;
+ SqMsgRrq = $01000;
+ SqMsgCpt = $02000;
+ SqMsgArq = $04000;
+ SqMsgUrg = $08000;
+ SqMsgScanned = $10000;
+
+Constructor TMsgBaseSquish.Init;
+Begin
+ New(SqInfo);
+ New(FreeArray);
+ If ((SqInfo = nil) or (FreeArray = nil)) Then Begin
+ If SqInfo <> Nil Then Dispose(SqInfo);
+ If FreeArray <> Nil Then Dispose(FreeArray);
+ Fail;
+ Exit;
+ End;
+ SqInfo^.SqdOpened := False;
+ SqInfo^.SqiOpened := False;
+ SqInfo^.FN := '';
+ SqInfo^.Error := 0;
+ SqInfo^.Locked := False;
+ SqInfo^.FreeLoaded := False;
+ SqInfo^.SqiAlloc := 0;
+End;
+
+Destructor TMsgBaseSquish.Done;
+Begin
+ If SqInfo^.SqdOpened Then SqdClose;
+ If SqInfo^.SqiOpened Then SqiClose;
+ If SqInfo^.SqIAlloc > 0 Then
+ If SqIdx <> Nil Then
+ FreeMem(SqIdx, SqInfo^.SqiAlloc * SizeOf(SqIdxType));
+ Dispose(FreeArray);
+ Dispose(SqInfo);
+End;
+
+Procedure TMsgBaseSquish.SetMsgPath(FN: String);
+Begin
+ SqInfo^.FN := FExpand(FN);
+ If Pos('.', SqInfo^.FN) > 0 Then
+ SqInfo^.FN := Copy(SqInfo^.FN,1,Pos('.', SqInfo^.FN) - 1);
+End;
+
+Function TMsgBaseSquish.OpenMsgBase: Boolean;
+Begin
+ If SqiOpen Then Begin
+ OpenMsgBase := SqdOpen;
+ ReadIdx;
+ End Else
+ OpenMsgBase := False;
+End;
+
+Function TMsgBaseSquish.SqdOpen: Boolean;
+Var
+ NumRead: LongInt;
+Begin
+ If Not SqInfo^.SqdOpened Then Begin
+ Assign(SqInfo^.SqdFile, SqInfo^.FN + '.sqd');
+ FileMode := 66; {ReadWrite + DenyNone}
+ If Not ioReset(SqInfo^.SqdFile, 1, fmreadwrite + fmdenynone) Then
+ SqdOpen := False
+ Else Begin
+ SqInfo^.SqdOpened := True;
+ SqdOpen := True;
+ If Not ioBlockRead(SqInfo^.SqdFile, SqInfo^.SqBase, 2, NumRead) Then
+ SqdOpen := False
+ Else Begin
+ If SqInfo^.SqBase.Len = 0 Then
+ SqInfo^.SqBase.Len := SqBSize;
+ If SqInfo^.SqBase.Len > (SizeOf(SqBaseType) + 100) Then
+ SqdOpen := False
+ Else Begin
+ SqBSize := SqInfo^.SqBase.Len;
+ ReadBase;
+ End;
+ End;
+ End;
+ End Else
+ SqdOpen := True;
+ End;
+
+Function TMsgBaseSquish.SqiOpen: Boolean;
+Begin
+ If Not SqInfo^.SqiOpened Then Begin
+ Assign(SqInfo^.SqiFile, SqInfo^.FN + '.sqi');
+// FileMode := 66; {fmReadWrite + fmDenyNone;}
+ If Not ioReset(SqInfo^.SqiFile, SizeOf(SqIdxType), fmReadWrite + fmDenyNone) Then
+ SqiOpen := False
+ Else Begin
+ SqInfo^.SqiOpened := True;
+ SqiOpen := True;
+ End;
+ End Else
+ SqiOpen := True;
+ End;
+
+Procedure TMsgBaseSquish.CloseMsgBase;
+Begin
+ SqdClose;
+ SqiClose;
+ FileMode := fmReadWrite + fmDenyNone; { shouldn't be needed... }
+End;
+
+Function TMsgBaseSquish.CreateMsgBase(MaxMsg: Word; MaxDays: Word): Boolean;
+Begin
+ If Not SqInfo^.SqdOpened Then Begin
+ FillChar(SqInfo^.SqBase, SizeOf(SqInfo^.SqBase), 0);
+ SqInfo^.SqBase.Len := 256;
+ SqInfo^.SqBase.SqHdrSize := SqFSize;
+ SqInfo^.SqBase.UID := 1;
+ SqInfo^.SqBase.NumMsg := 0;
+ SqInfo^.SqBase.Base := SqInfo^.FN;
+ Str2Az(SqInfo^.FN, 78, SqInfo^.SqBase.Base);
+
+ SqInfo^.SqBase.MaxMsg := MaxMsg;
+ SqInfo^.SqBase.KeepDays := MaxDays;
+ SqInfo^.SqBase.EndFrame := SqInfo^.SqBase.Len;
+
+ CreateMsgBase := (SaveFile(SqInfo^.FN + '.sqd', SqInfo^.SqBase, SqInfo^.SqBase.Len) = 0);
+ If SaveFile(SqInfo^.FN + '.sqi', SqInfo^.SqBase, 0) = 0 Then;
+ If SaveFile(SqInfo^.FN + '.sql', SqInfo^.SqBase, 0) = 0 Then;
+ End Else
+ CreateMsgBase := False;
+End;
+
+Function TMsgBaseSquish.MsgBaseExists: Boolean;
+Begin
+ MsgBaseExists := FileExist(SqInfo^.FN + '.sqd');
+End;
+
+Procedure TMsgBaseSquish.SqdClose;
+Begin
+ If SqInfo^.SqdOpened Then Close(SqInfo^.SqdFile);
+ If IOResult <> 0 Then;
+ SqInfo^.SqdOpened := False;
+End;
+
+Function TMsgBaseSquish.LockMsgBase: Boolean; {Lock msg base}
+Begin
+ If Not SqInfo^.Locked Then Begin
+ sqinfo^.locked := true;
+{ SqInfo^.Locked := shLock(SqInfo^.SqdFile, 0, 1) = 0;}
+ LockMsgBase := SqInfo^.Locked;
+ ReadBase;
+ ReadIdx;
+ SqInfo^.FreeLoaded := False;
+ End;
+End;
+
+Function TMsgBaseSquish.UnLockMsgBase: Boolean; {Unlock msg base}
+Begin
+ If SqInfo^.Locked Then Begin
+ WriteBase;
+ WriteIdx;
+ sqinfo^.locked := false;
+// SqInfo^.Locked := Not UnLockFile(SqInfo^.SqdFile, 0, 1) < 2;
+ UnLockMsgBase := Not SqInfo^.Locked;
+ End;
+End;
+
+Procedure TMsgBaseSquish.SqiClose;
+Begin
+ If SqInfo^.SqiOpened Then Close(SqInfo^.SqiFile);
+ If IoResult <> 0 Then;
+ SqInfo^.SqiOpened := False;
+End;
+
+Procedure TMsgBaseSquish.ReadBase;
+Var
+ NumRead: LongInt;
+Begin
+ Seek (SqInfo^.SqdFile, 0);
+ If Not ioBlockRead(SqInfo^.SqdFile, SqInfo^.SqBase, SqBSize, NumRead) Then
+ SqInfo^.Error := ioCode;
+
+ If SqInfo^.SqBase.SqHdrSize = 0 Then
+ SQInfo^.SqBase.SqHdrSize := SqFSize;
+
+ SqFSize := SqInfo^.SqBase.SqHdrSize;
+End;
+
+Procedure TMsgBaseSquish.WriteBase;
+Var
+ Res : LongInt;
+Begin
+ Seek (SqInfo^.SqdFile, 0);
+
+ If Not ioBlockWrite(SqInfo^.SqdFile, SqInfo^.SqBase, SQBSize, Res) Then
+ SqInfo^.Error := ioCode;
+End;
+
+Procedure TMsgBaseSquish.StartNewMsg; {Initialize msg header}
+Begin
+ FillChar (SqInfo^.MsgHdr, SizeOf(SqInfo^.MsgHdr), 0);
+ FillChar (SqInfo^.Frame, SizeOf(SqInfo^.Frame), 0);
+
+ SqInfo^.TxtCtr := 0;
+ SqInfo^.StrDate := '';
+ SqInfo^.StrTime := '';
+End;
+
+Function TMsgBaseSquish.GetFrom: String; {Get message from}
+Begin
+ GetFrom := strWide2Str(SqInfo^.MsgHdr.MsgFrom, 35);
+End;
+
+Function TMsgBaseSquish.GetTo: String; {Get message to}
+Begin
+ GetTo := strWide2Str(SqInfo^.MsgHdr.MsgTo, 35);
+End;
+
+Function TMsgBaseSquish.GetSubj: String; {Get message subject}
+Begin
+ GetSubj := strWide2Str(SqInfo^.MsgHdr.Subj, 72);
+End;
+
+Function TMsgBaseSquish.GetTextLen: LongInt; {Get text length}
+Begin
+{ GetTextLen := SqInfo^.TxtCtr;}
+ GetTextLen := SqInfo^.Frame.MsgLength - 320;
+End;
+
+Procedure TMsgBaseSquish.SetFrom(Str: String); {Set message from}
+Begin
+ Str2Az(Str, 35, SqInfo^.MsgHdr.MsgFrom);
+End;
+
+Procedure TMsgBaseSquish.SetTo(Str: String); {Set message to}
+Begin
+ Str2Az(Str,35, SqInfo^.MsgHdr.MsgTo);
+End;
+
+Procedure TMsgBaseSquish.SetSubj(Str: String); {Set message subject}
+Begin
+ Str2Az(Str,72, SqInfo^.MSgHdr.Subj);
+End;
+
+Function TMsgBaseSquish.GetDate: String; {Get message date mm-dd-yy}
+Var
+ TmpDate: LongInt;
+Begin
+ TmpDate := (SqInfo^.MsgHdr.DateWritten shr 16) +
+ ((SqInfo^.MsgHdr.DateWritten and $ffff) shl 16);
+ GetDate := DateDos2Str(TmpDate, 1);
+End;
+
+Function TMsgBaseSquish.GetTime: String; {Get message time hh:mm}
+Var
+ TmpDate: LongInt;
+Begin
+ TmpDate := (SqInfo^.MsgHdr.DateWritten shr 16) +
+ ((SqInfo^.MsgHdr.DateWritten and $ffff) shl 16);
+ GetTime := TimeDos2Str(TmpDate, False);
+End;
+
+Procedure TMsgBaseSquish.SetDate(Str: String);
+Begin
+ SqInfo^.StrDate := Copy(Str,1,8);
+End;
+
+Procedure TMsgBaseSquish.SetTime(Str: String);
+Begin
+ SqInfo^.StrTime := Copy(Str,1,8);
+End;
+
+Procedure TMsgBaseSquish.GetOrig(Var Addr: RecEchoMailAddr);
+Begin
+ Addr := SqInfo^.MsgHdr.Orig;
+End;
+
+Procedure TMsgBaseSquish.SetOrig(Var Addr: RecEchoMailAddr);
+Begin
+ SqInfo^.MsgHdr.Orig := Addr;
+End;
+
+Procedure TMsgBaseSquish.GetDest(Var Addr: RecEchoMailAddr);
+Begin
+ Addr := SqInfo^.MsgHdr.Dest;
+End;
+
+Procedure TMsgBaseSquish.SetDest(Var Addr: RecEchoMailAddr);
+Begin
+ SqInfo^.MsgHdr.Dest := Addr;
+End;
+
+Function TMsgBaseSquish.SqHashName(Name: String): LongInt;
+Var
+ Hash : LongInt;
+ Tmp : LongInt;
+ Counter : Word;
+Begin
+ Hash := 0;
+ Counter := 1;
+ While Counter <= Length(Name) Do Begin
+ Hash := (Hash shl 4) + Ord(LoCase(Name[Counter]));
+ Tmp := Hash and $F0000000;
+ If (Tmp <> 0) Then Hash := (Hash or (Tmp shr 24)) or Tmp;
+ Inc(Counter);
+ End;
+ SqHashName := Hash and $7fffffff;
+End;
+
+Procedure TMsgBaseSquish.ReadFrame(FPos: LongInt); {Read frame at FPos}
+Begin
+ ReadVarFrame(SqInfo^.Frame, FPos);
+End;
+
+Procedure TMsgBaseSquish.ReadVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); {Read frame at FPos}
+Var
+ NumRead : LongInt;
+Begin
+ Seek(SqInfo^.SqdFile, FPos);
+ SqInfo^.Error := IoResult;
+ If SqInfo^.Error = 0 Then Begin
+ If Not ioBlockRead(SqInfo^.SqdFile, Frame, SizeOf(SqFrameHdrType), NumRead) Then
+ SqInfo^.Error := ioCode;
+ End;
+End;
+
+Procedure TMsgBaseSquish.WriteFrame(FPos: LongInt); {Read frame at FPos}
+Begin
+ WriteVarFrame(SqInfo^.Frame, FPos);
+End;
+
+Procedure TMsgBaseSquish.WriteVarFrame(Var Frame: SqFrameHdrType; FPos: LongInt); {Write frame at FPos}
+Var
+ Res : LongInt;
+Begin
+ Seek(SqInfo^.SqdFile, FPos);
+ SqInfo^.Error := IoResult;
+ If SqInfo^.Error = 0 Then Begin
+ If Not ioBlockWrite(SqInfo^.SqdFile, Frame, SizeOf(SqFrameHdrType), Res) Then
+ SqInfo^.Error := ioCode;
+ End;
+End;
+
+Procedure TMsgBaseSquish.UnlinkFrame(Var Frame: SqFrameHdrType);
+Var
+ TmpFrame: SqFrameHdrType;
+Begin
+ If Frame.PrevFrame <> 0 Then Begin
+ ReadVarFrame(TmpFrame, Frame.PrevFrame);
+ TmpFrame.NextFrame := Frame.NextFrame;
+ WriteVarFrame(TmpFrame, Frame.PrevFrame);
+ End;
+ If Frame.NextFrame <> 0 Then Begin
+ ReadVarFrame(TmpFrame, Frame.NextFrame);
+ TmpFrame.PrevFrame := Frame.PrevFrame;
+ WriteVarFrame(TmpFrame, Frame.NextFrame);
+ End;
+End;
+
+Procedure TMsgBaseSquish.LoadFree;
+Var
+ i : Word;
+ TmpFrame : SqFrameHdrType;
+ TmpPos : LongInt;
+Begin
+ For i := 1 to MaxFree Do Begin
+ FreeArray^[i].FreePos := 0;
+ FreeArray^[i].FreeSize := 0;
+ End;
+ SqInfo^.FreeLoaded := True;
+ i := 0;
+ TmpPos := SqInfo^.SqBase.FirstFree;
+ While ((TmpPos <> 0) and (i < MaxFree)) Do Begin
+ ReadVarFrame(TmpFrame, TmpPos);
+ Inc(i);
+ FreeArray^[i].FreeSize := TmpFrame.FrameLength;
+ FreeArray^[i].FreePos := TmpPos;
+ TmpPos := TmpFrame.NextFrame;
+ End;
+ SqInfo^.HighestFree := i;
+End;
+
+Procedure TMsgBaseSquish.FindFrame(Var FL: LongInt; Var FramePos: LongInt);
+Var
+ TmpFrame : SqFrameHdrType;
+ BestFoundPos : LongInt;
+ BestFoundSize : LongInt;
+ BestIdx : Word;
+ i : Word;
+Begin
+ If Not SqInfo^.FreeLoaded Then LoadFree;
+
+ BestFoundPos := 0;
+ BestFoundSize := 0;
+
+ For i := 1 to SqInfo^.HighestFree Do Begin
+ If (FreeArray^[i].FreeSize > FL) Then Begin
+ If ((BestFoundSize = 0) or (FreeArray^[i].FreeSize < BestFoundSize)) Then Begin
+ BestFoundSize := FreeArray^[i].FreeSize;
+ BestFoundPos := FreeArray^[i].FreePos;
+ BestIdx := i;
+ End;
+ End
+ End;
+
+ FramePos := BestFoundPos;
+
+ If FramePos <> 0 Then Begin
+ ReadVarFrame(TmpFrame, FramePos);
+ FreeArray^[BestIdx].FreePos := 0;
+ FreeArray^[BestIdx].FreeSize := 0;
+ End;
+
+ If FramePos = 0 Then Begin
+ FL := 0;
+ FramePos := SqInfo^.SqBase.EndFrame;
+ End Else Begin
+ UnLinkFrame(TmpFrame);
+
+ If TmpFrame.PrevFrame = 0 Then SqInfo^.SqBase.FirstFree := TmpFrame.NextFrame;
+ If TmpFrame.NextFrame = 0 Then SqInfo^.SqBase.LastFree := TmpFrame.PrevFrame;
+
+ FL := TmpFrame.FrameLength;
+ End;
+End;
+
+Procedure TMsgBaseSquish.LinkFrameNext(Var Frame: SqFrameHdrType; OtherFrame: LongInt; FramePos: LongInt);
+Var
+ TmpFrame: SqFrameHdrType;
+Begin
+ If OtherFrame <> 0 Then Begin
+ ReadVarFrame(TmpFrame, OtherFrame);
+
+ TmpFrame.NextFrame := FramePos;
+ Frame.PrevFrame := OtherFrame;
+
+ WriteVarFrame(TmpFrame, OtherFrame);
+ End;
+End;
+
+Procedure TMsgBaseSquish.KillMsg(MsgNum: LongInt);
+Var
+ i: Word;
+ KillPos: LongInt;
+ IndexPos: LongInt;
+ KillFrame: SqFrameHdrType;
+ TmpFrame: SqFrameHdrType;
+ CurrMove: LongInt;
+ AlreadyLocked: Boolean;
+ FreeCtr: Word;
+Begin
+ AlreadyLocked := SqInfo^.Locked;
+ If Not AlreadyLocked Then
+ If LockMsgBase Then;
+ If SqIdx = Nil Then
+ SqInfo^.Error := 999
+ Else Begin
+ i := 1;
+ While ((i <= SqInfo^.SqBase.NumMsg) and (MsgNum <> SqIdx^[i].UMsgId)) Do
+ Inc(i);
+ If MsgNum = SqIdx^[i].UMsgId Then Begin
+ IndexPos := i;
+ KillPos := SqIdx^[i].Ofs;
+ ReadVarFrame(KillFrame, KillPos);
+ If KillFrame.PrevFrame = 0 Then
+ SqInfo^.SqBase.BeginFrame := KillFrame.NextFrame;
+ If KillFrame.NextFrame = 0 Then
+ SqInfo^.SqBase.LastFrame := KillFrame.PrevFrame;
+ KillFrame.FrameType := sqFrameFree;
+ UnLinkFrame(KillFrame);
+ If ((SqInfo^.SqBase.FirstFree = 0) or (SqInfo^.SqBase.LastFree = 0)) Then Begin
+ SqInfo^.SqBase.FirstFree := KillPos;
+ SqInfo^.SqBase.LastFree := KillPos;
+ KillFrame.PrevFrame := 0;
+ KillFrame.NextFrame := 0;
+ End Else Begin
+ KillFrame.NextFrame := 0;
+ KillFrame.PrevFrame := SqInfo^.SqBase.LastFree;
+ ReadVarFrame(TmpFrame, SqInfo^.SqBase.LastFree);
+ TmpFrame.NextFrame := KillPos;
+ WriteVarFrame(TmpFrame, SqInfo^.SqBase.LastFree);
+ SqInfo^.SqBase.LastFree := KillPos;
+ End;
+ WriteVarFrame(KillFrame, KillPos);
+ FreeCtr := 1;
+ While ((FreeCtr < MaxFree) and (FreeArray^[FreeCtr].FreePos <> 0)) Do
+ Inc(FreeCtr);
+ If FreeArray^[FreeCtr].FreePos = 0 Then Begin
+ FreeArray^[FreeCtr].FreePos := KillPos;
+ FreeArray^[FreeCtr].FreeSize := KillFrame.FrameLength;
+ End;
+ If FreeCtr > SqInfo^.HighestFree Then
+ SqInfo^.HighestFree := FreeCtr;
+ Dec(SqInfo^.SqBase.NumMsg);
+ Dec(SqInfo^.SqBase.HighMsg);
+ CurrMove := IndexPos;
+ While CurrMove <= SqInfo^.SqBase.NumMsg Do Begin
+ SqIdx^[CurrMove] := SqIdx^[CurrMove + 1];
+ Inc(CurrMove);
+ End;
+ End;
+ End;
+ If Not AlreadyLocked Then
+ If UnlockMsgBase Then;
+End;
+
+Procedure TMsgBaseSquish.ReadMsgHdr(FPos: LongInt); {Read msg hdr for frame at FPos}
+Var
+ NumRead: LongInt;
+Begin
+ Seek(SqInfo^.SqdFile, FPos + SqFSize);
+ SqInfo^.Error := IoResult;
+ If SqInfo^.Error = 0 Then Begin
+ If Not ioBlockRead(SqInfo^.SqdFile, SqInfo^.MsgHdr, SizeOf(SqMsgHdrType), NumRead) Then
+ SqInfo^.Error := ioCode;
+ End;
+End;
+
+Procedure TMsgBaseSquish.WriteMsgHdr(FPos: LongInt); {Read msg hdr for frame at FPos}
+Var
+ Res : LongInt;
+Begin
+ Seek(SqInfo^.SqdFile, FPos + SqFSize);
+ SqInfo^.Error := IoResult;
+ If SqInfo^.Error = 0 Then Begin
+ If Not ioBlockWrite(SqInfo^.SqdFile, SqInfo^.MsgHdr, SizeOf(SqMsgHdrType), Res) Then
+ SqInfo^.Error := ioCode;
+ End;
+End;
+
+Procedure TMsgBaseSquish.WriteText(FPos: LongInt); {Write text buffer for frame at Fpos}
+Var
+ Res : LongInt;
+Begin
+ Seek(SqInfo^.SqdFile, FPos + SqFSize + SqMSize);
+ SqInfo^.Error := IoResult;
+ If SqInfo^.Error = 0 Then Begin
+ If Not ioBlockWrite(SqInfo^.SqdFile, SqInfo^.MsgChars, SqInfo^.TxtCtr, Res) Then
+ SqInfo^.Error := ioCode;
+ End;
+End;
+
+Function TMsgBaseSquish.GetBeginFrame: LongInt; {Get beginning frame pos}
+Begin
+ GetBeginFrame := SqInfo^.SqBase.BeginFrame;
+End;
+
+Function TMsgBaseSquish.GetNextFrame: LongInt; {Get next frame pos}
+Begin
+ GetNextFrame := SqInfo^.Frame.NextFrame;
+End;
+
+Procedure TMsgBaseSquish.ReadText(FPos: LongInt);
+Begin
+ Seek(SqInfo^.SqdFile, FPos + SqFSize + SqMSize);
+ SqInfo^.Error := IoResult;
+ If SqInfo^.Error = 0 Then Begin
+ If SqInfo^.Frame.MsgLength > SqTxtBufferSize Then
+ BlockRead(SqInfo^.SqdFile, SqInfo^.MsgChars, SqTxtBufferSize)
+ Else
+ BlockRead(SqInfo^.SqdFile, SqInfo^.MsgChars, SqInfo^.Frame.MsgLength);
+ SqInfo^.Error := IoResult;
+ End;
+ SqInfo^.TxtCtr := 1 + SqInfo^.Frame.ControlLength;
+ SqInfo^.MsgDone := False;
+ LastSoft := False;
+End;
+
+Procedure TMsgBaseSquish.InitText;
+Begin
+ SqInfo^.TxtCtr := 0;
+End;
+
+Procedure TMsgBaseSquish.DoString (Str: String); {Add string to message text}
+Var
+ i: Word;
+Begin
+ i := 1;
+ While i <= Length(Str) Do Begin
+ DoChar(Str[i]);
+ Inc(i);
+ End;
+End;
+
+Procedure TMsgBaseSquish.DoChar (Ch: Char); {Add character to message text}
+Begin
+ If SqInfo^.TxtCtr < SqTxtBufferSize Then Begin
+ Inc(SqInfo^.TxtCtr);
+ SqInfo^.MsgChars[SqInfo^.TxtCtr] := Ch;
+ End;
+End;
+
+Procedure TMsgBaseSquish.DoStringLn(Str: String); {Add string and newline to msg text}
+Begin
+ DoString(Str);
+ DoChar(#13);
+End;
+
+Procedure TMsgBaseSquish.KillExcess;
+Var
+ AlreadyLocked: Boolean;
+Begin
+ AlreadyLocked := SqInfo^.Locked;
+ If Not AlreadyLocked Then
+ If LockMsgBase Then;
+ If SqIdx = Nil Then
+ SqInfo^.error := 999
+ Else Begin
+ If ((SqInfo^.SqBase.MaxMsg > 0) and
+ (SqInfo^.SqBase.MaxMsg > SqInfo^.SqBase.SkipMsg)) Then Begin
+ While (SqInfo^.SqBase.NumMsg > SqInfo^.SqBase.MaxMsg) Do
+ KillMsg(SqIdx^[SqInfo^.SqBase.SkipMsg + 1].UMsgId);
+ End;
+ End;
+ If Not AlreadyLocked Then
+ If UnlockMsgBase Then;
+End;
+
+Function TMsgBaseSquish.WriteMsg: Word; {Write msg to msg base}
+Var
+ MsgSize : LongInt;
+ FrameSize : LongInt;
+ FramePos : LongInt;
+ TmpFrame : SqFrameHdrType;
+ TmpDate : LongInt;
+ TmpDT : DateTime;
+ TmpStr : String;
+ AlreadyLocked : Boolean;
+Begin
+ DoChar(#0);
+ TmpDT.Year := strS2I(Copy(SqInfo^.StrDate,7,2));
+ If TmpDT.Year > 79 Then
+ Inc(TmpDT.Year, 1900)
+ Else
+ Inc(TmpDT.Year, 2000);
+ TmpDT.Month := strS2I(Copy(SqInfo^.StrDate,1,2));
+ TmpDT.Day := strS2I(Copy(SqInfo^.StrDate,4,2));
+ TmpDt.Hour := strS2I(Copy(SqInfo^.StrTime,1,2));
+ TmpDt.Min := strS2I(Copy(SqInfo^.StrTime, 4,2));
+ TmpDt.Sec := 0;
+ TmpStr := FormattedDate(TmpDT, 'DD NNN YY ') + Copy(SqInfo^.StrTime, 1, 5) + ':00';
+ PackTime(TmpDT, TmpDate);
+ SqInfo^.MsgHdr.DateWritten := (TmpDate shr 16) + ((TmpDate and $ffff) shl 16);
+ TmpDate := CurDateDos;
+ SqInfo^.MsgHdr.DateArrived := (TmpDate shr 16) + ((TmpDate and $ffff) shl 16);
+ Str2AZ(TmpStr, 20, SqInfo^.MsgHdr.AZDate);
+ AlreadyLocked := SqInfo^.Locked;
+ If Not AlreadyLocked Then
+ If LockMsgBase Then;
+ If SqInfo^.Locked Then Begin
+ MsgSize := SqInfo^.TxtCtr + SqMSize;
+ FrameSize := MsgSize;
+
+ FindFrame(FrameSize, FramePos);
+
+ If SqInfo^.SqBase.LastFrame <> 0 Then Begin
+ ReadVarFrame(TmpFrame, SqInfo^.SqBase.LastFrame);
+ TmpFrame.NextFrame := FramePos;
+ WriteVarFrame(TmpFrame, SqInfo^.SqBase.LastFrame);
+ TmpFrame.PrevFrame := SqInfo^.SqBase.LastFrame;
+ End Else Begin
+ SqInfo^.SqBase.BeginFrame := FramePos;
+ TmpFrame.PrevFrame := 0;
+ End;
+
+ TmpFrame.Id := SqHdrId;
+ TmpFrame.FrameType := SqFrameMsg;
+ SqInfo^.SqBase.LastFrame := FramePos;
+ TmpFrame.NextFrame := 0;
+ TmpFrame.FrameLength := FrameSize;
+ TmpFrame.MsgLength := MsgSize;
+ TmpFrame.ControlLength := 0;
+
+ If TmpFrame.FrameLength = 0 Then Begin
+ TmpFrame.FrameLength := TmpFrame.MsgLength + 0; {slack to minimize free frames}
+ SqInfo^.SqBase.EndFrame := FramePos + SqFSize + TmpFrame.FrameLength;
+ End;
+
+ If SqInfo^.SqBase.NumMsg >= SqInfo^.SqiAlloc Then Begin
+ WriteIdx;
+ ReadIdx;
+ End;
+
+ If SqIdx = Nil Then Begin
+ SqInfo^.Error := 999;
+ WriteMsg := 999;
+ End Else Begin
+ WriteVarFrame(TmpFrame, FramePos);
+ WriteMsgHdr(FramePos);
+ WriteText(FramePos);
+
+ Inc(SqInfo^.SqBase.NumMsg);
+
+ SqIdx^[SqInfo^.SqBase.NumMsg].Ofs := FramePos;
+ SqIdx^[SqInfo^.SqBase.NumMsg].UMsgId := SqInfo^.SqBase.UID;
+ SqIdx^[SqInfo^.SqBase.NumMsg].Hash := SqHashName(strWide2Str(SqInfo^.MsgHdr.MsgTo, 35));
+
+ Inc(SqInfo^.SqBase.UId);
+
+ SqInfo^.SqBase.HighMsg := SqInfo^.SqBase.NumMsg;
+
+ KillExcess;
+
+ SqInfo^.CurrIdx := SqInfo^.SqBase.NumMsg;
+
+ WriteMsg := 0;
+ End;
+ If Not AlreadyLocked Then
+ If UnLockMsgBase Then;
+ End Else
+ WriteMsg := 5;
+End;
+
+(*
+Function TMsgBaseSquish.GetString (MaxLen : Word) : String;
+Var
+ StartSoft : Boolean;
+ Str : String;
+ Ch : Char;
+ WLen : Byte;
+ WPos : Word;
+Begin
+ StartSoft := LastSoft;
+ LastSoft := True;
+ WLen := 0;
+ WPos := 0;
+ Str := '';
+
+ While (Length(Str) < MaxLen) and (not SqInfo^.MsgDone) Do Begin
+ Ch := GetChar;
+ Case Ch of
+ #00,
+ #13 : Begin
+ LastSoft := True;
+ Break;
+ End;
+ #10,
+ #141: ;
+ #32 : If (Str <> '') or (Not StartSoft) Then Begin
+ Str := Str + Ch;
+ WLen := Length(Str);
+ WPos := SqInfo^.TxtCtr;
+ End Else
+ StartSoft := False;
+ Else
+ Str := Str + Ch;
+ End;
+ End;
+
+ If (Not (Ch in [#00, #13])) and (Not SqInfo^.MsgDone) Then
+ If WLen = 0 Then
+ Dec(SqInfo^.TxtCtr)
+ Else Begin
+ Str[0] := Chr(WLen);
+ SqInfo^.TxtCtr := WPos;
+ End;
+
+ GetString := Str;
+End;
+*)
+
+Function TMsgBaseSquish.GetString(MaxLen: Word): String;
+Var
+ WPos : Word;
+ WLen : Byte;
+ StrDone : Boolean;
+// TxtOver : Boolean;
+ StartSoft : Boolean;
+ CurrLen : Word;
+// PPos : Word;
+ TmpCh : Char;
+Begin
+ StrDone := False;
+ CurrLen := 0;
+// PPos := SqInfo^.TxtCtr;
+ WPos := 0;
+ WLen := 0;
+ StartSoft := LastSoft;
+ LastSoft := True;
+{ TmpCh := GetChar;}
+ While ((Not StrDone) And (CurrLen < MaxLen) And (Not SqInfo^.MsgDone)) Do Begin
+ TmpCh := GetChar;
+ Case TmpCh of
+ #00,
+ #13 : Begin
+ StrDone := True;
+ LastSoft := False;
+ End;
+ #10,
+ #141: ;
+ #32 : Begin
+ If ((CurrLen <> 0) or (Not StartSoft)) Then Begin
+ Inc(CurrLen);
+ WLen := CurrLen;
+ GetString[CurrLen] := TmpCh;
+ WPos := SqInfo^.TxtCtr;
+ End Else
+ StartSoft := False;
+ End;
+ Else
+ Inc(CurrLen);
+ GetString[CurrLen] := TmpCh;
+ End;
+{ If Not StrDone Then TmpCh := GetChar;}
+ End;
+
+ If StrDone Then Begin
+ GetString[0] := Chr(CurrLen);
+ End Else
+ If SqInfo^.MsgDone Then Begin
+ GetString[0] := Chr(CurrLen);
+ End Else Begin
+ If WLen = 0 Then Begin
+ GetString[0] := Chr(CurrLen);
+ Dec(SqInfo^.TxtCtr);
+ End Else Begin
+ GetString[0] := Chr(WLen);
+ SqInfo^.TxtCtr := WPos;
+ End;
+ End;
+End;
+
+Function TMsgBaseSquish.EOM: Boolean;
+Begin
+ EOM := (SqInfo^.TxtCtr >= SqInfo^.Frame.MsgLength) or (SqInfo^.MsgChars[SqInfo^.TxtCtr] = #0);
+End;
+
+(*
+Function TMsgBaseSquish.WasWrap: Boolean;
+Begin
+ WasWrap := LastSoft;
+End;
+*)
+
+Function TMsgBaseSquish.GetChar: Char;
+Begin
+ If (SqInfo^.TxtCtr >= SqInfo^.Frame.MsgLength) or (SqInfo^.MsgChars[SqInfo^.TxtCtr] = #0) Then Begin
+ GetChar := #0;
+ SqInfo^.MsgDone := True;
+ End Else Begin
+ GetChar := SqInfo^.MsgChars[SqInfo^.TxtCtr];
+ Inc(SqInfo^.TxtCtr);
+ End;
+End;
+
+
+Function TMsgBaseSquish.GetHighWater: LongInt; {Get high water umsgid}
+Begin
+ GetHighWater := LongInt(SqInfo^.SqBase.HighWater);
+End;
+
+Function TMsgBaseSquish.GetHighMsgNum: LongInt; {Get highest msg number}
+Begin
+ GetHighMsgNum := LongInt(SqInfo^.SqBase.Uid) - 1;
+End;
+
+Procedure TMsgBaseSquish.ReadIdx;
+Var
+ NumRead: LongInt;
+Begin
+ If SqInfo^.SqiAlloc > 0 Then
+ If SqIdx <> Nil Then
+ FreeMem(SqIdx, SqInfo^.SqiAlloc * SizeOf(SqIdxType));
+ SqInfo^.SqiAlloc := FileSize(SqInfo^.SqiFile) + 100;
+ If SqInfo^.SqiAlloc > SqIdxArraySize Then
+ SqInfo^.SqiAlloc := SqIdxArraySize ;
+ GetMem(SqIdx, SqInfo^.SqiAlloc * SizeOf(SqIdxType));
+ If SqIdx = nil Then
+ SqInfo^.Error := 999
+ Else Begin
+ Seek(SqInfo^.SqiFile, 0);
+ If IoResult = 0 Then Begin
+ If Not ioBlockRead(SqInfo^.SqiFile, SqIdx^, SqInfo^.SqiAlloc, NumRead) Then
+ SqInfo^.Error := ioCode;
+ End Else
+ SqInfo^.Error := 300;
+ End;
+End;
+
+Procedure TMsgBaseSquish.WriteIdx;
+Var
+ Res : LongInt;
+Begin
+ If SqIdx = nil Then
+ SqInfo^.Error := 999
+ Else Begin
+ Seek(SqInfo^.SqiFile, 0);
+ Truncate(SqInfo^.SqiFile);
+ If IoResult = 0 Then Begin
+ If Not ioBlockWrite(SqInfo^.SqiFile, SqIdx^, SqInfo^.SqBase.NumMsg, Res) Then
+ SqInfo^.Error := ioCode;
+ End Else
+ SqInfo^.Error := 300;
+ End;
+End;
+
+Procedure TMsgBaseSquish.SeekFirst(MsgNum: LongInt);
+Begin
+ SqInfo^.CurrIdx := 1;
+ ReadIdx;
+ While ((SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg) and
+ (MsgNum > LongInt(SqIdx^[SqInfo^.CurrIdx].UMsgId))) Do
+ SeekNext;
+End;
+
+Function TMsgBaseSquish.IdxHighest: LongInt;
+Var
+ i: Word;
+ Tmp: LongInt;
+Begin
+ Tmp := 0;
+ i := 1;
+ While i <= SqInfo^.SqBase.NumMsg Do Begin
+ If SqIdx^[i].UMsgId > Tmp Then Tmp := SqIdx^[i].UMsgId;
+ Inc(i);
+ End;
+ IdxHighest := Tmp;
+End;
+
+Function TMsgBaseSquish.GetMsgNum: LongInt;
+Begin
+ If ((SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg) and
+ (SqInfo^.CurrIdx > 0)) Then
+ GetMsgNum := LongInt(SqIdx^[SqInfo^.CurrIdx].UMsgId)
+ Else
+ GetMsgNum := -1;
+End;
+
+Procedure TMsgBaseSquish.SeekNext;
+Begin
+ Inc(SqInfo^.CurrIdx);
+End;
+
+Procedure TMsgBaseSquish.SeekPrior;
+ Begin
+ If SqInfo^.CurrIdx > 1 Then
+ Dec(SqInfo^.CurrIdx)
+ Else
+ SqInfo^.CurrIdx := 0;
+ End;
+
+Function TMsgBaseSquish.SeekFound: Boolean;
+ Begin
+ SeekFound := GetMsgNum >= 0;
+ End;
+
+Function TMsgBaseSquish.GetIdxFramePos: LongInt;
+ Begin
+ If SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg Then
+ GetIdxFramePos := SqIdx^[SqInfo^.CurrIdx].Ofs
+ Else
+ GetIdxFramePos := -1;
+ End;
+
+Function TMsgBaseSquish.GetIdxHash: LongInt;
+ Begin
+ If SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg Then
+ GetIdxHash := SqIdx^[SqInfo^.CurrIdx].Hash
+ Else
+ GetIdxHash := 0;
+ End;
+
+Function TMsgBaseSquish.IsLocal: Boolean; {Is current msg local}
+Begin
+ IsLocal := ((SqInfo^.MsgHdr.Attr and SqMsgLocal) <> 0);
+End;
+
+Function TMsgBaseSquish.IsCrash: Boolean; {Is current msg crash}
+Begin
+ IsCrash := ((SqInfo^.MsgHdr.Attr and SqMsgCrash) <> 0);
+End;
+
+Function TMsgBaseSquish.IsKillSent: Boolean; {Is current msg kill sent}
+Begin
+ IsKillSent := ((SqInfo^.MsgHdr.Attr and SqMsgKill) <> 0);
+End;
+
+Function TMsgBaseSquish.IsSent: Boolean; {Is current msg sent}
+Begin
+ IsSent := ((SqInfo^.MsgHdr.Attr and SqMsgSent) <> 0);
+End;
+
+Function TMsgBaseSquish.IsFAttach: Boolean; {Is current msg file attach}
+Begin
+ IsFAttach := ((SqInfo^.MsgHdr.Attr and SqMsgFile) <> 0);
+End;
+
+Function TMsgBaseSquish.IsReqRct: Boolean; {Is current msg request receipt}
+Begin
+ IsReqRct := ((SqInfo^.MsgHdr.Attr and SqMsgRRQ) <> 0);
+End;
+
+Function TMsgBaseSquish.IsReqAud: Boolean; {Is current msg request audit}
+Begin
+ IsReqAud := ((SqInfo^.MsgHdr.Attr and SqMsgArq) <> 0);
+End;
+
+Function TMsgBaseSquish.IsRetRct: Boolean; {Is current msg a return receipt}
+Begin
+ IsRetRct := ((SqInfo^.MsgHdr.Attr and SqMsgCpt) <> 0);
+End;
+
+Function TMsgBaseSquish.IsFileReq: Boolean; {Is current msg a file request}
+Begin
+ IsFileReq := ((SqInfo^.MsgHdr.Attr and SqMsgFreq) <> 0);
+End;
+
+Function TMsgBaseSquish.IsRcvd: Boolean; {Is current msg received}
+Begin
+ IsRcvd := ((SqInfo^.MsgHdr.Attr and SqMsgRcvd) <> 0);
+End;
+
+Function TMsgBaseSquish.IsPriv: Boolean; {Is current msg priviledged/private}
+Begin
+ IsPriv := ((SqInfo^.MsgHdr.Attr and SqMsgPriv) <> 0);
+End;
+
+Function TMsgBaseSquish.IsEchoed: Boolean;
+Begin
+ IsEchoed := ((SqInfo^.MsgHdr.Attr and SqMsgScanned) = 0);
+End;
+
+Function TMsgBaseSquish.IsDeleted: Boolean; {Is current msg deleted}
+Begin
+ IsDeleted := False;
+End;
+
+Function TMsgBaseSquish.GetRefer: LongInt; {Get reply to of current msg}
+Begin
+ GetRefer := LongInt(SqInfo^.MsgHdr.ReplyTo);
+End;
+
+Procedure TMsgBaseSquish.SetRefer(Num: LongInt); {Set reply to of current msg}
+Begin
+ SqInfo^.MsgHdr.ReplyTo := LongInt(Num);
+End;
+
+Function TMsgBaseSquish.GetSeeAlso: LongInt; {Get see also msg}
+Begin
+ GetSeeAlso := LongInt(SqInfo^.MsgHdr.Replies[1]);
+End;
+
+Procedure TMsgBaseSquish.SetSeeAlso(Num: LongInt); {Set see also msg}
+Begin
+ SqInfo^.MsgHdr.Replies[1] := LongInt(Num);
+End;
+
+Procedure TMsgBaseSquish.SetAttr(St: Boolean; Mask: LongInt); {Set attribute}
+Begin
+ If St Then
+ SqInfo^.MsgHdr.Attr := SqInfo^.MsgHdr.Attr or Mask
+ Else
+ SqInfo^.MsgHdr.Attr := SqInfo^.MsgHdr.Attr and (Not Mask);
+End;
+
+Procedure TMsgBaseSquish.SetLocal(St: Boolean); {Set local status}
+Begin
+ SetAttr(St, SqMsgLocal);
+End;
+
+Procedure TMsgBaseSquish.SetRcvd(St: Boolean); {Set received status}
+Begin
+ SetAttr(St, SqMsgRcvd);
+End;
+
+Procedure TMsgBaseSquish.SetPriv(St: Boolean); {Set priveledge vs public status}
+Begin
+ SetAttr(St, SqMsgPriv);
+End;
+
+Procedure TMsgBaseSquish.SetEcho(ES: Boolean);
+Begin
+ SetAttr(Not ES, SqMsgScanned);
+End;
+
+Procedure TMsgBaseSquish.SetCrash(St: Boolean); {Set crash netmail status}
+Begin
+ SetAttr(St, SqMsgCrash);
+End;
+
+Procedure TMsgBaseSquish.SetHold (ST: Boolean);
+Begin
+ SetAttr (ST, SqMsgHold);
+End;
+
+Procedure TMsgBaseSquish.SetKillSent(St: Boolean); {Set kill/sent netmail status}
+Begin
+ SetAttr(St, SqMsgKill);
+End;
+
+Procedure TMsgBaseSquish.SetSent(St: Boolean); {Set sent netmail status}
+Begin
+ SetAttr(St, SqMsgSent);
+End;
+
+Procedure TMsgBaseSquish.SetFAttach(St: Boolean); {Set file attach status}
+Begin
+ SetAttr(St, SqMsgFile);
+End;
+
+Procedure TMsgBaseSquish.SetReqRct(St: Boolean); {Set request receipt status}
+Begin
+ SetAttr(St, SqMsgRrq);
+End;
+
+Procedure TMsgBaseSquish.SetReqAud(St: Boolean); {Set request audit status}
+Begin
+ SetAttr(St, SqMsgarq);
+End;
+
+Procedure TMsgBaseSquish.SetRetRct(St: Boolean); {Set return receipt status}
+Begin
+ SetAttr(St, SqMsgCpt);
+End;
+
+Procedure TMsgBaseSquish.SetFileReq(St: Boolean); {Set file request status}
+Begin
+ SetAttr(St, SqMsgFreq);
+End;
+
+Procedure TMsgBaseSquish.MsgStartUp;
+Begin
+ SqInfo^.CurrentFramePos := GetIdxFramePos;
+ SqInfo^.CurrentUID := SqIdx^[SqInfo^.CurrIdx].UMsgId;
+ ReadFrame(SqInfo^.CurrentFramePos);
+ ReadMsgHdr(SqInfo^.CurrentFramePos);
+End;
+
+Procedure TMsgBaseSquish.MsgTxtStartUp;
+//Var
+// CFrame: LongInt;
+Begin
+ ReadText(SqInfo^.CurrentFramePos);
+End;
+
+Procedure TMsgBaseSquish.SetMailType(MT: MsgMailType);
+Begin
+End;
+
+Function TMsgBaseSquish.GetSubArea: Word;
+Begin
+ GetSubArea := 0;
+End;
+
+Procedure TMsgBaseSquish.ReWriteHdr;
+Var
+ AlreadyLocked : Boolean;
+ I : LongInt;
+Begin
+ AlreadyLocked := SqInfo^.Locked;
+
+ If Not AlreadyLocked Then
+ If LockMsgBase Then;
+
+ WriteFrame(SqInfo^.CurrentFramePos);
+ WriteMsgHdr(SqInfo^.CurrentFramePos);
+
+ i := 1;
+
+ While ((i <= SqInfo^.SqBase.NumMsg) and (SqInfo^.CurrentFramePos <> SqIdx^[i].Ofs)) Do
+ Inc(i);
+
+ If SqIdx^[i].Ofs = SqInfo^.CurrentFramePos Then Begin
+ If IsRcvd Then
+ SqIdx^[i].Hash := 0
+ Else
+ SqIdx^[i].Hash := SqHashName(SqInfo^.MsgHdr.MsgTo);
+ End;
+
+ If Not AlreadyLocked Then
+ If UnLockMsgBase Then;
+End;
+
+Procedure TMsgBaseSquish.DeleteMsg;
+Begin
+ KillMsg(SqInfo^.CurrentUID);
+End;
+
+Function TMsgBaseSquish.NumberOfMsgs: LongInt;
+Var
+ TmpBase: SqBaseType;
+Begin
+ If LoadFile(SqInfo^.FN + '.sqd', TmpBase, SizeOf(TmpBase)) = 0 Then
+ NumberOfMsgs := TmpBase.NumMsg
+ Else
+ NumberOfMsgs := 0;
+End;
+
+Function TMsgBaseSquish.GetLastRead(UNum: LongInt): LongInt;
+Var
+ LRec: LongInt;
+Begin
+ If ((UNum + 1) * SizeOf(LRec)) > GetFileSize(SqInfo^.FN + '.sql') Then
+ GetLastRead := 0
+ Else Begin
+ If LoadFilePos(SqInfo^.FN + '.sql', LRec, SizeOf(LRec), UNum * SizeOf(LRec)) = 0 Then
+ GetLastRead := LRec
+ Else
+ GetLastRead := 0;
+ End;
+End;
+
+Procedure TMsgBaseSquish.SetLastRead(UNum: LongInt; LR: LongInt);
+Var
+ LRec : LongInt;
+ Status : Word;
+Begin
+ Status := 0;
+ If ((UNum + 1) * SizeOf(LRec)) > GetFileSize(SqInfo^.FN + '.sql') Then Begin
+ Status := ExtendFile(SqInfo^.FN + '.sql', (UNum + 1) * SizeOf(LRec));
+ End;
+ LRec := LR;
+ If Status = 0 Then
+ Status := SaveFilePos(SqInfo^.FN + '.sql', LRec, SizeOf(LRec),
+ UNum * SizeOf(LRec));
+End;
+
+Function TMsgBaseSquish.GetMsgLoc: LongInt;
+Begin
+ GetMsgLoc := GetMsgNum;
+End;
+
+Procedure TMsgBaseSquish.SetMsgLoc(ML: LongInt);
+Begin
+ SeekFirst(ML);
+End;
+
+Procedure TMsgBaseSquish.YoursFirst(Name: String; Handle: String);
+Begin
+ SqInfo^.CurrIdx := 0;
+ ReadIdx;
+ SqInfo^.SName := strUpper(Name);
+ SqInfo^.SHandle := strUpper(Handle);
+ SqInfo^.HName := SqHashName(Name);
+ SqInfo^.HHandle := SqHashName(Handle);
+ YoursNext;
+End;
+
+Procedure TMsgBaseSquish.YoursNext;
+Var
+ WasFound: Boolean;
+Begin
+ WasFound := False;
+ Inc(SqInfo^.CurrIdx);
+ While ((SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg) and (Not WasFound)) Do Begin
+ While ((SqIdx^[SqInfo^.CurrIdx].Hash <> SqInfo^.HName) And
+ (SqIdx^[SqInfo^.CurrIdx].Hash <> SqInfo^.HHandle) And
+ (SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg)) Do
+ Inc(SqInfo^.CurrIdx);
+ If SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg Then Begin
+ MsgStartUp;
+ If ((Not IsRcvd) and
+ ((strUpper(GetTo) = SqInfo^.SName) or (strUpper(GetTo) = SqInfo^.SHandle))) Then
+ WasFound := True
+ Else
+ Inc(SqInfo^.CurrIdx);
+ End;
+ End;
+End;
+
+Function TMsgBaseSquish.YoursFound: Boolean;
+Begin
+ YoursFound := SqInfo^.CurrIdx <= SqInfo^.SqBase.NumMsg;
+End;
+
+Function TMsgBaseSquish.GetMsgDisplayNum: LongInt;
+Begin
+ GetMsgDisplayNum := SqInfo^.CurrIdx;
+End;
+
+Function TMsgBaseSquish.GetTxtPos: LongInt;
+Begin
+ GetTxtPos := SqInfo^.TxtCtr;
+End;
+
+Procedure TMsgBaseSquish.SetTxtPos(TP: LongInt);
+Begin
+ SqInfo^.TxtCtr := TP;
+End;
+
+Procedure TMsgBaseSquish.EditMsgInit;
+Begin
+ SqInfo^.TxtCtr := 0;
+End;
+
+Procedure TMsgBaseSquish.EditMsgSave;
+Begin
+(*
+ DeleteMsg;
+
+ Dec(SqInfo^.CurrentUID);
+ Dec(SqInfo^.SqBase.UId);
+
+ WriteMsg;
+*)
+rewritehdr;
+End;
+
+End.
diff --git a/mystic/bbs_nodechat.pas b/mystic/bbs_nodechat.pas
new file mode 100644
index 0000000..a78cacc
--- /dev/null
+++ b/mystic/bbs_nodechat.pas
@@ -0,0 +1,497 @@
+Unit bbs_NodeChat;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure Node_Chat;
+
+Implementation
+
+Uses
+ m_Strings,
+ m_DateTime,
+ m_FileIO,
+ bbs_NodeInfo,
+ bbs_Common,
+ bbs_User,
+ bbs_Core;
+
+Var
+ ChatSize : Byte;
+ ChatUpdate : LongInt;
+ TextPos : Byte;
+ TopPage : Byte;
+ LinePos : Byte;
+ Full : Boolean;
+
+Procedure FullReDraw;
+Var
+ Count : Byte;
+ Temp : Byte;
+Begin
+ If Not Full Then Exit;
+
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[1].Y);
+
+ Temp := TopPage;
+
+ For Count := 0 to ChatSize Do Begin
+ Session.io.AnsiClrEOL;
+ If Temp <= TextPos Then Begin
+ Session.io.OutPipeLn (Session.Msgs.MsgText[Temp]);
+ Inc (Temp);
+ End Else
+ Session.io.OutRawLn('');
+ End;
+End;
+
+Procedure Change_Room (R : Byte);
+Var
+ CF : File of ChatRec;
+Begin
+ If (R < 1) or (R > 99) Then Exit;
+
+ Reset (RoomFile);
+ Seek (RoomFile, R-1);
+ Read (RoomFile, Room);
+ Close (RoomFile);
+
+ Chat.Room := R;
+ CurRoom := R;
+
+ Assign (CF, Config.DataPath + 'chat' + strI2S(Session.NodeNum) + '.dat');
+ Reset (CF);
+ Write (CF, Chat);
+ Close (CF);
+
+ Send_Node_Message (5, strI2S(Session.NodeNum) + ';' + 'Now chatting in channel ' + strI2S(CurRoom), 0); //++lang
+End;
+
+Procedure Update_Topic;
+Begin
+ If Not Full Then Exit;
+
+ { look around and make common function called goscreeninfo(num) that }
+ { goes to an x/y position and changes the attribute }
+
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[4].X, Session.io.ScreenInfo[4].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[4].A);
+
+ Session.io.OutRaw (strPadR(strI2S(CurRoom), 2, ' '));
+
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[5].X, Session.io.ScreenInfo[5].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[5].A);
+
+ Session.io.OutRaw (strPadR(Room.Name, 40, ' '));
+End;
+
+Function GetKeyNodeChatFunc (Forced : Boolean) : Boolean;
+{ 1 = node chat broadcast message (if room = 0)
+ node chat regular text (if room = room user is in)
+ 4 = node chat private message
+ 5 = chat broadcast (ie: xxx has entered chat)
+ 6 = chat action (ie: g00r00 claps his hands)
+ 7 = chat topic update }
+
+ Procedure AddText (Str : String);
+ Var
+ Count : Byte;
+ Begin
+ If TextPos < 200 Then
+ Inc (TextPos)
+ Else
+ For Count := 2 to 200 Do
+ Session.Msgs.MsgText[Count - 1] := Session.Msgs.MsgText[Count];
+
+ Session.Msgs.MsgText[TextPos] := Str;
+ End;
+
+Var
+ Str : String;
+ StrLen : Byte;
+ Indent : Byte;
+ Lines : Byte;
+ OldAttr : Byte;
+ OldX : Byte;
+ OldY : Byte;
+Begin
+ GetKeyNodeChatFunc := False;
+
+ If Session.User.InChat or Session.InUserEdit Then Exit;
+
+ If (TimerSeconds - ChatUpdate <> 0) or Forced Then Begin
+
+ Assign (NodeMsgFile, Session.TempPath + 'chat.tmp');
+ FileMode := 66;
+ {$I-} Reset (NodeMsgFile); {$I+}
+ If IoResult = 0 Then Begin
+
+ OldAttr := Screen.TextAttr;
+ OldX := Screen.CursorX;
+ OldY := Screen.CursorY;
+
+ While Not Eof(NodeMsgFile) Do Begin
+ Read (NodeMsgFile, NodeMsg);
+
+ If NodeMsg.MsgType in [1, 4..7] Then Begin
+ Session.io.OutRaw (Session.io.Pipe2Ansi(16));
+
+ Case NodeMsg.MsgType of
+ 1 : If NodeMsg.Room = 0 Then
+ Str := strReplace(Session.GetPrompt(319), '|&1', NodeMsg.FromWho)
+ Else
+ If NodeMsg.Room = CurRoom Then
+ Str := strReplace(Session.GetPrompt(181), '|&1', NodeMsg.FromWho)
+ Else
+ Continue;
+ 4 : Str := strReplace(Session.GetPrompt(218), '|&1', NodeMsg.FromWho);
+ 5 : Str := Session.GetPrompt(226);
+ 6 : Str := strReplace(Session.GetPrompt(229), '|&1', NodeMsg.FromWho);
+ 7 : Begin
+ Reset (RoomFile);
+ Seek (RoomFile, CurRoom - 1);
+ Read (RoomFile, Room);
+ Close (RoomFile);
+
+ Update_Topic;
+ Str := Session.GetPrompt(226);
+ End;
+ End;
+
+ If Full Then Begin
+ StrLen := Length(Str);
+ Indent := Length(strStripMCI(Str));
+ Lines := 0;
+
+ Repeat
+ Inc (Lines);
+
+ If Length(Str + NodeMsg.Message) > 79 Then Begin
+ Str := Str + Copy(NodeMsg.Message, 1, 79 - StrLen);
+ AddText(Str);
+ Delete (NodeMsg.Message, 1, 79 - StrLen);
+ Str := strRep(' ', Indent);
+ End Else Begin
+ AddText(Str + NodeMsg.Message);
+ Break;
+ End;
+ Until False;
+
+ If LinePos + Lines > Session.io.ScreenInfo[2].Y Then Begin
+ Indent := (ChatSize DIV 2) - 2;
+ TopPage := TextPos - Indent;
+ LinePos := Session.io.ScreenInfo[1].Y + Indent + 1;
+ FullReDraw;
+ End Else Begin
+ Session.io.AnsiGotoXY(1, LinePos);
+ For Indent := Lines DownTo 1 Do Begin
+ Session.io.AnsiClrEOL;
+ Session.io.OutPipeLn(Session.Msgs.MsgText[TextPos - Indent + 1]);
+ Inc (LinePos);
+ End;
+ End;
+
+ Session.io.AnsiGotoXY (OldX, OldY);
+ End Else Begin
+ If Session.io.Graphics = 0 Then
+ Session.io.OutBS (Screen.CursorX, True)
+ Else Begin
+ Session.io.AnsiMoveX(1);
+ Session.io.AnsiClrEOL;
+ End;
+
+ Session.io.OutPipe (Str);
+ Session.io.OutPipeLn (NodeMsg.Message);
+ End;
+ End;
+ End;
+
+ Close (NodeMsgFile);
+ Erase (NodeMsgFile);
+
+ If Not Full And Not Forced Then Begin
+ Session.io.PromptInfo[1] := Session.User.ThisUser.Handle;
+ Session.io.OutFull ('|CR' + Session.GetPrompt(427));
+ End;
+
+ Session.io.AnsiColor (OldAttr);
+
+ GetKeyNodeChatFunc := True;
+ End;
+
+ ChatUpdate := TimerSeconds;
+ End;
+End;
+
+Procedure Node_Chat;
+
+ Procedure Chat_Template;
+ Begin
+ If Not Full Then Begin
+ Session.io.OutFile('teleconf', True, 0);
+ Exit;
+ End;
+
+ Session.io.PromptInfo[1] := strI2S(CurRoom);
+ Session.io.PromptInfo[2] := Room.Name;
+
+ Session.io.OutFile ('ansitele', True, 0);
+
+ ChatSize := Session.io.ScreenInfo[2].Y - Session.io.ScreenInfo[1].Y;
+
+ Update_Topic;
+ End;
+
+ Procedure Show_Users_In_Chat;
+ Var
+ A : Byte;
+ Temp : ChatRec;
+ RM : RoomRec;
+ Begin
+ Session.io.OutFullLn (Session.GetPrompt(332));
+
+ For A := 1 to Config.INetTNMax Do Begin
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(A) + '.dat');
+ {$I-} Reset (ChatFile); {$I+}
+ If IoResult = 0 Then Begin
+ Read (ChatFile, Temp);
+ Close (ChatFile);
+ If Temp.InChat Then Begin
+ Reset (RoomFile);
+ Seek (RoomFile, Temp.Room - 1);
+ Read (RoomFile, RM);
+ Close (RoomFile);
+ Session.io.PromptInfo[1] := Temp.Name;
+ Session.io.PromptInfo[2] := strI2S(A);
+ Session.io.PromptInfo[3] := strI2S(Temp.Room);
+ Session.io.PromptInfo[4] := RM.Name;
+ Session.io.OutFullLn (Session.GetPrompt(333));
+ End;
+ End;
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(453));
+
+ Chat_Template;
+ FullReDraw;
+ End;
+
+ Procedure Send_Private_Message (Str : String);
+ Var
+ UserName : String;
+ Text : String;
+ Count : Byte;
+ Temp : ChatRec;
+ Begin
+ UserName := strUpper(strReplace(strWordGet(2, Str, ' '), '_', ' '));
+ Text := Copy(Str, strWordPos(3, Str, ' '), Length(Str));
+
+ If Text = '' Then Exit;
+
+ For Count := 1 to Config.INetTNMax Do Begin
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(Count) + '.dat');
+ {$I-} Reset (ChatFile); {$I+}
+ If IoResult = 0 Then Begin
+ Read (ChatFile, Temp);
+ Close (ChatFile);
+ If strUpper(Temp.Name) = UserName Then Begin
+ Send_Node_Message (4, strI2S(Count) + ';' + Text, 0);
+ Exit;
+ End;
+ End;
+ End;
+
+ Send_Node_Message (5, strI2S(Session.NodeNum) + ';' + 'User ' + UserName + ' not found', 0); //++lang
+ End;
+
+ Procedure ChatScrollBack;
+ Var
+ Ch : Char;
+ TopSave : Byte;
+ Begin
+ If Not Full Then Exit;
+
+ TopSave := TopPage;
+
+ Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[3].Y);
+ Session.io.AnsiClrEOL;
+ Session.io.OutFull (Session.GetPrompt(237));
+
+ Repeat
+ Ch := Session.io.GetKey;
+
+ If Ch = #27 Then Break;
+
+ If Session.io.IsArrow Then
+ Case Ch of
+ #71 : If TopPage > 1 Then Begin
+ TopPage := 1;
+ FullReDraw;
+ End;
+ #72 : If TopPage > 1 Then Begin
+ Dec(TopPage);
+ FullReDraw;
+ End;
+ #73,
+ #75 : If TopPage > 1 Then Begin
+ If TopPage < ChatSize Then
+ TopPage := 1
+ Else
+ Dec (TopPage, ChatSize);
+ FullReDraw;
+ End;
+ #79 : If TopPage < TopSave Then Begin
+ TopPage := TopSave;
+ FullReDraw;
+ End;
+ #80 : If TopPage < TopSave Then Begin
+ Inc(TopPage);
+ FullReDraw;
+ End;
+ #77,
+ #81 : If TopPage < TopSave Then Begin
+ If TopPage + ChatSize > TopSave Then
+ TopPage := TopSave
+ Else
+ Inc (TopPage, ChatSize);
+ FullReDraw;
+ End;
+ End;
+ Until False;
+
+ TopPage := TopSave;
+ FullReDraw;
+ End;
+
+Var
+ Str : String;
+ Str2 : String;
+ Avail : Boolean;
+Begin
+ Full := Session.User.ThisUser.UseFullChat And (Session.io.Graphics > 0);
+
+ Set_Node_Action (Session.GetPrompt(347));
+
+ Avail := Chat.Available;
+ Chat.InChat := True;
+ Chat.Available := False;
+
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(Session.NodeNum) + '.dat');
+ Reset (ChatFile);
+ Write (ChatFile, Chat);
+ Close (ChatFile);
+
+ FileErase(Session.TempPath + 'chat.tmp');
+
+ Send_Node_Message (5, '0;' + Session.User.ThisUser.Handle + ' has entered chat', 0); //++lang
+
+ Change_Room (1);
+
+ Chat_Template;
+
+ TopPage := 1;
+ TextPos := 0;
+ LinePos := Session.io.ScreenInfo[1].Y;
+
+ FullReDraw;
+
+ GetKeyFunc := GetKeyNodeChatFunc;
+
+ Repeat
+ Session.io.PromptInfo[1] := Session.User.ThisUser.Handle;
+
+ If Full Then Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[3].Y) Else Session.io.OutRawLn('');
+
+ Session.io.OutFull (Session.GetPrompt(427));
+
+ If Full Then
+ Str := Session.io.GetInput (79 - Screen.CursorX + 1, 250, 19, '')
+ Else
+ Str := Session.io.GetInput (79 - Screen.CursorX + 1, 250, 11, '');
+
+ If Str[1] = '/' Then Begin
+ GetKeyFunc := NoGetKeyFunc;
+
+ Str2 := strUpper(strWordGet(1, Str, ' '));
+
+ If Str2 = '/B' Then Begin
+ Str2 := Copy(Str, strWordPos(2, Str, ' '), Length(Str));
+ If Str2 <> '' Then
+ Send_Node_Message (1, '0;' + Str2, 0)
+ End Else
+ If Str2 = '/CLS' Then Begin
+ TopPage := 1;
+ TextPos := 0;
+ LinePos := Session.io.ScreenInfo[1].Y;
+
+ FullReDraw;
+ End Else
+ If Str2 = '/?' Then Begin
+ Session.io.OutFile ('telehelp', True, 0);
+ Chat_Template;
+ FullReDraw
+ End Else
+ If Str2 = '/SCROLL' Then
+ ChatScrollBack
+ Else
+ If Str2 = '/Q' Then
+ Break
+ Else
+ If Str2 = '/ME' Then Begin
+ Str := Copy(Str, 5, Length(Str));
+
+ If Str <> '' Then
+ Send_Node_Message (6, '0;' + Str, CurRoom);
+ End Else
+ If Str2 = '/MSG' Then
+ Send_Private_Message(Str)
+ Else
+ If Str2 = '/NAMES' Then
+ Show_Users_In_Chat
+ Else
+ If Str2 = '/JOIN' Then Begin
+ Change_Room (strS2I(strWordGet(2, Str, ' ')));
+ Update_Topic;
+ End Else
+ If Str2 = '/WHO' Then Begin
+ Session.io.AnsiClear;
+ Show_Whos_Online;
+ Chat_Template;
+ FullReDraw;
+ End Else
+ If Str2 = '/TOPIC' Then Begin
+ Room.Name := Copy(Str, strWordPos(2, Str, ' '), Length(Str));
+
+ Reset (RoomFile);
+ Seek (RoomFile, CurRoom - 1);
+ Write (RoomFile, Room);
+ Close (RoomFile);
+
+ Send_Node_Message (7, '0;Topic changed to "' + Room.Name + '"', CurRoom); // ++lang
+ End;
+
+ GetKeyFunc := GetKeyNodeChatFunc;
+ End Else
+ If Str <> '' Then Begin
+ Send_Node_Message (1, '0;' + Str, CurRoom);
+ If Not Full Then Session.io.OutRawLn('');
+ GetKeyNodeChatFunc(True);
+ End;
+ Until False;
+
+ GetKeyFunc := NoGetKeyFunc;
+ Chat.InChat := False;
+ Chat.Available := Avail;
+
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(Session.NodeNum) + '.dat');
+ Reset (ChatFile);
+ Write (ChatFile, Chat);
+ Close (ChatFile);
+
+ FileErase(Session.TempPath + 'chat.tmp');
+
+ Send_Node_Message (5, '0;' + Session.User.ThisUser.Handle + ' has left chat', 0); //++lang
+End;
+
+End.
diff --git a/mystic/bbs_nodeinfo.pas b/mystic/bbs_nodeinfo.pas
new file mode 100644
index 0000000..ceb3477
--- /dev/null
+++ b/mystic/bbs_nodeinfo.pas
@@ -0,0 +1,196 @@
+Unit bbs_NodeInfo; { Multinode functions }
+
+{$I M_OPS.PAS}
+
+Interface
+
+Function Is_User_Online (Name : String) : Word;
+Procedure Set_Node_Action (Action: String);
+Procedure Show_Whos_Online;
+Procedure Send_Node_Message (MsgType: Byte; Data: String; Room: Byte);
+
+Implementation
+
+Uses
+ m_DateTime,
+ m_Strings,
+ bbs_Common,
+ bbs_Core,
+ bbs_User;
+
+Function Is_User_Online (Name : String) : Word;
+Var
+ TempChat : ChatRec;
+ Count : Word;
+Begin
+ Is_User_Online := 0;
+
+ For Count := 1 to Config.INetTNMax Do Begin
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(Count) + '.dat');
+ {$I-} Reset(ChatFile); {$I+}
+ If IoResult <> 0 Then Continue;
+ Read (ChatFile, TempChat);
+ Close (ChatFile);
+
+ If (Count <> Session.NodeNum) and (TempChat.Active) and (TempChat.Name = Name) Then Begin
+ Is_User_Online := Count;
+ Exit;
+ End;
+ End;
+End;
+
+Procedure Set_Node_Action (Action: String);
+Begin
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(Session.NodeNum) + '.dat');
+ ReWrite (ChatFile);
+
+ If Action <> '' Then Begin
+ Chat.Active := True;
+ Chat.Name := Session.User.ThisUser.Handle;
+ Chat.Location := Session.User.ThisUser.City;
+ Chat.Action := Action;
+ Chat.Gender := Session.User.ThisUser.Gender;
+ Chat.Age := DaysAgo(Session.User.ThisUser.Birthday) DIV 365;
+ If Session.LocalMode Then
+ Chat.Baud := 'LOCAL' {++lang}
+ Else
+ Chat.Baud := 'TELNET'; {++lang}
+ End Else Begin
+ Chat.Active := False;
+ Chat.Invisible := False;
+ Chat.Available := False;
+ Chat.Age := 0;
+ Chat.Gender := '?';
+ End;
+
+ Write (ChatFile, Chat);
+ Close (ChatFile);
+
+ {$IFDEF WIN32}
+ Screen.SetWindowTitle (WinConsoleTitle + strI2S(Session.NodeNum) + ' - ' + Session.User.ThisUser.Handle + ' - ' + Action);
+ {$ENDIF}
+End;
+
+Procedure Show_Whos_Online;
+Var
+ TChat : ChatRec;
+ A : Word;
+Begin
+ Session.io.OutFullLn (Session.GetPrompt(138));
+
+ For A := 1 to Config.INetTNMax Do Begin
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(A) + '.dat');
+ {$I-} Reset(ChatFile); {$I+}
+ If IoResult <> 0 Then Continue;
+ Read (ChatFile, TChat);
+ Close (ChatFile);
+
+ If TChat.Active and ((Not TChat.Invisible) or (TChat.Invisible and Session.User.Access(Config.AcsSeeInvis))) Then Begin
+ Session.io.PromptInfo[1] := strI2S(A);
+ Session.io.PromptInfo[2] := TChat.Name;
+ Session.io.PromptInfo[3] := TChat.Action;
+ Session.io.PromptInfo[4] := TChat.Location;
+ Session.io.PromptInfo[5] := TChat.Baud;
+ Session.io.PromptInfo[6] := TChat.Gender;
+ Session.io.PromptInfo[7] := strI2S(TChat.Age);
+ Session.io.PromptInfo[8] := Session.io.OutYN(TChat.Available);
+ Session.io.OutFullLn (Session.GetPrompt(139));
+ End Else Begin
+ Session.io.PromptInfo[1] := strI2S(A);
+ Session.io.OutFullLn (Session.GetPrompt(268));
+ End;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(140));
+End;
+
+Procedure Send_Node_Message (MsgType: Byte; Data: String; Room: Byte);
+Var
+ ToNode : Byte;
+ A, B, C : Byte;
+ Temp : ChatRec;
+ Str : String[3];
+Begin
+ If Data = '' Then Begin
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(146));
+ Str := Session.io.GetInput(3, 3, 12, '');
+ If Str = '?' Then Show_Whos_Online Else Break;
+ Until False;
+
+ ToNode := strS2I(Str);
+
+ If (ToNode < 0) or (ToNode > Config.INetTNMax) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(147));
+ Exit;
+ End;
+
+ B := ToNode;
+ C := ToNode;
+ End Else Begin
+ If Pos(';', Data) = 0 Then Exit;
+ ToNode := strS2I(Copy(Data, 1, Pos(';', Data)-1));
+ Delete (Data, 1, Pos(';', Data));
+ If ToNode = 0 Then Begin
+ B := 1;
+ C := Config.INetTNMax;
+ If MsgType = 3 Then MsgType := 2;
+{ If Not (MsgType in [1, 4..7]) Then MsgType := 2;}
+{ used line above comment now... see if that does anything }
+
+ End Else Begin
+ B := ToNode;
+ C := ToNode;
+ End;
+ End;
+
+ For A := B to C Do Begin
+
+ Assign (ChatFile, Config.DataPath + 'chat' + strI2S(A) + '.dat');
+ FileMode := 66;
+ {$I-} Reset (ChatFile); {$I+}
+ If IoResult = 0 Then Begin
+ Read (ChatFile, Temp);
+ Close (ChatFile);
+
+ If (Not Temp.Active) and (ToNode > 0) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(147));
+ Exit;
+ End;
+
+ If (Not Temp.Available) and not (MsgType in [1, 4..7]) and (ToNode > 0) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(395));
+ Exit;
+ End;
+
+ If Temp.Active and (Temp.Available or Temp.InChat) Then Begin
+ If Data = '' Then Begin
+ Session.io.PromptInfo[1] := Temp.Name; { TEMP NODE NAME }
+ Session.io.PromptInfo[2] := strI2S(A);
+ Session.io.OutFullLn (Session.GetPrompt(148));
+ NodeMsg.Message := Session.io.GetInput(79, 79, 11, '');
+ End Else
+ NodeMsg.Message := Data;
+
+ If NodeMsg.Message = '' Then Exit;
+
+ NodeMsg.FromNode := Session.NodeNum;
+ NodeMsg.ToWho := Temp.Name;
+ NodeMsg.MsgType := MsgType;
+ NodeMsg.Room := Room;
+ NodeMsg.FromWho := Session.User.ThisUser.Handle;
+
+ Assign (NodeMsgFile, Config.SystemPath + 'temp' + strI2S(A) + PathChar + 'chat.tmp');
+ FileMode := 66;
+ {$I-} Reset (NodeMsgFile); {$I+}
+ If IoResult <> 0 Then ReWrite(NodeMsgFile);
+
+ Seek (NodeMsgFile, FileSize(NodeMsgFile));
+ Write (NodeMsgFile, NodeMsg);
+ Close (NodeMsgFile);
+ End;
+ End;
+ End;
+End;
+
+End.
diff --git a/mystic/bbs_sysopchat.pas b/mystic/bbs_sysopchat.pas
new file mode 100644
index 0000000..677bfe5
--- /dev/null
+++ b/mystic/bbs_sysopchat.pas
@@ -0,0 +1,292 @@
+Unit bbs_SysOpChat;
+
+(* some ideas for chat:
+
+split chat additions:
+
+- scroll half window instead of just the last line
+- allow full arrow key movement in chat windows...
+ kinda like a full screen editor...
+- ctrl-k brings up a command menu, which has:
+ OutFull file
+ display file
+ dos drop? add mini-dos internal to mystic?
+
+*)
+{$I M_OPS.PAS}
+
+Interface
+
+Procedure OpenChat (Split : Boolean);
+
+Implementation
+
+Uses
+ m_Types,
+ m_Strings,
+ m_DateTime,
+ bbs_Common,
+ bbs_Core,
+ bbs_User;
+
+Var
+ tFile : Text;
+
+Procedure Split_Chat;
+Var
+ Update : LongInt = 0;
+ LastUser : Boolean;
+ UserStr : String;
+ SysopStr : String;
+ Temp1,
+ Temp2 : String;
+ Ch : Char;
+ UserX,
+ UserY : Byte;
+ SysopX,
+ SysopY : Byte;
+ X, Y, A : Byte;
+
+Procedure Total_ReDraw;
+Begin
+ Session.io.PromptInfo[1] := Session.User.ThisUser.Handle;
+ Session.io.PromptInfo[2] := Config.SysopName;
+
+ Session.io.ScreenInfo[9].X := 0;
+ Session.io.ScreenInfo[0].X := 0;
+
+ Session.io.OutFile ('ansichat', True, 0);
+
+ UserStr := '';
+ UserX := Session.io.ScreenInfo[3].X;
+ UserY := Session.io.ScreenInfo[1].Y;
+ SysopX := Session.io.ScreenInfo[7].X;
+ SysopY := Session.io.ScreenInfo[5].Y;
+ SysopStr := '';
+
+ Session.io.AnsiGotoXY (SysopX, SysopY);
+ Session.io.AnsiColor (Session.io.ScreenInfo[5].A);
+
+ LastUser := False;
+End;
+
+Begin
+ Total_ReDraw;
+
+ Repeat
+ If Update <> TimerMinutes Then Begin
+ X := Screen.CursorX;
+ Y := Screen.CursorY;
+ A := Screen.TextAttr;
+
+ If Session.io.ScreenInfo[9].X <> 0 Then Begin
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[9].X, Session.io.ScreenInfo[9].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[9].A);
+ Session.io.OutFull ('|$L04|TL');
+ End;
+
+ If Session.io.ScreenInfo[0].X <> 0 Then Begin
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[0].X, Session.io.ScreenInfo[0].Y);
+ Session.io.AnsiColor (Session.io.ScreenInfo[0].A);
+ Session.io.OutFull ('|TI');
+ End;
+
+ Session.io.AnsiGotoXY (X, Y);
+ Session.io.AnsiColor(A);
+
+ Update := TimerMinutes;
+ End;
+
+ Ch := Session.io.GetKey;
+
+ If Not Session.io.LocalInput and Not LastUser Then Begin
+ Session.io.AnsiGotoXY (UserX, UserY);
+ Session.io.AnsiColor (Session.io.ScreenInfo[1].A);
+ LastUser := True;
+ End Else
+ If Session.io.LocalInput and LastUser Then Begin
+ Session.io.AnsiGotoXY (SysopX, SysopY);
+ Session.io.AnsiColor (Session.io.ScreenInfo[5].A);
+ LastUser := False;
+ End;
+
+ Case Ch of
+ #00 : If Session.io.LocalInput Then Process_Sysop_Cmd(Input.ReadKey);
+ ^R : If Session.io.LocalInput Then Total_ReDraw;
+ #08 : If Session.io.LocalInput Then Begin
+ If SysopX > Session.io.ScreenInfo[7].X Then Begin
+ Session.io.OutBS (1, True);
+ Dec (SysopX);
+ Dec (SysopStr[0]);
+ End;
+ End Else Begin
+ If UserX > Session.io.ScreenInfo[3].X Then Begin
+ Session.io.OutBS (1, True);
+ Dec (UserX);
+ Dec (UserStr[0]);
+ End;
+ End;
+ #10 : ;
+ #13 : If Session.io.LocalInput Then Begin
+ sysopx := Session.io.ScreenInfo[7].x;
+ if sysopy = Session.io.ScreenInfo[6].y then begin
+ for sysopy := Session.io.ScreenInfo[6].y downto Session.io.ScreenInfo[5].y do begin
+ Session.io.AnsiGotoXY(Session.io.ScreenInfo[7].x, sysopy);
+ Session.io.OutRaw (strRep(' ', Session.io.ScreenInfo[8].x - Session.io.ScreenInfo[7].x + 1));
+ Session.io.AnsiGotoXY(Session.io.ScreenInfo[7].x, sysopy);
+ end;
+ Session.io.OutRaw(sysopstr);
+ end;
+ If Config.ChatLogging Then WriteLn (tFile, 'S> ' + SysopSTR);
+ inc (sysopy);
+ sysopstr := '';
+ Session.io.AnsiGotoXY (sysopx, sysopy);
+ End Else Begin
+ userx := Session.io.ScreenInfo[3].x;
+ if usery = Session.io.ScreenInfo[2].y then begin
+ for usery := Session.io.ScreenInfo[2].y downto Session.io.ScreenInfo[1].y do begin
+ Session.io.AnsiGotoXY(userx, usery);
+ Session.io.OutRaw (strRep(' ', Session.io.ScreenInfo[4].x - Session.io.ScreenInfo[3].x + 1));
+ Session.io.AnsiGotoXY(userx, usery);
+ end;
+ Session.io.OutRaw(userstr);
+ end;
+ inc (usery);
+ If Config.ChatLogging Then WriteLn (tFile, 'U> ' + UserSTR);
+ userstr := '';
+ Session.io.AnsiGotoXY (userx, usery);
+ End;
+ #27 : If Session.io.LocalInput Then Break;
+ Else
+ If Session.io.LocalInput Then Begin
+ Session.io.BufAddChar (ch);
+ inc (sysopx);
+ sysopstr := sysopstr + ch;
+ if sysopx > Session.io.ScreenInfo[8].x then begin
+ strwrap (sysopstr, temp2, Session.io.ScreenInfo[8].x - session.io.screeninfo[7].x + 1);
+ temp1 := sysopstr;
+ If Config.ChatLogging Then WriteLn (tFile, 'S> ' + SysopSTR);
+ sysopstr := temp2;
+ Session.io.OutBS (length(temp2), True);
+ if sysopy=Session.io.ScreenInfo[6].y then begin
+ for sysopy := Session.io.ScreenInfo[6].y downto Session.io.ScreenInfo[5].y do begin
+ Session.io.AnsiGotoXY(Session.io.ScreenInfo[7].x, sysopy);
+ Session.io.OutRaw (strRep(' ', Session.io.ScreenInfo[8].x - Session.io.ScreenInfo[7].x + 1));
+ end;
+ Session.io.AnsiGotoXY(Session.io.ScreenInfo[7].x, sysopy);
+ Session.io.OutRaw(temp1);
+ end;
+ inc (sysopy);
+ Session.io.AnsiGotoXY(Session.io.ScreenInfo[7].x, sysopy);
+ Session.io.OutRaw (sysopstr);
+ sysopx := Screen.CursorX;
+ end;
+ End Else Begin
+ Session.io.BufAddChar (ch);
+ inc (userx);
+ userstr := userstr + ch;
+ if userx > Session.io.ScreenInfo[4].x then begin
+ strwrap (userstr, temp2, Session.io.ScreenInfo[4].x - session.io.screeninfo[3].x + 1);
+ temp1 := userstr;
+ If Config.ChatLogging Then WriteLn (tFile, 'U> ' + UserSTR);
+ userstr := temp2;
+ Session.io.OutBS (length(temp2), True);
+ if usery=Session.io.ScreenInfo[2].y then begin
+ for usery := Session.io.ScreenInfo[2].y downto Session.io.ScreenInfo[1].y do begin
+ Session.io.AnsiGotoXY(Session.io.ScreenInfo[3].x, usery);
+ Session.io.OutRaw (strRep(' ', Session.io.ScreenInfo[4].x - Session.io.ScreenInfo[3].x + 1));
+ end;
+ Session.io.AnsiGotoXY(Session.io.ScreenInfo[3].x, usery);
+ Session.io.OutRawln(temp1);
+ end;
+ inc(usery);
+ Session.io.AnsiGotoXY (Session.io.ScreenInfo[3].x, usery);
+ Session.io.OutRaw(userstr);
+ userx := Screen.CursorX;
+ end;
+ end;
+ End;
+ Until False;
+
+ Session.io.AnsiGotoXY (1, Session.User.ThisUser.ScreenSize);
+
+ Session.io.OutFull ('|16' + Session.GetPrompt(27));
+End;
+
+Procedure Line_Chat;
+Var
+ Ch : Char;
+ Str1,
+ Str2 : String;
+Begin
+ Str1 := '';
+ Str2 := '';
+ Session.io.OutFullLn (Session.GetPrompt(26));
+
+ Repeat
+ Ch := Session.io.GetKey;
+ Case Ch of
+ #27 : If Session.io.LocalInput Then Break;
+ #13 : Begin
+ If Config.ChatLogging Then WriteLn (tFile, Str1);
+ Session.io.OutRawLn('');
+ Str1 := '';
+ End;
+ #8 : If Str1 <> '' Then Begin
+ Session.io.OutBS(1, True);
+ Dec(Str1[0]);
+ End;
+ Else
+ Str1 := Str1 + Ch;
+ Session.io.BufAddChar(Ch);
+ If Length(Str1) > 78 Then Begin
+ strWrap (Str1, Str2, 78);
+ Session.io.OutBS(Length(Str2), True);
+ Session.io.OutRawLn ('');
+ Session.io.OutRaw (Str2);
+ If Config.ChatLogging Then WriteLn (tFile, Str1);
+ Str1 := Str2;
+ End;
+ End;
+ Until False;
+
+ Session.io.OutFull (Session.GetPrompt(27));
+End;
+
+Procedure OpenChat (Split : Boolean);
+Var
+ Image : TConsoleImageRec;
+Begin
+ Session.User.InChat := True;
+
+ Screen.GetScreenImage(1,1,79,24,Image);
+
+ Update_Status_Line (0, '(ESC) to Quit, (Ctrl-R) to Redraw');
+
+ If Config.ChatLogging Then Begin
+ Assign (tFile, Config.LogsPath + 'chat.log');
+ {$I-} Append (tFile); {$I+}
+ If IoResult <> 0 Then ReWrite (tFile);
+
+ WriteLn (tFile, '');
+ WriteLn (tFile, 'Chat recorded ' + DateDos2Str(CurDateDos, 1) + ' ' + TimeDos2Str(CurDateDos, True) +
+ ' with ' + Session.User.ThisUser.Handle);
+ WriteLn (tFile, strRep('-', 70));
+ End;
+
+ If ((Split) And (Session.io.Graphics > 0)) Then Split_Chat Else Line_Chat;
+
+ If Config.ChatLogging Then Begin
+ WriteLn (tFile, strRep('-', 70));
+ Close (tFile);
+ End;
+
+ Session.User.InChat := False;
+ Session.TimeOut := TimerSeconds;
+
+ Session.io.RemoteRestore(Image);
+
+ Update_Status_Line (StatusPtr, '');
+End;
+
+End.
diff --git a/mystic/bbs_user.pas b/mystic/bbs_user.pas
new file mode 100644
index 0000000..51d2ffe
--- /dev/null
+++ b/mystic/bbs_user.pas
@@ -0,0 +1,1316 @@
+Unit bbs_User;
+
+{$I M_OPS.PAS}
+
+Interface
+
+Uses
+ m_FileIO,
+ m_Strings,
+ m_DateTime,
+ bbs_Common,
+ bbs_General,
+ bbs_MsgBase,
+ bbs_FileBase,
+ bbs_Menus,
+ bbs_NodeInfo,
+ mpl_Execute;
+
+Type
+ TBBSUser = Class
+ UserFile : File of RecUser;
+ SecurityFile : File of RecSecurity;
+ Security : RecSecurity;
+ ThisUser : RecUser;
+ TempUser : RecUser;
+ UserNum : LongInt;
+ AcsOkFlag : Boolean;
+ IgnoreGroup : Boolean;
+ InChat : Boolean;
+ MatrixOK : Boolean;
+
+ Constructor Create (Var Owner: Pointer);
+ Destructor Destroy; Override;
+
+ Function IsThisUser (Str: String) : Boolean;
+ Function Access (Str: String) : Boolean;
+ Function SearchUser (Var Str : String; Real : Boolean) : Boolean;
+ Function FindUser (Str: String; Adjust: Boolean) : Boolean;
+ Function GetMatrixUser : Boolean;
+ Procedure DetectGraphics;
+ Procedure GetGraphics;
+ Procedure GetDateFormat (Edit: Boolean);
+ Procedure GetAddress (Edit: Boolean);
+ Procedure GetCityState (Edit: Boolean);
+ Procedure GetZipCode (Edit: Boolean);
+ Procedure GetHomePhone (Edit: Boolean);
+ Procedure GetDataPhone (Edit: Boolean);
+ Procedure GetBirthDate (Edit: Boolean);
+ Procedure GetGender (Edit: Boolean);
+ Procedure GetScreenLength (Edit: Boolean);
+ Procedure GetPassword (Edit: Boolean);
+ Procedure GetRealName (Edit: Boolean);
+ Procedure GetAlias (Edit: Boolean; Def: String);
+ Procedure GetEditor (Edit: Boolean);
+ Procedure GetFileList (Edit: Boolean);
+ Procedure GetMsgList (Edit: Boolean);
+ Procedure GetHotKeys (Edit: Boolean);
+ Procedure GetEmail (Edit: Boolean);
+ Procedure GetUserNote (Edit: Boolean);
+ Procedure GetOption1 (Edit: Boolean);
+ Procedure GetOption2 (Edit: Boolean);
+ Procedure GetOption3 (Edit: Boolean);
+ Procedure GetLanguage;
+ Procedure User_Logon (Var UN, PW, MPE : String);
+ Procedure User_Logon2;
+ Procedure User_Logon3;
+ Procedure CreateNewUser (DefName: String);
+ Procedure Edit_User_Settings (What: Byte);
+ Function Check_Trash (Name: String) : Boolean;
+ End;
+
+Implementation
+
+Uses
+ bbs_Core;
+
+Constructor TBBSUser.Create (Var Owner: Pointer);
+Begin
+ UserNum := -1;
+ ThisUser.ScreenSize := Config.DefScreenSize;
+ ThisUser.DateType := 1;
+ ThisUser.Security := 255;
+ ThisUser.HotKeys := True;
+ ThisUser.RealName := 'Unknown';
+ ThisUser.Handle := 'Unknown';
+ ThisUser.EditType := 1;
+ ThisUser.Birthday := CurDateJulian;
+ ThisUser.Gender := 'U';
+
+ IgnoreGroup := False;
+ InChat := False;
+ AcsOkFlag := False;
+ MatrixOK := False;
+End;
+
+Destructor TBBSUser.Destroy;
+Begin
+ Inherited Destroy;
+End;
+
+Function TBBSUser.IsThisUser (Str: String) : Boolean;
+Begin
+ Str := strUpper(Str);
+ Result := (strUpper(ThisUser.RealName) = Str) or (strUpper(ThisUser.Handle) = Str);
+End;
+
+Function TBBSUser.Access (Str: String) : Boolean;
+Const
+ OpCmds = ['%', '^', '(', ')', '&', '!', '|'];
+ AcsCmds = ['A', 'D', 'E', 'F', 'G', 'H', 'M', 'N', 'O', 'S', 'T', 'U', 'W', 'Z'];
+Var
+ Key : Char;
+ Data : String;
+ Check : Boolean;
+ Out : String;
+ First : Boolean;
+
+ Procedure CheckCommand;
+ Var
+ Res : Boolean;
+ Temp1 : LongInt;
+ Temp2 : LongInt;
+ Begin
+ Res := False;
+
+ Case Key of
+ 'A' : Res := DaysAgo(ThisUser.Birthday) DIV 365 >= strS2I(Data);
+ 'D' : Res := (Ord(Data[1]) - 64) in ThisUser.AF2;
+ 'E' : Case Data[1] of
+ '1' : Res := Session.io.Graphics = 1;
+ '0' : Res := Session.io.Graphics = 0;
+ End;
+ 'F' : Res := (Ord(Data[1]) - 64) in ThisUser.AF1;
+ 'G' : If IgnoreGroup Then Begin
+ First := True;
+ Check := False;
+ Data := '';
+ Exit;
+ End Else
+ Res := ThisUser.LastMGroup = strS2I(Data);
+ 'H' : Res := strS2I(Data) < strS2I(Copy(TimeDos2Str(CurDateDos, False), 1, 2));
+ 'M' : Res := strS2I(Data) < strS2I(Copy(TimeDos2Str(CurDateDos, False), 4, 2));
+ 'N' : Res := strS2I(Data) = Session.NodeNum;
+ 'O' : Case Data[1] of
+ 'A' : Res := Chat.Available;
+ 'I' : Res := Chat.Invisible;
+ 'K' : Res := AcsOkFlag;
+ 'M' : Begin
+ Res := Access(Session.Msgs.MBase.SysopACS);
+
+ If Session.Msgs.Reading Then
+ Res := Res or IsThisUser(Session.msgs.MsgBase^.GetFrom);
+ End;
+ 'P' : If (ThisUser.Calls > 0) And (ThisUser.Flags AND UserNoRatio = 0) Then Begin
+ Temp1 := Round(Security.PCRatio / 100 * 100);
+ Temp2 := Round(ThisUser.Posts / ThisUser.Calls * 100);
+ Res := (Temp2 >= Temp1);
+ End Else
+ Res := True;
+ End;
+ 'S' : Res := ThisUser.Security >= strS2I(Data);
+ 'T' : Res := Session.TimeLeft > strS2I(Data);
+ 'U' : Res := ThisUser.PermIdx = strS2I(Data);
+ 'W' : Res := strS2I(Data) = DayOfWeek;
+ 'Z' : If IgnoreGroup Then Begin
+ Check := False;
+ First := True;
+ Data := '';
+ Exit;
+ End Else
+ Res := strS2I(Data) = ThisUser.LastFGroup;
+ End;
+
+ If Res Then Out := Out + '^' Else Out := Out + '%';
+
+ Check := False;
+ First := True;
+ Data := '';
+ End;
+
+Var
+ A : Byte;
+ Paran1 : Byte;
+ Paran2 : Byte;
+ Ch1 : Char;
+ Ch2 : Char;
+ S1 : String;
+Begin
+ Data := '';
+ Out := '';
+ Check := False;
+ Str := strUpper(Str);
+ First := True;
+
+ For A := 1 to Length(Str) Do
+ If Str[A] in OpCmds Then Begin
+ If Check Then CheckCommand;
+ Out := Out + Str[A];
+ End Else
+ If (Str[A] in AcsCmds) and (First or Check) Then Begin
+ If Check Then CheckCommand;
+ Key := Str[A];
+ If First Then First := False;
+ End Else Begin
+ Data := Data + Str[A];
+ Check := True;
+ If A = Length(Str) Then CheckCommand;
+ End;
+
+ Out := '(' + Out + ')';
+
+ While Pos('&', Out) <> 0 Do Delete (Out, Pos('&', Out), 1);
+
+ While Pos('(', Out) <> 0 Do Begin
+ Paran2 := 1;
+ While ((Out[Paran2] <> ')') And (Paran2 <= Length(Out))) Do Begin
+ If (Out[Paran2] = '(') Then Paran1 := Paran2;
+ Inc (Paran2);
+ End;
+
+ S1 := Copy(Out, Paran1 + 1, (Paran2 - Paran1) - 1);
+
+ While Pos('!', S1) <> 0 Do Begin
+ A := Pos('!', S1) + 1;
+ If S1[A] = '^' Then S1[A] := '%' Else
+ If S1[A] = '%' Then S1[A] := '^';
+ Delete (S1, A - 1, 1);
+ End;
+
+ While Pos('|', S1) <> 0 Do Begin
+ A := Pos('|', S1) - 1;
+ Ch1 := S1[A];
+ Ch2 := S1[A + 2];
+
+ If (Ch1 in ['%', '^']) and (Ch2 in ['%', '^']) Then Begin
+ Delete (S1, A, 3);
+ If (Ch1 = '^') or (Ch2 = '^') Then
+ Insert ('^', S1, A)
+ Else
+ Insert ('%', S1, A)
+ End Else
+ Delete (S1, A + 1, 1);
+ End;
+
+ While Pos('%%', S1) <> 0 Do Delete (S1, Pos('%%', S1), 1);
+ While Pos('^^', S1) <> 0 Do Delete (S1, Pos('^^', S1), 1);
+ While Pos('%^', S1) <> 0 Do Delete (S1, Pos('%^', S1) + 1, 1);
+ While Pos('^%', S1) <> 0 Do Delete (S1, Pos('^%', S1), 1);
+
+ Delete (Out, Paran1, (Paran2 - Paran1) + 1);
+ Insert (S1, Out, Paran1);
+ End;
+
+ Result := (Pos('%', Out) = 0);
+End;
+
+Function TBBSUser.SearchUser (Var Str : String; Real : Boolean) : Boolean;
+Var
+ Found : Boolean;
+ First : Boolean;
+Begin
+ Str := strUpper(Str);
+
+ If Str = 'SYSOP' Then
+ Str := strUpper(Config.SysopName);
+
+ Found := False;
+ First := True;
+
+ Reset (UserFile);
+
+ While Not Eof(UserFile) Do Begin
+ Read (UserFile, TempUser);
+
+ If TempUser.Flags AND UserDeleted <> 0 Then Continue;
+
+ If Pos(Str, strUpper(TempUser.Handle)) > 0 Then Begin
+ If First Then Begin
+ Session.io.OutRawLn ('');
+ First := False;
+ End;
+
+ Session.io.PromptInfo[1] := TempUser.Handle;
+
+ If Session.io.GetYN (Session.GetPrompt(155), True) Then Begin
+ If Real Then
+ Str := TempUser.RealName
+ Else
+ Str := TempUser.Handle;
+ Found := True;
+ Break;
+ End;
+ End;
+ End;
+
+ Close (UserFile);
+
+ If Not Found Then
+ Session.io.OutFullLn (Session.GetPrompt(156));
+
+ Result := Found;
+End;
+
+Function TBBSUser.FindUser (Str: String; Adjust: Boolean) : Boolean;
+Begin
+ FindUser := False;
+
+ If Str = '' Then Exit;
+
+ Str := strUpper(Str);
+
+ Reset (UserFile);
+
+ While Not Eof(UserFile) Do Begin
+ Read (UserFile, TempUser);
+
+ If ((strUpper(TempUser.RealName) = Str) or (strUpper(TempUser.Handle) = Str)) and (TempUser.Flags And UserDeleted = 0) Then Begin
+ If Adjust Then UserNum := FilePos(UserFile);
+
+ FindUser := True;
+ Break;
+ End;
+ End;
+
+ Close (UserFile);
+End;
+
+Function TBBSUser.GetMatrixUser : Boolean;
+Var
+ SavedNum : LongInt;
+Begin
+ Result := False;
+
+ If UserNum <> -1 Then Begin
+ GetMatrixUser := True;
+ Exit;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(273));
+
+ ThisUser.Handle := Session.io.GetInput(30, 30, 18, '');
+
+ If Not FindUser(ThisUser.Handle, True) Then Exit;
+
+ SavedNum := UserNum;
+ UserNum := -1;
+
+ If Not Session.io.GetPW(Session.GetPrompt(274), Session.GetPrompt(293), TempUser.Password) Then Begin
+ If Config.PWInquiry Then
+ If Session.io.GetYN(Session.GetPrompt(475), False) Then
+ Session.Msgs.PostMessage(True, '/TO:' + strReplace(Config.FeedbackTo, ' ', '_') + ' /SUBJ:Password_Inquiry');
+
+ Session.Msgs.PostTextFile('hackwarn.txt;0;' + Config.SysopName + ';' + TempUser.Handle + ';Possible hack attempt', True);
+
+ Exit;
+ End;
+
+ ThisUser := TempUser;
+ UserNum := SavedNum;
+ Result := True;
+End;
+
+{$IFDEF UNIX}
+Procedure TBBSUser.DetectGraphics;
+Var
+ Loop : Byte;
+Begin
+ If Not Session.Lang.okANSI Then Begin
+ Session.io.Graphics := 0;
+ Exit;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(258));
+ Session.io.OutRaw (#27 + '[6n');
+ Session.io.BufFlush;
+
+ For Loop := 1 to 12 Do Begin
+ WaitMS(500);
+ While Input.KeyPressed Do
+ If Input.ReadKey in [#27, '[', '0'..'9', ';', 'R'] Then Begin
+ Session.io.Graphics := 1;
+ Break;
+ End;
+ End;
+
+ While Input.KeyPressed Do Loop := Byte(Input.ReadKey);
+
+ Session.io.OutFullLn (Session.GetPrompt(259));
+End;
+{$ELSE}
+Procedure TBBSUser.DetectGraphics;
+Var
+ Loop : Byte;
+Begin
+ If Not Session.Lang.okANSI Then Begin
+ Session.io.Graphics := 0;
+ Exit;
+ End;
+
+ Session.io.OutFull (Session.GetPrompt(258));
+
+ If Session.LocalMode Then
+ Session.io.Graphics := 1
+ Else Begin
+ Session.Client.PurgeInputData;
+ Session.io.OutRaw (#27 + '[6n');
+ Session.io.BufFlush;
+
+ For Loop := 1 to 6 Do Begin
+ If Session.Client.WaitForData(1000) > 0 Then
+ If Session.Client.ReadChar in [#27, '[', '0'..'9', ';', 'R'] Then Begin
+ Session.io.Graphics := 1;
+ Break;
+ End;
+ End;
+
+ Session.Client.PurgeInputData;
+ End;
+
+ Session.io.OutFullLn (Session.GetPrompt(259));
+End;
+{$ENDIF}
+
+Procedure TBBSUser.GetGraphics;
+Begin
+ Session.io.OutFull (Session.GetPrompt(154));
+ Session.io.Graphics := strS2I(Session.io.OneKey('01', True));
+End;
+
+Procedure TBBSUser.GetEmail (Edit : Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(440))
+ Else
+ Session.io.OutFull (Session.GetPrompt(439));
+
+ ThisUser.EMail := Session.io.GetInput(35, 35, 11, ThisUser.Email);
+End;
+
+Procedure TBBSUser.GetUserNote (Edit : Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(442))
+ Else
+ Session.io.OutFull (Session.GetPrompt(441));
+
+ ThisUser.UserInfo := Session.io.GetInput(30, 30, 11, ThisUser.UserInfo);
+End;
+
+Procedure TBBSUser.GetOption1 (Edit : Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(444))
+ Else
+ Session.io.OutFull (Session.GetPrompt(443));
+
+ ThisUser.Optional[1] := Session.io.GetInput(35, 35, 11, ThisUser.Optional[1]);
+End;
+
+Procedure TBBSUser.GetOption2 (Edit : Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(446))
+ Else
+ Session.io.OutFull (Session.GetPrompt(445));
+
+ ThisUser.Optional[2] := Session.io.GetInput(35, 35, 11, ThisUser.Optional[2]);
+End;
+
+Procedure TBBSUser.GetOption3 (Edit : Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(448))
+ Else
+ Session.io.OutFull (Session.GetPrompt(447));
+
+ ThisUser.Optional[3] := Session.io.GetInput(35, 35, 11, ThisUser.Optional[3]);
+End;
+
+Procedure TBBSUser.GetEditor (Edit : Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(373))
+ Else
+ Session.io.OutFull (Session.GetPrompt(303));
+
+ ThisUser.EditType := strS2I(Session.io.OneKey('012', True));
+End;
+
+Function TBBSUser.Check_Trash (Name: String) : Boolean;
+Var
+ tFile : Text;
+ Str : String[30];
+Begin
+ Result := False;
+ Name := strUpper(Name);
+
+ Assign (tFile, Config.DataPath + 'trashcan.dat');
+ {$I-} Reset (tFile); {$I+}
+
+ If IoResult <> 0 Then Exit;
+
+ While Not Eof(tFile) Do Begin
+ ReadLn (tFile, Str);
+ If strUpper(Str) = Name Then Begin
+ Result := True;
+ Session.io.OutFullLn (Session.GetPrompt(309));
+ Break;
+ End;
+ End;
+ Close (tFile);
+End;
+
+Procedure TBBSUser.GetRealName (Edit: Boolean);
+Var
+ Str : String[30];
+Begin
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(6));
+ Str := strStripB(Session.io.GetInput(30, 30, 18, ''), ' ');
+ If Pos(' ', Str) = 0 Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(7));
+ Str := '';
+ End Else
+ If Check_Trash(Str) Then
+ Str := ''
+ Else
+ If FindUser(Str, False) Then Begin
+ If Edit and (Str = ThisUser.RealName) Then Break;
+ Session.io.OutFullLn (Session.GetPrompt(8));
+ Str := '';
+ End;
+ Until Str <> '';
+
+ ThisUser.RealName := Str;
+End;
+
+Procedure TBBSUser.GetAlias (Edit: Boolean; Def: String);
+Var
+ Str : String[30];
+Begin
+ Repeat
+ Session.io.OutFull (Session.GetPrompt(9));
+ Str := strStripB(Session.io.GetInput(30, 30, 18, Def), ' ');
+ If Check_Trash(Str) Then
+ Str := ''
+ Else
+ If FindUser(Str, False) Then Begin
+ If Edit and (Str = ThisUser.Handle) Then Break;
+ Session.io.OutFullLn (Session.GetPrompt(8));
+ Str := '';
+ End;
+ Until Str <> '';
+
+ ThisUser.Handle := Str;
+End;
+
+Procedure TBBSUser.GetAddress (Edit: Boolean);
+Var
+ Str: String[30];
+Begin
+ If Edit Then Str := ThisUser.Address Else Str := '';
+
+ Repeat
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(364))
+ Else
+ Session.io.OutFull (Session.GetPrompt(10));
+ Str := Session.io.GetInput(30, 30, 18, Str);
+ Until Str <> '';
+
+ ThisUser.Address := Str;
+End;
+
+Procedure TBBSUser.GetCityState (Edit: Boolean);
+Var
+ Str : String[25];
+Begin
+ If Edit Then Str := ThisUser.City Else Str := '';
+
+ Repeat
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(365))
+ Else
+ Session.io.OutFull (Session.GetPrompt(11));
+ Str := Session.io.GetInput(25, 25, 18, Str);
+ Until Str <> '';
+
+ ThisUser.City := Str;
+End;
+
+Procedure TBBSUser.GetZipCode (Edit: Boolean);
+Var
+ Str : String[9];
+Begin
+ If Edit Then Str := ThisUser.ZipCode Else Str := '';
+
+ Repeat
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(366))
+ Else
+ Session.io.OutFull (Session.GetPrompt(12));
+ Str := Session.io.GetInput(9, 9, 12, Str);
+ Until Str <> '';
+
+ ThisUser.ZipCode := Str;
+End;
+
+Procedure TBBSUser.GetHomePhone (Edit: Boolean);
+Var
+ Str : String[15];
+Begin
+ If Edit Then Str := ThisUser.HomePhone Else Str := '';
+
+ Repeat
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(367))
+ Else
+ Session.io.OutFull (Session.GetPrompt(13));
+ If Config.UseUSAPhone Then
+ Str := Session.io.GetInput(12, 12, 14, Str)
+ Else
+ Str := Session.io.GetInput(15, 15, 12, Str);
+ Until (Length(Str) = 12) or (Not Config.UseUSAPhone and (Str <> ''));
+ ThisUser.HomePhone := Str;
+End;
+
+Procedure TBBSUser.GetDataPhone (Edit: Boolean);
+Var
+ Str : String[15];
+Begin
+ If Edit Then Str := ThisUser.DataPhone Else Str := '';
+
+ Repeat
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(368))
+ Else
+ Session.io.OutFull (Session.GetPrompt(14));
+ If Config.UseUSAPhone Then
+ Str := Session.io.GetInput(12, 12, 14, Str)
+ Else
+ Str := Session.io.GetInput(15, 15, 12, Str);
+ Until (Length(Str) = 12) or (Not Config.UseUSAPhone and (Str <> ''));
+ ThisUser.DataPhone := Str;
+End;
+
+Procedure TBBSUser.GetBirthDate (Edit: Boolean);
+Var
+ Str : String[8];
+Begin
+ If Edit Then Str := DateJulian2Str(ThisUser.Birthday, ThisUser.DateType) Else Str := '';
+ Repeat
+ If Edit Then
+ Session.io.OutFull(Session.GetPrompt(369))
+ Else
+ Session.io.OutFull (Session.GetPrompt(15));
+ Str := Session.io.GetInput(8, 8, 15, '');
+ Until Length(Str) = 8;
+ ThisUser.Birthday := DateStr2Julian(Str);
+End;
+
+Procedure TBBSUser.GetGender (Edit: Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(370))
+ Else
+ Session.io.OutFull (Session.GetPrompt(16));
+
+ ThisUser.Gender := Session.io.OneKey('MF', True);
+End;
+
+Procedure TBBSUser.GetDateFormat (Edit : Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(371))
+ Else
+ Session.io.OutFull (Session.GetPrompt(152));
+
+ ThisUser.DateType := strS2I(Session.io.OneKey('123', True));
+End;
+
+Procedure TBBSUser.GetHotKeys (Edit: Boolean);
+Begin
+ If Edit Then
+ ThisUser.HotKeys := Session.io.GetYN(Session.GetPrompt(409), True)
+ Else
+ ThisUser.HotKeys := Session.io.GetYN(Session.GetPrompt(410), True);
+End;
+
+Procedure TBBSUser.GetMsgList (Edit: Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(397))
+ Else
+ Session.io.OutFull (Session.GetPrompt(398));
+
+ ThisUser.MReadType := strS2I(Session.io.OneKey('01', True));
+End;
+
+Procedure TBBSUser.GetFileList (Edit: Boolean);
+Begin
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(374))
+ Else
+ Session.io.OutFull (Session.GetPrompt(320));
+
+ ThisUser.FileList := strS2I(Session.io.OneKey('01', True));
+End;
+
+Procedure TBBSUser.GetScreenLength (Edit: Boolean);
+Var
+ A : Byte;
+Begin
+ Session.io.PromptInfo[1] := strI2S(Config.DefScreenSize);
+
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(372))
+ Else
+ Session.io.OutFull (Session.GetPrompt(153));
+
+ A := strS2I(Session.io.GetInput(2, 2, 12, strI2S(Config.DefScreenSize)));
+
+ If (A < 1) or (A > 255) Then A := Config.DefScreenSize;
+
+ ThisUser.ScreenSize := A;
+End;
+
+Procedure TBBSUser.GetPassword (Edit: Boolean);
+Var
+ Str1 : String[15];
+ Str2 : String[15];
+Begin
+ If Edit Then Begin
+ Session.io.OutFull(Session.GetPrompt(151));
+ Str1 := Session.io.GetInput(15, 15, 16, '');
+ If Str1 <> ThisUser.Password Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(418));
+ Exit;
+ End;
+ End;
+
+ Repeat
+ Repeat
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(419))
+ Else
+ Session.io.OutFull (Session.GetPrompt(17));
+
+ Str1 := Session.io.GetInput(15, 15, 16, '');
+
+ If Length(Str1) < 4 Then
+ If Edit Then
+ Session.io.OutFullLn (Session.GetPrompt(420))
+ Else
+ Session.io.OutFullLn (Session.GetPrompt(18));
+ Until Length(Str1) >= 4;
+
+ If Edit Then
+ Session.io.OutFull (Session.GetPrompt(421))
+ Else
+ Session.io.OutFull (Session.GetPrompt(19));
+
+ Str2 := Session.io.GetInput(15, 15, 16, '');
+
+ If Str1 <> Str2 Then
+ If Edit Then
+ Session.io.OutFullLn (Session.GetPrompt(418))
+ Else
+ Session.io.OutFullLn (Session.GetPrompt(20));
+ Until (Str1 = Str2) or (Edit);
+
+ If Str1 = Str2 Then Begin
+ ThisUser.Password := Str1;
+ ThisUser.LastPWChange := DateDos2Str(CurDateDos, 1);
+ End;
+End;
+
+Procedure TBBSUser.GetLanguage;
+Var
+ Old : LangRec;
+ T : Byte;
+ A : Byte;
+Begin
+ T := 0;
+ Old := Session.Lang;
+
+ Session.io.OutFullLn (Session.GetPrompt(182));
+
+ Reset (Session.LangFile);
+ Repeat
+ Read (Session.LangFile, Session.Lang);
+ If ((Not Session.Lang.okASCII) and (Session.io.Graphics = 0)) or
+ ((Not Session.Lang.okANSI) and (Session.io.Graphics = 1)) Then Continue;
+ Inc (T);
+ Session.io.PromptInfo[1] := strI2S(T);
+ Session.io.PromptInfo[2] := Session.Lang.Desc;
+ Session.io.OutFullLn (Session.GetPrompt(183));
+ Until Eof(Session.LangFile);
+
+ { Lang := Old; }
+
+ Session.io.OutFull (Session.GetPrompt(184));
+ A := strS2I(Session.io.GetInput(2, 2, 12, ''));
+
+ {force user to select a language}
+ If (A < 1) or (A > T) Then A := 1;
+
+ T := 0;
+ Reset (Session.LangFile);
+ Repeat
+ Read (Session.LangFile, Session.Lang);
+ If ((Not Session.Lang.okASCII) and (Session.io.Graphics = 0)) or
+ ((Not Session.Lang.okANSI) and (Session.io.Graphics = 1)) Then Continue;
+ Inc (T);
+ Until T = A;
+{ Close (Session.LangFile);}
+
+ If Not Session.LoadThemeData(Session.Lang.FileName) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(185));
+ Session.Lang := Old;
+ End Else
+ ThisUser.Theme := Session.Lang.FileName;
+End;
+
+Procedure TBBSUser.CreateNewUser (DefName: String);
+Const
+ ExecMPE : Boolean = False; {placeholder for apply.mpx dev}
+Begin
+ If Not Config.AllowNewUsers Then Begin
+ Session.io.OutFile ('nonewusr', True, 0);
+ Halt(0);
+ End;
+
+ If Config.NewUserPW <> '' Then
+ If Not Session.io.GetPW(Session.GetPrompt(5), Session.GetPrompt(422), Config.NewUserPW) Then Halt(0);
+
+ Session.SystemLog ('NEW USER');
+
+ { make user intialize data function }
+
+ If ExecMPE Then Begin { replace this with apple.mpx once the above is done }
+(*
+ If User.ThisUser.RealName = '' Then User.ThisUser.RealName := User.ThisUser.Handle;
+ If User.ThisUser.Handle = '' Then User.ThisUser.Handle := User.ThisUser.RealName;
+
+ If { Test validity of user data }
+ IsUser(User.ThisUser.RealName) or
+ IsUser(User.ThisUser.Handle) or
+ (User.ThisUser.Password = '') or
+ (User.ThisUser.RealName = '') or
+ (User.ThisUser.Handle = '')
+ Then Begin
+ Sysop_Log('"apply.mpx" does not set minimum data elements');
+ Halt(1);
+ End;
+*)
+ End Else Begin
+ Session.io.OutFile ('newuser1', True, 0);
+
+ If strUpper(DefName) = 'NEW' Then DefName := '';
+
+ With Config Do Begin
+ If AskTheme Then GetLanguage Else ThisUser.Theme := DefThemeFile;
+ If AskAlias Then GetAlias(False, DefName);
+ If AskRealName Then GetRealName(False);
+ If AskStreet Then GetAddress(False);
+ If AskCityState Then GetCityState(False);
+ If AskZipCode Then GetZipCode(False);
+ If AskHomePhone Then GetHomePhone(False);
+ If AskDataPhone Then GetDataPhone(False);
+ If AskGender Then GetGender(False);
+ If UserDateType = 4 Then GetDateFormat(False) Else ThisUser.DateType := UserDateType;
+ If AskBirthdate Then GetBirthdate(False);
+ If AskEmail Then GetEmail(False);
+ If AskUserNote Then GetUserNote(False);
+ If OptionalField[1].Ask Then GetOption1(False);
+ If OptionalField[2].Ask Then GetOption2(False);
+ If OptionalField[3].Ask Then GetOption3(False);
+ If UserEditorType = 2 Then GetEditor(False) Else ThisUser.EditType := UserEditorType;
+
+ If UserQuoteWin = 2 Then
+ ThisUser.UseLBQuote := Session.io.GetYN(Session.GetPrompt(60), False)
+ Else
+ ThisUser.UseLBQuote := Boolean(UserQuoteWin);
+
+ If UserFileList = 2 Then GetFileList(False) Else ThisUser.FileList := UserFileList;
+ If UserReadType = 2 Then GetMsgList(False) Else ThisUser.MReadType := UserReadType;
+
+ If UserReadIndex = 2 Then
+ ThisUser.UseLBIndex := Session.io.GetYN(Session.GetPrompt(429), False)
+ Else
+ ThisUser.UseLBIndex := Boolean(UserReadIndex);
+
+ If UserMailIndex = 2 Then
+ ThisUser.UseLBMIdx := Session.io.GetYN(Session.GetPrompt(331), False)
+ Else
+ ThisUser.UseLBMIdx := Boolean(UserMailIndex);
+
+ If UserFullChat = 2 Then
+ ThisUser.UseFullChat := Session.io.GetYN(Session.GetPrompt(187), True)
+ Else
+ ThisUser.UseFullChat := Boolean(UserFullChat);
+
+ If UserHotKeys = 2 Then GetHotKeys(False) Else ThisUser.HotKeys := Boolean(UserHotKeys);
+ End;
+
+ If Config.AskScreenSize Then
+ GetScreenLength(False)
+ Else
+ ThisUser.ScreenSize := Config.DefScreenSize;
+
+ GetPassword(False);
+ End;
+
+ Upgrade_User_Level (True, ThisUser, Config.NewUserSec);
+
+ ThisUser.FirstOn := CurDateDos;
+ ThisUser.Archive := Config.qwkArchive;
+ ThisUser.LastFBase := 0;
+ ThisUser.LastFGroup := Config.StartFGroup;
+ ThisUser.LastMGroup := Config.StartMGroup;
+ ThisUser.LastMBase := 0;
+ ThisUser.Flags := 0;
+
+ If Not Config.AskRealName Then ThisUser.RealName := ThisUser.Handle;
+ If Not Config.AskAlias Then ThisUser.Handle := ThisUser.RealName;
+ {If either handles or realnames are toggled off, fill the gaps}
+
+ Session.Menu.MenuName := 'newinfo';
+
+ Session.Menu.ExecuteMenu (False, False, False);
+
+ Session.io.OutFullLn (Session.GetPrompt(21));
+
+ Reset (UserFile);
+ UserNum := Succ(FileSize(UserFile));
+
+ Inc (Config.UserIdxPos);
+ ThisUser.PermIdx := Config.UserIdxPos;
+
+ Seek (UserFile, UserNum - 1);
+ Write (UserFile, ThisUser);
+ Close (UserFile);
+
+ Reset (ConfigFile);
+ Write (ConfigFile, Config);
+ Close (ConfigFile);
+
+ Session.SystemLog ('Created Account: ' + ThisUser.Handle);
+
+ If Config.NewUserEmail Then Begin
+ Session.io.OutFile('feedback', True, 0);
+ If Session.Menu.ExecuteCommand ('MW', '/TO:' + strReplace(Config.FeedbackTo, ' ', '_') + ' /SUBJ:New_User_Feedback /F') Then;
+ End;
+
+ If FileExist(Config.ScriptPath + 'newuser.mpx') Then
+ ExecuteMPL(NIL, 'newuser');
+
+ If FileExist(Config.DataPath + 'newletter.txt') Then
+ Session.Msgs.PostTextFile('newletter.txt;0;' + Config.SysopName + ';' + ThisUser.Handle + ';Welcome', True);
+
+ If FileExist(Config.DataPath + 'sysletter.txt') Then
+ Session.Msgs.PostTextFile('sysletter.txt;0;' + Config.SysopName + ';' + Config.SysopName + ';New account created', True);
+End;
+
+Procedure TBBSUser.User_Logon3;
+Var
+ A : Byte;
+ Ch : Char;
+Begin
+ {$IFDEF LOGGING} Session.SystemLog('Logon3'); {$ENDIF}
+
+ Chat.Available := True;
+
+ If Access(Config.AcsInvisLogin) Then
+ Chat.Invisible := Session.io.GetYN(Session.GetPrompt(308), False);
+
+{ update last caller information }
+
+ If Not Session.LocalMode And Not Chat.Invisible And (ThisUser.Flags AND UserNoCaller = 0) Then Begin
+ Reset (LastOnFile);
+
+ If FileSize(LastOnFile) >= 10 Then
+ KillRecord (LastOnFile, 1, SizeOf(LastOnRec));
+
+ LastOn.Handle := ThisUser.Handle;
+ LastOn.City := ThisUser.City;
+ LastOn.Node := Session.NodeNum;
+ LastOn.DateTime := CurDateDos;
+ LastOn.CallNum := Config.SystemCalls;
+ LastOn.Address := ThisUser.Address;
+ LastOn.EmailAddr := ThisUser.Email;
+ LastOn.UserInfo := ThisUser.UserInfo;
+ LastOn.Option1 := ThisUser.Optional[1];
+ LastOn.Option2 := ThisUser.Optional[2];
+ LastOn.Option3 := ThisUser.Optional[3];
+
+ If Session.LocalMode Then
+ LastOn.Baud := 'LOCAL'
+ Else
+ LastOn.Baud := 'TELNET';
+
+ Seek (LastOnFile, FileSize(LastOnFile));
+ Write (LastOnFile, LastOn);
+ Close (LastOnFile);
+ End;
+
+{ update node info / settings }
+
+ Set_Node_Action(Session.GetPrompt(345));
+
+{ this (below) causes runtime 201 when range checking is ON }
+
+ For A := 1 to 9 Do
+ Session.io.OutFile ('logon' + strI2S(A), True, 0);
+
+ Session.io.OutFile ('sl' + strI2S(ThisUser.Security), True, 0);
+
+ For Ch := 'A' to 'Z' Do
+ If Ord(Ch) - 64 in ThisUser.AF1 Then Session.io.OutFile ('flag1' + Ch, True, 0);
+
+ For Ch := 'A' to 'Z' Do
+ If Ord(Ch) - 64 in ThisUser.AF2 Then Session.io.OutFile ('flag2' + Ch, True, 0);
+
+ If DateDos2Str(CurDateDos, 1) = DateJulian2Str(ThisUser.Birthday, 1) Then Session.io.OutFile ('birthday', True, 0);
+
+ { Check for forced voting questions }
+
+ Reset (VoteFile);
+ While Not Eof(VoteFile) Do Begin
+ Read (VoteFile, Vote);
+ If Access(Vote.ACS) and Access(Vote.ForceACS) and (ThisUser.Vote[FilePos(VoteFile)] = 0) Then Begin
+ A := FilePos(VoteFile);
+ Close (VoteFile);
+ Voting_Booth (True, A);
+ Reset (VoteFile);
+ Seek (VoteFile, A);
+ End;
+ End;
+ Close (VoteFile);
+
+ { END forced voting check }
+End;
+
+Procedure TBBSUser.User_Logon2;
+Begin
+ {$IFDEF LOGGING} Session.SystemLog('Logon2'); {$ENDIF}
+
+ Reset (SecurityFile);
+ Seek (SecurityFile, Pred(ThisUser.Security));
+ Read (SecurityFile, Security);
+ Close (SecurityFile);
+
+ If DateDos2Str(ThisUser.LastOn, 1) <> DateDos2Str(CurDateDos, 1) Then Begin
+ ThisUser.CallsToday := 0;
+ ThisUser.DLsToday := 0;
+ ThisUser.DLkToday := 0;
+ ThisUser.TimeLeft := Security.Time
+ End;
+
+ If Not Session.LocalMode And (ThisUser.Flags AND UserNoCaller = 0) Then Begin
+ Reset (ConfigFile);
+ Read (ConfigFile, Config);
+ Inc (Config.SystemCalls);
+
+ Reset (ConfigFile);
+ Write (ConfigFile, Config);
+ Close (ConfigFile);
+ End;
+
+ Inc (ThisUser.Calls);
+ Inc (ThisUser.CallsToday);
+
+ If (Not Access(Config.AcsMultiLogin)) and (Is_User_Online(ThisUser.Handle) <> 0) Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(426));
+ Halt(0);
+ End;
+
+ If ThisUser.Flags And UserLockedOut <> 0 Then Begin
+ Session.io.OutFull (Session.GetPrompt(129));
+ Session.SystemLog ('User has been locked out');
+ Halt(0);
+ End;
+
+ If (ThisUser.CallsToday >= Security.MaxCalls) and (Security.MaxCalls > 0) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(157));
+ Halt(0);
+ End;
+
+ {Find last message/file base and group}
+
+ If ThisUser.LastMGroup > 0 Then
+ Session.Msgs.MessageGroupChange (strI2S(ThisUser.LastMGroup), False, False)
+ Else
+ Session.Msgs.MessageGroupChange ('+', True, False);
+
+ If ThisUser.LastMBase > 0 Then
+ Session.Msgs.ChangeArea(strI2S(ThisUser.LastMBase - 1))
+ Else
+ Session.Msgs.ChangeArea('+');
+
+ If ThisUser.LastFGroup > 0 Then
+ Session.FileBase.FileGroupChange(strI2S(ThisUser.LastFGroup), False, False)
+ Else
+ Session.FileBase.FileGroupChange('+', True, False);
+
+ If ThisUser.LastFBase > 0 Then
+ Session.FileBase.ChangeFileArea(strI2S(ThisUser.LastFBase))
+ Else
+ Session.FileBase.ChangeFileArea('+');
+
+ If (Session.TimeOffset = 0) or (Session.TimeOffset > ThisUser.TimeLeft) Then
+ Session.SetTimeLeft (ThisUser.TimeLeft);
+
+ // check account expired -- DONE
+ // check password change
+ // check auto-upgrades posts/calls/downloads/uploads/etc
+
+ If DateValid(Session.User.ThisUser.Expires) Then
+ If CurDateJulian - DateStr2Julian(Session.User.ThisUser.Expires) >= 0 Then Begin
+ Session.SystemLog('Account expired to level ' + strI2S(Session.User.ThisUser.ExpiresTo));
+
+ Upgrade_User_Level(True, Session.User.ThisUser, Session.User.ThisUser.ExpiresTo);
+
+ If Session.User.ThisUser.Security = 0 Then Begin
+ Session.io.OutFullLn(Session.GetPrompt(477));
+ Session.User.ThisUser.Flags := Session.User.ThisUser.Flags AND UserDeleted;
+ Exit;
+ End Else
+ Session.io.OutFullLn(Session.GetPrompt(476));
+ End;
+
+ If (Config.PWChange > 0) and (Session.User.ThisUser.Flags AND UserNoPWChange = 0) Then
+ If Not DateValid(Session.User.ThisUser.LastPWChange) Then
+ Session.User.ThisUser.LastPWChange := DateDos2Str(CurDateDos, 1)
+ Else
+ If CurDateJulian - DateStr2Julian(Session.User.ThisUser.LastPWChange) >= 0 Then Begin
+ Session.SystemLog('Required password change');
+ Session.io.OutFullLn(Session.GetPrompt(478));
+ Session.User.GetPassword(False);
+ End;
+
+ {$IFNDEF UNIX}
+ Update_Status_Line(StatusPtr, '');
+ {$ENDIF}
+End;
+
+Procedure TBBSUser.User_Logon (Var UN, PW, MPE : String);
+Var
+ A : Integer;
+ Count : Byte;
+Begin
+ {$IFDEF LOGGING} Session.SystemLog('Logon1'); {$ENDIF}
+
+ Set_Node_Action (Session.GetPrompt(345));
+
+ Session.io.Graphics := 0;
+
+ Session.SystemLog ('Connect from ' + Session.UserIPInfo + ' (' + Session.UserHostInfo + ')');
+
+ If Config.SystemPW <> '' Then
+ If Not Session.io.GetPW(Session.GetPrompt(4), Session.GetPrompt(417), Config.SystemPW) Then Begin
+ Session.io.OutFile ('closed', True, 0);
+ Session.SystemLog('Failed system password');
+ Halt(0);
+ End;
+
+ Session.io.OutFullLn ('|CL' + mysSoftwareID + ' BBS v' + mysVersion + ' (' + OSID + ') Node |ND (|BD)');
+ Session.io.OutFullLn (CopyID);
+
+ If Config.DefTermMode = 0 Then
+ GetGraphics
+ Else
+ If Config.DefTermMode = 3 Then
+ Session.io.Graphics := 1
+ Else Begin
+ DetectGraphics;
+ If (Session.io.Graphics = 0) and (Config.DefTermMode = 2) Then GetGraphics;
+ End;
+
+ If FileExist(Config.ScriptPath + 'startup.mpx') Then
+ ExecuteMPL(NIL, 'startup');
+
+ If Not Session.Lang.okASCII and (Session.io.Graphics = 0) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(321));
+ Session.SystemLog ('ASCII login disabled');
+ Halt(0);
+ End Else
+ If Not Session.Lang.okANSI and (Session.io.Graphics = 1) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(322));
+ Session.SystemLog ('ANSI login disabled');
+ Halt(0);
+ End;
+
+ If UN <> '' Then Begin
+ If Not FindUser(UN, True) Then
+ Halt;
+
+ If strUpper(PW) <> TempUser.Password Then Begin
+ UserNum := -1;
+ Halt;
+ End;
+ End Else Begin
+ If Config.UseMatrix Then Begin
+ Repeat
+ Session.Menu.MenuName := Config.MatrixMenu;
+ Session.Menu.ExecuteMenu (True, False, False);
+ Until MatrixOK or Session.ShutDown;
+ End;
+
+ Session.io.OutFile ('prelogon', True, 0);
+
+ Count := 0;
+
+ Repeat
+ If Count = Config.LoginAttempts Then Halt;
+
+ Session.io.OutFull (Session.GetPrompt(0));
+ ThisUser.Handle := strStripB(Session.io.GetInput(30, 30, 18, ''), ' ');
+
+ If Not FindUser(ThisUser.Handle, True) Then Begin
+ Session.io.OutFile ('newuser', True, 0);
+
+ If Session.io.GetYN(Session.GetPrompt(1), False) Then Begin
+ CreateNewUser(ThisUser.Handle);
+ User_Logon2;
+ User_Logon3;
+ Exit;
+ End;
+
+ Inc (Count);
+ End Else Break;
+ Until False;
+
+ A := UserNum; {If user would drop carrier here itd save their info }
+ UserNum := -1; {which is only User.ThisUser.realname at this time }
+
+ If Not Session.io.GetPW(Session.GetPrompt(2), Session.GetPrompt(3), TempUser.Password) Then Begin
+ If Config.PWInquiry Then
+ If Session.io.GetYN(Session.GetPrompt(475), False) Then
+ Session.Msgs.PostMessage(True, '/TO:' + strReplace(Config.FeedbackTo, ' ', '_') + ' /SUBJ:Password_Inquiry');
+
+ Session.Msgs.PostTextFile('hackwarn.txt;0;' + Config.SysopName + ';' + TempUser.Handle + ';Possible hack attempt', True);
+
+ Halt(0);
+ End;
+
+ UserNum := A;
+ End;
+
+ ThisUser := TempUser;
+
+ Session.SystemLog ('User: ' + ThisUser.Handle + ' logged in');
+
+ If Not Session.LoadThemeData(ThisUser.Theme) Then Begin
+ Session.io.OutFullLn (Session.GetPrompt(186));
+ If Session.LoadThemeData(Config.DefThemeFile) Then
+ ThisUser.Theme := Config.DefThemeFile;
+ End;
+
+ User_Logon2;
+
+ If MPE <> '' Then Begin
+ ExecuteMPL(NIL, MPE);
+ Halt;
+ End Else
+ User_Logon3;
+End;
+
+Procedure TBBSUser.Edit_User_Settings (What: Byte);
+Begin
+ Case What of
+ 1 : GetAddress(True);
+ 2 : GetCityState(True);
+ 3 : GetZipCode(True);
+ 4 : GetHomePhone(True);
+ 5 : GetDataPhone(True);
+ 6 : GetBirthDate(True);
+ 7 : GetGender(True);
+ 8 : GetDateFormat(True);
+ 9 : Repeat
+ GetGraphics;
+ If (Not Session.Lang.okASCII and (Session.io.Graphics = 0)) or (Not Session.Lang.okANSI and (Session.io.Graphics = 1)) Then
+ Session.io.OutFullLn (Session.GetPrompt(325))
+ Else
+ Break;
+ Until False;
+ 10 : GetScreenLength(True);
+ 11 : GetPassword(True);
+ 12 : GetRealName(True);
+ 13 : GetAlias(True, '');
+ 14 : GetLanguage;
+ 15 : GetEditor(True);
+ 16 : If Access(Config.AcsInvisLogin) Then Begin
+ Chat.Invisible := Not Chat.Invisible;
+ Set_Node_Action (Chat.Action);
+ End;
+ 17 : GetFileList(True);
+ 18 : Chat.Available := Not Chat.Available;
+ 19 : GetHotKeys(True);
+ 20 : GetMsgList(True);
+ 21 : ThisUser.UseLBIndex := Not ThisUser.UseLBIndex;
+ 22 : GetEmail(True);
+ 23 : GetUserNote(True);
+ 24 : GetOption1(True);
+ 25 : GetOption2(True);
+ 26 : GetOption3(True);
+ 27 : ThisUser.UseLBQuote := Not ThisUser.UseLBQuote;
+ 28 : ThisUser.UseLBMIdx := Not ThisUser.UseLBMIdx;
+ 29 : ThisUser.UseFullChat := Not ThisUser.UseFullChat;
+ 30 : ThisUser.QwkFiles := Not ThisUser.QwkFiles;
+ 31 : Session.FileBase.SelectArchive;
+ End;
+End;
+
+End.