$PBExportHeader$n_pop3.sru forward global type n_pop3 from n_winsock end type type systemtime from structure within n_pop3 end type end forward type systemtime from structure unsignedinteger wyear unsignedinteger wmonth unsignedinteger wdayofweek unsignedinteger wday unsignedinteger whour unsignedinteger wminute unsignedinteger wsecond unsignedinteger wmilliseconds end type global type n_pop3 from n_winsock autoinstantiate end type type prototypes Function boolean CryptStringToBinary ( & string pszString, & ulong cchString, & ulong dwFlags, & Ref blob pbBinary, & Ref ulong pcbBinary, & Ref ulong pdwSkip, & Ref ulong pdwFlags & ) Library "crypt32.dll" Alias For "CryptStringToBinaryW" Function long CreateFile ( & string lpFileName, & ulong dwDesiredAccess, & ulong dwShareMode, & ulong lpSecurityAttributes, & ulong dwCreationDisposition, & ulong dwFlagsAndAttributes, & ulong hTemplateFile & ) Library "kernel32.dll" Alias For "CreateFileW" Function boolean CloseHandle ( & long hObject & ) Library "kernel32.dll" Function boolean ReadFile ( & long hFile, & Ref blob lpBuffer, & ulong nNumberOfBytesToRead, & Ref ulong lpNumberOfBytesRead, & ulong lpOverlapped & ) Library "kernel32.dll" Function boolean WriteFile ( & ulong hFile, & blob lpBuffer, & ulong nNumberOfBytesToWrite, & Ref ulong lpNumberOfBytesWritten, & ulong lpOverlapped & ) Library "kernel32.dll" Function long SendStringMessage ( & long hWnd, & uint Msg, & ulong wParam, & Ref string lParam & ) Library "user32.dll" Alias For "SendMessageW" Subroutine SleepMS ( & ulong dwMilliseconds & ) Library "kernel32.dll" Alias For "Sleep" Function boolean SystemTimeToTzSpecificLocalTime ( & ulong lpTimeZone, & SYSTEMTIME lpUniversalTime, & Ref SYSTEMTIME lpLocalTime & ) Library "kernel32.dll" // Cryptlib Functions Function long cryptInit ( & ) Library "cl32.dll" Function long cryptEnd ( & ) Library "cl32.dll" Function long cryptCreateSession ( & Ref long pSession, & long cryptUser, & long SessionType & ) Library "cl32.dll" Function long cryptDestroySession ( & long session & ) Library "cl32.dll" Function long cryptSetAttributeString ( & long hCrypt, & long CryptAttType, & Ref string pBuff, & long StrLen & ) Library "cl32.dll" Alias For "cryptSetAttributeString;Ansi" Function long cryptSetAttribute ( & long hCrypt, & long CryptAttType, & long value & ) Library "cl32.dll" Function long cryptPopData ( & long envelope, & Ref string pBuff, & long StrLen, & Ref long pBytesCopied & ) Library "cl32.dll" Alias For "cryptPopData;Ansi" Function long cryptPushData ( & long envelope, & Ref string pBuff, & long StrLen, & Ref long pBytesCopied & ) Library "cl32.dll" Alias For "cryptPushData;Ansi" Function long cryptFlushData ( & long envelope & ) Library "cl32.dll" Function long cryptGetAttributeString ( & long hCrypt, & long CryptAttType, & Ref string pBuff, & Ref integer StrLen & ) Library "cl32.dll" Alias For "cryptGetAttributeString;Ansi" Function long cryptGetAttribute ( & long hCrypt, & long CryptAttType, & Ref long value & ) Library "cl32.dll" end prototypes type variables Protected: Constant String CRLF = Char(13) + Char(10) // Cryptlib constants Constant Long CRYPT_OK = 0 Constant Long CRYPT_UNUSED = -101 Constant Long CRYPT_SESSION_SSL = 3 Constant Long CRYPT_SESSINFO_ACTIVE = 6001 Constant Long CRYPT_SESSINFO_SERVER_NAME = 6008 Constant Long CRYPT_SESSINFO_SERVER_PORT = 6009 Constant Long CRYPT_SESSINFO_NETWORKSOCKET = 6014 Constant Long CRYPT_SESSINFO_SSL_OPTIONS = 6026 Constant Long CRYPT_SSLOPTION_DISABLE_NAMEVERIFY = 16 Constant Long CRYPT_OPTION_CERT_COMPLIANCELEVEL = 118 Constant Long CRYPT_COMPLIANCELEVEL_PKIX_FULL = 4 Constant Long CRYPT_COMPLIANCELEVEL_PKIX_PARTIAL = 3 Constant Long CRYPT_COMPLIANCELEVEL_STANDARD = 2 Constant Long CRYPT_COMPLIANCELEVEL_REDUCED = 1 Constant Long CRYPT_COMPLIANCELEVEL_OBLIVIOUS = 0 // constants for CreateFile API function Constant Long INVALID_HANDLE_VALUE = -1 Constant ULong GENERIC_READ = 2147483648 Constant ULong GENERIC_WRITE = 1073741824 Constant ULong FILE_SHARE_READ = 1 Constant ULong FILE_SHARE_WRITE = 2 Constant ULong CREATE_NEW = 1 Constant ULong CREATE_ALWAYS = 2 Constant ULong OPEN_EXISTING = 3 Constant ULong OPEN_ALWAYS = 4 Constant ULong TRUNCATE_EXISTING = 5 Long il_Socket Long il_Session UInt iui_port = 110 Boolean ib_logfile = False String is_userid String is_passwd String is_server String is_logfile Long il_sleeptime = 25 Long il_totalbytes Long il_recvdbytes Long il_window Long il_event String is_email[] String is_uidl[] end variables forward prototypes public subroutine of_setlogfile (boolean ab_flag, string as_logfile) public subroutine of_setport (unsignedinteger aui_port) public subroutine of_setlogin (string as_userid, string as_passwd) public subroutine of_setserver (string as_server) public function boolean of_send (unsignedlong aul_socket, string as_data) public function long of_recv (unsignedlong aul_socket, ref string as_data) public function boolean of_recvmail_start () private function boolean of_sendmsg (string as_cmd, ref string as_reply) public function boolean of_recvmail_stop () public function integer of_msgcount () public function long of_msglength (integer ai_msgnum) public function string of_msgcontent (integer ai_msgnum) public function string of_msgsubject (integer ai_msgnum) public subroutine of_msgfrom (integer ai_msgnum, ref string as_name, ref string as_email) public function string of_crypterror (long al_retval) private function boolean of_sendmsg_crypt (string as_cmd, ref string as_reply) public function boolean of_recvmail_crypt_stop () public function boolean of_recvmail_crypt_start () private subroutine of_separate (readonly string as_content, ref string as_part[], ref string as_type[]) public function string of_msgproperty (integer ai_msgnum, string as_property) public function integer of_msgbody (integer ai_msgnum, ref string as_part[], ref string as_type[]) public function integer of_msgload (string as_filename) private function boolean of_readfile (string as_filename, ref blob ablob_data) public subroutine of_quotedprintable (ref string as_content) public function string of_attachname (string as_type) public function string of_replaceall (readonly string as_oldstring, readonly string as_findstr, readonly string as_replace) public function boolean of_writefile (readonly string as_filename, readonly blob ablob_filedata) public subroutine of_decodestring (ref string as_string) public function boolean of_msgreceipt (integer ai_msgnum, ref string as_name, ref string as_email) public subroutine of_progress_done () public subroutine of_progress_message (string as_message) public subroutine of_progress_notify (long al_window, long al_event) public subroutine of_progress_start (long al_length) public subroutine of_progress_update (long al_length) public subroutine of_setsleeptime (long al_sleeptime) public subroutine of_base64 (readonly string as_type, ref string as_part) public function boolean of_recvmail_msgs (boolean ab_delete, boolean ab_headers) public function boolean of_recvmail (boolean ab_delete, boolean ab_headers) public function boolean of_recvmail_crypt_msgs (boolean ab_delete, boolean ab_headers) public function boolean of_recvsslmail (boolean ab_delete, boolean ab_headers) public function datetime of_msgsentdate (integer ai_msgnum) public function integer of_msgadd (readonly string as_email, readonly string as_uidl) public function string of_msguidl (integer ai_msgnum) public function boolean of_deletemail (string as_uidl[]) public function boolean of_recvmail (integer ai_msgnum[]) public function boolean of_deletesslmail (string as_uidl[]) public function boolean of_recvsslmail (integer ai_msgnum[]) protected subroutine of_logfile (string as_logmsg) public function integer of_hex2int (string as_hex) public subroutine of_quotedprintable (ref string as_content, boolean ab_recurse) public function long of_parse (readonly string as_string, readonly string as_separator, ref string as_outarray[]) public function blob of_decode64 (readonly string as_encoded) end prototypes public subroutine of_setlogfile (boolean ab_flag, string as_logfile);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_SetLogFile // // PURPOSE: This function is used to turn on POP3 conversation logging. // // ARGUMENTS: ab_flag - True/False // as_logfile - Name of the logfile // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/10/2010 RolandS Initial coding // 07/06/2011 RolandS Added as_logfile // ----------------------------------------------------------------------------- ib_logfile = ab_flag is_logfile = as_logfile end subroutine public subroutine of_setport (unsignedinteger aui_port);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_SetPort // // PURPOSE: This function is used to set the port the server is using. // The default is 110 and usually does not need to change. // // ARGUMENTS: aui_port - Server port // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/08/2010 RolandS Initial coding // ----------------------------------------------------------------------------- iui_port = aui_port end subroutine public subroutine of_setlogin (string as_userid, string as_passwd);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_SetLogin // // PURPOSE: This function is used to set the userid and password. // // ARGUMENTS: as_userid - Server userid // as_passwd - Server password // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/08/2010 RolandS Initial coding // ----------------------------------------------------------------------------- is_userid = as_userid is_passwd = as_passwd end subroutine public subroutine of_setserver (string as_server);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_SetServer // // PURPOSE: This function is used to set the server name // // ARGUMENTS: as_server - Server name // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/08/2010 RolandS Initial coding // ----------------------------------------------------------------------------- is_server = as_server end subroutine public function boolean of_send (unsignedlong aul_socket, string as_data);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Send // // PURPOSE: This override of the ancestor adds optional logging. // // ARGUMENTS: aul_socket - Open socket // as_data - By ref string // // RETURN: True = Success // False = Error // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/10/2010 RolandS Initial coding // ----------------------------------------------------------------------------- of_LogFile(as_data) Return Super::of_Send(aul_socket, as_data) end function public function long of_recv (unsignedlong aul_socket, ref string as_data);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Recv // // PURPOSE: This override of the ancestor adds optional logging. // // ARGUMENTS: aul_socket - Open socket // as_data - By ref string // // RETURN: True = Success // False = Error // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/10/2010 RolandS Initial coding // ----------------------------------------------------------------------------- Long ll_return ll_return = Super::of_Recv(aul_socket, as_data) of_LogFile(as_data) Return ll_return end function public function boolean of_recvmail_start ();// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvMail_Start // // PURPOSE: This function starts the recvmail session. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/03/2012 RolandS Added progress tracking // 12/30/2013 RolandS Added logging // ----------------------------------------------------------------------------- Constant String REPLY_READY = "220" String ls_reply, ls_msg // update progress of_Progress_Message("Connecting to the server") // POP3 is Ansi of_SetUnicode(False) // initialize Winsock of_Startup() // connect to server of_LogFile("Connect to server " + is_server + " on port " + String(iui_port) + CRLF) il_Socket = of_Connect(is_server, iui_port) If il_Socket = 0 Then Return False // receive response of_Recv(il_Socket, ls_reply) If Left(ls_reply, 3) <> "+OK" Then // of_SetLastError(ls_reply) of_LogFile(ls_reply) Return False End If // update progress of_Progress_Message("Logging into the server") // send the Userid ls_msg = "USER " + is_userid + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If // send the Password ls_msg = "PASS " + is_passwd + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If Return True end function private function boolean of_sendmsg (string as_cmd, ref string as_reply);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_SendMsg // // PURPOSE: This function is used by other functions to send a message and // receive any reply. // // ARGUMENTS: as_cmd - POP3 command to be sent // as_reply - The reply received from the server // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 08/20/2010 RolandS Initial coding // 04/03/2012 RolandS Added in wait time // ----------------------------------------------------------------------------- // send the data If Not of_Send(il_Socket, as_cmd) Then Return False End If // wait before continuing SleepMS(il_sleeptime) // receive response of_Recv(il_Socket, as_reply) // check for errors choose case Left(as_reply, 3) case "+OK" Return True case else // of_SetLastError(as_reply) of_LogFile(as_reply) Return False end choose Return True end function public function boolean of_recvmail_stop ();// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvMail_Stop // // PURPOSE: This function ends the recvmail session. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/03/2012 RolandS Added progress tracking // ----------------------------------------------------------------------------- String ls_msg, ls_reply // update progress of_Progress_Message("Disconnecting from the server") // quit the session ls_msg = "QUIT" + CRLF If Not of_Send(il_Socket, ls_msg) Then of_CloseSocket(il_Socket) Return False End If // receive response of_Recv(il_Socket, ls_reply) // close the socket of_CloseSocket(il_Socket) Return True end function public function integer of_msgcount ();// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgCount // // PURPOSE: This function returns the number of received email messages. // // RETURN: Email count // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Return UpperBound(is_email) end function public function long of_msglength (integer ai_msgnum);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgLength // // PURPOSE: This function returns the length of an email message. // // ARGUMENTS: ai_msgnum - Message number // // RETURN: Email length // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Return Len(is_email[ai_msgnum]) end function public function string of_msgcontent (integer ai_msgnum);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgContent // // PURPOSE: This function returns the entire content of an email message. // // ARGUMENTS: ai_msgnum - Message number // // RETURN: Email content // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Return is_email[ai_msgnum] end function public function string of_msgsubject (integer ai_msgnum);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgSubject // // PURPOSE: This function returns the subject of an email message. // // ARGUMENTS: ai_msgnum - Message number // // RETURN: Email subject // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/13/2012 RolandS Added handling for subject going onto next line // ----------------------------------------------------------------------------- Long ll_pos String ls_result, ls_remain ll_pos = Pos(is_email[ai_msgnum], "~nSubject:") If ll_pos > 0 Then ls_result = Trim(Mid(is_email[ai_msgnum], ll_pos + 9, 250)) ls_result = of_ReplaceAll(ls_result, CRLF+" ", " ") ls_remain = Mid(ls_result, Pos(ls_result, CRLF) + 2) ls_result = Left(ls_result, Pos(ls_result, CRLF) - 1) ls_result = Trim(ls_result) of_DecodeString(ls_result) If Left(ls_remain, 1) = "~t" Then ls_remain = Mid(ls_remain, 2) ls_remain = Left(ls_remain, Pos(ls_remain, CRLF) - 1) ls_remain = Trim(ls_remain) of_DecodeString(ls_remain) ls_result += ls_remain End If End If Return ls_result end function public subroutine of_msgfrom (integer ai_msgnum, ref string as_name, ref string as_email);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgFrom // // PURPOSE: This function returns the from name/address of an email message. // // ARGUMENTS: ai_msgnum - Message number // as_name - From name (by ref) // as_email - From email (by ref) // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Long ll_pos String ls_result as_name = "" as_email = "" ll_pos = Pos(is_email[ai_msgnum], "~nFrom:") If ll_pos > 0 Then // parse out From: line ls_result = Trim(Mid(is_email[ai_msgnum], ll_pos + 6, 250)) ls_result = Left(ls_result, Pos(ls_result, CRLF) - 1) // separate name & email ll_pos = Pos(ls_result, "<") If ll_pos > 1 Then ll_pos = ll_pos + 1 as_email = Mid(ls_result, ll_pos, Len(ls_result) - ll_pos) as_name = Trim(Left(ls_result, ll_pos - 2)) If Left(as_name, 1) = "~"" Then as_name = Mid(as_name, 2) End If If Right(as_name, 1) = "~"" Then as_name = Left(as_name, Len(as_name) - 1) End If of_DecodeString(as_name) Else If Left(ls_result, 1) = "~"" Then ls_result = Mid(ls_result, 2) End If If Right(ls_result, 1) = "~"" Then ls_result = Left(ls_result, Len(ls_result) - 1) End If of_DecodeString(ls_result) as_email = ls_result as_name = as_email End If End If end subroutine public function string of_crypterror (long al_retval);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_CryptError // // PURPOSE: This function returns message text for Cryptlib errors. // // ARGUMENTS: al_retval - The error returned by a Cryptlib function // // RETURN: Error message // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/21/2010 RolandS Initial coding // 12/30/2013 RolandS Added extended error message // ----------------------------------------------------------------------------- Constant Long CRYPT_ATTRIBUTE_ERRORMESSAGE = 12 String ls_Return, ls_extended Integer li_Strlen Long ll_RetVal If al_RetVal = CRYPT_OK Then Return "" End If choose case al_RetVal // Errors in function calls case -1 ls_Return = "Bad argument - parameter 1" case -2 ls_Return = "Bad argument - parameter 2" case -3 ls_Return = "Bad argument - parameter 3" case -4 ls_Return = "Bad argument - parameter 4" case -5 ls_Return = "Bad argument - parameter 5" case -6 ls_Return = "Bad argument - parameter 6" case -7 ls_Return = "Bad argument - parameter 7" // Errors due to insufficient resources case -10 ls_Return = "Out of memory" case -11 ls_Return = "Data has not been initialised" case -12 ls_Return = "Data has already been init'd" case -13 ls_Return = "Operation not avail at requested sec level" case -14 ls_Return = "No reliable random data available" case -15 ls_Return = "Operation failed" case -16 ls_Return = "Internal consistency check failed" // Security violations case -20 ls_Return = "This type of operation not available" case -21 ls_Return = "No permission to perform this operation" case -22 ls_Return = "Incorrect key used to decrypt data" case -23 ls_Return = "Operation incomplete/still in progress" case -24 ls_Return = "Operation complete/can't continue" case -25 ls_Return = "Operation timed out before completion" case -26 ls_Return = "Invalid/inconsistent information" case -27 ls_Return = "Resource destroyed by extnl.event" // High-level function errors case -30 ls_Return = "Resources/space exhausted" case -31 ls_Return = "Not enough data available" case -32 ls_Return = "Bad/unrecognised data format" case -33 ls_Return = "Signature/integrity check failed" // Data access function errors case -40 ls_Return = "Cannot open object" case -41 ls_Return = "Cannot read item from object" case -42 ls_Return = "Cannot write item to object" case -43 ls_Return = "Requested item not found in object" case -44 ls_Return = "Item already present in object" // Data enveloping errors case -50 ls_Return = "Need resource to proceed" case else ls_Return = "Unknown error code!" end choose // get extended error information li_Strlen = 255 ls_Extended = Space(li_Strlen) ll_RetVal = cryptGetAttributeString(il_Session, & CRYPT_ATTRIBUTE_ERRORMESSAGE, ls_Extended, li_Strlen) If li_Strlen > 0 Then ls_Return += "~r~n~r~n" + ls_Extended End If Return ls_Return end function private function boolean of_sendmsg_crypt (string as_cmd, ref string as_reply);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_sendmsg_crypt // // PURPOSE: This function is used by other TLS functions to send an // encrypted message and receive any reply. // // ARGUMENTS: as_cmd - POP3 command to be sent // as_reply - The reply received from the server // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/21/2010 RolandS Initial coding // 04/03/2012 RolandS Added in wait time // 05/20/2015 MarcoC Not all answers are errors. Added a check to // understand if the answer from server was OK. // ----------------------------------------------------------------------------- Constant String MSGEND = CRLF + Char(46) + CRLF String ls_reply, ls_msg, ls_Buffer Long ll_RetVal, ll_ReplyBytes, ll_SentBytes of_LogFile(as_cmd) // trap unexpected returns ls_Reply = Space(256) ll_RetVal = cryptPopData(il_session, & ls_Reply, Len(ls_Reply), ll_ReplyBytes) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptPopData: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False ElseIf ll_ReplyBytes > 0 Then // MarcoC 05/20/2015 - Added check to see if OK if Upper(Left(ls_reply, 3)) <> '+OK' then ls_msg = "Unexpected bytes in buffer: " + ls_Reply // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False end if End If // Push data ll_RetVal = cryptPushData(il_session, as_cmd, Len(as_cmd), ll_SentBytes) If ll_RetVal <> CRYPT_OK Then ls_msg = "CryptPushData: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False End If If Len(as_cmd) <> ll_SentBytes Then ls_msg = String(ll_SentBytes) + " bytes sent, " + String(Len(as_cmd)) + " expected." // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False End If // Flush outgoing data ll_RetVal = cryptFlushData(il_session) If ll_RetVal <> CRYPT_OK Then ls_msg = "cryptFlushData: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False End If as_Reply = "" ll_ReplyBytes = 0 do while ll_ReplyBytes = 0 // wait before getting data SleepMS(il_sleeptime) // get next Reply ls_Buffer = Space(256) ll_RetVal = cryptPopData(il_session, ls_Buffer, Len(ls_Buffer), ll_ReplyBytes) If ll_RetVal <> CRYPT_OK Then ls_msg = "cryptPopData: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False End If loop If as_cmd = "QUIT" + CRLF Then // add previous Buffer to Reply as_Reply += Left(ls_Buffer, ll_ReplyBytes) of_LogFile(as_Reply) Return True End If do while ll_ReplyBytes > 0 // update progress il_recvdbytes = il_recvdbytes + ll_ReplyBytes of_Progress_Update(il_recvdbytes) // wait before getting data SleepMS(il_sleeptime) // add previous Buffer to Reply as_Reply += Left(ls_Buffer, ll_ReplyBytes) // get next Reply ls_Buffer = Space(256) ll_RetVal = cryptPopData(il_session, ls_Buffer, Len(ls_Buffer), ll_ReplyBytes) If ll_RetVal <> CRYPT_OK Then ls_msg = "cryptPopData: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False End If loop // check the reply for errors If Left(as_Reply, 3) <> "+OK" Then ls_msg = "Command failed: " + as_Reply // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False End If of_LogFile("1. Command: " + as_cmd + "~n2. Reply: " + as_Reply) Return True end function public function boolean of_recvmail_crypt_stop ();// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_recvmail_crypt_Stop // // PURPOSE: This function ends the encrypted sendmail session. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/03/2012 RolandS Added progress tracking // ----------------------------------------------------------------------------- String ls_msg, ls_reply // update progress of_Progress_Message("Disconnecting from the server") // quit the session ls_msg = "QUIT" + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then cryptEnd() Return False End If // Close the session cryptDestroySession(il_Session) // Close the Library cryptEnd() Return True end function public function boolean of_recvmail_crypt_start ();// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvMail_Crypt_Start // // PURPOSE: This function starts the encrypted recvmail session for port 995. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/03/2012 RolandS Added progress tracking // 12/30/2013 RolandS Added logging // 12/30/2013 RolandS Added call to set certificate compliance level // 01/02/2014 RolandS Added call to disable name checking // ----------------------------------------------------------------------------- String ls_reply, ls_msg Long ll_RetVal, ll_ReplyBytes, ll_Version // update progress of_Progress_Message("Connecting to the server") // Initialize the Library ll_RetVal = cryptInit() IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptInit: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) Return False End If of_LogFile("cryptInit: OK") // Create the session ll_RetVal = cryptCreateSession(il_Session, CRYPT_UNUSED, CRYPT_SESSION_SSL) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptCreateSession: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptCreateSession: OK") // Set compliance level ll_RetVal = cryptSetAttribute(il_Session, & CRYPT_OPTION_CERT_COMPLIANCELEVEL, CRYPT_COMPLIANCELEVEL_OBLIVIOUS) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptSetAttribute (Set compliance level): " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptSetAttribute (Set compliance level): OK") // Disable name checking ll_RetVal = cryptSetAttribute(il_Session, & CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_NAMEVERIFY) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptSetAttribute (Disable name checking): " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptSetAttribute (Disable name checking): OK") // Add the server name ll_RetVal = cryptSetAttributeString(il_Session, & CRYPT_SESSINFO_SERVER_NAME, is_server, Len(is_server)) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptSetAttributeString (Add the server name): " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptSetAttributeString (Add the server name): OK") // Specify the Port ll_RetVal = cryptSetAttribute(il_Session, & CRYPT_SESSINFO_SERVER_PORT, iui_port) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptSetAttribute (Specify the Port): " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptSetAttribute (Specify the Port): OK") // Activate the session ll_RetVal = cryptSetAttribute(il_Session, CRYPT_SESSINFO_ACTIVE, 1) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptSetAttribute (Activate the session): " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptSetAttribute (Activate the session): OK") // Remove any response created by connecting ls_Reply = Space(256) ll_RetVal = cryptPopData(il_session, & ls_Reply, Len(ls_Reply), ll_ReplyBytes) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptPopData: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptPopData: OK") // update progress of_Progress_Message("Logging into the server") // send the Userid ls_msg = "USER " + is_userid + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If // send the Password ls_msg = "PASS " + is_passwd + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If // Remove any response created by setting the userid/password ls_Reply = Space(256) ll_RetVal = cryptPopData(il_session, & ls_Reply, Len(ls_Reply), ll_ReplyBytes) IF ll_RetVal <> CRYPT_OK Then ls_msg = "cryptPopData: " + of_CryptError(ll_RetVal) // of_SetLastError(ls_msg) of_LogFile(ls_msg) cryptEnd() Return False End If of_LogFile("cryptPopData: OK") Return True end function private subroutine of_separate (readonly string as_content, ref string as_part[], ref string as_type[]);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Separate // // PURPOSE: This function is used to separate message parts. // // ARGUMENTS: as_content - The message content // as_part - Array of parts (by ref) // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 03/17/2012 RolandS Added Base64 Encoding support // 04/11/2012 RolandS Corrected parsing issues // 04/13/2012 RolandS Moved base64 decoding to of_Base64 // 08/05/2013 RolandS Changed to handle content-type and // content-transfer-encoding in different order // ----------------------------------------------------------------------------- String ls_boundary, ls_body, ls_work, ls_part[] Long ll_pos, ll_idx, ll_max, ll_index Boolean lb_ContentFound ls_boundary = Mid(as_content, Pos(as_content, "boundary=") + 9, 256) ls_boundary = Left(ls_boundary, Pos(ls_boundary, CRLF) - 1) If Left(ls_boundary, 1) = "~"" Then ls_boundary = Mid(ls_boundary, 2) ls_boundary = Left(ls_boundary, Pos(ls_boundary, "~"") - 1) End If ls_boundary = "--" + ls_boundary ll_pos = Pos(as_content, ls_boundary) ls_body = Mid(as_content, ll_pos + Len(ls_boundary)) ll_max = of_Parse(ls_body, ls_boundary, ls_part) For ll_idx = 1 To ll_max ll_pos = Pos(Lower(ls_part[ll_idx]), CRLF + "content-type: ") If ll_pos > 0 Then lb_ContentFound = True ls_work = Mid(ls_part[ll_idx], ll_pos + 2) If Lower(Left(ls_work, 24)) = "content-type: multipart/" Then of_Separate(ls_work, as_part, as_type) Else ll_index = UpperBound(as_part) + 1 // separate out part content ll_pos = Pos(ls_work, CRLF + CRLF) as_part[ll_index] = Mid(ls_work, ll_pos + 4) If Left(as_part[ll_index], 2) = CRLF Then as_part[ll_index] = Mid(as_part[ll_index], 3) End If If Right(as_part[ll_index], 2) = CRLF Then as_part[ll_index] = Left(as_part[ll_index], Len(as_part[ll_index]) - 2) End If // separate out the content type ls_work = Left(ls_work, ll_pos - 1) ls_work = of_ReplaceAll(ls_work, "~t", ";") ls_work = of_ReplaceAll(ls_work, "~r~n", ";") ls_work = of_ReplaceAll(ls_work, ";;;", ";") ls_work = of_ReplaceAll(ls_work, ";;", ";") ls_work = of_ReplaceAll(ls_work, "ype:text", "ype: text") If Left(ls_work, 1) = ";" Then ls_work = Mid(ls_work, 2) End If as_type[ll_index] = ls_work // determine encoding ls_work = Lower(ls_work) If Left(ls_work, 19) = "content-type: text/" Then ls_work = Lower(Left(ls_part[ll_idx], 25000)) ll_pos = Pos(ls_work, CRLF + "content-transfer-encoding:") If ll_pos > 0 Then ls_work = of_ReplaceAll(ls_work, "~r~n", ";") ll_pos = Pos(ls_work, "content-transfer-encoding:") ls_work = Mid(ls_work, ll_pos + 26) + ";" ls_work = Trim(Left(ls_work, Pos(ls_work, ";") - 1)) choose case ls_work case "quoted-printable" of_QuotedPrintable(as_part[ll_index]) case "base64" of_Base64(as_type[ll_index], as_part[ll_index]) end choose End If End If // remove trailing CRLF do while Right(as_part[ll_index], 2) = CRLF as_part[ll_index] = Left(as_part[ll_index], Len(as_part[ll_index]) - 2) loop End If End If Next If Not lb_ContentFound Then of_Separate(ls_body, as_part, as_type) End If end subroutine public function string of_msgproperty (integer ai_msgnum, string as_property);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgSubject // // PURPOSE: This function returns the subject of an email message. // // ARGUMENTS: ai_msgnum - Message number // as_property - Name of the property to return // // RETURN: Property value // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Long ll_pos String ls_result ll_pos = Pos(is_email[ai_msgnum], "~n" + as_property + ":") If ll_pos > 0 Then ll_pos = ll_pos + Len(as_property) + 2 ls_result = Trim(Mid(is_email[ai_msgnum], ll_pos, 250)) ls_result = Left(ls_result, Pos(ls_result, CRLF)) If Left(ls_result, 1) = "<" Then ls_result = Mid(ls_result, 2) ll_pos = LastPos(ls_result, ">") ls_result = Left(ls_result, ll_pos - 1) End If End If Return Trim(ls_result) end function public function integer of_msgbody (integer ai_msgnum, ref string as_part[], ref string as_type[]);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgBody // // PURPOSE: This function returns the parts of an email message. // // ARGUMENTS: ai_msgnum - Message number // as_part - Body Parts (by ref) // as_type - Body Part Content Type (by ref) // // RETURN: Number of parts // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 03/17/2012 RolandS Added Base64 Encoding support // 04/03/2012 RolandS Changes to how encoding parsed // 04/13/2012 RolandS Moved base64 decoding to of_Base64 // 05/20/2015 MarcoC Fixed issue with finding 'content-transfer-encoding' // ----------------------------------------------------------------------------- String ls_empty[], ls_work Long ll_pos, ll_index = 1 // initialize as_part = ls_empty as_type = ls_empty // break out the body content from headers ls_work = Lower(Left(is_email[ai_msgnum], 25000)) ll_pos = Pos(ls_work, "~ncontent-type:") If ll_pos = 0 Then ll_pos = Pos(ls_work, CRLF + CRLF) as_part[ll_index] = Mid(is_email[ai_msgnum], ll_pos + 4) as_type[ll_index] = "Content-Type: text/plain;" Return ll_index End If ls_work = Mid(is_email[ai_msgnum], ll_pos + 1) // remove trailing CRLF do while Right(ls_work, 2) = CRLF ls_work = Left(ls_work, Len(ls_work) - 2) loop // check for multipart If Lower(Left(ls_work, 24)) = "content-type: multipart/" Then of_Separate(ls_work, as_part, as_type) Else // separate out part content ll_pos = Pos(ls_work, CRLF + CRLF) as_part[ll_index] = Mid(ls_work, ll_pos + 4) If Right(as_part[ll_index], 2) = CRLF Then as_part[ll_index] = Left(as_part[ll_index], Len(as_part[ll_index]) - 2) End If // separate out the content type ls_work = Left(ls_work, ll_pos - 1) ls_work = of_ReplaceAll(ls_work, "~t", ";") ls_work = of_ReplaceAll(ls_work, "~r~n", ";") ls_work = of_ReplaceAll(ls_work, ";;;", ";") ls_work = of_ReplaceAll(ls_work, ";;", ";") ls_work = of_ReplaceAll(ls_work, "ype:text", "ype: text") If Left(ls_work, 1) = ";" Then ls_work = Mid(ls_work, 2) End If as_type[ll_index] = ls_work // determine encoding ls_work = Lower(Left(is_email[ai_msgnum], 25000)) ll_pos = Pos(ls_work, CRLF + "content-transfer-encoding:") If ll_pos > 0 Then ls_work = of_ReplaceAll(ls_work, "~r~n", ";") // MarcoC 05/20/2015 - Changed to use semi-colon instead of CRLF ll_pos = Pos(ls_work, ";content-transfer-encoding:") ls_work = Mid(ls_work, ll_pos + 28) + ";" ls_work = Trim(Left(ls_work, Pos(ls_work, ";") - 1)) choose case ls_work case "quoted-printable" of_QuotedPrintable(as_part[ll_index]) case "base64" of_Base64(as_type[ll_index], as_part[ll_index]) end choose End If // remove trailing CRLF do while Right(as_part[ll_index], 2) = CRLF as_part[ll_index] = Left(as_part[ll_index], Len(as_part[ll_index]) - 2) loop End If Return UpperBound(as_part) end function public function integer of_msgload (string as_filename);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgLoad // // PURPOSE: This function loads a message from a file. // // ARGUMENTS: as_filename - File name of an email message file (.eml) // // RETURN: Message number // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/10/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Integer li_index Blob lblob_data If of_ReadFile(as_filename, lblob_data) Then li_index = UpperBound(is_email) + 1 is_email[li_index] = String(lblob_data, EncodingAnsi!) is_uidl[li_index] = "" End If Return li_index end function private function boolean of_readfile (string as_filename, ref blob ablob_data);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_ReadFile // // PURPOSE: This function is used to read a file from disk to a blob. // // ARGUMENTS: as_filename - Filename // ablob_data - By ref blob to receive the file contents // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/10/2012 RolandS Initial coding // ----------------------------------------------------------------------------- ULong lul_bytes, lul_length Long ll_hFile Blob lblob_filedata Boolean lb_result // get file length lul_length = FileLength(as_filename) // open file for read ll_hFile = CreateFile(as_filename, GENERIC_READ, & FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0) If ll_hFile = INVALID_HANDLE_VALUE Then Return False End If // read the entire file contents in one shot lblob_filedata = Blob(Space(lul_length), EncodingAnsi!) lb_result = ReadFile(ll_hFile, lblob_filedata, & lul_length, lul_bytes, 0) ablob_data = BlobMid(lblob_filedata, 1, lul_length) // close the file CloseHandle(ll_hFile) Return lb_result end function public subroutine of_quotedprintable (ref string as_content);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_QuotedPrintable // // PURPOSE: This function decodes quoted-printable text. // // ARGUMENTS: as_content - Text content (by ref) // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 05/20/2015 MarcoC Sets default for new argument ab_recurse // ----------------------------------------------------------------------------- of_QuotedPrintable(as_content, False) end subroutine public function string of_attachname (string as_type);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_AttachName // // PURPOSE: This function extracts attachment file name from content type. // // ARGUMENTS: as_type - The message Content-Type string // // RETURN: Filename // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/14/2012 RolandS Initial coding // ----------------------------------------------------------------------------- String ls_filename Long ll_pos ll_pos = Pos(as_type, "filename=") ls_filename = Mid(as_type, ll_pos + 10) ll_pos = Pos(ls_filename, "~"") ls_filename = Left(ls_filename, ll_pos - 1) Return ls_filename end function public function string of_replaceall (readonly string as_oldstring, readonly string as_findstr, readonly string as_replace);// ----------------------------------------------------------------------------- // FUNCTION: n_pop3.of_ReplaceAll // // PURPOSE: This function replaces all occurrences of one string in another. // // ARGUMENTS: as_oldstring - The string to search within // as_findstr - The string to look for // as_replace - The string to replace with // // RETURN: Updated string // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 06/24/2008 RolandS Initial coding // 03/17/2012 RolandS Changed arguments to readonly // ----------------------------------------------------------------------------- String ls_newstring Long ll_findstr, ll_replace, ll_pos // get length of strings ll_findstr = Len(as_findstr) ll_replace = Len(as_replace) // find first occurrence ls_newstring = as_oldstring ll_pos = Pos(ls_newstring, as_findstr) Do While ll_pos > 0 // replace old with new ls_newstring = Replace(ls_newstring, ll_pos, ll_findstr, as_replace) // find next occurrence ll_pos = Pos(ls_newstring, as_findstr, (ll_pos + ll_replace)) Loop Return ls_newstring end function public function boolean of_writefile (readonly string as_filename, readonly blob ablob_filedata);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_WriteFile // // PURPOSE: This function writes a blob to a file on disk. // // ARGUMENTS: as_filename - The name of the file // ablob_filedata - The blob data of the file // // RETURN: 0 = Success // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/10/2012 RolandS Initial coding // 03/17/2012 RolandS Changed arguments to readonly // ----------------------------------------------------------------------------- ULong lul_file, lul_length, lul_written Boolean lb_rtn lul_length = Len(ablob_filedata) // open file for write lul_file = CreateFile(as_filename, GENERIC_WRITE, & FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0) If lul_file = INVALID_HANDLE_VALUE Then Return False End If // write file to disk lb_rtn = WriteFile(lul_file, ablob_filedata, & lul_Length, lul_written, 0) // close the file CloseHandle(lul_file) Return True end function public subroutine of_decodestring (ref string as_string);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_DecodeString // // PURPOSE: This function decodes encoded strings. // // ARGUMENTS: as_string - The string to be decoded // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/21/2012 RolandS Initial coding // 04/18/2012 RolandS Added handling for Subject going to next line // Changed GB2312 to Ansi // 05/20/2015 MarcoC Added Lower() when looking for format tags // Added decode header =?iso-8859-15?q? // ----------------------------------------------------------------------------- Long ll_pos // quoted-printable If Left(Lower(as_string), 15) = "=?iso-8859-1?q?" Then as_string = Mid(as_string, 16, Len(as_string) - 17) ll_pos = Pos(Lower(as_string), "?= =?iso-8859-1?q?") If ll_pos > 0 Then as_string = Left(as_string, ll_pos - 1) + & Mid(as_string, ll_pos + 18) End If of_QuotedPrintable(as_string) Return End If // MarcoC 05/20/2015 - Added this decode header If Left(Lower(as_string), 16) = "=?iso-8859-15?q?" Then as_string = Mid(as_string, 17, Len(as_string) - 18) ll_pos = Pos(Lower(as_string), "?= =?iso-8859-15?q?") If ll_pos > 0 Then as_string = Left(as_string, ll_pos - 1) + & Mid(as_string, ll_pos + 19) End If of_QuotedPrintable(as_string) Return End If If Left(Lower(as_string), 10) = "=?utf-8?q?" Then as_string = Mid(as_string, 11, Len(as_string) - 12) ll_pos = Pos(Lower(as_string), "?= =?utf-8?q?") If ll_pos > 0 Then as_string = Left(as_string, ll_pos - 1) + & Mid(as_string, ll_pos + 13) End If of_QuotedPrintable(as_string) Return End If // base64 If Left(Lower(as_string), 15) = "=?iso-8859-1?b?" Then as_string = Mid(as_string, 16, Len(as_string) - 17) ll_pos = Pos(Lower(as_string), "?= =?iso-8859-1?b?") If ll_pos > 0 Then as_string = Left(as_string, ll_pos - 1) + & Mid(as_string, ll_pos + 18) End If as_string = String(of_Decode64(as_string), EncodingUTF8!) Return End If If Left(Lower(as_string), 10) = "=?utf-8?b?" Then as_string = Mid(as_string, 11, Len(as_string) - 12) ll_pos = Pos(Lower(as_string), "?= =?utf-8?b?") If ll_pos > 0 Then as_string = Left(as_string, ll_pos - 1) + & Mid(as_string, ll_pos + 13) End If as_string = String(of_Decode64(as_string), EncodingUTF8!) Return End If If Left(Lower(as_string), 11) = "=?gb2312?b?" Then as_string = Mid(as_string, 12, Len(as_string) - 13) ll_pos = Pos(Lower(as_string), "?= =?gb2312?b?") If ll_pos > 0 Then as_string = Left(as_string, ll_pos - 1) + & Mid(as_string, ll_pos + 14) End If as_string = String(of_Decode64(as_string), EncodingAnsi!) Return End If end subroutine public function boolean of_msgreceipt (integer ai_msgnum, ref string as_name, ref string as_email);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgReceipt // // PURPOSE: This function returns the Reply Receipt name/address of // an email message if one was provided. // // ARGUMENTS: ai_msgnum - Message number // as_name - From name (by ref) // as_email - From email (by ref) // // RETURN: True = Reply Receipt found, False = not found // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/24/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Long ll_pos String ls_result as_name = "" as_email = "" ll_pos = Pos(is_email[ai_msgnum], "~nDisposition-Notification-To:") If ll_pos = 0 Then Return False Else // parse out From: line ls_result = Trim(Mid(is_email[ai_msgnum], ll_pos + 29, 250)) ls_result = Left(ls_result, Pos(ls_result, CRLF) - 1) // separate name & email ll_pos = Pos(ls_result, "<") If ll_pos > 1 Then ll_pos = ll_pos + 1 as_email = Mid(ls_result, ll_pos, Len(ls_result) - ll_pos) as_name = Left(ls_result, ll_pos - 3) of_DecodeString(as_name) If Left(as_name, 1) = "~"" Then as_name = Mid(as_name, 2) End If If Right(as_name, 1) = "~"" Then as_name = Left(as_name, Len(as_name) - 1) End If Else of_DecodeString(ls_result) If Left(ls_result, 1) = "~"" Then ls_result = Mid(ls_result, 2) End If If Right(ls_result, 1) = "~"" Then ls_result = Left(ls_result, Len(ls_result) - 1) End If as_email = ls_result as_name = as_email End If Return True End If end function public subroutine of_progress_done ();// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Progress_Done // // PURPOSE: This function notifies the progress bar that the process is done. // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- If il_window > 0 Then // 1=Start, 2=Update, 3=Done Send(il_window, il_event, 3, 0) Yield() End If end subroutine public subroutine of_progress_message (string as_message);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Progress_Message // // PURPOSE: This function sends a text message to the progress event. // // ARGUMENTS: as_message - The status message to be displayed // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- If il_window > 0 Then SendStringMessage(il_window, il_event, 0, as_message) Yield() End If end subroutine public subroutine of_progress_notify (long al_window, long al_event);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Progress_Notify // // PURPOSE: This function records the window handle and custom event number // that will be notified when progress is made. // // ARGUMENTS: al_window - Handle of the window to be notified // al_event - Custom Event number ( 1 - 75 ) // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- il_window = al_window il_event = 1023 + al_event end subroutine public subroutine of_progress_start (long al_length);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Progress_Start // // PURPOSE: This function notifies the progress bar process is starting. // // ARGUMENTS: al_length - Number of bytes downloaded so far // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- If il_window > 0 Then il_totalbytes = al_length // 1=Start, 2=Update, 3=Done Send(il_window, il_event, 1, 0) Yield() End If end subroutine public subroutine of_progress_update (long al_length);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Progress_Update // // PURPOSE: This function notifies the progress bar with percent complete. // // ARGUMENTS: al_length - Number of bytes downloaded so far // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Long ll_percent If il_window > 0 Then If il_totalbytes > 0 Then ll_percent = 100 * (al_length / il_totalbytes) // 1=Start, 2=Update, 3=Done Send(il_window, il_event, 2, ll_percent) Yield() End If End If end subroutine public subroutine of_setsleeptime (long al_sleeptime);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_SetSleepTime // // PURPOSE: This function sets the sleep time used in several places in the // download process. // // ARGUMENTS: al_sleeptime - Sleep interval in milliseconds. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/03/2012 RolandS Initial coding // ----------------------------------------------------------------------------- il_sleeptime = al_sleeptime end subroutine public subroutine of_base64 (readonly string as_type, ref string as_part);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Base64 // // PURPOSE: This function decodes base64 encoded messages. // // Note that the PB8 and PB10 versions of this function are // very different due to Unicode conversion! // // ARGUMENTS: as_type - Content descriptors // as_part - Text content (by ref) // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/13/2012 RolandS Initial coding // ----------------------------------------------------------------------------- String ls_charset ls_charset = Mid(as_type, Pos(as_type, "charset=") + 8) ls_charset = Left(ls_charset, Pos(ls_charset, ";") - 1) If Left(ls_charset, 1) = "~"" Then ls_charset = Mid(ls_charset, 2) End If If Right(ls_charset, 1) = "~"" Then ls_charset = Left(ls_charset, Len(ls_charset) - 1) End If choose case Lower(ls_charset) case "utf8", "utf-8" as_part = String(of_Decode64(as_part), EncodingUTF8!) case "gb2312" as_part = String(of_Decode64(as_part), EncodingAnsi!) case else as_part = String(of_Decode64(as_part), EncodingUTF8!) end choose end subroutine public function boolean of_recvmail_msgs (boolean ab_delete, boolean ab_headers);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvMail_Msgs // // PURPOSE: This function receives email messages from the server. // // ARGUMENTS: ab_delete - Indicates whether messages are deleted from server // ab_headers - True if only headers and first 10 lines of the // messages are to be downloaded. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/03/2012 RolandS Added progress tracking // 04/13/2012 RolandS Added retrieval of UIDL (unique identifier) // 04/13/2012 RolandS Added ab_headers argument // ----------------------------------------------------------------------------- Constant String MSGEND = CRLF + Char(46) + CRLF String ls_msg, ls_reply, ls_content, ls_wrk, ls_empty[] Integer li_cnt, li_idx, li_pos Long ll_pos, ll_total, ll_bytes is_email = ls_empty is_uidl = ls_empty // request the number of messages ls_msg = "STAT" + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If // parse out msg count ls_wrk = Mid(ls_reply, 5) li_pos = Pos(ls_wrk, " ") li_cnt = Integer(Left(ls_wrk, li_pos - 1)) ll_total = Long(Mid(ls_wrk, li_pos + 1)) of_Progress_Start(ll_total) For li_idx = 1 To li_cnt // update progress of_Progress_Message("Downloading message " + String(li_idx) + " of " + String(li_cnt)) // get the unique identifier ls_msg = "UIDL " + String(li_idx) + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If If Left(ls_reply, 3) = "+OK" Then ls_content = Mid(ls_reply, 9) // copy content to array (removing CRLF at end) If Right(ls_content, 2) = CRLF Then is_uidl[li_idx] = Left(ls_content, Len(ls_content) - 2) End If End If // send retrieve message If ab_headers Then ls_msg = "TOP " + String(li_idx) + " 10" + CRLF Else ls_msg = "RETR " + String(li_idx) + CRLF End If If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If If Left(ls_reply, 3) = "+OK" Then // parse out data already received ll_pos = Pos(ls_reply, CRLF) ls_content = Mid(ls_reply, ll_pos + 2) ll_bytes = ll_bytes + Len(ls_content) of_Progress_Update(ll_bytes) // receive the rest of the data do while Right(ls_reply, 5) <> MSGEND // wait before continuing SleepMS(il_sleeptime) // receive the next block of data of_Recv(il_Socket, ls_reply) ls_content += ls_reply ll_bytes = ll_bytes + Len(ls_reply) of_Progress_Update(ll_bytes) loop // copy content to array (removing MSGEND) is_email[li_idx] = Left(ls_content, Len(ls_content) - 3) // delete message If ab_delete Then ls_msg = "DELE " + String(li_idx) + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If End If Else // of_SetLastError(ls_reply) of_LogFile(ls_reply) Return False End If // wait before continuing SleepMS(il_sleeptime) Next of_Progress_Done() Return True end function public function boolean of_recvmail (boolean ab_delete, boolean ab_headers);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvMail // // PURPOSE: This function is the main process to receive email messages. // // ARGUMENTS: ab_delete - Whether messages are deleted from server // ab_headers - True if only headers and first 10 lines of the // messages are to be downloaded. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/13/2012 RolandS Added ab_headers argument // ----------------------------------------------------------------------------- DateTime ldt_current // log start of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("~r~nof_RecvMail Start: " + String(ldt_current) + "~r~n") // start the server session If Not of_RecvMail_Start() Then Return False End If // receive the email messages If Not of_RecvMail_Msgs(ab_delete, ab_headers) Then Return False End If // stop the server session If Not of_RecvMail_Stop() Then Return False End If // log end of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("of_RecvMail End: " + String(ldt_current)) Return True end function public function boolean of_recvmail_crypt_msgs (boolean ab_delete, boolean ab_headers);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvMail_Crypt_Msgs // // PURPOSE: This function receives the email messages from the server. // // ARGUMENTS: ab_delete - Whether messages are deleted from server // ab_headers - True if only headers and first 10 lines of the // messages are to be downloaded. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/03/2012 RolandS Added progress tracking // 04/13/2012 RolandS Added retrieval of UIDL (unique identifier) // 04/13/2012 RolandS Added ab_headers argument // 05/20/2015 MarcoC If STAT command fails (whatever reason), // use LIST instead to get same informations // ----------------------------------------------------------------------------- Constant String MSGEND = CRLF + Char(46) + CRLF String ls_msg, ls_reply, ls_content, ls_wrk, ls_empty[] Integer li_cnt, li_idx, li_pos Long ll_pos, ll_total is_email = ls_empty is_uidl = ls_empty SleepMS(50) // request the number of messages ls_msg = "STAT" + CRLF ls_reply = "" If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If // MarcoC 05/20/2015 - If STAT command fails (whatever reason), // use LIST instead to get same information if Pos(Lower(ls_reply), "welcome") > 0 then // request the number of messages ls_msg = "LIST" + CRLF ls_reply = "" If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If li_pos = Pos(ls_wrk, "(") ll_total = Long(Mid(ls_wrk, li_pos + 1, (Pos(ls_wrk, ')') - li_pos) -1 )) else li_pos = Pos(ls_wrk, " ") ll_total = Long(Mid(ls_wrk, li_pos + 1)) end if // parse out msg count ls_wrk = Mid(ls_reply, 5) li_pos = Pos(ls_wrk, " ") li_cnt = Integer(Left(ls_wrk, li_pos - 1)) il_recvdbytes = 0 of_Progress_Start(ll_total) of_logfile("Found messages: " + string(li_cnt)) For li_idx = 1 To li_cnt // update progress of_Progress_Message("Downloading message " + String(li_idx) + " of " + String(li_cnt)) // get the unique identifier ls_msg = "UIDL " + String(li_idx) + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If If Left(ls_reply, 3) = "+OK" Then ls_content = Mid(ls_reply, 9) // copy content to array (removing CRLF at end) If Right(ls_content, 2) = CRLF Then is_uidl[li_idx] = Left(ls_content, Len(ls_content) - 2) End If End If // send retrieve message If ab_headers Then ls_msg = "TOP " + String(li_idx) + " 10" + CRLF Else ls_msg = "RETR " + String(li_idx) + CRLF End If If Not of_sendmsg_crypt(ls_msg, ls_reply) Then of_logfile("Failed during send of message '" + ls_msg + "' with error string: " + ls_reply) Return False End If If Left(ls_reply, 3) = "+OK" Then // copy content (removing status line) ls_content = Mid(ls_reply, Pos(ls_reply, CRLF) + 2) // copy content (removing MSGEND) is_email[li_idx] = Left(ls_content, Len(ls_content) - 3) // delete message If ab_delete Then ls_msg = "DELE " + String(li_idx) + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then of_logfile("Failed during send of message '" + ls_msg + "' with error string: " + ls_reply) Return False End If End If Else // of_SetLastError(ls_reply) of_LogFile(ls_reply) Return False End If // wait before continuing SleepMS(il_sleeptime) Next of_Progress_Done() Return True end function public function boolean of_recvsslmail (boolean ab_delete, boolean ab_headers);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvSSLMail // // PURPOSE: This function is the main process to send the email. // // ARGUMENTS: ab_delete - Whether messages are deleted from server // ab_headers - True if only headers and first 10 lines of the // messages are to be downloaded. // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/03/2012 RolandS Initial coding // 04/13/2012 RolandS Added ab_headers argument // ----------------------------------------------------------------------------- DateTime ldt_current // log start of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("~r~nof_RecvSSLMail Start: " + String(ldt_current) + "~r~n") // start the server session If Not of_RecvMail_Crypt_Start() Then Return False End If // receive the email messages If Not of_RecvMail_Crypt_Msgs(ab_delete, ab_headers) Then Return False End If // stop the server session If Not of_RecvMail_Crypt_Stop() Then Return False End If // log end of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("of_RecvSSLMail End: " + String(ldt_current)) Return True end function public function datetime of_msgsentdate (integer ai_msgnum);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgSentDate // // PURPOSE: This function returns the datetime the email message was sent. // // ARGUMENTS: ai_msgnum - Message number // // RETURN: DateTime value // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/13/2012 RolandS Initial coding // 02/26/2014 RolandS Changed to handle when no time given // ----------------------------------------------------------------------------- SYSTEMTIME lstr_UTCTime, lstr_LocalTime String ls_date, ls_parts[], ls_time, ls_offset Integer li_offset_hrs, li_offset_day Boolean lb_OffsetPlus, lb_rtn DateTime ldt_datetime Date ld_date Time lt_time Long ll_pos ls_date = of_MsgProperty(ai_msgnum, "Date") ll_pos = Pos(ls_date, ",") If ll_pos > 0 Then ls_date = Trim(Mid(ls_date, ll_pos + 1)) End If of_Parse(ls_date, " ", ls_parts) // prevent out of array range abort ls_parts[9] = "" ls_offset = ls_parts[5] If Left(ls_offset, 1 ) = "+" Then lb_OffsetPlus = True li_offset_hrs = Integer(Mid(ls_offset, 2, 2)) End If lstr_UTCTime.wDay = Integer(ls_parts[1]) choose case Lower(ls_parts[2]) case "jan" lstr_UTCTime.wMonth = 1 case "feb" lstr_UTCTime.wMonth = 2 case "mar" lstr_UTCTime.wMonth = 3 case "apr" lstr_UTCTime.wMonth = 4 case "may" lstr_UTCTime.wMonth = 5 case "jun" lstr_UTCTime.wMonth = 6 case "jul" lstr_UTCTime.wMonth = 7 case "aug" lstr_UTCTime.wMonth = 8 case "sep" lstr_UTCTime.wMonth = 9 case "oct" lstr_UTCTime.wMonth = 10 case "nov" lstr_UTCTime.wMonth = 11 case "dec" lstr_UTCTime.wMonth = 12 end choose lstr_UTCTime.wYear = Integer(ls_parts[3]) ls_time = ls_parts[4] of_Parse(ls_time, ":", ls_parts) If UpperBound(ls_parts) < 3 Then // no time was given lstr_LocalTime = lstr_UTCTime Else // convert UTC Time to Local Time lstr_UTCTime.wHour = Integer(ls_parts[1]) lstr_UTCTime.wMinute = Integer(ls_parts[2]) lstr_UTCTime.wSecond = Integer(ls_parts[3]) lb_rtn = SystemTimeToTzSpecificLocalTime(0, & lstr_UTCTime, lstr_LocalTime) If lb_OffsetPlus Then If lstr_LocalTime.wHour < li_offset_hrs Then lstr_LocalTime.wHour = (24 + lstr_LocalTime.wHour) - li_offset_hrs li_offset_day = -1 Else lstr_LocalTime.wHour = lstr_LocalTime.wHour - li_offset_hrs End If Else lstr_LocalTime.wHour = lstr_LocalTime.wHour + li_offset_hrs If lstr_LocalTime.wHour > 23 Then lstr_LocalTime.wHour = lstr_LocalTime.wHour - 24 li_offset_day = 1 End If End If End If ld_date = Date( lstr_LocalTime.wYear, & lstr_LocalTime.wMonth, & lstr_LocalTime.wDay ) ld_date = RelativeDate(ld_date, li_offset_day) lt_time = Time( lstr_LocalTime.wHour, & lstr_LocalTime.wMinute, & lstr_LocalTime.wSecond ) ldt_datetime = DateTime(ld_date, lt_time) Return ldt_datetime end function public function integer of_msgadd (readonly string as_email, readonly string as_uidl);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgAdd // // PURPOSE: This function loads a message from strings. // // ARGUMENTS: as_email - Email content // as_uidl - Unique identifier // // RETURN: Message number // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/15/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Integer li_index li_index = UpperBound(is_email) + 1 is_email[li_index] = as_email is_uidl[li_index] = as_uidl Return li_index end function public function string of_msguidl (integer ai_msgnum);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_MsgUIDL // // PURPOSE: This function returns the unique identifier of an email message. // // ARGUMENTS: ai_msgnum - Message number // // RETURN: Unique identifier // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/15/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Return is_uidl[ai_msgnum] end function public function boolean of_deletemail (string as_uidl[]);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_DeleteMail // // PURPOSE: This function deletes specified email messages. // // ARGUMENTS: as_uidl - Unique identifiers of the emails to delete // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/15/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Constant String MSGEND = CRLF + Char(46) + CRLF String ls_msg, ls_reply, ls_wrk, ls_uidl, ls_content Integer li_pos, li_idx, li_cnt, li_idx2, li_cnt2 DateTime ldt_current // log start of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("~r~nof_DeleteMail Start: " + String(ldt_current) + "~r~n") // start the server session If Not of_RecvMail_Start() Then Return False End If // request the number of messages ls_msg = "STAT" + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If // parse out msg count ls_wrk = Mid(ls_reply, 5) li_pos = Pos(ls_wrk, " ") li_cnt = Integer(Left(ls_wrk, li_pos - 1)) li_cnt2 = UpperBound(as_uidl) For li_idx = 1 To li_cnt ls_uidl = "" // get the unique identifier ls_msg = "UIDL " + String(li_idx) + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If If Left(ls_reply, 3) = "+OK" Then ls_content = Mid(ls_reply, 9) // remove any CRLF at end If Right(ls_content, 2) = CRLF Then ls_uidl = Left(ls_content, Len(ls_content) - 2) End If End If For li_idx2 = 1 To li_cnt2 // delete this email if it matches If ls_uidl = as_uidl[li_idx2] Then // update progress of_Progress_Message("Deleting message " + & String(li_idx) + " from server") ls_msg = "DELE " + String(li_idx) + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If End If Next Next // stop the server session If Not of_RecvMail_Stop() Then Return False End If of_Progress_Done() // log end of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("of_DeleteMail End: " + String(ldt_current)) Return True end function public function boolean of_recvmail (integer ai_msgnum[]);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvMail // // PURPOSE: This function downloads specified email messages that were // downloaded in header only mode previously. // // ARGUMENTS: ai_msgnum - Message numbers of the emails to download // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/15/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Constant String MSGEND = CRLF + Char(46) + CRLF String ls_msg, ls_reply, ls_wrk, ls_uidl, ls_content Integer li_pos, li_idx, li_cnt, li_idx2, li_cnt2, li_msgnum DateTime ldt_current Long ll_pos // log start of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("~r~nof_RecvMail Start: " + String(ldt_current) + "~r~n") // start the server session If Not of_RecvMail_Start() Then Return False End If // request the number of messages ls_msg = "STAT" + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If // parse out msg count ls_wrk = Mid(ls_reply, 5) li_pos = Pos(ls_wrk, " ") li_cnt = Integer(Left(ls_wrk, li_pos - 1)) li_cnt2 = UpperBound(ai_msgnum) For li_idx = 1 To li_cnt ls_uidl = "" // get the unique identifier ls_msg = "UIDL " + String(li_idx) + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If If Left(ls_reply, 3) = "+OK" Then ls_content = Mid(ls_reply, 9) // remove any CRLF at end If Right(ls_content, 2) = CRLF Then ls_uidl = Left(ls_content, Len(ls_content) - 2) End If End If For li_idx2 = 1 To li_cnt2 // download this email if it matches li_msgnum = ai_msgnum[li_idx2] If ls_uidl = is_uidl[li_msgnum] Then // update progress of_Progress_Message("Downloading message " + & String(li_idx) + " from server") // send retrieve message ls_msg = "RETR " + String(li_idx) + CRLF If Not of_SendMsg(ls_msg, ls_reply) Then of_CloseSocket(il_Socket) Return False End If If Left(ls_reply, 3) = "+OK" Then // parse out data already received ll_pos = Pos(ls_reply, CRLF) ls_content = Mid(ls_reply, ll_pos + 2) // receive the rest of the data do while Right(ls_reply, 5) <> MSGEND // wait before continuing SleepMS(il_sleeptime) // receive the next block of data of_Recv(il_Socket, ls_reply) ls_content += ls_reply loop // copy content to array (removing MSGEND) is_email[li_msgnum] = Left(ls_content, Len(ls_content) - 3) End If End If Next Next // stop the server session If Not of_RecvMail_Stop() Then Return False End If of_Progress_Done() // log end of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("of_RecvMail End: " + String(ldt_current)) Return True end function public function boolean of_deletesslmail (string as_uidl[]);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_DeleteSSLMail // // PURPOSE: This function deletes specified email messages. // // ARGUMENTS: as_uidl - Unique identifiers of the emails to delete // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/15/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Constant String MSGEND = CRLF + Char(46) + CRLF String ls_msg, ls_reply, ls_wrk, ls_uidl, ls_content Integer li_pos, li_idx, li_cnt, li_idx2, li_cnt2 DateTime ldt_current // log start of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("~r~nof_DeleteSSLMail Start: " + String(ldt_current) + "~r~n") // start the server session If Not of_RecvMail_Crypt_Start() Then Return False End If // request the number of messages ls_msg = "STAT" + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If // parse out msg count ls_wrk = Mid(ls_reply, 5) li_pos = Pos(ls_wrk, " ") li_cnt = Integer(Left(ls_wrk, li_pos - 1)) li_cnt2 = UpperBound(as_uidl) For li_idx = 1 To li_cnt ls_uidl = "" // get the unique identifier ls_msg = "UIDL " + String(li_idx) + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If If Left(ls_reply, 3) = "+OK" Then ls_content = Mid(ls_reply, 9) // remove any CRLF at end If Right(ls_content, 2) = CRLF Then ls_uidl = Left(ls_content, Len(ls_content) - 2) End If End If For li_idx2 = 1 To li_cnt2 // delete this email if it matches If ls_uidl = as_uidl[li_idx2] Then // update progress of_Progress_Message("Deleting message " + & String(li_idx) + " from server") ls_msg = "DELE " + String(li_idx) + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If End If Next Next // stop the server session If Not of_RecvMail_Crypt_Stop() Then Return False End If of_Progress_Done() // log end of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("of_DeleteSSLMail End: " + String(ldt_current)) Return True end function public function boolean of_recvsslmail (integer ai_msgnum[]);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_RecvSSLMail // // PURPOSE: This function downloads specified email messages that were // downloaded in header only mode previously. // // ARGUMENTS: ai_msgnum - Message numbers of the emails to download // // RETURN: True = Success, False = Failure // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 04/15/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Constant String MSGEND = CRLF + Char(46) + CRLF String ls_msg, ls_reply, ls_wrk, ls_uidl, ls_content Integer li_pos, li_idx, li_cnt, li_idx2, li_cnt2, li_msgnum DateTime ldt_current Long ll_pos // log start of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("~r~nof_RecvSSLMail Start: " + String(ldt_current) + "~r~n") // start the server session If Not of_RecvMail_Crypt_Start() Then Return False End If // request the number of messages ls_msg = "STAT" + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If // parse out msg count ls_wrk = Mid(ls_reply, 5) li_pos = Pos(ls_wrk, " ") li_cnt = Integer(Left(ls_wrk, li_pos - 1)) li_cnt2 = UpperBound(ai_msgnum) For li_idx = 1 To li_cnt ls_uidl = "" // get the unique identifier ls_msg = "UIDL " + String(li_idx) + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If If Left(ls_reply, 3) = "+OK" Then ls_content = Mid(ls_reply, 9) // remove any CRLF at end If Right(ls_content, 2) = CRLF Then ls_uidl = Left(ls_content, Len(ls_content) - 2) End If End If For li_idx2 = 1 To li_cnt2 // download this email if it matches li_msgnum = ai_msgnum[li_idx2] If ls_uidl = is_uidl[li_msgnum] Then // update progress of_Progress_Message("Downloading message " + & String(li_idx) + " from server") // send retrieve message ls_msg = "RETR " + String(li_idx) + CRLF If Not of_sendmsg_crypt(ls_msg, ls_reply) Then Return False End If If Left(ls_reply, 3) = "+OK" Then // copy content (removing status line) ls_content = Mid(ls_reply, Pos(ls_reply, CRLF) + 2) // copy content (removing MSGEND) is_email[li_msgnum] = Left(ls_content, Len(ls_content) - 3) Else // of_SetLastError(ls_reply) of_LogFile(ls_reply) Return False End If End If Next Next // stop the server session If Not of_RecvMail_Crypt_Stop() Then Return False End If of_Progress_Done() // log end of POP3 conversation ldt_current = DateTime(Today(), Now()) of_LogFile("of_RecvSSLMail End: " + String(ldt_current)) Return True end function protected subroutine of_logfile (string as_logmsg);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_LogFile // // PURPOSE: This function writes messages to the POP3 logfile. // // ARGUMENTS: as_logmsg - Message text // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 09/10/2010 RolandS Initial coding // 07/06/2011 RolandS Changed to use filename from instance variable // ----------------------------------------------------------------------------- Integer li_fnum If ib_logfile Then li_fnum = FileOpen(is_logfile, LineMode!, Write!, Shared!, Append!) FileWrite(li_fnum, as_logmsg) FileClose(li_fnum) End If end subroutine public function integer of_hex2int (string as_hex);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Hex2Int // // PURPOSE: This function converts hexidecimal to a number. // // ARGUMENTS: as_string - Hexidecimal string // // RETURN: Number // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/10/2012 RolandS Initial coding // ----------------------------------------------------------------------------- Integer li_work String ls_code, ls_hex = '123456789ABCDEF' Long ll_rc, ll_lp ls_code = Reverse(Upper(as_hex)) ll_rc = Len(ls_code) For ll_lp = 1 To ll_rc li_work = li_work + Pos(ls_hex, & Mid(ls_code, ll_lp, 1), 1) * (16^(ll_lp - 1)) Next Return li_work end function public subroutine of_quotedprintable (ref string as_content, boolean ab_recurse);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_QuotedPrintable // // PURPOSE: This function decodes quoted-printable text. // // ARGUMENTS: as_content - Text content (by ref) // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 03/10/2012 RolandS Initial coding // 04/18/2012 RolandS Added check for spaces encoded as underscore // 05/20/2015 MarcoC Added possibility to read multiple consecutive // hex values. Made function recursive. // ----------------------------------------------------------------------------- String ls_hex, ls_findstr, ls_replace Integer li_char Long ll_pos, ll_pos2, ll_pos_start, ll_pos_end, ll_pos_new blob{20} lbl_multi_hex string ls_multi_hex, ls_converted string ls_blocks[] long ll boolean lb_recurse if not ab_recurse then // Equal signs at end of lines as_content = of_ReplaceAll(as_content, "="+CRLF, "") // Encoded Space as_content = of_ReplaceAll(as_content, "_", " ") end if // There can be characters longer then one byte to decode. // Look for a starting char of an hex code, exclude the "equal" sign ll_pos_start = 0 DO ll_pos_start = Pos(as_content, "=", ll_pos_start +1) ls_hex = Mid(as_content, ll_pos_start +1, 2) LOOP WHILE ls_hex = "3D" if ll_pos_start > 0 then ll_pos_end = ll_pos_start ll = 1 ls_multi_hex = "" // Collect all consecutive hex values Do ll_pos_new = ll_pos_end // Get hex value ls_hex = Mid(as_content, ll_pos_end +1, 2) // Save hex values ls_blocks[ll] = ls_hex // Convert hex to ascii li_char = of_hex2int(ls_hex) // Concatenate this hex with previous if li_char > 0 then ls_multi_hex = ls_multi_hex + CharA(li_char) end if // Get next hex value pos ll_pos_end = Pos(as_content, "=", ll_pos_end +1) ll ++ loop while ll_pos_end = ll_pos_new + 3 ll_pos_end = ll_pos_new +3 // If it only has one hex, use standard method if upperbound(ls_blocks) = 1 then li_char = of_hex2int(ls_blocks[1]) If li_char > 0 Then ls_findstr = "="+ls_hex ls_converted = Char(li_char) else ls_converted = "" End If else // Convert multiple hex values BlobEdit(lbl_multi_hex, 1, ls_multi_hex, EncodingANSI!) ls_converted = String(lbl_multi_hex, EncodingUtf8!) end if // Replace original hex code with decoded string as_content = Replace(as_content, ll_pos_start, ll_pos_end - ll_pos_start, ls_converted) // Recursivly call this function and decode other hex values of_quotedprintable(as_content, TRUE) end if // Encoded Equal signs as_content = of_ReplaceAll(as_content, "=3D", "=") end subroutine public function long of_parse (readonly string as_string, readonly string as_separator, ref string as_outarray[]);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Parse // // PURPOSE: This function parses a string into an array. // // ARGUMENTS: as_string - The string to be separated // as_separate - The separator characters // as_outarray - By ref output array // // RETURN: The number of items in the array // // DATE CHANGED BY DESCRIPTION OF CHANGE / REASON // ---------- ---------- ----------------------------------------------------- // 03/17/2012 RolandS Rewritten based on an example by Mike Bartos // ----------------------------------------------------------------------------- Long ll_PosEnd, ll_PosStart = 1, ll_SeparatorLen, ll_Counter = 1 If UpperBound(as_OutArray) > 0 Then as_OutArray = {""} ll_SeparatorLen = Len(as_Separator) ll_PosEnd = Pos(as_String, as_Separator, 1) Do While ll_PosEnd > 0 as_OutArray[ll_Counter] = Mid(as_String, ll_PosStart, ll_PosEnd - ll_PosStart) ll_PosStart = ll_PosEnd + ll_SeparatorLen ll_PosEnd = Pos(as_String, as_Separator, ll_PosStart) ll_Counter++ Loop as_OutArray[ll_Counter] = Right(as_String, Len(as_String) - ll_PosStart + 1) Return ll_Counter end function public function blob of_decode64 (readonly string as_encoded);// ----------------------------------------------------------------------------- // SCRIPT: n_pop3.of_Decode64 // // PURPOSE: This function converts a Base64 encoded string to binary. // // Note: Requires Windows XP or Server 2003 // // ARGUMENTS: as_encoded - String containing encoded data // // RETURN: Blob containing decoded data // // DATE PROG/ID DESCRIPTION OF CHANGE / REASON // ---------- -------- ----------------------------------------------------- // 08/20/2010 RolandS Initial coding // 03/17/2012 RolandS Changed argument to read-only // ----------------------------------------------------------------------------- Constant ULong CRYPT_STRING_BASE64 = 1 Blob lblob_data ULong lul_len, lul_buflen, lul_skip, lul_pflags Boolean lb_rtn lul_len = Len(as_encoded) lul_buflen = lul_len lblob_data = Blob(Space(lul_len)) lb_rtn = CryptStringToBinary(as_encoded, & lul_len, CRYPT_STRING_BASE64, lblob_data, & lul_buflen, lul_skip, lul_pflags) Return BlobMid(lblob_data, 1, lul_buflen) end function on n_pop3.create call super::create end on on n_pop3.destroy call super::destroy end on
File: n_pop3.sru
Size: 91011
Date: Sun, 12 Sep 2021 01:01:34 +0200
Size: 91011
Date: Sun, 12 Sep 2021 01:01:34 +0200
- n_winsock autoinstantiate n_pop3(sru)
- of_attachname (string as_type) returns string
- of_base64 (readonly string as_type, ref string as_part)
- of_crypterror (long al_retval) returns string
- of_decode64 (readonly string as_encoded) returns blob
- of_decodestring (ref string as_string)
- of_deletemail (string as_uidl[]) returns boolean
- of_deletesslmail (string as_uidl[]) returns boolean
- of_hex2int (string as_hex) returns integer
- of_logfile (string as_logmsg)
- of_msgadd (readonly string as_email, readonly string as_uidl) returns integer
- of_msgbody (integer ai_msgnum, ref string as_part[], ref string as_type[]) returns integer
- of_msgcontent (integer ai_msgnum) returns string
- of_msgcount () returns integer
- of_msgfrom (integer ai_msgnum, ref string as_name, ref string as_email)
- of_msglength (integer ai_msgnum) returns long
- of_msgload (string as_filename) returns integer
- of_msgproperty (integer ai_msgnum, string as_property) returns string
- of_msgreceipt (integer ai_msgnum, ref string as_name, ref string as_email) returns boolean
- of_msgsentdate (integer ai_msgnum) returns datetime
- of_msgsubject (integer ai_msgnum) returns string
- of_msguidl (integer ai_msgnum) returns string
- of_parse (readonly string as_string, readonly string as_separator, ref string as_outarray[]) returns long
- of_progress_done ()
- of_progress_message (string as_message)
- of_progress_notify (long al_window, long al_event)
- of_progress_start (long al_length)
- of_progress_update (long al_length)
- of_quotedprintable (ref string as_content)
- of_quotedprintable (ref string as_content, boolean ab_recurse)
- of_readfile (string as_filename, ref blob ablob_data) returns boolean
- of_recv (unsignedlong aul_socket, ref string as_data) returns long
- of_recvmail (boolean ab_delete, boolean ab_headers) returns boolean
- of_recvmail (integer ai_msgnum[]) returns boolean
- of_recvmail_crypt_msgs (boolean ab_delete, boolean ab_headers) returns boolean
- of_recvmail_crypt_start () returns boolean
- of_recvmail_crypt_stop () returns boolean
- of_recvmail_msgs (boolean ab_delete, boolean ab_headers) returns boolean
- of_recvmail_start () returns boolean
- of_recvmail_stop () returns boolean
- of_recvsslmail (boolean ab_delete, boolean ab_headers) returns boolean
- of_recvsslmail (integer ai_msgnum[]) returns boolean
- of_replaceall (readonly string as_oldstring, readonly string as_findstr, readonly string as_replace) returns string
- of_send (unsignedlong aul_socket, string as_data) returns boolean
- of_sendmsg (string as_cmd, ref string as_reply) returns boolean
- of_sendmsg_crypt (string as_cmd, ref string as_reply) returns boolean
- of_separate (readonly string as_content, ref string as_part[], ref string as_type[])
- of_setlogfile (boolean ab_flag, string as_logfile)
- of_setlogin (string as_userid, string as_passwd)
- of_setport (unsignedinteger aui_port)
- of_setserver (string as_server)
- of_setsleeptime (long al_sleeptime)
- of_writefile (readonly string as_filename, readonly blob ablob_filedata) returns boolean