argumentum Posted October 10 Share Posted October 10 (edited) I wanted to download a portable eMail server but didn't find one. Spoiler expandcollapse popup#NoTrayIcon ; https://www.autoitscript.com/forum/topic/212373-smtp-server-example/ ; ..like an eMail catcher of sorts. It logs what it gets. That is all it does. ; No users, no passwords, just send to this and it'll say, "ok: got it". ; PS: did change the code around a bit to make it work in v3.2.12.1 of AutoIt ; just in case you ( or I ) need to run this in something old. ; Should run from Win2000 to Win11, but I just tested in Win11 Global $g_iListenSocket = 0, $g_SMTP_NowTime = tStamp('.', '_', '.') Global $g_AppName = StringTrimRight(@ScriptName, 4) Global $g_DumpsPath = @ScriptDir & '\SMTP_Dumps' If Not FileExists($g_DumpsPath) Then DirCreate($g_DumpsPath) Global $g_iCountGot = 0, $_g__Socket = 0 Global $g_Compiled = StringRight(@ScriptName, 4) = ".exe" Global $g_InScite = StringInStr($CmdLineRaw, '/ErrorStdOut') Global $g_ini = StringTrimRight(@ScriptFullPath, 4) & ".ini" Global $g_RoundTripTimer = TimerInit() If $CmdLine[0] Then DoCmdLine() If @extended Then Exit EndIf Func DoCmdLine() For $n = 1 To $CmdLine[0] Switch $CmdLine[$n] Case "/test" Exit Case "/msgbox" If $n < $CmdLine[0] And Int(IniRead($g_ini, "Settings", "DoMsgbox", "1")) Then ; ..some day may continue with it EndIf Exit EndSwitch Next EndFunc ;==>DoCmdLine Opt("TrayMenuMode", 3) Opt("TrayOnEventMode", 1) TraySetClick(16) TCPStartup() $g_iListenSocket = TCPListen("0.0.0.0", 25, 100) If @error Then Console_Write('! Could not listen: ' & __WinAPI_GetErrorMessage(@error) & @CRLF) Exit 5 EndIf main() Func main() TrayCreateItem("Open this folder") TrayItemSetOnEvent(-1, "Open_this_folder") TrayCreateItem("") TrayCreateItem("Reset icon") TrayItemSetOnEvent(-1, "Reset_icon") TrayCreateItem("") TrayCreateItem("Restart") TrayItemSetOnEvent(-1, "RestartScript") TrayCreateItem("") TrayCreateItem("Exit") TrayItemSetOnEvent(-1, "ExitScript") TraySetOnEvent(-13, "On_EVENT_PRIMARYDOUBLE") TraySetState(1) TraySetIcon(@ScriptDir & "\" & $g_AppName & ".ico") TraySetToolTip($g_AppName & @CR & 'Count: ' & $g_iCountGot & @CR & @CR & @CR & ' ') While (1) smtp_server_ReadyForNextConnection() $g_iCountGot += 1 TraySetIcon(@ScriptDir & "\" & $g_AppName & "_Red.ico") TraySetToolTip($g_AppName & @CR & 'Count: ' & $g_iCountGot & @CR & @CR & @CR & ' ') WEnd EndFunc ;==>main Func OnAutoItExit() $_g__Socket = 0 EndFunc ;==>OnAutoItExit Func Open_this_folder() ShellExecute($g_DumpsPath) EndFunc ;==>Open_this_folder Func On_EVENT_PRIMARYDOUBLE() Open_this_folder() Reset_icon() EndFunc ;==>On_EVENT_PRIMARYDOUBLE Func Reset_icon() TraySetIcon(@ScriptDir & "\" & $g_AppName & "_Yellow.ico") Sleep(200) TraySetIcon(@ScriptDir & "\" & $g_AppName & ".ico") $g_iCountGot = 0 TraySetToolTip($g_AppName & @CR & 'Count: ' & $g_iCountGot & @CR & @CR & @CR & ' ') EndFunc ;==>Reset_icon Func RestartScript() TraySetState(2) OnAutoItExit() ShellExecute(@AutoItExe, _Iif($g_Compiled, '', '"' & @ScriptFullPath & '"')) Exit EndFunc ;==>RestartScript Func _Iif($fTest, $vTrueVal, $vFalseVal, Const $_iCallerError = @error, Const $_iCallerExtended = @extended) If $fTest Then Return SetError($_iCallerError, $_iCallerExtended, $vTrueVal) Return SetError($_iCallerError, $_iCallerExtended, $vFalseVal) EndFunc ;==>_Iif Func ExitScript() Exit EndFunc ;==>ExitScript Func tStamp($sSepDate = "/", $sSepSep = " ", $sSepTime = ":", $iSepMSec = ".") Return @YEAR & $sSepDate & StringRight('00' & @MON, 2) & $sSepDate & StringRight('00' & @MDAY, 2) & $sSepSep & StringRight('00' & @HOUR, 2) & $sSepTime & StringRight('00' & @MIN, 2) & $sSepTime & StringRight('00' & @SEC, 2) & $iSepMSec & StringRight('000' & TimerInit(), 3) EndFunc ;==>tStamp Func smtp_server_ReadyForNextConnection() ; Coded loosely based on this example: ; https://datatracker.ietf.org/doc/html/rfc3461#section-10.2 Local $sReceived, $iSocket, $iDoneWithThis = 0, $sEmail = "", $hTimer = TimerInit() Do $iSocket = TCPAccept($g_iListenSocket) If @error Then $iError = @error Console_Write("! Server:" & @CRLF & "Could not accept the incoming connection, Error code: " & $iError & @CRLF & __WinAPI_GetErrorMessage($iError) & @CRLF & @CRLF) Return 1 EndIf Sleep(50) Until $iSocket <> -1 $g_RoundTripTimer = TimerInit() $g_SMTP_NowTime = tStamp('.', '_', '.') TCP_Send($hTimer, $iSocket, "220 Trap-SMTP says hello" & @CRLF) Do Sleep(10) $aCmd = TCP_GetCmd($hTimer, $iSocket) If @error Then Console_Write('! ""(' & 110 & ',0) - ' & @error & ',' & @extended & @CRLF) Switch $aCmd[0] Case "ehlo" TCP_Send($hTimer, $iSocket, "421 ERROR" & @CRLF) If @error Then Console_Write('! ""(' & 114 & ',0) - ' & @error & ',' & @extended & @CRLF) Case "helo" TCP_Send($hTimer, $iSocket, "250 DSN" & @CRLF) If @error Then Console_Write('! ' & 117 & ' - ' & @error & ',' & @extended & @CRLF) Case "mail", "rcpt" TCP_Send($hTimer, $iSocket, "250 okay" & @CRLF) If @error Then Console_Write('! ' & 120 & ' - ' & @error & ',' & @extended & @CRLF) Case "data" TCP_Send($hTimer, $iSocket, "354 send message" & @CRLF) If @error Then Console_Write('! ' & 123 & ' - ' & @error & ',' & @extended & @CRLF) $sEmail = TCP_GetData($hTimer, $iSocket) If @error Then Console_Write('! ' & 125 & ' - ' & @error & ',' & @extended & @CRLF) TCP_Send($hTimer, $iSocket, "250 message received" & @CRLF) If @error Then Console_Write('! ' & 127 & ' - ' & @error & ',' & @extended & @CRLF) Case "quit" TCP_Send($hTimer, $iSocket, "221 goodbye" & @CRLF) If @error Then Console_Write('! ' & 130 & ' - ' & @error & ',' & @extended & @CRLF) $iDoneWithThis = 1 EndSwitch If TimerDiff($hTimer) > 2000 Then ExitLoop Until $iDoneWithThis TCPCloseSocket($iSocket) FileWrite($g_DumpsPath & "\smtp_" & $g_SMTP_NowTime & ".eml", $sEmail) FileWriteLine($g_DumpsPath & "\smtp_" & $g_SMTP_NowTime & ".txt", eml_GetGist($sEmail)) Console_Write("+ Total time spent: " & TimerDiff($g_RoundTripTimer) & " ms. " & @CRLF) EndFunc ;==>smtp_server_ReadyForNextConnection Func TCP_GetData(ByRef $hTimer, ByRef $iSocket) $hTimer = TimerInit() Local $sReceived, $sReceivedBuff = "", $iError = 0 Do $sReceived = TCPRecv($iSocket, 4096) If @error Then $iError = 1 ExitLoop EndIf If TimerDiff($hTimer) > 10000 Then $iError = 2 ExitLoop EndIf If $sReceived = "" Then Sleep(20) ContinueLoop EndIf $hTimer = TimerInit() $sReceivedBuff &= $sReceived Console_Write("< GetData: Received:" & @CRLF & " -----------8<-----------" & @CRLF & $sReceived & @CRLF & " ----------->8-----------" & @CRLF) Until StringRight($sReceivedBuff, 5) = @CRLF & "." & @CRLF Return SetError($iError, StringLen($sReceivedBuff), $sReceivedBuff) EndFunc ;==>TCP_GetData Func TCP_GetCmd(ByRef $hTimer, ByRef $iSocket) $hTimer = TimerInit() Local $sReceived, $aRet[2], $iError = 0 Do $sReceived = TCPRecv($iSocket, 4096) If @error Then $iError = 1 ExitLoop EndIf If TimerDiff($hTimer) > 1000 Then $iError = 2 ExitLoop EndIf If $sReceived = "" Then Sleep(20) ContinueLoop EndIf $hTimer = TimerInit() Console_Write("< GetCmd: Received: >" & StringReplace($sReceived, @CRLF, "\r\n") & "<" & @CRLF) Until $sReceived <> "" $aRet[1] = $sReceived $sReceived = StringTrimRight($sReceived, 2) Local $iPos = StringInStr($sReceived, " ") If Not $iPos Then $aRet[0] = $sReceived Else $aRet[0] = StringLeft($sReceived, StringInStr($sReceived, " ") - 1) EndIf Return SetError($iError, StringLen($aRet[0]), $aRet) EndFunc ;==>TCP_GetCmd Func TCP_Send(ByRef $hTimer, ByRef $iSoc, $sStr, $iLine = 193) Sleep(10) $hTimer = TimerInit() Local $iSent = TCPSend($iSoc, $sStr) Console_Write('> (' & $iLine & ',' & @error & ',' & $iSent & ') Send: >' & StringReplace($sStr, @CRLF, "\r\n") & '<' & @CRLF) Return SetError(@error, $iSent, $iSent) EndFunc ;==>TCP_Send Func eml_GetGist($sFileContent) Local $ib64 = 0, $sb64 = "", $sTypeB64, $aArray = StringSplit($sFileContent, @CRLF, 1) Local $s_Subject, $s_From, $s_To, $s_Date, $s_Log = "" For $n = 1 To $aArray[0] If $ib64 And StringInStr($aArray[$n], "--===============") Then If $sTypeB64 = "plain" Then $s_Log &= @CRLF & BinaryToString(_Base64Decode_v2($sb64), 8) & @CRLF ExitLoop EndIf $ib64 = 0 EndIf If StringInStr($aArray[$n], "Subject: ") = 1 Then $s_Log &= $aArray[$n] & @CRLF If StringInStr($aArray[$n], "From: ") = 1 Then $s_Log &= $aArray[$n] & @CRLF If StringInStr($aArray[$n], "To: ") = 1 Then $s_Log &= $aArray[$n] & @CRLF If StringInStr($aArray[$n], "Date: ") = 1 Then $s_Log &= $aArray[$n] & @CRLF If StringInStr($aArray[$n], "Content-Type: ") = 1 Then $sTypeB64 = _Iif(StringInStr($aArray[$n], "html"), "html", "plain") EndIf If StringInStr($aArray[$n], "Content-Transfer-Encoding: base64") Then $ib64 = 1 $sb64 = "" ContinueLoop EndIf If $ib64 Then $sb64 &= $aArray[$n] Next Return $s_Log EndFunc ;==>eml_GetGist Func _Base64Decode_v2($Data) Local $Opcode = "0xC81000005356578365F800E8500000003EFFFFFF3F3435363738393A3B3C3DFFFFFF00FFFFFF000102030405060708090A0B0C0D0E0F10111213141516171819FFFFFFFFFFFF1A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132338F45F08B7D0C8B5D0831D2E9910000008365FC00837DFC047D548A034384C0750383EA033C3D75094A803B3D75014AB00084C0751A837DFC047D0D8B75FCC64435F400FF45FCEBED6A018F45F8EB1F3C2B72193C7A77150FB6F083EE2B0375F08A068B75FC884435F4FF45FCEBA68D75F4668B06C0E002C0EC0408E08807668B4601C0E004C0EC0208E08847018A4602C0E00624C00A46038847028D7F038D5203837DF8000F8465FFFFFF89D05F5E5BC9C21000" Local $CodeBuffer = DllStructCreate("byte[" & BinaryLen($Opcode) & "]") DllStructSetData($CodeBuffer, 1, $Opcode) Local $Ouput = DllStructCreate("byte[" & BinaryLen($Data) & "]") Local $Ret = DllCall("user32.dll", "int", "CallWindowProc", "ptr", DllStructGetPtr($CodeBuffer), "str", $Data, "ptr", DllStructGetPtr($Ouput), "int", 0, "int", 0) Return BinaryMid(DllStructGetData($Ouput, 1), 1, $Ret[0]) EndFunc ;==>_Base64Decode_v2 Func Console_Write($sStr, Const $_iCallerError = @error, Const $_iCallerExtended = @extended) If $g_InScite Then ConsoleWrite($sStr) FileWriteLine($g_DumpsPath & "\smtp_" & $g_SMTP_NowTime & ".log", $sStr) Return SetError($_iCallerError, $_iCallerExtended, 0) EndFunc ;==>Console_Write Func __WinAPI_GetErrorMessage($iCode, $iLanguage = 0, Const $_iCallerError = @error, Const $_iCallerExtended = @extended) Local $FORMAT_MESSAGE_FROM_SYSTEM = 4096, $FORMAT_MESSAGE_IGNORE_INSERTS = 512 Local $aCall = DllCall('kernel32.dll', 'dword', 'FormatMessageW', 'dword', BitOR($FORMAT_MESSAGE_FROM_SYSTEM, $FORMAT_MESSAGE_IGNORE_INSERTS), 'ptr', 0, 'dword', $iCode, 'dword', $iLanguage, 'wstr', '', 'dword', 4096, 'ptr', 0) If @error Or Not $aCall[0] Then Return SetError(@error, @extended, '') Return SetError($_iCallerError, $_iCallerExtended, StringRegExpReplace($aCall[5], '[' & @LF & ',' & @CR & ']*\Z', '')) EndFunc ;==>__WinAPI_GetErrorMessage So I wrote one that gets the eMail and ... nothing. It just changes the color of the tray icon. My idea is like a SNMP (UDP) trap but for TCP. So you'd get the eMail alerts from the device ( TrueNAS in my case ) and ... keep'em there. Better than flooding you eMail box from your secured on-line eMail provider, when setup to send "info" instead of "warning" level information. Again, this just keeps them as dumps. But it may be a good start for your own coding Note: This accepts all that is thrown at it on port 25. For experimental local network use only. I'll put the icons and AutoIt v3.2 compiled script along with the source in the downloads area. Edited October 11 by argumentum TheDcoder 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
TheDcoder Posted October 11 Share Posted October 11 Quite an interesting idea, right up my ally of making quick and jank solutions like this argumentum 1 EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time) DcodingTheWeb Forum - Follow for updates and Join for discussion Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now