diff --git a/mystic/HISTORY.txt b/mystic/HISTORY.txt index b318efd..b0fb13c 100644 --- a/mystic/HISTORY.txt +++ b/mystic/HISTORY.txt @@ -4535,4 +4535,19 @@ + Restructured some class code to reduce executable sizes in the Mystic binary. The result is about 120kb smaller executable size in Windows for - Mystic.exe. + Mystic.exe. The other versions should see similar results. + + ! Mystic was not properly parsing long filenames with spaces in them during + external file transfer. + + ! Mystic was not properly setting the "Transfering files" node status when + executing transfer protocols. + + + Mystic now has internal Zmodem protocol. To enable it, just use the + @ZMODEM text for the send/receive commands in the Protocol editor, and + Mystic will do the rest. The Zmodem has been tested extensively with: + + mTelnet + SyncTerm + SEXYZ + NetRunner diff --git a/mystic/bbs_cfg_protocol.pas b/mystic/bbs_cfg_protocol.pas index bee64bf..89ca71f 100644 --- a/mystic/bbs_cfg_protocol.pas +++ b/mystic/bbs_cfg_protocol.pas @@ -33,7 +33,7 @@ Begin 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.AddTog ('O', ' OS ' , 18, 8, 24, 8, 4, 7, 0, 3, 'Windows Linux OSX All', @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, ''); @@ -71,6 +71,7 @@ Var 0 : OS := 'Windows'; 1 : OS := 'Linux '; 2 : OS := 'OSX'; + 3 : OS := 'All'; End; //'Active OSID Batch Key Description'); diff --git a/mystic/bbs_cfg_useredit.pas b/mystic/bbs_cfg_useredit.pas index c59a37b..fca5c28 100644 --- a/mystic/bbs_cfg_useredit.pas +++ b/mystic/bbs_cfg_useredit.pas @@ -325,6 +325,8 @@ Procedure Configuration_LocalUserEdit; Var SavedLocal : Boolean; Begin + Session.io.BufFlush; + SavedLocal := Session.LocalMode; Session.InUserEdit := True; diff --git a/mystic/bbs_common.pas b/mystic/bbs_common.pas index 84d19b6..81ab108 100644 --- a/mystic/bbs_common.pas +++ b/mystic/bbs_common.pas @@ -5,6 +5,10 @@ Unit bbs_Common; Interface Uses + {$IFDEF WINDOWS} + m_io_Base, + m_io_Sockets, + {$ENDIF} {$IFDEF UNIX} Unix, {$ENDIF} @@ -13,8 +17,7 @@ Uses m_Output, m_Input, m_DateTime, - m_FileIO, - m_Socket_Class; + m_FileIO; {$I RECORDS.PAS} diff --git a/mystic/bbs_core.pas b/mystic/bbs_core.pas index 741c6d7..ff74241 100644 --- a/mystic/bbs_core.pas +++ b/mystic/bbs_core.pas @@ -5,10 +5,13 @@ Unit BBS_Core; Interface Uses + m_io_Base, + {$IFNDEF UNIX} + m_io_Sockets, + {$ENDIF} m_FileIO, m_Strings, m_DateTime, - m_Socket_Class, BBS_Common, BBS_IO, BBS_MsgBase, @@ -22,12 +25,14 @@ Const Type TBBSCore = Class + {$IFNDEF UNIX} + Client : TIOBase; + {$ENDIF} User : TBBSUser; Msgs : TMsgBase; FileBase : TFileBase; Menu : TMenuEngine; IO : TBBSIO; - Client : TSocketClass; EventFile : File of EventRec; ThemeFile : File of RecTheme; VoteFile : File of VoteRec; @@ -122,9 +127,9 @@ Begin InMessage := False; MessageCheck := mysMessageThreshold; - {$IFDEF WINDOWS} - Client := TSocketClass.Create; - Client.FTelnetServer := True; + {$IFNDEF UNIX} + Client := TIOSocket.Create; + TIOSocket(Client).FTelnetServer := True; {$ENDIF} User := TBBSUser.Create(Pointer(Self)); diff --git a/mystic/bbs_doors.pas b/mystic/bbs_doors.pas index 5001f3d..46282f7 100644 --- a/mystic/bbs_doors.pas +++ b/mystic/bbs_doors.pas @@ -11,6 +11,8 @@ Implementation Uses {$IFDEF WINDOWS} Windows, + m_io_Base, + m_io_Sockets, {$ENDIF} m_Types, m_Strings, @@ -218,7 +220,7 @@ Begin PassHandle := 0; If Not Session.LocalMode Then - PassHandle := Session.Client.FSocketHandle; + PassHandle := TIOSocket(Session.Client).FSocketHandle; If Session.User.UserNum <> -1 Then Begin Reset (Session.User.UserFile); @@ -297,7 +299,7 @@ Begin {$IFDEF UNIX} If Cmd[A] = '0' Then Temp := Temp + '1' Else {$ELSE} - If Cmd[A] = '0' Then Temp := Temp + strI2S(Session.Client.FSocketHandle) Else + If Cmd[A] = '0' Then Temp := Temp + strI2S(TIOSocket(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 diff --git a/mystic/bbs_filebase.pas b/mystic/bbs_filebase.pas index 538fe28..d204db3 100644 --- a/mystic/bbs_filebase.pas +++ b/mystic/bbs_filebase.pas @@ -1,19 +1,31 @@ Unit bbs_FileBase; {$I M_OPS.PAS} + {$MODESWITCH NESTEDPROCVARS-} Interface Uses + m_io_Base, + {$IFDEF WINDOWS} + m_io_Sockets, + {$ENDIF} + {$IFDEF UNIX} + m_io_STDIO, + {$ENDIF} DOS, mkCrap, m_Strings, m_FileIO, m_DateTime, + m_Protocol_Queue, + m_Protocol_Base, + m_Protocol_Zmodem, bbs_Common, bbs_General, bbs_NodeInfo, + bbs_Ansi_MenuBox, AView; Type @@ -46,7 +58,7 @@ Type 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); + Procedure ExecuteProtocol (Mode: Byte; FName: String); Function SelectArchive : Boolean; Function ListFileAreas (Compress: Boolean) : Integer; Procedure ChangeFileArea (Data: String); @@ -103,14 +115,11 @@ Begin Inherited Destroy; End; -Procedure TFileBase.dszGetFile (Var LogFile: Text; Var FName: String; Var Res: Boolean); +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 @@ -132,6 +141,7 @@ Begin While WordPos < 11 Do Begin If LineBuf[Count] = #32 Then Begin Inc (WordPos); + Repeat Inc (Count); Until LineBuf[Count] <> #32; @@ -142,14 +152,17 @@ Begin Repeat FName := FName + LineBuf[Count]; Inc (Count); - Until (LineBuf[Count] = #32) or (LineBuf[Count] = #0) or (Count = 1024); + Until (LineBuf[Count] = #0) or (Count = 1024); - FSplit(FName, TempStr1, TempStr2, TempStr3); + While FName[Length(FName)] <> #32 Do + Dec(FName[0]); - FName := TempStr2 + TempStr3; + Dec(FName[0]); + + FName := JustFile(FName); End; -Function TFileBase.dszSearch (FName: String) : Boolean; +Function TFileBase.DszSearch (FName: String) : Boolean; Var LogFile : Text; FileName : String; @@ -159,13 +172,14 @@ Begin 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); + DszGetFile(LogFile, FileName, Status); {$IFDEF FS_SENSITIVE} If FileName = FName Then Begin @@ -173,6 +187,7 @@ Begin If strUpper(FileName) = strUpper(FName) Then Begin {$ENDIF} Result := Status; + Break; End; End; @@ -180,70 +195,214 @@ Begin Close (LogFile); End; -Procedure TFileBase.ExecuteProtocol (Send: Boolean; FName: String); +{$IFNDEF UNIX} +Procedure ProtocolStatus (Start, Finish: Boolean; Status: RecProtocolStatus); Var - T : Text; - Cmd : String; - Count : Byte; - Res : String; - Path : String; + KBRate : LongInt; Begin - If Send Then - Cmd := Protocol.SendCmd - Else - Cmd := Protocol.RecvCmd; + Screen.WriteXY (19, 10, 113, strPadR(Status.FileName, 56, ' ')); + Screen.WriteXY (19, 11, 113, strPadR(strComma(Status.FileSize), 15, ' ')); + Screen.WriteXY (19, 12, 113, strPadR(strComma(Status.Position), 15, ' ')); + Screen.WriteXY (64, 11, 113, strPadR(strI2S(Status.Errors), 3, ' ')); - Res := ''; - Path := ''; - Count := 1; + KBRate := 0; - 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 + If (TimerSeconds - Status.StartTime > 0) and (Status.Position > 0) Then + KBRate := Round((Status.Position / (TimerSeconds - Status.StartTime)) / 1024); + + Screen.WriteXY (64, 12, 113, strPadR(strI2S(KBRate) + ' k/sec', 12, ' ')); +End; +{$ENDIF} + +Procedure TFileBase.ExecuteProtocol (Mode: Byte; FName: String); +// mode: 0=recv batch, 1=recv file, 2=send file, 3= send batch +Var + Command : String; + T : Text; + Res : String; + + {$IFNDEF UNIX} + Box : TAnsiMenuBox; + SavedL : Boolean; + SavedA : Boolean; + {$ENDIF} + + Procedure ExecInternal; + Var + Protocol : TProtocolBase; + Queue : TProtocolQueue; + Count : Word; + Client : TIOBase; + Begin + {$IFDEF UNIX} + Client := TSTDIO.Create; + {$ELSE} + Client := Session.Client; + {$ENDIF} + + Command := strStripB(strUpper(Command), ' '); + Queue := TProtocolQueue.Create; + + If Command = '@ZMODEM' Then + Protocol := TProtocolZmodem.Create(Client, Queue) + Else Begin + {$IFDEF UNIX} + Client.Free; {$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]; + Queue.Free; + Exit; + End; - Inc (Count); + Case Mode of + 0, + 1 : Protocol.ReceivePath := DirSlash(FName); + 2 : Queue.Add(JustPath(FName), JustFile(FName)); + 3 : Begin + Assign (T, Session.TempPath + 'file.lst'); + Reset (T); + + While Not Eof(T) Do Begin + ReadLn (T, Res); + + Queue.Add(JustPath(Res), JustFile(Res)); + End; + + Close (T); + End; + End; + + Session.io.BufFlush; + + {$IFNDEF UNIX} + SavedL := Session.LocalMode; + SavedA := Screen.Active; + Session.LocalMode := True; + Protocol.StatusProc := ProtocolStatus; + + Session.io.LocalScreenEnable; + + Box := TAnsiMenuBox.Create; + + Case Mode of + 0..1 : Box.Header := ' ' + Protocol.Status.Protocol + ' Upload '; + 2..3 : Box.Header := ' ' + Protocol.Status.Protocol + ' Download '; + End; + + Box.Open (6, 8, 76, 14); + + Screen.WriteXY ( 8, 10, 112, 'File Name:'); + Screen.WriteXY (13, 11, 112, 'Size:'); + Screen.WriteXY ( 9, 12, 112, 'Position:'); + Screen.WriteXY (56, 11, 112, 'Errors:'); + Screen.WriteXY (58, 12, 112, 'Rate:'); + {$ENDIF} + + Case Mode of + 0..1 : Protocol.QueueReceive; + 2..3 : Protocol.QueueSend; + End; + + {$IFNDEF UNIX} + Box.Free; + + Session.io.BufFlush; + + If Not SavedA Then + Session.io.LocalScreenDisable; + + Session.LocalMode := SavedL; + {$ENDIF} + + Assign (T, Session.TempPath + 'xfer.log'); + ReWrite (T); + + For Count := 1 to Queue.QSize Do Begin + Res[1] := 'E'; + + If Queue.QData[Count]^.Status = QueueSuccess Then Res[1] := 'Z'; + + WriteLn(T, Res[1] + ' 0 0 0 0 0 0 0 0 0 ' + Queue.QData[Count]^.FileName + ' -1'); + End; + + Close (T); + + Protocol.Free; + Queue.Free; + {$IFDEF UNIX} + Client.Free; + {$ENDIF} 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} + Procedure ExecExternal; + Var + Path : String; + Count : Byte; + Begin + Res := ''; + Path := ''; + Count := 1; - { If uploading and batch, switch to upload directory via shelldos } - If Not Send And Protocol.Batch Then Path := FName; + While Count <= Length(Command) Do Begin + If Command[Count] = '%' Then Begin + Inc(Count); + {$IFNDEF UNIX} + If Command[Count] = '0' Then Res := Res + strI2S(TIOSocket(Session.Client).FSocketHandle) Else + {$ENDIF} + If Command[Count] = '1' Then Res := Res + '1' Else + If Command[Count] = '2' Then Res := Res + strI2S(Session.Baud) Else + If Command[Count] = '3' Then Res := Res + FName Else + If Command[Count] = '4' Then Res := Res + Session.UserIPInfo Else + If Command[Count] = '5' Then Res := Res + Session.UserHostInfo Else + If Command[Count] = '6' Then Res := Res + strReplace(Session.User.ThisUser.Handle, ' ', '_') Else + If Command[Count] = '7' Then Res := Res + strI2S(Session.NodeNum); + End Else + Res := Res + Command[Count]; - 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} + Inc (Count); + End; - DirChange (Config.SystemPath); + {$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 (Mode < 2) 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} + + DirChange (Config.SystemPath); + End; + +Begin + Set_Node_Action(Session.GetPrompt(351)); + + If Mode > 1 Then + Command := Protocol.SendCmd + Else + Command := Protocol.RecvCmd; + + If Command[1] = '@' Then + ExecInternal + Else + ExecExternal; End; Procedure TFileBase.GetTransferTime (Size: Longint; Var Mins : Integer; Var Secs: Byte); @@ -324,6 +483,7 @@ Begin 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; @@ -408,7 +568,7 @@ Begin If SelectProtocol(True, False) = 'Q' Then Exit; - ExecuteProtocol(True, Data); + ExecuteProtocol(2, Data); Session.io.OutRawLn (''); @@ -511,6 +671,7 @@ Begin 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); @@ -557,7 +718,7 @@ Begin Session.io.OutFullLn (Session.GetPrompt(225)); - Result := (TotalFiles = 0); + Result := (TotalFiles > 0); If Not Result Then Session.io.OutFullLn(Session.GetPrompt(425)); End; @@ -947,7 +1108,7 @@ Function TFileBase.SelectProtocol (UseDefault, Batch: Boolean) : Char; While Not Eof(ProtocolFile) Do Begin Read (ProtocolFile, Protocol); - If ((Protocol.Active) And (Key = Protocol.Key) And (Protocol.Batch = Batch) And (Protocol.OSType = OSType)) Then Begin + If ((Protocol.Active) And (Key = Protocol.Key) And (Protocol.Batch = Batch) And ((Protocol.OSType = OSType) or (Protocol.OSType = 3))) Then Begin Result := True; Break; End; @@ -1528,6 +1689,7 @@ Begin If Total = 0 Then Begin Session.io.OutFullLn (Session.GetPrompt(37)); + FBase := Old; End Else Begin Repeat @@ -2580,7 +2742,7 @@ Begin Exit; End; - ExecuteProtocol(False, FBase.Path); + ExecuteProtocol(0, FBase.Path); { ++lang ADD: update node status to transferring file? } @@ -2649,7 +2811,7 @@ Begin {$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 + If Config.TestCmdLine[A] = '0' Then Temp := Temp + strI2S(TIOSocket(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 @@ -2893,7 +3055,7 @@ Begin Close (FBaseFile); Close (FL); - ExecuteProtocol(True, Session.TempPath + 'file.lst'); + ExecuteProtocol(3, Session.TempPath + 'file.lst'); Reset (FBaseFile); diff --git a/mystic/bbs_general.pas b/mystic/bbs_general.pas index e9608bc..5d79b49 100644 --- a/mystic/bbs_general.pas +++ b/mystic/bbs_general.pas @@ -206,7 +206,7 @@ Begin Close (TF); End; -Function SearchBBS (Str : String; Temp : BBSListRec) : Boolean; +Function SearchBBS (Str: String; Temp: BBSListRec) : Boolean; Begin Str := strUpper(Str); diff --git a/mystic/bbs_io.pas b/mystic/bbs_io.pas index dde998a..575fb40 100644 --- a/mystic/bbs_io.pas +++ b/mystic/bbs_io.pas @@ -8,6 +8,8 @@ Uses {$IFDEF WINDOWS} Windows, WinSock2, + m_io_Base, + m_io_Sockets, {$ENDIF} m_Types, m_DateTime, @@ -98,7 +100,7 @@ Type Function DoInputEvents (Var Ch: Char) : Boolean; Function GetKey : Char; Function GetYN (Str: String; Yes: Boolean) : Boolean; - Function GetPW (Str : String; BadStr : String; PW : String) : Boolean; + Function GetPW (Str: String; BadStr: String; PW: String) : Boolean; Function OneKey (Str: String; Echo: Boolean) : Char; Procedure RemoteRestore (Var Image: TConsoleImageRec); Procedure PurgeInputBuffer; @@ -914,7 +916,7 @@ Begin If O Then OutON := 'On' Else OutON := 'Off'; {++lang} End; -Function TBBSIO.OutFile (FName : String; DoPause: Boolean; Speed: Byte) : Boolean; +Function TBBSIO.OutFile (FName: String; DoPause: Boolean; Speed: Byte) : Boolean; Var Buffer : Array[1..4096] of Char; BufPos : LongInt; @@ -943,6 +945,11 @@ Var Ext := '.asc'; FName := Temp; Result := True; + End Else + If FileExist(Temp) Then Begin + Ext := '.' + JustFileExt(FName); + FName := Path + JustFileName(FName); + Result := True; End; End; @@ -968,7 +975,7 @@ Begin Result := False; NoFile := True; - If (Pos(PathSep, FName) > 0) or (Pos('.', FName) > 0) Then Begin + If (Pos(PathSep, FName) > 0) Then Begin If Not FileExist(FName) Then If Not CheckFileInPath('') Then Exit; End Else Begin @@ -979,7 +986,7 @@ Begin Exit; End; - If Pos('.', FName) = 0 Then + If (Pos('.', FName) = 0) Then If FileExist(FName + Copy(Ext, 1, 3) + '1') Then Begin Repeat BufPos := Random(9); @@ -1175,7 +1182,7 @@ Begin Handles[1] := SocketEvent; WSAResetEvent (Handles[1]); - WSAEventSelect (TBBSCore(Core).Client.FSocketHandle, Handles[1], FD_READ OR FD_CLOSE); + WSAEventSelect (TIOSocket(TBBSCore(Core).Client).FSocketHandle, Handles[1], FD_READ OR FD_CLOSE); Case WaitForMultipleObjects(2, @Handles, False, Wait) of WAIT_OBJECT_0 : InType := 1; diff --git a/mystic/bbs_menus.pas b/mystic/bbs_menus.pas index 9ca291d..9219d05 100644 --- a/mystic/bbs_menus.pas +++ b/mystic/bbs_menus.pas @@ -596,6 +596,8 @@ Begin If SpecialKey(Data.Item[Count]^.HotKey) Then Continue; + // check command acs for validkey? + Found := Data.Item[Count]^.HotKey = Temp + UpCase(Ch); If Not ValidKey Then diff --git a/mystic/bbs_msgbase.pas b/mystic/bbs_msgbase.pas index 93024b9..5b4ffd2 100644 --- a/mystic/bbs_msgbase.pas +++ b/mystic/bbs_msgbase.pas @@ -1114,7 +1114,7 @@ Begin If Session.FileBase.SelectProtocol(True, False) = 'Q' Then Exit; - Session.FileBase.ExecuteProtocol(False, FN); + Session.FileBase.ExecuteProtocol(1, FN); OK := Session.FileBase.dszSearch(JustFile(FN)); End; @@ -3276,7 +3276,7 @@ Begin Else Begin If Session.FileBase.SelectProtocol(True, False) = 'Q' Then Exit; - Session.FileBase.ExecuteProtocol(False, Session.TempPath + Config.qwkBBSID + '.rep'); + Session.FileBase.ExecuteProtocol(1, Session.TempPath + Config.qwkBBSID + '.rep'); If Not Session.FileBase.dszSearch(Config.qwkBBSID + '.rep') Then Begin Session.io.PromptInfo[1] := Config.qwkBBSID + '.rep'; diff --git a/mystic/default.txt b/mystic/default.txt index 45d1475..be004eb 100644 --- a/mystic/default.txt +++ b/mystic/default.txt @@ -248,7 +248,7 @@ 132 |01[|10þ|01] |09More: |01(|07Y|01)|09es, |01(|07N|01)|09o, |01(|07C|01)|09ontinueous? 133 |CR|12WARNING: |14System event approaching in |15|NE |14minutes!|CR|PA 134 |CR|12WARNING: You only have |TL minutes remaining! -135 |CR|12Sorry, you have no time left for today.|CR|CR|PA +135 |CR|12Sorry, you have no time left for today.|CR 136 |CR|CR|14Inactivity timeout. Hanging up! 137 |CR|14System Event: Disconnecting. ; Who's online list header @@ -729,9 +729,9 @@ ; View text file filename prompt 384 |CR|09File name|CR: ; Download OK &1 = filename -385 |CR|03- Transfer of |11|&1|03: OK +385 |03- Transfer of |11|&1|03: OK ; Download failed &1 = filename -386 |CR|03- Transfer of |11|&1|03: |12Failed! +386 |03- Transfer of |11|&1|03: |12Failed! ; Mass mail prompt 387 |CL|09Send mass mail by|09:|CR|CR|11(|031|08) |09ACS level|CR|11(|032|08)|09 List of users|CR|11(|033|08)|09 All users|CR|11(|03Q|08)|09 Quit|CR|CRCommand |08-> |07 ; Mass mail ACS prompt diff --git a/mystic/install.pas b/mystic/install.pas index 3ee8244..eb98ce4 100644 --- a/mystic/install.pas +++ b/mystic/install.pas @@ -25,6 +25,7 @@ Program Install; {$MODESWITCH NESTEDPROCVARS-} Uses + m_FileIO, m_Strings, m_Input, m_Output, @@ -119,20 +120,23 @@ Begin IsDir := ((wAttr And Directory) = Directory); End; -Procedure MakeDir (Str: String); +Function MakeDir (Str: String) : Boolean; Var - A : Byte; - CurDIR : String; - Prefix : String; + PathPos : Byte; + CurDIR : String; + Prefix : String; Begin - Prefix := ''; + Result := True; - A := Pos(PathChar, Str); + If DirExists(Str) Then Exit; - While (A > 0) Do Begin - CurDIR := Copy(Str, 1, A); + Prefix := ''; + PathPos := Pos(PathChar, Str); - Delete (Str, 1, A); + While (PathPos > 0) Do Begin + CurDIR := Copy(Str, 1, PathPos); + + Delete (Str, 1, PathPos); Prefix := Prefix + CurDIR; @@ -143,7 +147,7 @@ Begin End; End; - A := Pos(PathChar, Str); + PathPos := Pos(PathChar, Str); End; End; @@ -633,7 +637,7 @@ Begin End; #60 : Begin GetPaths := True; - Break; + Break; End; #72 : If Pos > 1 Then Dec(Pos) Else Pos := 9; #80 : If Pos < 9 Then Inc(Pos) Else Pos := 1; diff --git a/mystic/mis.pas b/mystic/mis.pas index bc5cc74..265d978 100644 --- a/mystic/mis.pas +++ b/mystic/mis.pas @@ -36,7 +36,8 @@ Uses m_Output, m_Input, m_DateTime, - m_Socket_Class, + m_io_Base, + m_io_Sockets, m_FileIO, m_Strings, m_Term_Ansi, @@ -264,16 +265,17 @@ Procedure LocalLogin; Const BufferSize = 1024 * 4; Var - Client : TSocketClass; + Client : TIOSocket; Res : LongInt; Buffer : Array[1..BufferSize] of Char; Done : Boolean; Ch : Char; Begin + Console.TextAttr := 7; Console.ClearScreen; Console.WriteStr ('Connecting to 127.0.0.1... '); - Client := TSocketClass.Create; + Client := TIOSocket.Create; If Not Client.Connect('127.0.0.1', bbsConfig.InetTNPort) Then Console.WriteLine('Unable to connect') @@ -284,7 +286,7 @@ Begin Console.SetWindow (1, 1, 80, 24, True); Console.WriteXY (1, 25, 112, strPadC('Local TELNET: ALT-X to Quit', 80, ' ')); - Term.SetReplyClient(Client); + Term.SetReplyClient(TIOBase(Client)); Repeat If Client.WaitForData(0) > 0 Then Begin diff --git a/mystic/mis_client_ftp.pas b/mystic/mis_client_ftp.pas index e0c1b47..d7a217b 100644 --- a/mystic/mis_client_ftp.pas +++ b/mystic/mis_client_ftp.pas @@ -10,15 +10,16 @@ Interface Uses SysUtils, + m_io_Base, + m_io_Sockets, m_Strings, m_FileIO, - m_Socket_Class, m_DateTime, MIS_Server, MIS_NodeData, MIS_Common; -Function CreateFTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateFTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Type TFTPServer = Class(TServerClient) @@ -33,7 +34,7 @@ Type Data : String; DataPort : Word; DataIP : String; - DataSocket : TSocketClass; + DataSocket : TIOSocket; User : RecUser; UserPos : LongInt; FBasePos : LongInt; @@ -41,7 +42,7 @@ Type SecLevel : RecSecurity; FileMask : String; - Constructor Create (Owner: TServerManager; CliSock: TSocketClass); + Constructor Create (Owner: TServerManager; CliSock: TIOSocket); Procedure Execute; Override; Destructor Destroy; Override; @@ -101,12 +102,12 @@ Const re_DLLimit = '550 Download limit would be exceeded'; re_DLRatio = '550 Download/upload ratio would be exceeded'; -Function CreateFTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateFTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Begin Result := TFTPServer.Create(Owner, CliSock); End; -Constructor TFTPServer.Create (Owner: TServerManager; CliSock: TSocketClass); +Constructor TFTPServer.Create (Owner: TServerManager; CliSock: TIOSocket); Begin Inherited Create(Owner, CliSock); @@ -245,7 +246,7 @@ End; Function TFTPServer.OpenDataSession : Boolean; Var - WaitSock : TSocketClass; + WaitSock : TIOSocket; Begin Result := False; @@ -258,7 +259,7 @@ Begin Client.WriteLine(re_DataOpening); If IsPassive Then Begin - WaitSock := TSocketClass.Create; + WaitSock := TIOSocket.Create; WaitSock.WaitInit(DataPort); @@ -272,7 +273,7 @@ Begin WaitSock.Free; End Else Begin - DataSocket := TSocketClass.Create; + DataSocket := TIOSocket.Create; If Not DataSocket.Connect(DataIP, DataPort) Then Begin Client.WriteLine(re_NoData); @@ -415,7 +416,7 @@ End; Procedure TFTPServer.cmdPASV; Var - WaitSock : TSocketClass; + WaitSock : TIOSocket; Begin If LoggedIn Then Begin DataPort := Random(bbsConfig.inetFTPPortMax - bbsConfig.inetFTPPortMin) + bbsConfig.inetFTPPortMin; @@ -424,7 +425,7 @@ Begin IsPassive := True; - WaitSock := TSocketClass.Create; + WaitSock := TIOSocket.Create; WaitSock.WaitInit(DataPort); @@ -708,7 +709,7 @@ End; Procedure TFTPServer.cmdEPSV; Var - WaitSock : TSocketClass; + WaitSock : TIOSocket; Begin If LoggedIn Then Begin If Data = '' Then Begin @@ -717,7 +718,7 @@ Begin Client.WriteLine('229 Entering Extended Passive Mode (|||' + strI2S(DataPort) + '|)'); - WaitSock := TSocketClass.Create; + WaitSock := TIOSocket.Create; WaitSock.WaitInit(DataPort); diff --git a/mystic/mis_client_nntp.pas b/mystic/mis_client_nntp.pas index de8d119..0f7ad3a 100644 --- a/mystic/mis_client_nntp.pas +++ b/mystic/mis_client_nntp.pas @@ -8,15 +8,16 @@ Interface Uses SysUtils, + m_io_Base, + m_io_Sockets, m_Strings, m_FileIO, - m_Socket_Class, m_DateTime, MIS_Server, MIS_NodeData, MIS_Common; -Function CreateNNTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateNNTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Type TNNTPServer = Class(TServerClient) @@ -32,7 +33,7 @@ Type CurArticle : LongInt; EndSession : Boolean; - Constructor Create (Owner: TServerManager; CliSock: TSocketClass); + Constructor Create (Owner: TServerManager; CliSock: TIOSocket); Procedure Execute; Override; Destructor Destroy; Override; @@ -70,12 +71,12 @@ Const re_Unknown = '500 Unknown command'; re_UnknownOption = '501 Unknown option'; -Function CreateNNTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateNNTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Begin Result := TNNTPServer.Create(Owner, CliSock); End; -Constructor TNNTPServer.Create (Owner: TServerManager; CliSock: TSocketClass); +Constructor TNNTPServer.Create (Owner: TServerManager; CliSock: TIOSocket); Begin Inherited Create(Owner, CliSock); diff --git a/mystic/mis_client_pop3.pas b/mystic/mis_client_pop3.pas index eeaed46..134acef 100644 --- a/mystic/mis_client_pop3.pas +++ b/mystic/mis_client_pop3.pas @@ -12,9 +12,10 @@ Uses MD5, Classes, SysUtils, + m_io_Base, + m_io_Sockets, m_Strings, m_FileIO, - m_Socket_Class, m_DateTime, MIS_Server, MIS_NodeData, @@ -23,7 +24,7 @@ Uses BBS_MsgBase_JAM, BBS_MsgBase_Squish; -Function CreatePOP3 (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreatePOP3 (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Const MaxMailBoxSize = 1000; @@ -50,7 +51,7 @@ Type MailInfo : Array[1..MaxMailBoxSize] of PMailMessageRec; MailSize : LongInt; - Constructor Create (Owner: TServerManager; CliSock: TSocketClass); + Constructor Create (Owner: TServerManager; CliSock: TIOSocket); Procedure Execute; Override; Destructor Destroy; Override; @@ -91,12 +92,12 @@ Const re_ResetOK = re_OK + 'Messages reset'; re_MsgDeleted = re_OK + 'Message deleted'; -Function CreatePOP3 (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreatePOP3 (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Begin Result := TPOP3Server.Create(Owner, CliSock); End; -Constructor TPOP3Server.Create (Owner: TServerManager; CliSock: TSocketClass); +Constructor TPOP3Server.Create (Owner: TServerManager; CliSock: TIOSocket); Begin Inherited Create(Owner, CliSock); diff --git a/mystic/mis_client_smtp.pas b/mystic/mis_client_smtp.pas index e30d8fb..7ff13db 100644 --- a/mystic/mis_client_smtp.pas +++ b/mystic/mis_client_smtp.pas @@ -10,9 +10,10 @@ Interface Uses Classes, SysUtils, + m_io_Base, + m_io_Sockets, m_Strings, m_FileIO, - m_Socket_Class, m_DateTime, bbs_MsgBase_ABS, bbs_MsgBase_JAM, @@ -21,7 +22,7 @@ Uses MIS_NodeData, MIS_Common; -Function CreateSMTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateSMTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Type TSMTPServer = Class(TServerClient) @@ -35,7 +36,7 @@ Type FromPos : LongInt; ToList : TStringList; - Constructor Create (Owner: TServerManager; CliSock: TSocketClass); + Constructor Create (Owner: TServerManager; CliSock: TIOSocket); Procedure Execute; Override; Destructor Destroy; Override; @@ -62,12 +63,12 @@ Const re_NeedRcpt = '503 Must send RCPT TO: first'; re_ErrorSending = '550 Mailbox not found'; -Function CreateSMTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateSMTP (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Begin Result := TSMTPServer.Create(Owner, CliSock); End; -Constructor TSMTPServer.Create (Owner: TServerManager; CliSock: TSocketClass); +Constructor TSMTPServer.Create (Owner: TServerManager; CliSock: TIOSocket); Begin Inherited Create(Owner, CliSock); diff --git a/mystic/mis_client_telnet.pas b/mystic/mis_client_telnet.pas index d2d1d15..b90eeb6 100644 --- a/mystic/mis_client_telnet.pas +++ b/mystic/mis_client_telnet.pas @@ -15,32 +15,33 @@ Uses {$IFDEF WINDOWS} Windows, {$ENDIF} + m_io_Base, + m_io_Sockets, m_Strings, - m_Socket_Class, MIS_Common, MIS_NodeData, MIS_Server; -Function CreateTelnet (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateTelnet (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; { must match server create or there will be access violations } Type TTelnetServer = Class(TServerClient) ND : TNodeData; Snooping : Boolean; - Constructor Create (Owner: TServerManager; ND: TNodeData; CliSock: TSocketClass); + Constructor Create (Owner: TServerManager; ND: TNodeData; CliSock: TIOSocket); Procedure Execute; Override; Destructor Destroy; Override; End; Implementation -Function CreateTelnet (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TSocketClass) : TServerClient; +Function CreateTelnet (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient; Begin Result := TTelnetServer.Create(Owner, ND, CliSock); End; -Constructor TTelnetServer.Create (Owner: TServerManager; ND: TNodeData; CliSock: TSocketClass); +Constructor TTelnetServer.Create (Owner: TServerManager; ND: TNodeData; CliSock: TIOSocket); Begin Inherited Create(Owner, CliSock); diff --git a/mystic/mis_server.pas b/mystic/mis_server.pas index 809f9d7..7287fe2 100644 --- a/mystic/mis_server.pas +++ b/mystic/mis_server.pas @@ -6,7 +6,8 @@ Interface Uses Classes, - m_Socket_Class, + m_io_Base, + m_io_Sockets, MIS_Common, MIS_NodeData; @@ -16,12 +17,12 @@ Const Type TServerManager = Class; TServerClient = Class; - TServerCreateProc = Function (Manager: TServerManager; Config: RecConfig; ND: TNodeData; Client: TSocketClass): TServerClient; + TServerCreateProc = Function (Manager: TServerManager; Config: RecConfig; ND: TNodeData; Client: TIOSocket): TServerClient; TServerManager = Class(TThread) Critical : TRTLCriticalSection; NodeInfo : TNodeData; - Server : TSocketClass; + Server : TIOSocket; ServerStatus : TStringList; StatusUpdated : Boolean; ClientList : TList; @@ -41,15 +42,15 @@ Type Procedure Execute; Override; Procedure Status (Str: String); Function CheckIP (IP, Mask: String) : Boolean; - Function IsBlockedIP (Var Client: TSocketClass) : Boolean; - Function DuplicateIPs (Var Client: TSocketClass) : Byte; + Function IsBlockedIP (Var Client: TIOSocket) : Boolean; + Function DuplicateIPs (Var Client: TIOSocket) : Byte; End; TServerClient = Class(TThread) - Client : TSocketClass; + Client : TIOSocket; Manager : TServerManager; - Constructor Create (Owner: TServerManager; CliSock: TSocketClass); + Constructor Create (Owner: TServerManager; CliSock: TIOSocket); Destructor Destroy; Override; End; @@ -75,7 +76,7 @@ Begin ClientActive := 0; ClientMaxIPs := 1; NewClientProc := CreateProc; - Server := TSocketClass.Create; + Server := TIOSocket.Create; ServerStatus := TStringList.Create; StatusUpdated := False; ClientList := TList.Create; @@ -122,7 +123,7 @@ Begin End; End; -Function TServerManager.IsBlockedIP (Var Client: TSocketClass) : Boolean; +Function TServerManager.IsBlockedIP (Var Client: TIOSocket) : Boolean; Var TF : Text; Str : String; @@ -146,16 +147,26 @@ Begin End; End; -Function TServerManager.DuplicateIPs (Var Client: TSocketClass) : Byte; +Function TServerManager.DuplicateIPs (Var Client: TIOSocket) : Byte; Var Count : Byte; Begin Result := 0; - +(* For Count := 0 to ClientMax - 1 Do If ClientList[Count] <> NIL Then // use Assigned? - If Client.PeerIP = TSocketClass(ClientList[Count]).PeerIP Then + If Client.PeerIP = TIOSocket(ClientList[Count]).PeerIP Then + Inc(Result);*) + + For Count := 0 to ClientMax - 1 Do + If Assigned(ClientList[Count]) Then Begin +// writeln('client ip:', client.peerip); +// writeln('comp ip :', TIOSocket(clientlist[count]).fpeerip); +// waitms(3000); + + If Client.PeerIP = TIOSocket(ClientList[Count]).PeerIP Then Inc(Result); + End; End; Procedure TServerManager.Status (Str: String); @@ -193,7 +204,7 @@ End; Procedure TServerManager.Execute; Var - NewClient : TSocketClass; + NewClient : TIOSocket; Begin Repeat Until Server <> NIL; // Synchronize with server class Repeat Until ServerStatus <> NIL; // Syncronize with status class @@ -224,7 +235,7 @@ Begin If Not NewClient.WriteFile(TextPath + 'blocked.txt') Then NewClient.WriteLine('BLOCKED'); NewClient.Free; End Else - If (ClientMaxIPs > 0) and (DuplicateIPs(NewClient) > ClientMaxIPs) Then Begin + If (ClientMaxIPs > 0) and (DuplicateIPs(NewClient) >= ClientMaxIPs) Then Begin Inc (ClientRefused); Status('MULTI: ' + NewClient.PeerIP + ' (' + NewClient.PeerName + ')'); If Not NewClient.WriteFile(TextPath + 'dupeip.txt') Then NewClient.WriteLine('Only ' + strI2S(ClientMaxIPs) + ' connection(s) per user'); @@ -273,7 +284,7 @@ Begin Inherited Destroy; End; -Constructor TServerClient.Create (Owner: TServerManager; CliSock: TSocketClass); +Constructor TServerClient.Create (Owner: TServerManager; CliSock: TIOSocket); Var Count : Byte; Begin diff --git a/mystic/mkcrap.pas b/mystic/mkcrap.pas index 61128eb..45bae48 100644 --- a/mystic/mkcrap.pas +++ b/mystic/mkcrap.pas @@ -43,9 +43,11 @@ Var SecsPast, DaysPast: LongInt; Begin DateG2J (DT.Year, DT.Month, DT.Day, DaysPast); + DaysPast := DaysPast - DATEc1970; SecsPast := DaysPast * 86400; SecsPast := SecsPast + (LongInt(DT.Hour) * 3600) + (DT.Min * 60) + (DT.Sec); + DTToUnixDate := SecsPast; End; @@ -114,40 +116,36 @@ Function SaveFile(FN: String; Var Rec; FS: Word): Word; Procedure Str2Az(Str: String; MaxLen: Byte; Var AZStr); {Convert string to asciiz} - Begin - If Length(Str) >= MaxLen Then - Begin - Str[MaxLen] := #0; - Move(Str[1], AZStr, MaxLen); - End - Else - Begin - Str[Length(Str) + 1] := #0; - Move(Str[1], AZStr, Length(Str) + 1); - End; - End; +Begin + If Length(Str) >= MaxLen Then Begin + Str[MaxLen] := #0; + Move(Str[1], AZStr, MaxLen); + End Else Begin + Str[Length(Str) + 1] := #0; + Move(Str[1], AZStr, Length(Str) + 1); + End; +End; Function MonthStr(MonthNo: Word): String; Begin - Case MonthNo of - 01: MonthStr := 'Jan'; - 02: MonthStr := 'Feb'; - 03: MonthStr := 'Mar'; - 04: MonthStr := 'Apr'; - 05: MonthStr := 'May'; - 06: MonthStr := 'Jun'; - 07: MonthStr := 'Jul'; - 08: MonthStr := 'Aug'; - 09: MonthStr := 'Sep'; - 10: MonthStr := 'Oct'; - 11: MonthStr := 'Nov'; - 12: MonthStr := 'Dec'; - Else - MonthStr := '???'; - End; + Case MonthNo of + 01: MonthStr := 'Jan'; + 02: MonthStr := 'Feb'; + 03: MonthStr := 'Mar'; + 04: MonthStr := 'Apr'; + 05: MonthStr := 'May'; + 06: MonthStr := 'Jun'; + 07: MonthStr := 'Jul'; + 08: MonthStr := 'Aug'; + 09: MonthStr := 'Sep'; + 10: MonthStr := 'Oct'; + 11: MonthStr := 'Nov'; + 12: MonthStr := 'Dec'; + Else + MonthStr := '???'; + End; End; - Function FormattedDate(DT: DateTime; Mask: String): String; Var DStr : String[2]; diff --git a/mystic/mystic.pas b/mystic/mystic.pas index 3b94f06..c434131 100644 --- a/mystic/mystic.pas +++ b/mystic/mystic.pas @@ -28,6 +28,10 @@ Uses HeapTrc, LineInfo, {$ENDIF} + {$IFDEF WINDOWS} + m_io_Base, + m_io_Sockets, + {$ENDIF} {$IFDEF UNIX} BaseUnix, {$ENDIF} @@ -411,7 +415,7 @@ Begin Session.LocalMode := Session.CommHandle = -1; If Not Session.LocalMode Then Begin - Session.Client.FSocketHandle := Session.CommHandle; + TIOSocket(Session.Client).FSocketHandle := Session.CommHandle; Session.io.LocalScreenDisable; End; diff --git a/mystic/todo.pas b/mystic/todo.pas index b7c40de..6de5ea1 100644 --- a/mystic/todo.pas +++ b/mystic/todo.pas @@ -19,10 +19,15 @@ BUGS AND POSSIBLE ISSUES FPC BUG? DirAttr is suspect in MPL is it 1 byte or 4 in size? ! View archive not working if its external view? [Griffin] ! Message header does not ackknowledge user's date format. +! Test MIS blocking features or just rewrite MIS completely. +! AllFiles/NewFiles not properly prompting to zip and download. +! Check multiple BBS list functionality. FUTURE / IDEAS / WORK IN PROGRESS / NOTES ========================================= +- BBS email autoforwarded to Internet email +- Ability to send internet email to people from within the BBS. - ANSI post-processor for message uploads via FSE - ANSI reading support in fullscreen reader - Ability to override read-type per message base (usersetting/normal/lightbar)