mysticbbs/mystic/mis_client_pop3.pas

487 lines
12 KiB
ObjectPascal

Unit MIS_Client_POP3;
{$I M_OPS.PAS}
// RFC 1939
// optional TOP and APOP not implemented
// needs to reformat long messages > 79 chars?
Interface
Uses
MD5,
Classes,
SysUtils,
m_io_Base,
m_io_Sockets,
m_Strings,
m_FileIO,
m_DateTime,
MIS_Server,
MIS_NodeData,
MIS_Common,
BBS_MsgBase_ABS,
BBS_MsgBase_JAM,
BBS_MsgBase_Squish,
BBS_Records,
BBS_DataBase;
Function CreatePOP3 (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient;
Const
MaxMailBoxSize = 1000;
Type
PMailMessageRec = ^TMailMessageRec;
TMailMessageRec = Record
MsgSize : LongInt;
MD5 : String[32];
Deleted : Boolean;
GotRETR : Boolean;
Text : TStringList;
End;
TPOP3Server = Class(TServerClient)
Server : TServerManager;
UserName : String[30];
LoggedIn : Boolean;
GotQuit : Boolean;
Cmd : String;
Data : String;
User : RecUser;
UserPos : LongInt;
MailInfo : Array[1..MaxMailBoxSize] of PMailMessageRec;
MailSize : LongInt;
Constructor Create (Owner: TServerManager; CliSock: TIOSocket);
Procedure Execute; Override;
Destructor Destroy; Override;
Function IsUser (Str: String) : Boolean;
Procedure ResetSession;
Procedure CreateMailBoxData;
Procedure DeleteMessages;
Function GetMessageUID (Var MsgBase: PMsgBaseABS) : String;
Procedure GetMessageCount (Var TotalMsg: LongInt; Var TotalSize: LongInt);
Procedure cmdLIST;
Procedure cmdUSER;
Procedure cmdPASS;
Procedure cmdSTAT;
Procedure cmdUIDL;
Procedure cmdRETR;
Procedure cmdRSET;
Procedure cmdDELE;
Procedure cmdTOP;
End;
Implementation
Const
re_OK = '+OK ';
re_Error = '-ERR ';
re_UnknownCommand = re_Error + 'Unknown command';
re_UnknownUser = re_Error + 'Unknown user';
re_BadLogin = re_Error + 'Bad credentials';
re_NotLoggedIn = re_Error + 'Not logged in';
re_UnknownMail = re_Error + 'Unknown message';
re_Greeting = re_OK + 'Mystic POP3 Server';
re_Goodbye = re_OK + 'Goodbye';
re_SendUserPass = re_OK + 'Send user password';
re_LoggedIn = re_OK + 'Welcome';
re_GetMessage = re_OK + 'Sending message ';
re_ResetOK = re_OK + 'Messages reset';
re_MsgDeleted = re_OK + 'Message deleted';
Function CreatePOP3 (Owner: TServerManager; Config: RecConfig; ND: TNodeData; CliSock: TIOSocket) : TServerClient;
Begin
Result := TPOP3Server.Create(Owner, CliSock);
End;
Constructor TPOP3Server.Create (Owner: TServerManager; CliSock: TIOSocket);
Begin
Inherited Create(Owner, CliSock);
Server := Owner;
MailSize := 0;
End;
Function TPOP3Server.IsUser (Str: String) : Boolean;
Begin
Str := strUpper(Str);
Result := LoggedIn and ((strUpper(User.RealName) = Str) or (strUpper(User.Handle) = Str));
End;
Procedure TPOP3Server.ResetSession;
Var
Count : LongInt;
Begin
LoggedIn := False;
GotQuit := False;
UserName := '';
UserPos := -1;
For Count := 1 to MailSize Do
If MailInfo[Count] <> NIL Then Begin
If Assigned(MailInfo[Count]^.Text) Then
MailInfo[Count]^.Text.Free;
Dispose (MailInfo[Count]);
End;
MailSize := 0;
End;
Procedure TPOP3Server.GetMessageCount (Var TotalMsg: LongInt; Var TotalSize: LongInt);
Var
Count : LongInt;
Begin
TotalMsg := 0;
TotalSize := 0;
For Count := 1 to MailSize Do
If Not MailInfo[Count]^.Deleted Then Begin
Inc (TotalMsg);
Inc (TotalSize, MailInfo[Count]^.MsgSize);
End;
End;
Function TPOP3Server.GetMessageUID (Var MsgBase: PMsgBaseABS) : String;
Var
TempStr : String;
Begin
// FP might calc this wrong if we do it all at once, so annoying
TempStr := strI2S(User.PermIdx);
TempStr := TempStr + MsgBase^.GetFrom;
TempStr := TempStr + MsgBase^.GetDate;
TempStr := TempStr + MsgBase^.GetTime;
Result := MD5Print(MD5String(TempStr));
End;
Procedure TPOP3Server.CreateMailBoxData;
Var
MBaseFile : File of RecMessageBase;
MBase : RecMessageBase;
MsgBase : PMsgBaseABS;
Function ParseDateTime (Date, Time : String) : String;
Begin
DateSeparator := '-';
ParseDateTime := FormatDateTime('ddd, dd mmm yyyy hh:nn:ss', StrToDateTime(Date + ' ' + Time));
End;
Procedure AddLine (Str: String);
Begin
MailInfo[MailSize]^.Text.Add(Str);
Inc (MailInfo[MailSize]^.MsgSize, Length(Str) + 2); {CRLF}
End;
Begin
Assign (MBaseFile, bbsCfg.DataPath + 'mbases.dat');
If Not ioReset(MBaseFile, SizeOf(RecMessageBase), fmRWDN) Then Exit;
ioRead (MBaseFile, MBase);
Close (MBaseFile);
If Not MBaseOpenCreate (MsgBase, MBase, TempPath) Then Exit;
MsgBase^.SeekFirst(1);
While MsgBase^.SeekFound Do Begin
MsgBase^.MsgStartup;
if IsUser(MsgBase^.GetTo) Then Begin
MsgBase^.MsgTxtStartup;
Inc (MailSize);
New (MailInfo[MailSize]);
MailInfo[MailSize]^.Text := TStringList.Create;
AddLine ('Date: ' + ParseDateTime(MsgBase^.GetDate, MsgBase^.GetTime));
AddLine ('From: ' + MsgBase^.GetFrom + ' <' + strReplace(MsgBase^.GetFrom, ' ', '_') + '@' + bbsCfg.inetDomain + '>');
AddLine ('X-Mailer: Mystic ' + mysVersion);
AddLine ('To: ' + MsgBase^.GetTo + ' <' + strReplace(MsgBase^.GetTo, ' ', '_') + '@' + bbsCfg.inetDomain + '>');
AddLine ('Subject: ' + MsgBase^.GetSubj);
AddLine ('Content-Type: text/plain; charset=us-ascii');
AddLine ('');
While Not MsgBase^.EOM Do
AddLine(MsgBase^.GetString(79));
MailInfo[MailSize]^.MD5 := GetMessageUID(MsgBase);
MailInfo[MailSize]^.GotRETR := False;
MailInfo[MailSize]^.Deleted := False;
End;
MsgBase^.SeekNext;
End;
MsgBase^.CloseMsgBase;
Dispose (MsgBase, Done);
End;
Procedure TPOP3Server.DeleteMessages;
Var
Count : LongInt;
MBaseFile : File of RecMessageBase;
MBase : RecMessageBase;
MsgBase : PMsgBaseABS;
Begin
Assign (MBaseFile, bbsCfg.DataPath + 'mbases.dat');
If Not ioReset(MBaseFile, SizeOf(RecMessageBase), fmRWDN) Then Exit;
ioRead (MBaseFile, MBase);
Close (MBaseFile);
If Not MBaseOpenCreate (MsgBase, MBase, TempPath) Then Exit;
For Count := 1 to MailSize Do Begin
If MailInfo[Count]^.Deleted or (MailInfo[Count]^.GotRETR and bbsCfg.inetPOP3Delete) Then Begin
MsgBase^.SeekFirst(1);
While MsgBase^.SeekFound Do Begin
MsgBase^.MsgStartUp;
If IsUser(MsgBase^.GetTo) Then
If GetMessageUID(MsgBase) = MailInfo[Count]^.MD5 Then Begin
MsgBase^.DeleteMsg;
Break;
End;
MsgBase^.SeekNext;
End;
End;
End;
MsgBase^.CloseMsgBase;
Dispose (MsgBase, Done);
End;
Procedure TPOP3Server.cmdUSER;
Begin
ResetSession;
If SearchForUser(Data, User, UserPos) Then Begin
Client.WriteLine(re_SendUserPass);
UserName := Data;
End Else
Client.WriteLine(re_UnknownUser);
End;
Procedure TPOP3Server.cmdPASS;
Begin
If (UserName = '') or (UserPos = -1) Then Begin
Client.WriteLine(re_UnknownUser);
Exit;
End;
If strUpper(Data) = User.Password Then Begin
LoggedIn := True;
CreateMailboxData;
Client.WriteLine(re_LoggedIn);
Server.Status(ProcessID, User.Handle + ' logged in');
End Else
Client.WriteLine(re_BadLogin);
End;
Procedure TPOP3Server.cmdSTAT;
Var
DataSize : LongInt;
DataMsg : LongInt;
Begin
If LoggedIn Then Begin
GetMessageCount(DataMsg, DataSize);
Client.WriteLine(re_OK + strI2S(DataMsg) + ' ' + strI2O(Datasize));
End Else
Client.WriteLine(re_NotLoggedIn);
End;
Procedure TPOP3Server.cmdLIST;
Var
MsgNum : LongInt;
MsgSize : LongInt;
Count : LongInt;
Begin
If LoggedIn Then Begin
If Data <> '' Then Begin
MsgNum := strS2I(Data);
If (MsgNum > 0) and (MsgNum <= MailSize) and (Not MailInfo[MsgNum]^.Deleted) Then
Client.WriteLine(re_OK + strI2S(MsgNum) + ' ' + strI2O(MailInfo[MsgNum]^.MsgSize))
Else
Client.WriteLine(re_UnknownMail);
End Else Begin
GetMessageCount(MsgNum, MsgSize);
Client.WriteLine (re_OK + strI2S(MsgNum) + ' messages (' + strI2O(MsgSize) + ' octets)');
For Count := 1 to MailSize Do
If Not MailInfo[Count]^.Deleted Then
Client.WriteLine (strI2S(Count) + ' ' + strI2O(MailInfo[Count]^.MsgSize));
Client.WriteLine('.');
End;
End Else
Client.WriteLine(re_NotLoggedIn);
End;
Procedure TPOP3Server.cmdUIDL;
Var
MsgNum : LongInt;
Count : LongInt;
Begin
If LoggedIn Then Begin
If Data <> '' Then Begin
MsgNum := strS2I(Data);
If (MsgNum > 0) and (MsgNum <= MailSize) and (Not MailInfo[MsgNum]^.Deleted) Then
Client.WriteLine(re_OK + strI2S(MsgNum) + ' ' + MailInfo[MsgNum]^.MD5)
Else
Client.WriteLine(re_UnknownMail);
End Else Begin
Client.WriteLine (re_OK + 'Message list follows');
For Count := 1 to MailSize Do
If Not MailInfo[Count]^.Deleted Then Begin
Client.WriteLine (strI2S(Count) + ' ' + MailInfo[Count]^.MD5);
End;
Client.WriteLine('.');
End;
End Else
Client.WriteLine(re_NotLoggedIn);
End;
Procedure TPOP3Server.cmdRETR;
Var
MsgNum : LongInt;
Count : LongInt;
Begin
If LoggedIn Then Begin
MsgNum := strS2I(Data);
If (MsgNum > 0) and (MsgNum <= MailSize) and (Not MailInfo[MsgNum]^.Deleted) Then Begin
Client.WriteLine (re_GetMessage + strI2S(MsgNum));
For Count := 0 to MailInfo[MsgNum]^.Text.Count - 1 Do
Client.WriteLine(MailInfo[MsgNum]^.Text[Count]);
Client.WriteLine('.');
MailInfo[MsgNum]^.GotRETR := True;
End Else
Client.WriteLine(re_UnknownMail);
End Else
Client.WriteLine(re_NotLoggedIn);
End;
Procedure TPOP3Server.cmdRSET;
Var
Count : LongInt;
Begin
If LoggedIn Then Begin
For Count := 1 to MailSize Do
MailInfo[Count]^.Deleted := False;
Client.WriteLine (re_ResetOK);
End Else
Client.WriteLine (re_NotLoggedIn);
End;
Procedure TPOP3Server.cmdDELE;
Var
MsgNum : LongInt;
Begin
If LoggedIn Then Begin
MsgNum := strS2I(Data);
If (MsgNum > 0) and (MsgNum <= MailSize) and (Not MailInfo[MsgNum]^.Deleted) Then Begin
MailInfo[MsgNum]^.Deleted := True;
Client.WriteLine(re_MsgDeleted);
End Else
Client.WriteLine(re_UnknownMail);
End Else
Client.WriteLine(re_NotLoggedIn);
End;
Procedure TPOP3Server.cmdTOP;
Begin
If LoggedIn Then Begin
End Else
Client.WriteLine(re_NotLoggedIn);
End;
Procedure TPOP3Server.Execute;
Var
Str : String;
Begin
ResetSession;
Client.WriteLine(re_Greeting);
Repeat
If Client.WaitForData(bbsCfg.inetPOP3TimeOut * 1000) = 0 Then Break;
If Terminated Then Exit;
If Client.ReadLine(Str) = -1 Then Exit;
Cmd := strUpper(strWordGet(1, Str, ' '));
If Pos(' ', Str) > 0 Then
Data := strStripB(Copy(Str, Pos(' ', Str) + 1, Length(Str)), ' ')
Else
Data := '';
// If Cmd = 'APOP' Then cmdAPOP Else
If Cmd = 'DELE' Then cmdDELE Else
If Cmd = 'LIST' Then cmdLIST Else
If Cmd = 'NOOP' Then Client.WriteLine(re_OK) Else
If Cmd = 'PASS' Then cmdPASS Else
If Cmd = 'RETR' Then cmdRETR Else
If Cmd = 'RSET' Then cmdRSET Else
If Cmd = 'STAT' Then cmdSTAT Else
// If Cmd = 'TOP' Then cmdTOP Else
If Cmd = 'UIDL' Then cmdUIDL Else
If Cmd = 'USER' Then cmdUSER Else
If Cmd = 'QUIT' Then Begin
GotQuit := True;
Break;
End Else
Client.WriteLine(re_UnknownCommand);
Until Terminated;
If GotQuit Then Begin
Client.WriteLine(re_Goodbye);
Server.Status (ProcessID, User.Handle + ' logged out');
DeleteMessages;
End;
End;
Destructor TPOP3Server.Destroy;
Begin
ResetSession;
Inherited Destroy;
End;
End.