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