-- ************************************************************************** -- -- FreePOPs @fastmail.com webmail interface -- -- Released under the GNU/GPL license -- Written by Russell Schwager -- ************************************************************************** -- -- Globals -- PLUGIN_VERSION = "0.0.2a" PLUGIN_NAME = "fastmail.com" PLUGIN_REQUIRE_VERSION = "0.0.97" PLUGIN_LICENSE = "GNU/GPL" PLUGIN_URL = "http://www.freepops.org/download.php?file=fastmail.lua" PLUGIN_HOMEPAGE = "http://www.freepops.org/" PLUGIN_AUTHORS_NAMES = {"Russell Schwager"} PLUGIN_AUTHORS_CONTACTS = {"russells (at) despammed (.) com"} PLUGIN_DOMAINS = { "@123mail.org", "@150mail.com", "@150ml.com", "@16mail.com", "@2-mail.com", "@4email.net", "@50mail.com", "@airpost.net", "@allmail.net", "@bestmail.us", "@cluemail.com", "@elitemail.org", "@emailgroups.net", "@emailplus.org", "@emailuser.net", "@eml.cc", "@fastem.com", "@fast-email.com", "@fastemail.us", "@fastemailer.com", "@fastest.cc", "@fastimap.com", "@fastmail.cn", "@fastmail.com.au", "@fastmail.fm", "@fastmail.us", "@fastmail.co.uk", "@fastmail.to", "@fmail.co.uk", "@fast-mail.org", "@fastmailbox.net", "@fastmessaging.com", "@fea.st", "@f-m.fm", "@fmailbox.com", "@fmgirl.com", "@fmguy.com", "@ftml.net", "@hailmail.net", "@imap.cc", "@imap-mail.com", "@imapmail.org", "@internet-e-mail.com", "@internetemails.net", "@internet-mail.org", "@internetmailing.net", "@jetemail.net", "@justemail.net", "@letterboxes.org", "@mailandftp.com", "@mailas.com", "@mailbolt.com", "@mailc.net", "@mailcan.com", "@mail-central.com", "@mailforce.net", "@mailftp.com", "@mailhaven.com", "@mailingaddress.org", "@mailite.com", "@mailmight.com", "@mailnew.com", "@mail-page.com", "@mailsent.net", "@mailservice.ms", "@mailup.net", "@mailworks.org", "@ml1.net", "@mm.st", "@myfastmail.com", "@mymacmail.com", "@nospammail.net", "@ownmail.net", "@petml.com", "@postinbox.com", "@postpro.net", "@proinbox.com", "@promessage.com", "@realemail.net", "@reallyfast.biz", "@reallyfast.info", "@rushpost.com", "@sent.as", "@sent.at", "@sent.com", "@speedpost.net", "@speedymail.org", "@ssl-mail.com", "@swift-mail.com", "@the-fastest.net", "@theinternetemail.com", "@the-quickest.com", "@veryfast.biz", "@veryspeedy.net", "@warpmail.net", "@xsmail.com", "@yepmail.net", "@your-mail.com", } PLUGIN_PARAMETERS = { {name="folder", description={ it=[[La cartella che vuoi ispezionare. Quella di default è Inbox, gli altri valori possibili sono: Junk, Trash, Draft, Sent.]], en=[[The folder you want to interact with. Default is Inbox, other values are: Junk, Trash, Draft, Sent.]]} }, {name = "emptytrash", description = { it = [[ Viene usato per forzare il plugin a svuotare il cestino quando ha finito di scaricare i messaggi. Se il valore è 1 questo comportamento viene attivato.]], en = [[ Parameter is used to force the plugin to empty the trash when it is done pulling messages. Set the value to 1.]] } }, } PLUGIN_DESCRIPTIONS = { it=[[ Questo plugin vi permette di scaricare la posta da mailbox con dominio della famiglia di @fastmail.com. Per usare questo plugin dovrete usare il vostro indirizzo email completo come nome utente e la vostra vera password come password.]], en=[[ This plugin lets you download mail from fastmail. To use this plugin you have to use your full email address as the username and your real password as the password. For support, please post a question to the forum instead of emailing the author(s).]] } -- ************************************************************************** -- -- Global Strings -- ************************************************************************** -- local globals = { -- Server URL -- strLoginUrl = "http://www.fmailbox.com/", -- Login strings -- strLoginPostData = "MLS=LN-*&FLN-UserName=%s&FLN-Password=%s&MSignal_LN-Authenticate*=Login&FLN-ScreenSize=-1", strLoginFailed = "Login Failed - Invalid User name and/or password", -- Regular expression to extract the mail server -- strFormActionPat = '
0 and dcnt < 5 then log.dbg("Sending Delete URL: " .. cmdUrl .. "Post Data: " .. post .. "\n") local body, err = browser:post_uri(cmdUrl, post) if not body or err then log.error_print("Unable to delete messages.\n") end end -- Empty the trash -- if internalState.bEmptyTrash then if internalState.strCrumb ~= '' then cmdUrl = string.format(globals.strCmdEmptyTrash, internalState.strMailServer, internalState.strUst, internalState.strMBoxID, internalState.strUdm, internalState.strTrashID) log.dbg("Sending Empty Trash URL: " .. cmdUrl .."\n") local body, err = browser:get_uri(cmdUrl) if not body or err then log.error_print("Error when trying to empty the trash with url: ".. cmdUrl .."\n") end else log.error_print("Cannot empty trash - crumb not found\n") end end -- Save and then Free up the session -- session.save(hash(), serialize_state(), session.OVERWRITE) session.unlock(hash()) log.dbg("Session saved - Account: " .. internalState.strUser .. "@" .. internalState.strDomain .. "\n") return POPSERVER_ERR_OK end -- Stat command - Get the number of messages and their size -- function stat(pstate) -- Have we done this already? If so, we've saved the results -- if internalState.bStatDone then return POPSERVER_ERR_OK end -- Local variables -- local browser = internalState.browser local nPage = 1 local nMsgs = 0 local nTotMsgs = 0; local firstUrl = string.format(globals.strCmdMsgList, internalState.strMailServer, internalState.strUst, internalState.strMBoxID, internalState.strUdm, internalState.strMBoxID); local cmdUrl = firstUrl local nextUrl = string.format(globals.strCmdMsgListNextPage, internalState.strMailServer, internalState.strUst, internalState.strMBoxID, internalState.strUdm, internalState.strMBoxID); -- Debug Message -- log.dbg("Stat URL: " .. cmdUrl .. "\n"); -- Initialize our state -- set_popstate_nummesg(pstate, nMsgs) -- Local function to process the list of messages, getting id's and sizes -- local function funcProcess(body) -- Find out if there are any messages -- local _, _, nomesg = string.find(body, globals.strMsgListNoMsgPat) if (nomesg ~= nil) then return true, nil end -- Tokenize out the message ID and size for each item in the list -- local items = mlex.match(body, globals.strMsgLineLitPattern, globals.strMsgLineAbsPattern) log.dbg("Stat Count: " .. items:count()) -- Remember the count -- local cnt = items:count() if cnt == 0 then return true, nil end -- Cycle through the items and store the msg id and size -- for i = 1, cnt do local size = items:get(0, i - 1) local uidl = items:get(1, i - 1) if not uidl or not size then log.say("Fastmail Module needs to fix it's individual message list pattern matching.\n") return nil, "Unable to parse the size and uidl from the html" end -- Get the message id. It's in the format of "MSG[numbers].[number(s)]". -- _, _, uidl = string.find(uidl, 'MSignal=MR%-%*%*([^"]+)"') -- Convert the size from it's string (4KB or 2MB) to bytes -- First figure out the unit (KB or just B) -- local _, _, kbUnit = string.find(size, "([Kk])") _, _, size = string.find(size, "([%d]+)[KkMm]") if not kbUnit then size = math.max(tonumber(size), 0) * 1024 * 1024 else size = math.max(tonumber(size), 0) * 1024 end -- Save the information -- nMsgs = nMsgs + 1 log.dbg("Processed STAT - Msg: " .. nMsgs .. ", UIDL: " .. uidl .. ", Size: " .. size) set_popstate_nummesg(pstate, nMsgs) set_mailmessage_size(pstate, nMsgs, size) set_mailmessage_uidl(pstate, nMsgs, uidl) end -- We are done with this page, increment the counter -- nPage = nPage + 1 return true, nil end -- Local Function to check for more pages of messages. If found, the -- change the command url -- local function funcCheckForMorePages(body) -- See if there are messages remaining -- if nMsgs < nTotMsgs then cmdUrl = firstUrl if (nPage > 1) then cmdUrl = nextUrl .. (nPage - 1) end return false end return true end -- Local Function to get the list of messages -- local function funcGetPage() -- Debug Message -- log.dbg("Debug - Getting page: ".. cmdUrl) -- Get the page and check to see if we got results -- local body, err if (internalState.strStatBodyCache ~= nil and internalState.strMBox == globals.strInbox) then body = internalState.strStatBodyCache internalState.strStatBodyCache = nil else body, err = browser:get_uri(cmdUrl) if body == nil then return body, err end end -- Is the session expired -- local _, _, strSessExpr = string.find(body, globals.strRetLoginSessionExpired) if strSessExpr == nil then -- Invalidate the session -- internalState.bLoginDone = nil session.remove(hash()) log.raw("Session Expired - Last page loaded: " .. cmdUrl .. ", Body: " .. body) -- Try Logging back in -- local status = login() if status ~= POPSERVER_ERR_OK then return nil, "Session expired. Unable to recover" end -- Reset the local variables -- browser = internalState.browser cmdUrl = firstUrl if nPage > 1 then cmdUrl = nextUrl .. (nPage - 1) end -- Retry to load the page -- browser:get_uri(cmdUrl) end -- Get the total number of messages -- if nTotMsgs == 0 then local _, _, strTotMsgs = string.find(body, globals.strMsgListCntPattern) if strTotMsgs == nil then nTotMsgs = 0 else nTotMsgs = tonumber(strTotMsgs) end log.dbg("Total messages in message list: " .. nTotMsgs) end return body, err end -- Run through the pages and pull out all the message pieces from -- all the message lists -- if not support.do_until(funcGetPage, funcCheckForMorePages, funcProcess) then log.error_print("STAT Failed.\n") session.remove(hash()) log.raw("Session removed (STAT Failure) - Account: " .. internalState.strUser .. "@" .. internalState.strDomain) return POPSERVER_ERR_NETWORK end -- Update our state -- internalState.bStatDone = true -- Check to see that we completed successfully. If not, return a network -- error. This is the safest way to let the email client now that there is -- a problem but that it shouldn't drop the list of known uidls. if (nMsgs < nTotMsgs) then return POPSERVER_ERR_NETWORK end -- Return that we succeeded -- return POPSERVER_ERR_OK end -- Fill msg uidl field -- function uidl(pstate,msg) return common.uidl(pstate, msg) end -- Fill all messages uidl field -- function uidl_all(pstate) return common.uidl_all(pstate) end -- Fill msg size -- function list(pstate,msg) return common.list(pstate, msg) end -- Fill all messages size -- function list_all(pstate) return common.list_all(pstate) end -- Unflag each message marked for deletion -- function rset(pstate) return common.rset(pstate) end -- Mark msg for deletion -- function dele(pstate,msg) return common.dele(pstate, msg) end -- Do nothing -- function noop(pstate) return common.noop(pstate) end -- Retrieve the message -- function retr(pstate, msg, data) downloadMsg(pstate, msg, -2, data) return POPSERVER_ERR_OK end -- Top Command (like retr) -- function top(pstate, msg, nLines, data) downloadMsg(pstate, msg, nLines, data) return POPSERVER_ERR_OK end -- Plugin Initialization - Pretty standard stuff. Copied from the manual -- function init(pstate) -- Let the log know that we have been found -- log.dbg(PLUGIN_NAME .. "(" .. PLUGIN_VERSION ..") found!\n") -- Import the freepops name space allowing for us to use the status messages -- freepops.export(pop3server) -- Load dependencies -- -- Serialization -- require("serial") -- Browser -- require("browser") -- MIME Parser/Generator -- require("mimer") -- Common module -- require("common") -- Run a sanity check -- freepops.set_sanity_checks() -- Let the log know that we have initialized ok -- log.dbg(PLUGIN_NAME .. "(" .. PLUGIN_VERSION ..") initialized!\n") -- Everything loaded ok -- return POPSERVER_ERR_OK end -- EOF -- ************************************************************************** --