--****************************************************************************
-- Software: SMTP_CLASS
-- Version: 0.5
-- Date: 2011-11-04
-- Author: Valentin Schmidt
-- Contact: fluxus@freenet.de
-- License: Freeware
--
-- Requirements/Dependencies:
-- + Multiuser Xtra
-- + Base64 encoder class (internally either using a fast xtra like e.g. Crypto Xtra or slow lingo code)
--
--****************************************************************************
-- server
property pServer -- Address of SMTP server
property pPort -- SMTP port number
property pUsername -- Username for SMTP AUTH
property pPassword -- Password for SMTP AUTH
-- email
property pProps
property pRecipients --
property pMailContent --
-- private stuff
property pScriptVersion
property pMUXtra -- Instance of Multiuser Xtra
property pStatus -- Status of messaging
property pEOL
property pBase64Encoder
property pTextEncoding
property pMimeTypes
----------------------------------------
-- CONSTRUCTOR
----------------------------------------
on new (me, server, port, user, password)
pScriptVersion = "0.5"
pTextEncoding = "UTF-8"
pServer = server
pPort = port
pUsername = user
pPassword = password
pBase64Encoder = script("BASE64_CLASS").new()
pMimeTypes =[:]
pMimeTypes["jpg"] = "image/jpeg"
pMimeTypes["png"] = "image/png"
pMimeTypes["gif"] = "image/gif"
pEOL = numToChar(13)&numToChar(10)
the randomseed = the ticks
return me
end
----------------------------------------
-- start sending mail
----------------------------------------
on sendMail (me, props)
-- REQUIRED:
-- to
-- from
-- subject
-- message
-- OPTIONAL:
-- cc
-- bcc
-- headers
-- attachments
-- callback
-- callback_target
-- allow strings
if stringP(props["to"]) then props["to"] = [props["to"]]
if stringP(props["cc"]) then props["cc"] = [props["cc"]]
if stringP(props["bcc"]) then props["bcc"] = [props["bcc"]]
if stringP(props["headers"]) then props["headers"] = [props["headers"]]
if stringP(props["attachments"]) then props["attachments"] = [props["attachments"]]
-- defaults
if voidP(props["cc"]) then props["cc"] = []
if voidP(props["bcc"]) then props["bcc"] = []
if voidP(props["headers"]) then props["headers"] = []
if voidP(props["attachments"]) then props["headers"] = []
if voidP(props["callback_target"]) then props["callback_target"] = _movie
pProps = props
pRecipients = props["to"].duplicate()
repeat with a in props["cc"]
pRecipients.add(a)
end repeat
repeat with a in props["bcc"]
pRecipients.add(a)
end repeat
-- initialize connection instance
pMUXtra = xtra("multiuser").new()
-- define callback handlers and error check
errCode = pMUXtra.setNetMessageHandler(#messageHandler, me)
if errCode <> 0 then
-- error defining callback handlers
me.dbg("Error with setNetMessageHandler")
else
pMailContent = ""
-- DEFAULT HEADERS
put "From: "& pProps["from"] &pEOL after pMailContent
put "Subject: "& pProps["subject"] &pEOL after pMailContent
put "To: "& implode(", ", pProps["to"]) &pEOL after pMailContent
if count(pProps["cc"]) then
put "Cc: "& implode(", ", pProps["cc"]) &pEOL after pMailContent
end if
if count(pProps["bcc"]) then
put "Bcc: "& implode(", ", pProps["bcc"]) &pEOL after pMailContent
end if
put "Reply-To: "& pProps["from"] &pEOL after pMailContent
put "X-Mailer: SMTP CLASS v"&pScriptVersion &pEOL after pMailContent
-- ADDITIONAL HEADERS
repeat with h in pProps["headers"]
put h &pEOL after pMailContent
end repeat
if count(pProps["attachments"])=0 then
put "Content-Type: text/plain; charset="&pTextEncoding &pEOL after pMailContent
put pEOL& pProps["message"] &pEOL&pEOL&"." after pMailContent
else
--MIME
bound=string(random(the maxinteger)) -- "----=_NextPart_000_001F_01C3C4D5.E21E2390"
put "MIME-Version: 1.0" &pEOL after pMailContent
put "Content-Type: multipart/mixed; boundary=""E&bound"E &pEOL &pEOL after pMailContent
put "This is a multi-part message in MIME format."&pEOL &pEOL after pMailContent
put "--"&bound &pEOL after pMailContent
put "Content-Type: text/plain; charset="&pTextEncoding &pEOL after pMailContent
put "Content-Transfer-Encoding: 7bit" &pEOL &pEOL after pMailContent
--put "Content-Transfer-Encoding: quoted-printable" &pEOL &pEOL after pMailContent
put pProps["message"] &pEOL after pMailContent
cnt = count(pProps["attachments"])
repeat with i = 1 to cnt
fn = pProps["attachments"].getPropAt(i)
t = fn.char[fn.length-2..fn.length]
mt = pMimeTypes[t]
if voidP(mt) then mt = "application/octet-stream"
ba = pProps["attachments"][i]
put "--"&bound &pEOL after pMailContent
put "Content-Type: "&mt&"; name=""E&fn"E &pEOL after pMailContent
put "Content-Transfer-Encoding: base64"&pEOL after pMailContent
put "Content-Disposition: attachment; filename=""E&fn"E &pEOL &pEOL after pMailContent
me.dbg("ENCODING" && fn)
put pBase64Encoder.encode(ba, true) &pEOL &pEOL after pMailContent
end repeat
put "--"&bound&"--" &pEOL &pEOL&"." after pMailContent
end if
maxMessageSize = max(16*1024, pMailContent.length + 64)
errCode = pMUXtra.setNetBufferLimits(16 * 1024, maxMessageSize, 100)
-- make connection and error check
errCode = pMUXtra.ConnectToNetServer("String", "String", pServer, pPort, "smtpClient", 1)
if errCode = 0 then
message = "ConnectToNetServer sent to server"
pStatus = "ConnectToNetServer sent"
else
message = "Error sending ConnectToNetServer to server"
pStatus = "ERROR"
end if
me.dbg(message)
end if
end
----------------------------------------
-- handle MU messages
----------------------------------------
on messageHandler (me)
-- get message from message queue
newMsg = pMUXtra.getNetMessage()
-- error check
errCode = newMsg.errorCode
if (errCode = 0) then
-- set local variables
senderID = newMsg.senderID
subject = newMsg.subject
content = newMsg.content
case(TRUE) of:
-- initial response from server
(senderID = "System" and subject = "ConnectToNetServer") :
message = "ConnectToNetServer successful"
pStatus = "ConnectToNetServer"
(content starts "334") :
-- if the SMTP returns "334" then it's ready for an SMTP Authentification
case(pStatus) of
"AUTH_LOGIN":
errCode = me.snd(pBase64Encoder.encode(pUsername))
if errCode = 0 then
message = "USERNAME sent to server"
pStatus = "USER"
else
message = "Error with USERNAME"
pStatus = "ERROR"
end if
"USER":
errCode = me.snd(pBase64Encoder.encode(pPassword))
if errCode = 0 then
message = "PASSWORD sent to server"
pStatus = "PASSWORD"
else
message = "Error with PASSWORD"
pStatus = "ERROR"
end if
end case
(content starts "235") :
-- if the SMTP returns "235" then send MAIL FROM
errCode = me.snd("MAIL FROM:" && pProps["from"])
if errCode = 0 then
message = "MAIL FROM message sent to server"
pStatus = "MAIL_FROM"
else
message = "Error with MAIL FROM message"
pStatus = "ERROR"
end if
(content starts "220") :
-- if the SMTP returns "220" then it's ready for an SMTP session
-- send HELO command with the sender's domain name
the itemDelimiter = "@"
domain = pProps["from"].item[2]
errCode = me.snd("HELO" && domain)
if errCode = 0 then
message = "HELO message sent to server"
pStatus = "HELO"
else
message = "Error with HELO message"
pStatus = "ERROR"
end if
(content starts "250") :
-- an STMP server response of "250"
case(pStatus) of
-- the most recent step in the sequence is stored in pStatus
-- use pStatus to determine the next sequence
"HELO" :
if voidP(pUsername) then
-- if the most recent step in the sequence was HELO then send MAIL FROM
errCode = me.snd("MAIL FROM:"&&pProps["from"])
if errCode = 0 then
message = "MAIL FROM message sent to server"
pStatus = "MAIL_FROM"
else
message = "Error with MAIL FROM message"
pStatus = "ERROR"
end if
else
-- if the most recent step in the sequence was HELO then send AUTH LOGIN
errCode = me.snd("AUTH LOGIN")
if errCode = 0 then
message = "AUTH LOGIN message sent to server"
pStatus = "AUTH_LOGIN"
else
message = "Error with MAIL FROM message"
pStatus = "ERROR"
end if
end if
"MAIL_FROM", "RCPT_TO" :
if (count(pRecipients)>0) then
tTo = pRecipients[1]
pRecipients.deleteAt(1)
errCode = me.snd("RCPT TO:"&&tTo)
if errCode = 0 then
message = "RCPT TO message sent to server"
pStatus = "RCPT_TO"
else
message = "Error with RCPT TO message"
pStatus = "ERROR"
end if
else
-- if the most recent step in the sequence was RCPT TO then send DATA
errCode = me.snd("DATA")
if errCode = 0 then
message = "DATA message sent to server"
pStatus = "DATA"
else
message = "Error with DATA message"
pStatus = "ERROR"
end if
end if
"Message Content" :
-- if the most recent step in the sequence was message content then send QUIT
errCode = me.snd("QUIT")
if errCode = 0 then
message = "QUIT message sent to server"
pStatus = "QUIT"
else
message = "Error with QUIT message"
pStatus = "ERROR"
end if
end case
(content starts "354") :
-- an SMTP server respons of "354" means it's ready to receive message content
errCode = me.snd(pMailContent)
if errCode = 0 then
message = "Message content sent to server"
pStatus = "Message Content"
else
message = "Error with email content"
pStatus = "ERROR"
end if
(content starts "221") :
-- an SMTP server response of "221"
if errCode = 0 then
message = "Email sent successfully"
pStatus = "Success"
-- clear multiuser xtra reference from memory
me.clearMU()
if NOT voidP(pProps["callback"]) then
call(pProps["callback"], [pProps["callback_target"]], 1)
end if
else
message = "Error in server's response to QUIT"
pStatus = "ERROR"
end if
otherwise:
-- an unexpected SMTP server response
message = "Server Message: " & content.line[1]
pStatus = "ERROR"
end case
else -- if errCode <> 0
if senderID = "System" and subject = "ConnectionProblem" then
-- trap messages with the subject "ConnectionProblem".
-- useful to eliminate the alert box that would otherwise appear
nothing
else
-- put other errors into status field
message = pMUXtra.GetNetErrorString(errCode)
pStatus = "ERROR"
end if
end if
if pStatus = "ERROR" then
-- clear multiuser xtra reference from memory
me.clearMU()
if NOT voidP(pProps["callback"]) then
call(pProps["callback"], [pProps["callback_target"]], 0)
end if
end if
me.dbg(message)
end
----------------------------------------
--
----------------------------------------
on snd (me, str)
return pMUXtra.SendNetMessage("", "", str&pEOL)
end
----------------------------------------
-- clear out netMessageHandler callbacks and void instance
----------------------------------------
on clearMU (me)
-- clear all callbacks by setting #handlerSymbol parameter to 0
errCode = pMUXtra.setNetMessageHandler(0, me)
errCode = pMUXtra.setNetMessageHandler(0, me, "ConnectToNetServer")
-- clear instance of the Multiuser xtra
pMUXtra = VOID
me.dbg("CLOSE MUXtra")
end
----------------------------------------
-- shows network status information
----------------------------------------
on dbg (me, s)
put "SMTP:" && s -- delete/comment line for silent execution
end
----------------------------------------
--
----------------------------------------
on implode (delim, l)
str = ""
repeat with i=1 to l.count
put l[i]&delim after str
end repeat
return str.char[1..(str.length-delim.length)]
end