use space better under HD skins, decode quoted printables, cosmetic changes
[enigma2-plugins.git] / emailclient / src / plugin.py
1 from Components.ActionMap import ActionMap
2 from Components.GUIComponent import GUIComponent
3 from Components.HTMLComponent import HTMLComponent
4 from Components.Label import Label
5 from Screens.MessageBox import MessageBox
6 from Components.MenuList import MenuList
7 from Components.MultiContent import MultiContentEntryText
8 from Components.ScrollLabel import ScrollLabel
9 from Components.Button import Button
10 from Components.config import config, ConfigSubsection, ConfigInteger, ConfigText
11 from EmailConfig import EmailConfigScreen
12 from Plugins.Plugin import PluginDescriptor
13 from Screens.ChoiceBox import ChoiceBox
14 from Screens.Screen import Screen
15 from enigma import eListboxPythonMultiContent, eListbox, gFont
16 from twisted.mail import imap4
17 from zope.interface import implements
18 import email
19 import email.Parser
20 from email.header import decode_header
21 from TagStrip import strip_readable
22 from protocol import createFactory
23
24 config.plugins.emailimap = ConfigSubsection()
25 config.plugins.emailimap.username = ConfigText("user", fixed_size=False)
26 config.plugins.emailimap.password = ConfigText("password", fixed_size=False)
27 config.plugins.emailimap.server = ConfigText("please.config.first", fixed_size=False)
28 config.plugins.emailimap.port = ConfigInteger(143, limits = (1, 65536))
29
30 # 0= fetch all header , 10= fetch only the last 10 headers/messages of a mailbox
31 config.plugins.emailimap.maxheadertoload = ConfigInteger(0, limits = (1, 100))
32
33 from enigma import getDesktop
34 DESKTOP_WIDTH = getDesktop(0).size().width()
35 DESKTOP_HEIGHT = getDesktop(0).size().height()
36 #
37 # this is pure magic.
38 # It returns the first value, if HD (1280x720),
39 # the second if SD (720x576),
40 # else something scaled accordingly
41 # if one of the parameters is -1, scale proportionally
42 #
43 def scaleH(y2, y1):
44         if y2 == -1:
45                 y2 = y1*1280/720
46         elif y1 == -1:
47                 y1 = y2*720/1280
48         return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
49 def scaleV(y2, y1):
50         if y2 == -1:
51                 y2 = y1*720/576
52         elif y1 == -1:
53                 y1 = y2*576/720
54         return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
55 def scale(y2, y1, x2, x1, x):
56         return (y2 - y1) * (x - x1) / (x2 - x1) + y1
57
58 def decodeHeader(text, default=''):
59         if text is None:
60                 return _(default)
61         textNew = ""
62         for part in decode_header(text):
63                 (content, charset) = part
64                 if charset:
65                         textNew += content.decode(charset)
66                 else:
67                         textNew += content
68         return textNew.encode('utf-8')
69
70
71 class EmailHandler:
72         def __init__(self):
73                 pass
74         def onConnect(self, proto):
75                 pass
76
77 class EmailScreen(Screen, EmailHandler):
78         implements(imap4.IMailboxListener)
79
80         width = scaleH(-1,530)
81         height = scaleV(-1,430)
82         boxlistWidth = scaleH(-1,150)
83         messagelistWidth = width-boxlistWidth
84         infolabelHeight = scaleV(-1,30)
85         skin = """
86                 <screen position="%d,%d" size="%d,%d" title="Email" >
87                         <widget name="boxlist" position="0,0" size="%d,%d" scrollbarMode="showOnDemand" />
88                         <widget name="messagelist" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
89                         <widget name="infolabel" position="%d,%d" size="%d,%d"   foregroundColor=\"white\" font=\"Regular;%d\" />
90                 </screen>""" %(
91                                            (DESKTOP_WIDTH-width)/2, (DESKTOP_HEIGHT-height)/2, width, height,
92                                            boxlistWidth, height-infolabelHeight,
93                                            boxlistWidth, 0, messagelistWidth, height-infolabelHeight,
94                                            0, height-infolabelHeight, width, infolabelHeight, scaleV(20,18)
95                                            )
96
97         currentmailbox = None
98         proto = None
99
100         def __init__(self, session, args = 0):
101                 EmailHandler.__init__(self)
102                 self.session = session
103
104                 self.skin = EmailScreen.skin
105                 Screen.__init__(self, session)
106                 createFactory(self, config.plugins.emailimap.username.value, config.plugins.emailimap.server.value, config.plugins.emailimap.port.value)
107
108                 self["actions"] = ActionMap(["InfobarChannelSelection", "WizardActions", "DirectionActions", "MenuActions", "ShortcutActions", "GlobalActions", "HelpActions", "NumberActions", "ChannelSelectBaseActions"],
109                         {
110                          "ok": self.action_ok,
111                          "back": self.action_exit,
112                          "historyNext": self.selectMessagelist,
113                          "historyBack": self.selectBoxlist,
114                          "prevBouquet": self.selectMessagelist,
115                          "nextBouquet": self.selectBoxlist,
116                          "down":                self.down,
117                          "up":            self.up,
118                          "left":                self.left,
119                          "right":          self.right,
120                          "menu":                self.action_menu,
121                          }, -1)
122                 self["boxlist"] = MenuList([])
123                 self["messagelist"] = MailList([])
124                 self["infolabel"] = Label("")
125                 self.onLayoutFinish.append(self.selectBoxlist)
126
127         def action_menu(self):
128                 self.session.open(EmailConfigScreen)
129
130         def selectBoxlist(self):
131                 self.currList = "boxlist"
132                 self["boxlist"].selectionEnabled(1)
133                 self["messagelist"].selectionEnabled(0)
134
135         def selectMessagelist(self):
136                 self.currList = "messagelist"
137                 self["boxlist"].selectionEnabled(0)
138                 self["messagelist"].selectionEnabled(1)
139
140         def up(self):
141                 self[self.currList].up()
142
143         def down(self):
144                 self[self.currList].down()
145
146         def left(self):
147                 self[self.currList].pageUp()
148
149         def right(self):
150                 self[self.currList].pageDown()
151
152         def action_ok(self):
153                 if self.currList == "boxlist":
154                         self.onBoxSelected()
155                 else:
156                         self.onMessageSelected()
157
158         def onBoxSelected(self):
159                 c = self["boxlist"].getCurrent()
160                 if c is not None:
161                         self.proto.examine(UTF7toUTF8(c[1][2])
162                                                            ).addCallback(self.onExamine, c[0] , self.proto
163                                                           ).addErrback(self.onExamineFailed, self.proto
164                                                           )
165
166         def onMessageSelected(self):
167                 c = self["messagelist"].getCurrent()
168                 if c is not None:
169                         self.fetchMessageSize(c[0])
170
171         def fetchMessageSize(self, message):
172                 print "fetchMessageSize",message
173                 self.proto.fetchSize(message.uid
174                         ).addCallback(self.onMessageSizeLoaded, message, self.proto
175                         ).addErrback(self.onMessageLoadFailed, message, self.proto
176                         )
177
178         def onMessageSizeLoaded(self, result, message, proto):
179                 print "onMessageSizeLoaded", result, message
180                 size = int(result[message.uid]['RFC822.SIZE'])
181                 self.MAX_MESSAGE_SIZE_TO_OPEN = 4000000
182                 if size >= self.MAX_MESSAGE_SIZE_TO_OPEN:
183                         #ask here to open message
184                         print "message to large to open (size=", size, ")"
185                 else:
186                         self.loadMessage(message)
187
188 #       def fetchBodyStructure(self, message):
189 #               print "fetchBodyStructure",message
190 #               self.proto.fetchBodyStructure(message.uid
191 #                       ).addCallback(self.onBodystructureLoaded, message, self.proto
192 #                       ).addErrback(self.onMessageLoadFailed, message, self.proto
193 #                       )
194
195         def loadMessage(self, message):
196                 print "loadMessage",message
197                 self["infolabel"].setText("loading message")
198
199                 self.proto.fetchMessage(message.uid
200                         ).addCallback(self.onMessageLoaded, message, self.proto
201                         ).addErrback(self.onMessageLoadFailed, message, self.proto
202                         )
203
204         def onMessageLoaded(self, result, message, proto):
205                 self["infolabel"].setText("parsing message")
206                 print "onMessageLoaded"#,result,message
207                 msgstr = result[message.uid]['RFC822']
208                 msg = email.Parser.Parser().parsestr(msgstr)
209                 msg.messagebodys = []
210                 msg.attachments = []
211
212                 if msg.is_multipart():
213                         for part in msg.walk():
214                                 if part.get_content_maintype()=="multipart":
215                                         continue
216                                 if part.get_content_maintype() == 'text' and part.get_filename() is None:
217                                         if part.get_content_subtype() == "html":
218                                                 msg.messagebodys.append(EmailBody(part))
219                                         elif part.get_content_subtype() == "plain":
220                                                 msg.messagebodys.append(EmailBody(part))
221                                         else:
222                                                 print "unkown content type= ", part.get_content_maintype(), "/", part.get_content_subtype()
223                                 else:
224                                         print "found Attachment with  ", part.get_content_type(), "and name", part.get_filename()
225                                         msg.attachments.append(EmailAttachment(part.get_filename(), part.get_content_type(), part.get_payload()))
226                 else:
227                         msg.messagebodys.append(EmailBody(msg))
228                 self.session.open(ScreenMailView, msg)
229
230         def onMessageLoadFailed(self, failure, message, proto):
231                 print "onMessageLoadFailed", failure, message
232                 self["infolabel"].setText(failure.getErrorMessage())
233
234         def action_exit(self):
235                 if self.proto is not None:
236                         self.proto.logout(
237                                                         ).addCallback(self.onLogedOut, self.proto
238                                 ).addErrback(self.onLogedOut, self.proto
239                                                         )
240                 else:
241                         self.close()
242
243         def onLogedOut(self, result, proto):
244                 print "onLogedOut", result
245                 self.close()
246
247         def onConnect(self, proto):
248                 self["infolabel"].setText("connected")
249                 proto.getCapabilities(
250                                                 ).addCallback(self.cbCapabilities, proto
251                                                 ).addErrback(self.ebCapabilities, proto
252                                                 )
253
254         def cbCapabilities(self,reason,proto):
255                 print "#"*30
256                 print "# If you have problems to log into your imap-server, please send me the output of the following line"
257                 print "# cbCapabilities",reason
258                 print "#"*30
259                 self.doLogin(proto)
260
261         def ebCapabilities(self,reason,proto):
262                 print "ebCapabilities",reason
263
264         def onConnectFailed(self, reason):
265                 self["infolabel"].setText(reason.getErrorMessage())
266
267         def onAuthentication(self, result, proto):
268                 self.proto = proto
269                 self["infolabel"].setText("logged in")
270                 proto.list("", "*"
271                 ).addCallback(self.onMailboxList, proto
272                 )
273
274         def doLogin(self, proto):
275                 print "login secure"
276                 useTLS = False #True
277                 if useTLS:
278                         context = proto.context.getContext()
279                         d = proto.startTLS(context)
280                         d = d.addCallback(proto.authenticate, config.plugins.emailimap.password.value)
281                 else:
282                         d = proto.authenticate(config.plugins.emailimap.password.value)
283                 d.addCallback(self.onAuthentication, proto)
284                 d.addErrback(self.onAuthenticationFailed, proto)
285                 return d
286
287         def onAuthenticationFailed(self, failure, proto):
288                 # If it failed because no SASL mechanisms match
289                 print "onAuthenticationFailed", failure, proto
290                 self["infolabel"].setText(failure.getErrorMessage())
291                 try:
292                         failure.trap(imap4.NoSupportedAuthentication)
293                         self.doLoginInsecure(proto)
294                 except Exception,e:
295                         print e,e.message
296
297         def doLoginInsecure(self, proto):
298                 print "login INSECURE"
299                 proto.login(config.plugins.emailimap.username.value, config.plugins.emailimap.password.value
300                                 ).addCallback(self.onAuthentication, proto
301                                 ).addErrback(self.onInsecureAuthenticationFailed, proto
302                                 )
303
304         def onInsecureAuthenticationFailed(self, failure, proto):
305                 print "onInsecureAuthenticationFailed", failure, proto
306                 self["infolabel"].setText(failure.getErrorMessage())
307
308         def onMailboxList(self, result, proto):
309                 print "onMailboxList", result, proto
310                 list = []
311                 for i in result:
312                         flags, hierarchy_delimiter, name = i #@UnusedVariable
313                         list.append((UTF7toUTF8(name).encode('utf-8'), i))
314                 self["boxlist"].l.setList(list)
315                 # TODO: set current on INBOX? Or last mailbox (through config... item?
316
317         def onExamine(self, result, mboxname, proto):
318                 print "onExamine", result, mboxname
319                 self.setTitle("Mailbox: "+mboxname)
320                 self.currentmailbox = mboxname
321                 numMessagesinFolder = int(result['EXISTS'])
322                 if numMessagesinFolder <= 0:
323                         self["infolabel"].setText("Box '"+mboxname+"' is empty")
324                         self["messagelist"].l.setList([])
325
326                 else:
327                         if config.plugins.emailimap.maxheadertoload.value > 0:
328                                 maxMessagesToFetch = config.plugins.emailimap.maxheadertoload.value
329                                 startmsg = numMessagesinFolder-maxMessagesToFetch+1
330                                 if startmsg <= 0:
331                                         startmsg = 1
332                                 rangeToFetch = [startmsg, numMessagesinFolder]
333                         else:
334                                 rangeToFetch = [1, numMessagesinFolder]
335                         self["infolabel"].setText("loading headers "+str(rangeToFetch[0])+"-"+str(rangeToFetch[1])+" of Box '"+mboxname+"'")
336
337                         try:
338 #                               proto.fetchEnvelope('%i:%i'%(rangeToFetch[0], rangeToFetch[1])  #'1:*'
339 #                                                  ).addCallback(self.onEnvelopeList, proto
340 #                                                  )
341                                 proto.fetchHeaders('%i:%i'%(rangeToFetch[0], rangeToFetch[1])   #'1:*'
342                                                    ).addCallback(self.onHeaderList, proto
343                                                    )
344
345                         except imap4.IllegalServerResponse, e:
346                                 print e
347
348         def onExamineFailed(self, failure, proto):
349                 print "onExamineFailed", failure, proto
350                 self["infolabel"].setText(failure.getErrorMessage())
351
352         def onHeaderList(self, result, proto):
353                 print "onHeaderList"#,result,proto
354                 self["infolabel"].setText("headers loaded, now parsing ...")
355                 list = []
356                 for m in result:
357                         try:
358                                 list.append(self.buildMessageListItem(MessageHeader(m, result[m]['RFC822.HEADER'])))
359                         except Exception,e:
360                                 print e
361                 list.reverse()
362                 self["messagelist"].l.setList(list)
363                 self["infolabel"].setText("have "+str(len(result))+" messages ")
364
365         def buildMessageListItem(self, message):
366                 return [
367                         message,
368                         MultiContentEntryText(pos=(5, 0), size=(self.messagelistWidth, scaleV(20,18)+5), font=0, text=message.getSenderString()),
369                         MultiContentEntryText(pos=(5, scaleV(20,18)+1), size=(self.messagelistWidth, scaleV(20,18)+5), font=0, text=message.get('date', default='kein Datum')),
370                         MultiContentEntryText(pos=(5, 2*(scaleV(20,18)+1)), size=(self.messagelistWidth, scaleV(20,18)+5), font=0, text=message.getSubject())
371                 ]
372         #
373         # IMailboxListener methods
374         #
375         def modeChanged(self, writeable):
376                 print "modeChanged", writeable
377
378         def flagsChanged(self, newFlags):
379                 print "flagsChanged", newFlags
380
381         def newMessages(self, exists, recent):
382                 print "newMessages", exists, recent
383
384 class ScreenMailView(Screen):
385         skin=""
386         def __init__(self, session, email, args = 0):
387                 self.session = session
388                 self.email = email
389                 width = max(4*140,scaleH(-1,550))
390                 height = scaleV(-1,476)
391                 fontSize = scaleV(24,20)
392                 lineHeight = fontSize+5
393                 buttonsGap = (width-4*140)/5
394                 self.skin = """
395                 <screen position="%d,%d" size="%d,%d" title="view Email" >
396                         <widget name="from" position="%d,%d" size="%d,%d"  font="Regular;%d" />
397                         <widget name="date" position="%d,%d" size="%d,%d"  font="Regular;%d" />
398                         <widget name="subject" position="%d,%d" size="%d,%d"  font="Regular;%d" />
399                         <eLabel position="%d,%d" size="%d,2" backgroundColor="#aaaaaa" />
400                         <widget name="body" position="%d,%d" size="%d,%d"  font="Regular;%d" />
401                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
402                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
403                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
404                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
405                         <widget name="buttonred" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
406                         <widget name="buttongreen" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
407                         <widget name="buttonyellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
408                         <widget name="buttonblue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
409                 </screen>""" %(
410                                            (DESKTOP_WIDTH-width)/2, (DESKTOP_HEIGHT-height)/2, width, height,
411                                            0, 0, width, lineHeight, fontSize-1, # from
412                                            0, lineHeight, width, lineHeight, fontSize-1, # date
413                                            0, 2*lineHeight, width, lineHeight, fontSize-1, # subject 
414                                            0, 3*lineHeight+1, width, # line 
415                                            0, 3*lineHeight+5, width, height-3*lineHeight-5-5-30-5, fontSize, # body
416                                            buttonsGap, height-30-5,
417                        2*buttonsGap+140, height-30-5,
418                        3*buttonsGap+2*140, height-30-5,
419                        4*buttonsGap+3*140, height-30-5,
420                        buttonsGap, height-30-5, scaleV(18,16),
421                        2*buttonsGap+140, height-30-5, scaleV(18,16),
422                        3*buttonsGap+2*140, height-30-5, scaleV(18,16),
423                        4*buttonsGap+3*140, height-30-5, scaleV(18,16),
424                                            )
425                 Screen.__init__(self, session)
426                 self["from"] = Label(decodeHeader(_("From") +": %s" %self.email.get('from', 'no-from')))
427                 self["date"] = Label(_("Date") +": %s" %self.email.get('date', 'no-date'))
428                 self["subject"] = Label(decodeHeader(_("Subject") +": %s" %self.email.get('subject', 'no-subject')))
429                 self["body"] = ScrollLabel(_(self.email.messagebodys[0].getData()))
430                 self["buttonred"] = Button(_(""))
431                 self["buttongreen"] = Button("")
432                 self["buttonyellow"] = Button(_("Headers"))
433                 self["buttonblue"] = Button(_("delete"))
434                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "MenuActions", "ShortcutActions"],
435                         {
436                          "back": self.close,
437                          "up": self["body"].pageUp,
438                          "down": self["body"].pageDown,
439                          "left": self["body"].pageUp,
440                          "right": self["body"].pageDown,
441                          "red": self.selectBody,
442                          "green": self.selectAttachment,
443                          "yellow": self.openMessagesHeaders,
444                          "blue": self.delete,
445
446                          }, -1)
447                 self.onLayoutFinish.append(self.updateButtons)
448
449         def delete(self):
450                 pass #self.session.openWithCallback(self.deleteCB, ChoiceBox, title="really delete Mail?", list=[(_("yes"), True),(_("no"), False)])
451
452         def deleteCB(self, returnValue):
453                 if returnValue[1] is True:
454                         pass
455
456         def openMessagesHeaders(self):
457                 pass #self.session.open(ScreenMailViewHeader,self.profil,self.email)
458
459         def updateButtons(self):
460                 self["buttonred"].setText(_("Bodys"))
461                 if len(self.email.attachments):
462                         self["buttongreen"].setText("Attachments")
463                 else:
464                         self["buttongreen"].setText("")
465
466         def selectBody(self):
467                 if len(self.email.messagebodys):
468                         list = []
469                         for a in self.email.messagebodys:
470                                 list.append((a.getContenttype(), a))
471                         self.session.openWithCallback(self.selectBodyCB, ChoiceBox, _("select Body"), list)
472
473         def selectBodyCB(self, choice):
474                 if choice is not None:
475                         self["body"].setText(choice[1].getData())
476
477         def selectAttachment(self):
478                 if len(self.email.attachments):
479                         list = []
480                         for a in self.email.attachments:
481                                 list.append((a.getFilename(), a))
482                         self.session.openWithCallback(self.selectAttachmentCB, ChoiceBox, _("select Attachment"), list)
483
484         def selectAttachmentCB(self, choice):
485                 if choice is not None:
486                         print "Attachment selected", choice[1].getFilename()
487                         #showMessageBox(self.session)
488
489 class MailList(MenuList):
490         def __init__(self, list, enableWrapAround = False):
491                 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
492                 self.l.setFont(0, gFont("Regular", scaleV(20,18)))
493                 self.l.setFont(1, gFont("Regular", scaleV(22,20)))
494
495         def postWidgetCreate(self, instance):
496                 MenuList.postWidgetCreate(self, instance)
497                 instance.setItemHeight(scaleV(70,60))
498
499 class MessageHeader(object):
500         def __init__(self, uid, message):
501                 self.uid = uid #must be int
502                 self.message = email.Parser.Parser().parsestr(message)
503
504         def getSenderString(self):
505                 return decodeHeader(self.get("from"), _("no sender"))
506
507         def getSubject(self):
508                 return decodeHeader(self.get("subject"), _("no subject"))
509
510         def get(self, key, default=None):
511                 return self.message.get(key,failobj=default)
512
513         def __str__(self):
514                 return "<MessageHeader uid="+str(self.uid)+", subject="+self.get("subject","no-subject")+">"
515
516 ############
517 class EmailBody:
518         def __init__(self,data):
519                 self.data = data
520
521         def getEncoding(self):
522                 return self.data.get_content_charset()
523
524         def getData(self):
525                 text = self.data.get_payload(decode=True)
526                 if self.getEncoding() is not None:
527                         print "decoding text with charset ",self.getEncoding()
528                         text = text.decode(self.getEncoding())
529                 if self.getContenttype() == "text/html":
530                         print "stripping html"
531                         text = strip_readable(text)
532                 try:
533                         return text.encode('utf-8')
534                 except Exception,e:
535                         print e
536                         return text
537
538         def getContenttype(self):
539                 return self.data.get_content_type()
540
541 ############
542 class EmailAttachment:
543         def __init__(self, filename, contenttype, data):
544                 self.filename = filename
545                 self.contenttype = contenttype
546                 self.data = data
547
548         def save(self,folder):
549                 try:
550                         fp = open(folder+"/"+self.getFilename(),"wb")
551                         fp.write(self.data)
552                         fp.close()
553                 except Exception,e:
554                         print e
555                         return False
556                 return True
557
558         def getFilename(self):
559                 return self.filename
560
561         def getContenttype(self):
562                 return self.contenttype
563
564         def getData(self):
565                 return self.data
566
567 def UTF7toUTF8(str):
568         return imap4.decoder(str)[0]
569
570 def UTF8toUTF7(str):
571         return imap4.encoder(str.decode('utf-8'))[0]
572
573 def main(session, **kwargs):
574         import os,shutil
575         if os.path.isfile('/usr/lib/python2.5/uu.py') is not True:
576                 shutil.copy('/usr/lib/enigma2/python/Plugins/Extensions/EmailClient/uu.py', '/usr/lib/python2.5/uu.py')
577                 global session2
578                 session2 = session
579                 session.openWithCallback(MessageCB, MessageBox, 'In order of missing standart python library files\ni have copied the nessary files now.\nBut you have to restart your Box\n to apply this!', type = MessageBox.TYPE_INFO)
580         else:
581                 session.open(EmailScreen)
582
583 def MessageCB(*args):
584         global session2
585         session2.open(EmailScreen)
586
587 def Plugins(path, **kwargs):
588         global plugin_path
589         plugin_path = path
590         return [
591                          PluginDescriptor(name="Email Client", description="view Emails via IMAP4",
592                          where = PluginDescriptor.WHERE_PLUGINMENU,
593                          fnc = main,
594                          icon="plugin.png"
595                          ),
596                 ]