-- ************************************************************************** -- -- FreePOPs @foo.xx webmail interface, the plugin made in the tutorial, -- see the manal for more infos about this simple example -- -- $Id: supereva.lua,v 1.14 2006/02/04 19:36:43 gareuselesinge Exp $ -- -- Released under the GNU/GPL license -- Written by Me -- ************************************************************************** -- PLUGIN_VERSION = "0.0.9" PLUGIN_NAME = "Supereva web mail" PLUGIN_REQUIRE_VERSION = "0.0.97" PLUGIN_LICENSE = "GNU/GPL" PLUGIN_URL = "http://www.freepops.org/download.php?file=supereva.lua" PLUGIN_HOMEPAGE = "http://www.freepops.org" PLUGIN_AUTHORS_NAMES = {"Andrea Dalle Molle","Enrico Tassi","Visioning"} PLUGIN_AUTHORS_CONTACTS = {"Tund3r (at) fastwebnet (dot) it", "gareuselesinge (at) users (dot) sourceforge (dot) net","unknown"} PLUGIN_DOMAINS = {"@supereva.it","@supereva.com","@freemail.it","@freeweb.org","@mybox.it","@superdada.com","@cicciociccio.com","@mp4.it","@dadacasa.com","@clarence.com","@concento.it"} PLUGIN_PARAMETERS = { {name="onlynew", description={ en="Set this to 1 if you want to see only new messages. Should be useless since version 0.0.6", it="Mettilo a 1 se non vuoi che vengano scaricati 2 volte i messaggi se lasci una copia dei messaggi sul server. Verranno visualizzati solo i messaggi nuovi. Dalla versione 0.0.6 non dovrebbe essercene bisogno."}}, } PLUGIN_DESCRIPTIONS = { it=[[Questo plugin consente di scaricare la posta del portale supereva.it]], en=[[This plugin is for the supereva.it portal]] } -- ------------------------------------------------------------------------ -- -- Global plugin state supereva_globals= { username="nothing", password="nothing", browser=nil } -- ------------------------------------------------------------------------ -- -- Mapping: handling new/ --> cur/ uidls stat_mapping = { } function get_stat_mapping(k) return stat_mapping[k] or k end -- ------------------------------------------------------------------------ -- -- Constants local strings = { login_url = "http://www.supereva.com/cgi-bin/sn_my/login.chm?".. "uri=http://webmail.supereva.com/cgi-bin/login.chm&ser=email&username=%s&password=%s", first = "http://webmail.supereva.it/cgi-bin/main.chm?".. "mailfolder=in&proaction=readmailbox&mlt_msgs=%d", base = "http://webmail.supereva.it/cgi-bin/", body_begin = '
', body_end = '
\n\n*
\n\n*', body_end_with_attach = '\n\n*
\n\n*
', head_begin = '
\n
', head_end = '
\n\n*
', get_url = "http://webmail.supereva.it/cgi-bin/nrmail03.chm?".. "setflags=yes&msgnum=%s&mailaction=read&mailfolder=in", next_page = "mailfolder=in&changefolder=changefolder&".. "proaction=readmailbox&check_mail=&mlt_msgs=%d", e = ".*.*".. ".*".. "[.*]{img}.*.*".. "[.*]{b}[.*]{font}[.*]{/font}[.*]{/b}.*[.*]{b}[.*]{/b}.*.*.*".. ".*

[.*]{img}[.*]{b}[.*]{/b}.*

[.*].*".. "[.*]{b}[.*]{/b}.*.*".. ".*.*".. "", g ="OO".. "O".. "[O]{O}OO".. "[O]{O}[O]{O}[O]{O}[O]{O}O[O]{O}[O]{O}OOO".. "O[O]{O}[O]{O}[O]{O}O[O]O".. "[O]{O}[O]{O}OO".. "XO".. "", attach_a=".*.*".. ".*.*.*.*".. ".*.*
.*.*.*
.*
.*.*".. ".*.*.*.*
.*
.*".. ".*.*.*
.*
.*".. ".*.*.*
.*
.*.*".. "", attach_b="OO".. "OOOO".. "OOOXOOOO".. "OOOOOO".. "OOOOO".. "OOOOOO".. "" } -------------------------------------------------------------------------------- -- Serialize the internal_state -- -- serial. serialize is not enough powerful to correcly serialize the -- internal state. The field b is the problem. b is an object. This means -- that it is a table (and no problem for this) that has some field that are -- pointers to functions. this is the problem. there is no easy way for the -- serial module to know how to serialize this. so we call b:serialize -- method by hand hacking a bit on names -- function serialize_state() supereva_globals.stat_done = false; return serial.serialize("supereva_globals",supereva_globals) .. supereva_globals.browser:serialize("supereva_globals.browser") end -------------------------------------------------------------------------------- -- The key used to store session info -- -- This key must be unique for all webmails, since the session pool is one -- for all the webmails -- function key() return (supereva_globals.username or "").. (supereva_globals.password or "").. (supereva_globals.folder or "") end -- ************************************************************************** -- -- -- This is the interface to the external world. These are the functions -- that will be called by FreePOPs. -- -- param pstate is the userdata to pass to (set|get)_popstate_* functions -- param username is the mail account name -- param password is the account password -- param msg is the message number to operate on (may be decreased dy 1) -- param pdata is an opaque data for popserver_callback(buffer,pdata) -- -- return POPSERVER_ERR_* -- -- ************************************************************************** -- -- Is called to initialize the module function init(pstate) freepops.export(pop3server) log.dbg("FreePOPs plugin '".. PLUGIN_NAME.."' version '"..PLUGIN_VERSION.."' started!\n") require("serial") -- the serialization module require("browser") -- the browser module require("common") -- the common implementation module require("mimer") -- the mimer module -- checks on globals freepops.set_sanity_checks() return POPSERVER_ERR_OK end -- -------------------------------------------------------------------------- -- -- Must save the mailbox name function user(pstate,username) local domain = freepops.get_domain(username) local name = freepops.get_name(username) supereva_globals.username = name .. "@" .. domain -- parameter to skip cur/ and get only new/ supereva_globals.onlynew = (freepops.MODULE_ARGS or {}).onlynew ~= nil return POPSERVER_ERR_OK end -- ------------------------------------------------------------------------ -- -- Login helper function supereva_login() if supereva_globals.login_done then return POPSERVER_ERR_OK end -- create a new browser local b = browser.new() -- store the browser object in globals supereva_globals.browser = b --b:verbose_mode() local post_uri = string.format(strings.login_url, supereva_globals.username,supereva_globals.password) -- post it local file,err = nil, nil file,err = supereva_globals.browser:get_uri(post_uri) if file == nil then log.error_print(err or "unknown error") return POPSERVER_ERR_NETWORK end -- print("we received this webpage: ".. file) -- search the session ID -- local id = string.find(file,"Benvenuto nella tua webMail") local id = string.find(file,"logout.chm") if id == nil then local _,_,cause = string.find(file,"ATTENZIONE:([A-Za-z%s]-)<") log.error_print(cause or "Unable to catch error details") return POPSERVER_ERR_AUTH else log.say("Session started for " .. supereva_globals.username) supereva_globals.login_done = true end return POPSERVER_ERR_OK end -- -------------------------------------------------------------------------- -- -- Must login function pass(pstate,password) supereva_globals.password = password -- eventually load session local s = session.load_lock(key()) -- check if loaded properly if s ~= nil then -- "\a" means locked if s == "\a" then log.say("Session for "..supereva_globals.username.. " is already locked\n") return POPSERVER_ERR_LOCKED end -- load the session local c,err = loadstring(s) if not c then log.error_print("Unable to load saved session: "..err) return supereva_login() end -- exec the code loaded from the session string c() log.say("Session loaded for " .. supereva_globals.username .. "\n") return POPSERVER_ERR_OK else -- call the login procedure return supereva_login() end end -- -------------------------------------------------------------------------- -- -- Must quit without updating function quit(pstate) session.unlock(key()) return POPSERVER_ERR_OK end -- -------------------------------------------------------------------------- -- -- Update the mailbox status and quit function quit_update(pstate) -- we need the stat local st = stat(pstate) if st ~= POPSERVER_ERR_OK then return st end -- shorten names, not really important local post_uri = "http://".. supereva_globals.browser:wherearewe() .. "/cgi-bin/del_mov.cgi" local post_data = "" -- here we need the stat, we build the uri and we check if we -- need to delete something local delete_something = false; local conta=0 for i=1,get_popstate_nummesg(pstate) do if get_mailmessage_flag(pstate,i,MAILMESSAGE_DELETE) then post_data = post_data .. "&MSG"..conta.. "=" .. get_stat_mapping(get_mailmessage_uidl(pstate,i)) delete_something = true conta=conta+1 end end if delete_something then post_data="mailfolder=in&max_msg="..conta..post_data.. "&pldelete=Delete&tobox=sent" --post_data = string.gsub(post_data,"new","cur") local file,err = supereva_globals.browser:post_uri(post_uri,post_data) if file == nil then log.error_print(err or "unknown error") return POPSERVER_ERR_NETWORK end end -- save fails if it is already saved session.save(key(),serialize_state(),session.OVERWRITE) -- unlock is useless if it have just been saved, but if we save -- without overwriting the session must be unlocked manually -- since it would fail instead overwriting session.unlock(key()) log.say("Session saved for " .. supereva_globals.username .. "\n") return POPSERVER_ERR_OK end -- -------------------------------------------------------------------------- -- -- Fill the number of messages and their size function stat(pstate) if supereva_globals.stat_done == true then return POPSERVER_ERR_OK end local pagina = 1 local uri local file,err = nil, nil local conta=0 local fine = nil --local b = supereva_globals.browser repeat -- show browser state --supereva_globals.browser:show() uri = string.format(strings.first,pagina) file,err = supereva_globals.browser:get_uri(uri) if file == nil then log.error_print(err or "unknown error") return POPSERVER_ERR_NETWORK end fine = string.find(file, string.format(strings.next_page,pagina+1)) local x = mlex.match(file,strings.e,strings.g) --debug print --x:print() -- increment message number if supereva_globals.onlynew then -- we will increase nummesg later else set_popstate_nummesg(pstate,x:count()+conta) --print ("\n\n messaggi: " .. x:count().."\n") end -- set uidl/size for each message for i=1,x:count() do local _,_,size = string.find(x:get(1,i-1),"(%d+)") local _,_,size_mult_k = string.find(x:get(1,i-1),"([Kk])") local _,_,size_mult_m = string.find(x:get(1,i-1),"([Mm])") local _,_,uidl = string.find(x:get(0,i-1),'value="([^"]*)"') if size_mult_k ~= nil then size = size * 1024 end if size_mult_m ~= nil then size = size * 1024 * 1024 end -- add only new/ mails or bot new/ and cur/ if supereva_globals.onlynew then local _,_,x = string.find(uidl,"^(new)") if x ~= nil then local num = get_popstate_nummesg(pstate) num = math.max ( num , 0 ) set_popstate_nummesg(pstate,num+1) set_mailmessage_size(pstate,num+1,size) set_mailmessage_uidl(pstate,num+1,uidl) end else set_mailmessage_size(pstate,i+conta,size) local uidl_new=string.gsub(uidl,"new","cur") stat_mapping[uidl_new] = uidl set_mailmessage_uidl(pstate,i+conta,uidl_new) end end -- fox i=1,x:count() conta=get_popstate_nummesg(pstate) --print ("conta: ",conta) pagina=pagina+1 --print("continuare? ",fine) until fine == nil supereva_globals.stat_done = true 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 merked 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 -- -------------------------------------------------------------------------- -- -- Get first lines message msg lines, must call -- popserver_callback to send the data function top(pstate,msg,lines,pdata) local head,body,body_html,attach = supereva_parse_webmessage(pstate,msg) local b = supereva_globals.browser local global = common.new_global_for_top(lines,nil) local cb = mimer.callback_mangler(common.top_cb(global,pdata,true)) mimer.pipe_msg( head,body,body_html, "http://" .. b:wherearewe(),attach,b,cb) return POPSERVER_ERR_OK end -- -------------------------------------------------------------------------- -- -- Get message msg, must call -- popserver_callback to send the data function retr(pstate,msg,data) local head,body,body_html,attach = supereva_parse_webmessage(pstate,msg) local b = supereva_globals.browser local cb = mimer.callback_mangler(common.retr_cb(data)) mimer.pipe_msg( head,body,body_html, "http://" .. b:wherearewe(),attach,b,cb) return POPSERVER_ERR_OK end -- ------------------------------------------------------------------------- -- -- helper function supereva_parse_webmessage(pstate,msg) -- we need the stat local st = stat(pstate) if st ~= POPSERVER_ERR_OK then return st end -- some local stuff local b = supereva_globals.browser -- build the uri local uidl = get_stat_mapping(get_mailmessage_uidl(pstate,msg)) local uri = string.format(strings.get_url, uidl) -- get the main mail page local f,rc = b:get_uri(uri) -- now it is in cur/ so we hack the mapping stat_mapping[get_mailmessage_uidl(pstate,msg)] = string.gsub(get_mailmessage_uidl(pstate,msg),"new","cur") -- extract the body an the attach local from,to = string.find(f,strings.body_begin) local from1,to1 = string.find(f,strings.body_end_with_attach) if to1 == nil then from1,to1 = string.find(f,strings.body_end) end local attach = "" local body = "" if to1 ~= nil then -- normal body = string.sub(f,to+1,from1-1) attach = string.sub(f,from1+1,-1) else -- no body body = "" attach = string.sub(f,to+1,-1) end -- extracts the attach list local x = mlex.match(attach,strings.attach_a,strings.attach_b) --x:print() local n = x:count() local attach = {} for i = 1,n do local _,_,url = string.find(x:get(0,i-1),'HREF=..([^"]*)"') url = string.gsub(url,"&", "&") --print("\nattach:"..n.."http://" .. b:wherearewe().."/" .. url) attach[x:get(1,i-1)] = "http://" .. b:wherearewe().."/" .. url table.setn(attach,table.getn(attach) + 1) end -- mangles the body --local body,body_html = mangle_body(body) local body_html = body local body = nil -- extracts the important part local from,to = string.find(f,strings.head_begin) local f1 = string.sub(f,to+1,-1) local from1,to1 = string.find(f1,strings.head_end) local head = string.sub(f1,1,from1-1) -- mangles the header head = mangle_head(head) return head,body,body_html,attach end -- ------------------------------------------------------------------------- -- -- helper function mangle_body(s) local _,_,x = string.find(s,"^%s*(<[Pp][Rr][Ee]>)") if x ~= nil then local base = "http://" .. supereva_globals.browser:wherearewe() s = mimer.html2txtmail(s,base) return s,nil else --s = mimer.remove_lines_in_proper_mail_header(s, --{"content%-type", --"content%-disposition","mime%-version"}) -- the webmail damages these tags s = mimer.remove_tags(s, {"html","head","body","doctype","void","style"}) -- s = squirrelmail_string.html_preamble .. s .. -- squirrelmail_string.html_conclusion return nil,s end end -- ------------------------------------------------------------------------- -- -- given the HTML piece with the header, this returns the *clean* mail header -- function mangle_head(s) -- helper #1 - extract a header field from HTML local function extract(field,data) local exM = ".*.*%s.*.*.*<.*>" local exG = "OOOOOX" local x = mlex.match(data,string.format(exM,field),exG) --x:print() if x:count() < 1 then return nil else local s = x:get(0,0) s = string.gsub(s,"%s+"," ") s = string.gsub(s,"\r","") s = string.gsub(s,"\n","") s = string.gsub(s,"<","<") s = string.gsub(s,">",">") s = string.gsub(s," ","") return s end end -- helper #2 - translate the date back to english local function translate(data) data=string.gsub(data,"Gen","Jan") data=string.gsub(data,"Mag","May") data=string.gsub(data,"Giu","Jun") data=string.gsub(data,"Lug","Jul") data=string.gsub(data,"Ago","Aug") data=string.gsub(data,"Set","Sep") data=string.gsub(data,"Ott","Oct") data=string.gsub(data,"Dic","Dec") return data end -- helper #3 - build the header local function build(to,oggetto,from,cc,data) local s = "" if to ~= nil then s = s .. "To: "..to end if oggetto ~= nil then s = s .. "\nSubject: "..oggetto end if from ~= nil then s = s .. "\nFrom: "..from end if cc ~= nil then s = s .. "\nCC: "..cc end if data ~= nil then s = s .. "\nDate: "..data end return s end -- here we are, now do the job -- extract all interesting fields local from = extract("^Da:",s) or "" local to = extract("^A:",s) or "" local cc = extract("^CC:",s) or "" local data = extract("^Data:",s) or "" local oggetto = extract("^Oggetto:",s) or "" -- magle them data = translate(data) -- build the header s = build(to,oggetto,from,cc,data) -- not sure it is needed s = mimer.remove_tags(s,{"tt","nobr","a"}) -- ?? s = mimer.txt2mail(s) -- go! return s end -- EOF -- ************************************************************************** --