--****************************************************************************
-- Software: POP3_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)
--
--****************************************************************************
-- public
property pServer
property pPort
property pUser
property pPass
property pDeleteFlag
property pHeaderOnly
property pCallbackHandler
property pCallbackTarget
-- private
property pMUXtra
property pMailCount
property pMailNum
property pMails
property pStatus -- Status of messaging
property pEOL
property pLF
property pCR
property pMaxLines -- lines of msg when header only
property pBase64Encoder
----------------------------------------------------
-- CONSTRUCTOR
----------------------------------------------------
on new (me, server, port, user, pw)
pServer = server
pPort = port
pUser = user
pPass = pw
pLF = numToChar(10)
pCR = numToChar(13) -- RETURN
pEOL = pCR & pLF
pMaxLines = 10
pBase64Encoder = script("BASE64_CLASS").new()
return me
end
----------------------------------------------------
-- Start connection
----------------------------------------------------
on getMails (me, deleteFlag, headerOnly, callbackHandler, callbackTarget)
pDeleteFlag = deleteFlag
pHeaderOnly = headerOnly
pCallbackHandler = callbackHandler
pCallbackTarget = callbackTarget
if voidP(pCallbackTarget) then pCallbackTarget = _movie
pMails=[]
pMailCount = 0
pMailNum = 0
pMUXtra = new(xtra "Multiuser")
pStatus = "START"
-- Set default message handler for ALL messages
errCode = pMUXtra.setNetMessageHandler( #defaultMsg, me)
me.CheckError( errCode )
-- Set a message handler to get the initial logon reponse, keyword is the last param
errCode = pMUXtra.setNetMessageHandler( #firstConnect, me, "connectToNetServer" )
me.CheckError( errCode )
-- Connect to the server in TEXT mode (note the 1 as the last parameter)
errCode = pMUXtra.connectToNetServer( "x", "x", pServer, pPort, "x", 1)
me.CheckError( errCode )
end
----------------------------------------------------
-- Send messages after we first connect
----------------------------------------------------
on firstConnect (me)
netMessage = pMUXtra.getNetMessage()
me.dbg("Connection Established")
end
----------------------------------------------------
-- process received messages
----------------------------------------------------
on defaultMsg (me)
netMessage = pMUXtra.getNetMessage()
msg = str_replace(pLF, "", netMessage.content) -- CR only
if the last char of msg=pCR then delete the last char of msg
if msg="x" then return
case (pStatus) of
"LIST":
if the last char of msg="." then
--if msg.line.count>2 then pMailCount = msg.line.count-2
pMailCount = msg.line.count-1
if msg.line[1] starts "+OK" then pMailCount=pMailCount-1
pMailNum=0
me.dbg("NUMBER OF MAILS:" && pMailCount)
repeat with i = 1 to pMailCount
pMails[i]=""
end repeat
me.nextMessage()
end if
"RETR","TOP":
pMails[pMailNum]=pMails[pMailNum] & msg
if the last char of msg="." then
mail=pMails[pMailNum]
delete the last line of mail
if mail starts "+OK" then delete char 1 to 3 of mail
if mail.line[1].word.count=0 then delete line 1 of mail
-- if (mail.line[1] starts "+OK ") then delete line 1 of mail --msg
pMails[pMailNum]=mail
header=me.getHeader(pMailNum)
me.dbg("--------------------")
me.dbg("MAIL" && pMailNum)
me.dbg("FROM:" && me.getFrom(pMailNum))
me.dbg("SUBJECT:" && me.getSubject(pMailNum))
-- me.dbg("BODY:" && me.getBody(pMailNum))
me.dbg("--------------------")
me.nextMessage()
end if
"QUIT":
me.clearMU()
if NOT voidP(pCallbackHandler) then
call(pCallbackHandler, [pCallbackTarget], me)
end if
otherwise:
me.nextMessage()
end case
end
----------------------------------------------------
-- send messages
----------------------------------------------------
on nextMessage (me)
-- "START","USER" && pUser,"PASS" && pPass,"LIST","RETR","DELE","QUIT"
case (pStatus) of
"START":
com = "USER" && pUser
pStatus = "USER"
"USER":
com = "PASS" && pPass
pStatus = "PASS"
"PASS":
com = "LIST"
pStatus = "LIST"
"LIST":
if pMailCount>0 then
pMailNum=1
if pHeaderOnly then
com = "TOP 1" && pMaxLines
pStatus = "TOP"
else
com = "RETR 1"
pStatus = "RETR"
end if
else
com = "QUIT"
pStatus = "QUIT"
end if
"RETR","TOP":
if pDeleteFlag then
com = "DELE" && pMailNum
pStatus = "DELE"
else
if pMailNum<pMailCount then
pMailNum=pMailNum+1
if pHeaderOnly then
com = "TOP" && pMailNum && pMaxLines
pStatus = "TOP"
else
com = "RETR" && pMailNum
pStatus = "RETR"
end if
else
com = "QUIT"
pStatus = "QUIT"
end if
end if
"DELE":
if pMailNum<pMailCount then
pMailNum=pMailNum+1
if pHeaderOnly then
com = "TOP" && pMailNum && pMaxLines
pStatus = "TOP"
else
com = "RETR" && pMailNum
pStatus = "RETR"
end if
else
com = "QUIT"
pStatus = "QUIT"
end if
end case
errCode = pMUXtra.sendNetMessage( "", "", com & pEOL)
me.dbg("SEND" && com)
me.CheckError( errCode )
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
----------------------------------------------------
-- Display errors
----------------------------------------------------
on CheckError (me, errCode)
if ( errCode <> 0 ) then
me.dbg("Error " & string( errCode ) & " or: " & pMUXtra.GetNetErrorString( errCode ))
end if
end
----------------------------------------------------
-- shows network status information
----------------------------------------------------
on dbg (me, s)
put "POP:" && s -- delete/comment line for silent execution
end
----------------------------------------------------
-- MAIL UTILITIES
----------------------------------------------------
----------------------------------------------------
-- returns full email
----------------------------------------------------
on getMsg (me, msgNum)
return pMails[msgNum]
end
----------------------------------------------------
-- returns mail header
----------------------------------------------------
on getHeader (me, msgNum)
msg=pMails[msgNum]
return msg.char[1..offset(pCR&pCR,msg)-1]
end
----------------------------------------
-- returns mail body
----------------------------------------
on getBody (me, msgNum)
msg=pMails[msgNum]
return msg.char[offset(pCR&pCR,msg)+2..msg.length] -- -2]
end
----------------------------------------
-- returns mail from
----------------------------------------
on getFrom (me, msgNum)
msg=pMails[msgNum]
von = offset("From:",msg)+6
msg=msg.char[von..msg.length-2]
from = msg.line[1]
if from contains "<" then
from=from.char[offset("<",from)+1..offset(">",from)-1]
end if
return from
end
----------------------------------------
-- returns mail subject
----------------------------------------
on getSubject (me, msgNum)
msg=pMails[msgNum]
von = offset("Subject:",msg)+9
msg=msg.char[von..msg.length-2]
sub = msg.line[1]
return sub
end
----------------------------------------
--
----------------------------------------
on isMimeMail (me, msgNum)
msg=pMails[msgNum]
return (msg contains "MIME-Version:")
end
----------------------------------------
-- extracts mail attachments from MIME mail
-- returns list of files as property list: filename - > data (bytearray)
----------------------------------------
on getMimeFiles (me, msgNum)
msg=pMails[msgNum]
r=numtochar(13)
-- get boundary
von = offset("boundary=",msg)+10
bis = von + offset(QUOTE,msg.char[von..msg.length])-2
bound = "--" & msg.char[von..bis]
files = [:]
parts = []
repeat with i = 1 to msg.line.count
if msg.line[i] starts bound then parts.add(i)
end repeat
repeat with i = 1 to (parts.count-1)
part=msg.line[parts[i]+1..parts[i+1]-1]
if part contains "Content-Disposition: attachment;" then
von = offset("filename=",part)+10
bis = von + offset(QUOTE,part.char[von..part.length])-2
fn = part.char[von..bis]
von = offset(r&r,part)+2
data = part.char[von..part.length-1]
data = str_replace(pCR, "", data) -- ???
ba = pBase64Encoder.decode(data)
files.addProp(fn, ba)
end if
end repeat
return files
end
----------------------------------------
-- extracts Text from MIME mail
----------------------------------------
on getMimeText (me, msgNum)
msg=pMails[msgNum]
r=numtochar(13)
-- get boundary
von = offset("boundary=",msg)+10
bis = von + offset(QUOTE,msg.char[von..msg.length])-2
bound = "--" & msg.char[von..bis]
parts=[]
repeat with i = 1 to msg.line.count
if msg.line[i] starts bound then parts.add(i)
end repeat
repeat with i = 1 to (parts.count-1)
part=msg.line[parts[i]+1..parts[i+1]-1]
if part contains "Content-Type: text/plain;" then
return part.char[(offset(r&r,part)+2)..part.length-1]
end if
end repeat
return ""
end
----------------------------------------
-- extracts HTML from MIME mail
----------------------------------------
on getMimeHtml (me, msgNum)
msg=pMails[msgNum]
r=numtochar(13)
-- get boundary
von = offset("boundary=",msg)+10
bis = von + offset(QUOTE,msg.char[von..msg.length])-2
bound = "--" & msg.char[von..bis]
parts=[]
repeat with i = 1 to msg.line.count
if msg.line[i] starts bound then parts.add(i)
end repeat
repeat with i = 1 to (parts.count-1)
part=msg.line[parts[i]+1..parts[i+1]-1]
if part contains "Content-Type: text/html;" then
return part.char[(offset(r&r,part)+2)..part.length-1]
end if
end repeat
return ""
end
----------------------------------------
-- replace in string
----------------------------------------
on str_replace (stringToFind, stringToInsert, input)
output = ""
findLen = stringToFind.length - 1
repeat while true
currOffset = offset(stringToFind, input)
if currOffset=0 then exit repeat
put input.char [1..currOffset] after output
delete the last char of output
put stringToInsert after output
delete input.char [1.. (currOffset + findLen)]
end repeat
put input after output
return output
end