Allegato "libero.lua"
Scarica 1 -- --------------------------- READ THIS PLEASE ----------------------------- --
2 -- This file is not only the libero webmail plugin. It is also a well
3 -- documented example of webmail plugin.
4 --
5 -- Before reading this you should learn something about lua. The lua
6 -- language is an excellent (at least in my opinion), small and easy
7 -- language. You can learn something at http://www.lua.org (the main website)
8 -- or at http://lua-users.org/wiki/TutorialDirectory (a good and short tutorial)
9 --
10 -- Feel free to contact the author if you have problems in understanding
11 -- this file
12 --
13 -- To start writing a new plugin please use skeleton.lua as the base.
14 -- -------------------------------------------------------------------------- --
15
16
17 -- ************************************************************************** --
18 -- FreePOPs @libero.it, @inwind.it, @blu.it, @iol.it webmail interface
19 --
20 -- $Id: libero.lua,v 1.11 2008/06/26 20:12:48 gareuselesinge Exp $
21 --
22 -- Released under the GNU/GPL license
23 -- Written by Enrico Tassi <gareuselesinge@users.sourceforge.net>
24 -- ************************************************************************** --
25
26 -- these are used in the init function and by the website,
27 -- fill them in the right way
28
29 -- single string, all required
30 PLUGIN_VERSION = "0.2.17"
31 PLUGIN_NAME = "Libero.IT"
32 PLUGIN_REQUIRE_VERSION = "0.2.0"
33 PLUGIN_LICENSE = "GNU/GPL"
34 PLUGIN_URL = "http://www.freepops.org/download.php?module=libero.lua"
35 PLUGIN_HOMEPAGE = "http://www.freepops.org/"
36
37 -- list of strings, one required, one contact for each author
38 PLUGIN_AUTHORS_NAMES = {"Enrico Tassi"}
39 PLUGIN_AUTHORS_CONTACTS = {"gareuselesinge (at) users (.) sourceforge (.) net"}
40
41 -- list of strings, one required
42 PLUGIN_DOMAINS = {"@libero.it","@inwind.it","@iol.it","@blu.it"}
43
44 -- list of tables with fields name and description.
45 -- description must be in the stle of PLUGIN_DESCRIPTIONS,
46 -- so something like {it="bla bla bla", en = "bla bla bla"}
47 PLUGIN_PARAMETERS = {
48 {name = "folder", description = {
49 it = [[
50 Serve per selezionare la cartella (inbox è quella di default)
51 su cui operare.
52 Le cartelle standard disponibili sono draft, inbox, outbox, trash.
53 Se hai creato delle cartelle dalla webmail allora puoi accedervi usando il
54 loro nome. Se la cartella non è al livello principale
55 puoi accederci usando
56 una / per separala dalla cartella padre. Questo è un esempio di uno
57 user name per leggere la cartella son, che è
58 una sotto cartella della cartella
59 father: foo@libero.it?folder=father/son]],
60 }
61 },
62 }
63
64 -- map from lang to strings, like {it="bla bla bla", en = "bla bla bla"}
65 PLUGIN_DESCRIPTIONS = {
66 it="Questo plugin è per gli account di "..
67 "posta del portale libero.it. "..
68 "Utilizzare lo username completo di dominio e l'usuale password. ",
69 en="This plugin is for italian users only."
70 }
71
72
73
74 -- ************************************************************************** --
75 -- strings
76 -- ************************************************************************** --
77
78 -- these are the webmail-dependent strings
79 --
80 -- Some of them are incomplete, in the sense that are used as string.format()
81 -- (read sprintf) arguments, so their %s and %d are filled properly
82 --
83 -- C, E, G are postfix respectively to Captures (lua string pcre-style
84 -- expressions), mlex expressions, mlex get expressions.
85 --
86 local libero_string = {
87 site = ".libero.it",
88 -- The uri the browser uses when you click the "login" button
89 login_url = "https://login.libero.it/logincheck.php",
90 login_post = "DOMAIN=%s&LOGINID=%s&PASSWORD=%s&AJAX=0&"..
91 "RET_URL=http://wpop%d%s/email.php&SERVICE_ID=old_email",
92 -- This is the capture to get the session ID from the login-done webpage
93 sessionC = "/cgi%-bin/webmail%.[ce][gx][ie]%?ID=([a-zA-Z0-9_%-]+)&",
94 -- This is the mlex expression to interpret the message list page.
95 -- Read the mlex C module documentation to understand the meaning
96 --
97 -- This is probably one of the more boaring tasks of the story.
98 -- An easy and not so boring way of writing a mlex expression is
99 -- to cut and paste the html source and work on it. For example
100 -- you could copy a message table row in a blank file, substitute
101 -- every useless field with '.*'.
102
103 statE = ".*<a.*doitMsg.*>[.*]{!--.*--}.*<script>.*IMGEv.*</script>.*</a>.*<script>.*AEv.*</script>[.*]{!--.*--}.*<script>.*</script>.*</a>.*</TD>.*<TD>.*<div>.*</TD>.*<TD>.*<script>.*</script>[.*]{!--.*--}.*<script>.*</script>.*</a>.*</TD>.*<TD>.*<div>.*</TD>.*<script>.*</script>[.*]{b}.*{/b}[.*]</TD>[.*]{!--.*--}.*<TD>.*<div>.*</TD>.*<script>.*</script>[.*]{b}.*{/b}[.*]</TD>[.*]{!--.*--}.*</TR>";
104
105 -- This is the mlex get expression to choose the important fields
106 -- of the message list page. Used in combination with statE
107
108 statG = "O<X>[O]{O}O<O>O<O>O<O>O<O>O<O>[O]{O}O<O>O<O>O<O>O<O>O<O>O<O>O<O>O<O>O<O>O<O>[O]{O}O<O>O<O>O<O>O<O>O<O>O<O>O<O>O<O>O<O>[O]{O}O{O}[O]<O>[O]{O}O<O>O<O>O<O>O<O>O<O>[O]{O}X{O}[O]<O>[O]{O}O<O>";
109
110 -- The uri for the first page with the list of messages
111 first = "http://%s/cgi-bin/webmail.cgi?ID=%s&Act_Msgs=1&"..
112 "C_Folder=%s",
113 -- The capture to check if there is one more page of message list
114 next_checkC = "<a href=\"javascript:doit"..
115 "%('Act_Msgs_Page_Next',1,1%)\"",
116 -- The uri to get the next page of messages
117 next = "http://%s/cgi-bin/webmail.cgi?ID=%s&Act_Msgs_Page_Next=1&"..
118 "HELP_ID=inbox&SEL_ALL=0&"..
119 "From_Vu=1&C_Folder=%s&msgID=&Msg_Read=&"..
120 "R_Folder=&ZONEID=&Fld_P_List=%s&"..
121 "dummy1_List=%s&dummy2_List=%s",
122 -- The capture to understand if the session ended
123 timeoutC = "(Sessione non valida. Riconnettersi)",
124 -- The uri to save a message (read download the message)
125 save = "http://%s/cgi-bin/webmail.cgi/message.txt?ID=%s&"..
126 "msgID=%s&Act_V_Save=1&"..
127 "R_Folder=%s&Body=0&filename=message.txt",
128 -- The uri to delete some messages
129 delete = "http://%s/cgi-bin/webmail.cgi?ID=%s&Act_Msgs_Del_CF_Ok=1&"..
130 "HELP_ID=inbox&SEL_ALL=0&From_Vu=1&C_Folder=%s&"..
131 "msgID=&Msg_Read=&R_Folder=&ZONEID=&Fld_P_List=%s&"..
132 "dummy1_List=%s&dummy2_List=%s&Msg_Nb=%d",
133 -- The peace of uri you must append to delete to choose the messages
134 -- to delete
135 delete_next = "&Msg_Sel_%d=%s"
136 }
137
138
139 -- ************************************************************************** --
140 -- State
141 -- ************************************************************************** --
142
143 -- this is the internal state of the plugin. This structure will be serialized
144 -- and saved to remember the state.
145 internal_state = {
146 stat_done = false,
147 login_done = false,
148 popserver = nil,
149 session_id = nil,
150 domain = nil,
151 name = nil,
152 password = nil,
153 b = nil
154 }
155
156 -- ************************************************************************** --
157 -- Helpers functions
158 -- ************************************************************************** --
159
160 --------------------------------------------------------------------------------
161 -- Checks if a message number is in range
162 --
163 function check_range(pstate,msg)
164 local n = get_popstate_nummesg(pstate)
165 return msg >= 1 and msg <= n
166 end
167
168 --------------------------------------------------------------------------------
169 -- Serialize the internal_state
170 --
171 -- serial. serialize is not enough powerful to correcly serialize the
172 -- internal state. The field b is the problem. b is an object. This means
173 -- that it is a table (and no problem for this) that has some field that are
174 -- pointers to functions. this is the problem. there is no easy way for the
175 -- serial module to know how to serialize this. so we call b:serialize
176 -- method by hand hacking a bit on names
177 --
178 function serialize_state()
179 internal_state.stat_done = false;
180
181 return serial.serialize("internal_state",internal_state) ..
182 internal_state.b:serialize("internal_state.b")
183 end
184
185 --------------------------------------------------------------------------------
186 -- The key used to store session info
187 --
188 -- This key must be unique for all webmails, since the session pool is one
189 -- for all the webmails
190 --
191 function key()
192 return (internal_state.name or "")..
193 (internal_state.domain or "")..
194 (internal_state.password or "")..
195 (internal_state.folder or "")
196 end
197
198 --------------------------------------------------------------------------------
199 -- Login to the libero website
200 --
201 function libero_login()
202 if internal_state.login_done then
203 return POPSERVER_ERR_OK
204 end
205
206 -- build the uri
207 local password = internal_state.password
208 local popnumber = math.fmod(os.time(),15) + 1 -- == random(1..15)
209 local domain = internal_state.domain
210 local site = libero_string.site
211 local user = internal_state.name.."@"..internal_state.domain
212 local x,y = math.fmod(os.time(),16),math.fmod(os.time()*2,16)
213 local uri = libero_string.login_url
214 log.dbg("Using webserver " .. uri);
215 local post= string.format(libero_string.login_post,
216 domain,user,password,popnumber,site)
217
218 -- the browser must be preserved
219 internal_state.b = browser.new()
220 local b = internal_state.b
221 --b:verbose_mode()
222 b:ssl_init_stuff()
223
224 -- load some cookies
225
226 -- the functions for do_until
227 -- extract_f uses the support function to extract a capture specifyed
228 -- in libero_string.sessionC, and pu ts the result in
229 -- internal_state["session_id"]
230 -- check_f the is the failure funtion, that means that the do_until
231 -- will not repeat
232 -- retrive_f is the function that do_retrive the uri uri with the
233 -- browser b. The function will be retry_n 3 times if it fails
234 local extract_f = support.do_extract(
235 internal_state,"session_id",libero_string.sessionC)
236 local check_f = support.check_fail
237 local retrive_f = support.retry_n(
238 3,support.do_post(internal_state.b,uri,post))
239
240 -- maybe implement a do_once
241 if not support.do_until(retrive_f,check_f,extract_f) then
242 -- not sure that it is a password error, maybe a network error
243 -- the do_until will log more about the error before us...
244 -- maybe we coud add a sanity_check function to do until to
245 -- check if the received page is a server error page or a
246 -- good page.
247 log.error_print("Login failed\n")
248 return POPSERVER_ERR_AUTH
249 end
250
251 -- check if do_extract has correctly extracted the session ID
252 if internal_state.session_id == nil then
253 log.error_print("Login failed, unable to get session ID\n")
254 return POPSERVER_ERR_AUTH
255 end
256
257 -- save all the computed data
258 internal_state.popserver = "wpop" .. popnumber .. site
259 internal_state.login_done = true
260
261 -- log the creation of a session
262 log.say("Session started for " .. internal_state.name .. "@" ..
263 internal_state.domain ..
264 "(" .. internal_state.session_id .. ")\n")
265
266 return POPSERVER_ERR_OK
267 end
268
269 -- ************************************************************************** --
270 -- Libero functions
271 -- ************************************************************************** --
272
273 -- Must save the mailbox name
274 function user(pstate,username)
275
276 -- extract and check domain
277 local domain = freepops.get_domain(username)
278 local name = freepops.get_name(username)
279
280 -- default is @libero.it (probably useless)
281 if not domain then
282 -- default domain
283 domain = "libero.it"
284 end
285
286 -- save domain and name
287 internal_state.domain = domain
288 internal_state.name = name
289 local f = (freepops.MODULE_ARGS or {}).folder or "inbox"
290 local f64 = base64.encode(f)
291 local f64u = base64.encode(string.upper(f))
292 internal_state.folder = f64
293 internal_state.folder_uppercase = f64u
294
295 return POPSERVER_ERR_OK
296 end
297
298 -- -------------------------------------------------------------------------- --
299 -- Must login
300 function pass(pstate,password)
301 -- save the password
302 internal_state.password = password
303
304 -- eventually load session
305 local s = session.load_lock(key())
306
307 -- check if loaded properly
308 if s ~= nil then
309 -- "\a" means locked
310 if s == "\a" then
311 log.say("Session for "..internal_state.name..
312 " is already locked\n")
313 return POPSERVER_ERR_LOCKED
314 end
315
316 -- load the session
317 local c,err = loadstring(s)
318 if not c then
319 log.error_print("Unable to load saved session: "..err)
320 return libero_login()
321 end
322
323 -- exec the code loaded from the session string
324 c()
325
326 log.say("Session loaded for " .. internal_state.name .. "@" ..
327 internal_state.domain ..
328 "(" .. internal_state.session_id .. ")\n")
329
330 return POPSERVER_ERR_OK
331 else
332 -- call the login procedure
333 return libero_login()
334 end
335 end
336
337 -- -------------------------------------------------------------------------- --
338 -- Must quit without updating
339 function quit(pstate)
340 session.unlock(key())
341 return POPSERVER_ERR_OK
342 end
343
344 -- -------------------------------------------------------------------------- --
345 -- Update the mailbox status and quit
346 function quit_update(pstate)
347 -- we need the stat
348 local st = stat(pstate)
349 if st ~= POPSERVER_ERR_OK then return st end
350
351 -- shorten names, not really important
352 local popserver = internal_state.popserver
353 local session_id = internal_state.session_id
354 local b = internal_state.b
355
356 local uri = string.format(libero_string.delete,popserver,session_id,
357 internal_state.folder_uppercase,
358 internal_state.folder,
359 internal_state.folder,
360 internal_state.folder,
361 get_popstate_nummesg(pstate))
362
363 -- here we need the stat, we build the uri and we check if we
364 -- need to delete something
365 local delete_something = false;
366
367 for i=1,get_popstate_nummesg(pstate) do
368 if get_mailmessage_flag(pstate,i,MAILMESSAGE_DELETE) then
369 uri = uri .. string.format(libero_string.delete_next,
370 i,get_mailmessage_uidl(pstate,i))
371 delete_something = true
372 end
373 end
374
375 if delete_something then
376 -- Build the functions for do_until
377 local extract_f = function(s) return true,nil end
378 local check_f = support.check_fail
379 local retrive_f = support.retry_n(3,support.do_retrive(b,uri))
380
381 if not support.do_until(retrive_f,check_f,extract_f) then
382 log.error_print("Unable to delete messages\n")
383 return POPSERVER_ERR_UNKNOWN
384 end
385 end
386
387 -- save fails if it is already saved
388 session.save(key(),serialize_state(),session.OVERWRITE)
389 -- unlock is useless if it have just been saved, but if we save
390 -- without overwriting the session must be unlocked manually
391 -- since it would fail instead overwriting
392 session.unlock(key())
393
394 log.say("Session saved for " .. internal_state.name .. "@" ..
395 internal_state.domain .. "(" ..
396 internal_state.session_id .. ")\n")
397
398 return POPSERVER_ERR_OK
399 end
400
401 -- -------------------------------------------------------------------------- --
402 -- Fill the number of messages and their size
403 function stat(pstate)
404
405 -- check if already called
406 if internal_state.stat_done then
407 return POPSERVER_ERR_OK
408 end
409
410 -- shorten names, not really important
411 local popserver = internal_state.popserver
412 local session_id = internal_state.session_id
413 local b = internal_state.b
414
415 -- this string will contain the uri to get. it may be updated by
416 -- the check_f function, see later
417 local uri = string.format(libero_string.first,popserver,session_id,
418 internal_state.folder)
419
420 -- The action for do_until
421 --
422 -- uses mlex to extract all the messages uidl and size
423 local function action_f (s)
424 -- calls match on the page s, with the mlexpressions
425 -- statE and statG
426 -- print(s)
427 local x = mlex.match(s,libero_string.statE,libero_string.statG)
428 --x:print()
429
430 -- the number of results
431 local n = x:count()
432
433 if n == 0 then
434 return true,nil
435 end
436
437 -- this is not really needed since the structure
438 -- grows automatically... maybe... don't remember now
439 local nmesg_old = get_popstate_nummesg(pstate)
440 local nmesg = nmesg_old + n
441 set_popstate_nummesg(pstate,nmesg)
442
443 -- gets all the results and puts them in the popstate structure
444 for i = 1,n do
445 local uidl = x:get (0,i-1)
446 local size = x:get (1,i-1)
447
448 -- arrange message size
449 local k = nil
450 k = string.match(size,"([Kk][Bb])")
451 size = string.match(size,"(%d+)")
452 --uidl = string.match(uidl,"value=\"(%d+)\"")
453 uidl = string.match(uidl, "doitMsg.'Act_View',%d+,%d+,'(%d+)'")
454 size = tonumber(size) + 2
455 if k ~= nil then
456 size = size * 1024
457 end
458
459 if not uidl or not size then
460 return nil,"Unable to parse page"
461 end
462
463 -- set it
464 set_mailmessage_size(pstate,i+nmesg_old,size)
465 set_mailmessage_uidl(pstate,i+nmesg_old,uidl)
466 end
467
468 return true,nil
469 end
470
471 -- check must control if we are not in the last page and
472 -- eventually change uri to tell retrive_f the next page to retrive
473 local function check_f (s)
474 local tmp1,tmp2 = string.find(s,libero_string.next_checkC)
475 if tmp1 ~= nil then
476 -- change retrive behaviour
477 uri = string.format(libero_string.next,
478 popserver,session_id,
479 internal_state.folder,
480 internal_state.folder,
481 internal_state.folder,
482 internal_state.folder)
483 -- continue the loop
484 return false
485 else
486 return true
487 end
488 end
489
490 -- this is simple and uri-dependent
491 local function retrive_f ()
492 local f,err = b:get_uri(uri)
493 if f == nil then
494 return f,err
495 end
496
497 local c = string.match(f,libero_string.timeoutC)
498 if c ~= nil then
499 internal_state.login_done = nil
500 session.remove(key())
501
502 local rc = libero_login()
503 if rc ~= POPSERVER_ERR_OK then
504 return nil,--{
505 --error=
506 "Session ended,unable to recover"
507 --} hope it is ok now
508 end
509
510 popserver = internal_state.popserver
511 session_id = internal_state.session_id
512 b = internal_state.b
513
514 uri = string.format(libero_string.first,
515 popserver,session_id,internal_state.folder)
516 return b:get_uri(uri)
517 end
518
519 return f,err
520 end
521
522 -- initialize the data structure
523 set_popstate_nummesg(pstate,0)
524
525 -- do it
526 if not support.do_until(retrive_f,check_f,action_f) then
527 log.error_print("Stat failed\n")
528 session.remove(key())
529 return POPSERVER_ERR_UNKNOWN
530 end
531
532 -- save the computed values
533 internal_state["stat_done"] = true
534
535 return POPSERVER_ERR_OK
536 end
537
538 -- -------------------------------------------------------------------------- --
539 -- Fill msg uidl field
540 function uidl(pstate,msg)
541 return common.uidl(pstate,msg)
542 end
543 -- -------------------------------------------------------------------------- --
544 -- Fill all messages uidl field
545 function uidl_all(pstate)
546 return common.uidl_all(pstate)
547 end
548 -- -------------------------------------------------------------------------- --
549 -- Fill msg size
550 function list(pstate,msg)
551 return common.list(pstate,msg)
552 end
553 -- -------------------------------------------------------------------------- --
554 -- Fill all messages size
555 function list_all(pstate)
556 return common.list_all(pstate)
557 end
558 -- -------------------------------------------------------------------------- --
559 -- Unflag each message marked for deletion
560 function rset(pstate)
561 return common.rset(pstate)
562 end
563 -- -------------------------------------------------------------------------- --
564 -- Mark msg for deletion
565 function dele(pstate,msg)
566 return common.dele(pstate,msg)
567 end
568 -- -------------------------------------------------------------------------- --
569 -- Do nothing
570 function noop(pstate)
571 return common.noop(pstate)
572 end
573
574 -- -------------------------------------------------------------------------- --
575 -- Get first lines message msg lines, must call
576 -- popserver_callback to send the data
577 function retr(pstate,msg,data)
578 -- we need the stat
579 local st = stat(pstate)
580 if st ~= POPSERVER_ERR_OK then return st end
581
582 -- the callback
583 local cb = common.retr_cb(data)
584
585 -- some local stuff
586 local popserver = internal_state.popserver
587 local session_id = internal_state.session_id
588 local b = internal_state.b
589
590 -- build the uri
591 local uidl = get_mailmessage_uidl(pstate,msg)
592 local uri = string.format(libero_string.save,popserver,session_id,uidl,
593 internal_state.folder)
594
595 -- tell the browser to pipe the uri using cb
596 local f,rc = b:pipe_uri(uri,cb)
597
598 if not f then
599 log.error_print("Asking for "..uri.."\n")
600 log.error_print(rc.."\n")
601 -- don't remember if this should be done
602 --session.remove(key())
603 return POPSERVER_ERR_NETWORK
604 end
605
606 return POPSERVER_ERR_OK
607 end
608
609 -- -------------------------------------------------------------------------- --
610 -- Get message msg, must call
611 -- popserver_callback to send the data
612 function top(pstate,msg,lines,data)
613 -- we need the stat
614 local st = stat(pstate)
615 if st ~= POPSERVER_ERR_OK then return st end
616
617 -- some local stuff
618 local popserver = internal_state.popserver
619 local session_id = internal_state.session_id
620 local b = internal_state.b
621 local size = get_mailmessage_size(pstate,msg)
622
623 -- build the uri
624 local uidl = get_mailmessage_uidl(pstate,msg)
625 local uri = string.format(libero_string.save,popserver,session_id,uidl,
626 internal_state.folder)
627
628 return common.top(b,uri,key(),size,lines,data,false)
629 end
630
631 -- -------------------------------------------------------------------------- --
632 -- This function is called to initialize the plugin.
633 -- Since we need to use the browser and save sessions we have to use
634 -- some modules with the dofile function
635 --
636 -- We also export the pop3server.* names to global environment so we can
637 -- write POPSERVER_ERR_OK instead of pop3server.POPSERVER_ERR_OK.
638 --
639 function init(pstate)
640 freepops.export(pop3server)
641
642 log.dbg("FreePOPs plugin '"..
643 PLUGIN_NAME.."' version '"..PLUGIN_VERSION.."' started!\n")
644
645 require("serial") -- the serialization module
646 require("browser") -- the browser module
647 require("common") -- the common implementation module
648
649 -- checks on globals
650 freepops.set_sanity_checks()
651
652 return POPSERVER_ERR_OK
653 end
654
655 -- EOF
656 -- ************************************************************************** --
Allegati
Per riferirsi agli allegati di una pagina, usare attachment:NOME_FILE, come mostrato qui sotto nell'elenco degli allegati. NON usare l'URL che si trova in corrispondenza del collegamento [scarica], potrebbe cambiare in futuro.Non รจ consentito inserire allegati su questa pagina.