Remove "networkwizard" plugin, which lives in enigma2.git
[vuplus_dvbapp-plugin] / emailclient / src / plugin.py
1 # -*- coding: utf-8 -*-
2 '''
3 $Author$
4 $Revision$
5 $Date$
6 $Id$
7 '''
8 from Components.ActionMap import ActionMap
9 from Components.Label import Label
10 from Screens.MessageBox import MessageBox
11 from Components.MenuList import MenuList
12 from Components.MultiContent import MultiContentEntryText
13 from Components.ScrollLabel import ScrollLabel
14 from Components.Button import Button
15 from Components.config import config, ConfigSubsection, ConfigInteger, ConfigEnableDisable
16 from Plugins.Plugin import PluginDescriptor
17 from Screens.ChoiceBox import ChoiceBox
18 from Screens.Screen import Screen
19 from Tools import Notifications
20 from enigma import eListboxPythonMultiContent, gFont, eTimer #@UnresolvedImport # pylint: disable-msg=E0611
21 from twisted.mail import imap4 #@UnresolvedImport
22 from zope.interface import implements
23 import email, re, os
24 from email.header import decode_header
25 import time
26 from TagStrip import strip_readable
27 from protocol import createFactory
28
29 from . import _, initLog, debug, scaleH, scaleV, DESKTOP_WIDTH, DESKTOP_HEIGHT #@UnresolvedImport # pylint: disable-msg=F0401
30 mailAccounts = [] # contains all EmailAccount objects
31 from EmailConfig import EmailConfigOptions, EmailConfigAccount
32
33 config.plugins.emailimap = ConfigSubsection()
34 config.plugins.emailimap.showDeleted = ConfigEnableDisable(default=False)
35 config.plugins.emailimap.timeout = ConfigInteger(default=0, limits=(0, 90)) # in seconds
36 config.plugins.emailimap.verbose = ConfigEnableDisable(default=True)
37 config.plugins.emailimap.debug = ConfigEnableDisable(default=False)
38
39 def decodeHeader(text, default=''):
40         if text is None:
41                 return _(default)
42         text = text.replace('\r',' ').replace('\n',' ').replace('\t',' ')
43         text = re.sub('\s\s+', ' ', text)
44         textNew = ""
45         for part in decode_header(text):
46                 (content, charset) = part
47                 # print("decodeHeader content/charset: %s/%s" %(repr(content),charset))
48                 if charset:
49                         textNew += content.decode(charset)
50                 else:
51                         textNew += content
52         try:
53                 return textNew.encode('utf-8')
54         except UnicodeDecodeError: # for faulty mail software systems
55                 return textNew.decode('iso-8859-1').encode('utf-8')
56
57 IS_UNSEEN = 0
58 IS_SEEN = 1
59 IS_DELETED = 2 
60
61 class EmailScreen(Screen):
62         '''
63         This is the main screen for interacting with the user.
64         It contains the list of mailboxes (boxlist) on the left and
65         the list of messages (messagelist) on the right.
66         At the bottom we have a line for info messages.
67         It is specific for one account.
68         '''
69
70         width = scaleH(-1, 530)
71         height = scaleV(-1, 430)
72         boxlistWidth = scaleH(-1, 150)
73         messagelistWidth = width-boxlistWidth
74         infolabelHeight = scaleV(-1, 30)
75         skin = """
76                 <screen position="%d,%d" size="%d,%d" title="Email" >
77                         <widget name="boxlist" position="0,0" size="%d,%d" scrollbarMode="showOnDemand" />
78                         <widget name="messagelist" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
79                         <widget name="infolabel" position="%d,%d" size="%d,%d"   foregroundColor=\"white\" font=\"Regular;%d\" />
80                 </screen>""" % (
81                                            (DESKTOP_WIDTH-width)/2, (DESKTOP_HEIGHT-height)/2, width, height,
82                                            boxlistWidth, height-infolabelHeight,
83                                            boxlistWidth, 0, messagelistWidth, height-infolabelHeight,
84                                            0, height-infolabelHeight, width, infolabelHeight, scaleV(20,18)
85                                            )
86
87         def __init__(self, session, account):
88                 '''
89                 This is the main screen for interacting with the user.
90                 It contains the list of mailboxes (boxlist) on the left and
91                 the list of messages (messagelist) on the right.
92                 At the bottom we have a line for info messages.
93                 It is specific for one account.
94         
95                 @param session: session in which this screen is running
96                 @param account: account for which mailboxes are shown 
97                 '''
98                 self._session = session
99                 self._account = account
100                 self.skin = EmailScreen.skin
101                 Screen.__init__(self, session)
102
103                 self["actions"] = ActionMap(["InfobarChannelSelection", "WizardActions", "DirectionActions", "MenuActions", "ShortcutActions", "GlobalActions", "HelpActions", "NumberActions", "ChannelSelectBaseActions"],
104                         {
105                          "ok": self._ok,
106                          "back": self._exit,
107                          "historyNext": self._selectMessagelist,
108                          "historyBack": self._selectBoxlist,
109                          "nextBouquet": self._selectMessagelist,
110                          "prevBouquet": self._selectBoxlist,
111                          "down":                self._down,
112                          "up":            self._up,
113                          "left":                self._left,
114                          "right":          self._right,
115                          }, -1)
116                 self["messagelist"] = MenuList([], content=eListboxPythonMultiContent)
117                 self["messagelist"].l.setItemHeight(scaleV(70, 60))
118                 self["messagelist"].l.setFont(0, gFont("Regular", scaleV(20, 18))) # new
119                 self["messagelist"].l.setFont(1, gFont("Regular", scaleV(18, 16))) # deleted
120                 self["messagelist"].l.setFont(2, gFont("Regular", scaleV(18, 16))) # seen
121
122                 if self._account.isConnected():
123                         self["infolabel"] = Label("")
124                         self["boxlist"] = MenuList(self._account.mailboxList)
125                         self.onLayoutFinish.append(self._finishBoxlist)
126                 else:
127                         self["infolabel"] = Label(_("account not connected"))
128                         self["boxlist"] = MenuList([])
129                 self.currList = "boxlist"
130
131         def _exit(self):
132                 self.close()
133
134         def _finishBoxlist(self):
135                 # pylint: disable-msg=W0212
136                 self.setTitle(_("%(name)s (%(user)s@%(server)s)")
137                                 %{
138                                 'name':self._account._name,
139                                 'user':self._account._user,
140                                 'server':self._account._server
141                                 })
142                 self["boxlist"].moveToIndex(self._account.inboxPos-1)
143                 self._selectBoxlist()
144                 self._onBoxSelected()
145         
146         def _selectBoxlist(self):
147                 self.currList = "boxlist"
148                 self["messagelist"].selectionEnabled(0)
149                 self["boxlist"].selectionEnabled(1)
150
151         def _selectMessagelist(self):
152                 self.currList = "messagelist"
153                 self["boxlist"].selectionEnabled(0)
154                 self["messagelist"].selectionEnabled(1)
155
156         def _up(self):
157                 self[self.currList].up()
158
159         def _down(self):
160                 self[self.currList].down()
161
162         def _left(self):
163                 self[self.currList].pageUp()
164
165         def _right(self):
166                 self[self.currList].pageDown()
167
168         def _ok(self):
169                 if self.currList == "boxlist":
170                         self._onBoxSelected()
171                 else:
172                         self._onMessageSelected()
173
174         def _ebNotify(self, where, what):
175                 '''
176                 Error notification via calling back
177                 @param where: location, where the error occurred
178                 @param what: message, what happened
179                 '''
180                 # pylint: disable-msg=W0212
181                 debug("[EmailScreen] _ebNotify error in %s: %s" %(where, what))
182                 self.session.open(MessageBox, _("EmailClient for %(account)s:\n\n%(error)s") %{'account': self._account._name, 'error':what}, type=MessageBox.TYPE_ERROR, timeout=config.plugins.emailimap.timeout.value)
183
184         def _onBoxSelected(self):
185                 self["messagelist"].l.setList([])
186                 self._onBoxSelectedNoClear()
187
188         def _onBoxSelectedNoClear(self):
189                 self["infolabel"].setText(_("loading headers ..."))
190                 if self["boxlist"].getCurrent():
191                         if self._account.getMessageList(self._onHeaderList, self["boxlist"].getCurrent()):
192                                 self._selectMessagelist()
193                         else:
194                                 self["infolabel"].setText(_("account not connected"))
195                 else:
196                         self["infolabel"].setText(_("no mailbox?!?!"))
197                         
198
199         def _onHeaderList(self, result, flagsList):
200                 '''
201                 
202                 @param result: list of message
203                 @param flagsList: list of corresponding flags
204                 '''
205                 debug("[EmailScreen] onHeaderList: %s" %len(result))
206                 self["infolabel"].setText(_("headers loaded, now parsing ..."))
207                 self._flagsList = flagsList
208                 mylist = []
209                 for m in result:
210                         state = IS_UNSEEN
211                         # debug("onHeaderList :" + repr(flagsList[m]['FLAGS']))
212                         if '\\Seen' in flagsList[m]['FLAGS']:
213                                 state = IS_SEEN
214                         if '\\Deleted' in flagsList[m]['FLAGS']:
215                                 if not config.plugins.emailimap.showDeleted.value:
216                                         continue
217                                 else:
218                                         state = IS_DELETED
219                         mylist.append(self._buildMessageListItem(MessageHeader(m, result[m]['RFC822.HEADER']), state))
220
221                 mylist.sort(key=lambda x: x[0].getTimestampUTC(), reverse=True)
222                 self["messagelist"].l.setList(mylist)
223                 if len(mylist) > 0:
224                         self["infolabel"].setText(_("have %d messages") %(len(mylist)))
225                 else:
226                         self["infolabel"].setText(_("have no messages"))
227                         # self.onBoxSelected() # brings us into endless loop, when still deleted messages are in there...
228                         self._selectBoxlist()
229
230         def _onMessageSelected(self):
231                 self["infolabel"].setText(_("getting message ..."))
232                 c = self["messagelist"].getCurrent()
233                 if c is not None:
234                         if not self._account.getMessage(c[0], self._onMessageLoaded, self._ebNotify):
235                                 self["infolabel"] = Label(_("account not connected"))
236
237         def _onMessageLoaded(self, result, message):
238                 self["infolabel"].setText(_("parsing message ..."))
239                 debug("[EmailScreen] onMessageLoaded") #,result,message
240                 try:
241                         msgstr = result[message.uid]['RFC822']
242                 except KeyError:
243                         self._account.getMessage(message, self._onMessageLoaded, self._ebNotify)
244                         # self.loadMessage(message)
245                         return
246                 msg = email.Parser.Parser().parsestr(msgstr) #@UndefinedVariable # pylint: disable-msg=E1101
247                 msg.messagebodys = []
248                 msg.attachments = []
249
250                 if msg.is_multipart():
251                         for part in msg.walk():
252                                 if part.get_content_maintype()=="multipart":
253                                         continue
254                                 if part.get_content_maintype() == 'text' and part.get_filename() is None:
255                                         if part.get_content_subtype() == "html":
256                                                 msg.messagebodys.append(EmailBody(part))
257                                         elif part.get_content_subtype() == "plain":
258                                                 msg.messagebodys.append(EmailBody(part))
259                                         else:
260                                                 debug("[EmailScreen] onMessageLoaded: unknown content type=%s/%s" %(str(part.get_content_maintype()), str(part.get_content_subtype())))
261                                 else:
262                                         debug("[EmailScreen] onMessageLoaded: found Attachment with  %s and name %s" %(str(part.get_content_type()), str(part.get_filename())))
263                                         msg.attachments.append(EmailAttachment(part.get_filename(), part.get_content_type(), part.get_payload()))
264                 else:
265                         msg.messagebodys.append(EmailBody(msg))
266                 debug("[EmailScreen] onMessageLoaded:" + str(message.uid) +';'+ repr(self._flagsList[message.uid]['FLAGS']))
267                 self.session.open(ScreenMailView, self._account, msg, message.uid, self._flagsList[message.uid]['FLAGS']).onHide.append(self._onBoxSelectedNoClear)
268                 self["infolabel"].setText("")
269
270         def _buildMessageListItem(self, message, state):
271                 '''
272                 Construct a MultiContentEntryText from parameters
273                 @param message: message
274                 @param state: IS_UNSEEN (grey), IS_DELETED (red) are especially colored
275                 '''
276                 if state == IS_UNSEEN:
277                         font = 0
278                         color = 0x00FFFFFF # white
279                 elif state == IS_DELETED:
280                         font = 1 
281                         color = 0x00FF6666 # redish :)
282                 else:
283                         font = 2
284                         color = 0x00888888 # grey
285                 return [
286                         message,
287                         MultiContentEntryText(pos=(5, 0), size=(self.messagelistWidth, scaleV(20,18)+5), font=font, text=message.getSenderString(), color=color, color_sel=color),
288                         MultiContentEntryText(pos=(5, scaleV(20,18)+1), size=(self.messagelistWidth, scaleV(20,18)+5), font=font, text=message.getLocalDateTimeString(), color=color, color_sel=color),
289                         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)
290                 ]
291
292 class ScreenMailView(Screen):
293         skin = ""
294         def __init__(self, session, account, message, uid, flags):
295                 '''
296                 Principal screen to show one mail message.
297                 @param session:
298                 @param account: mail acoount, this message is coming from 
299                 @param message: the message itself
300                 @param uid: uid of the message, needed to (un)delete and unmark
301                 @param flags: the flags of the message, needed to check, whether IS_DELETED
302                 '''
303                 self._session = session
304                 self._email = message
305                 self._account = account
306                 # debug('ScreenMailView ' + repr(email) + ' dir: ' + repr(dir(email)))
307                 width = max(4*140, scaleH(-1, 550))
308                 height = scaleV(-1, 476)
309                 fontSize = scaleV(24, 20)
310                 lineHeight = fontSize+5
311                 buttonsGap = (width-4*140)/5
312                 self.skin = """
313                 <screen position="%d,%d" size="%d,%d" title="view Email" >
314                         <widget name="from" position="%d,%d" size="%d,%d"  font="Regular;%d" />
315                         <widget name="date" position="%d,%d" size="%d,%d"  font="Regular;%d" />
316                         <widget name="subject" position="%d,%d" size="%d,%d"  font="Regular;%d" />
317                         <eLabel position="%d,%d" size="%d,2" backgroundColor="#aaaaaa" />
318                         <widget name="body" position="%d,%d" size="%d,%d"  font="Regular;%d" />
319                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
320                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
321                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
322                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
323                         <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" />
324                         <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" />
325                         <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" />
326                         <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" />
327                 </screen>""" % (
328                                            (DESKTOP_WIDTH-width)/2, (DESKTOP_HEIGHT-height)/2, width, height,
329                                            0, 0, width, lineHeight, fontSize-1, # from
330                                            0, lineHeight, width, lineHeight, fontSize-1, # date
331                                            0, 2*lineHeight, width, lineHeight, fontSize-1, # subject 
332                                            0, 3*lineHeight+1, width, # line 
333                                            0, 3*lineHeight+5, width, height-3*lineHeight-5-5-30-5, fontSize, # body
334                                            buttonsGap, height-30-5,
335                                            2*buttonsGap+140, height-30-5,
336                                            3*buttonsGap+2*140, height-30-5,
337                                            4*buttonsGap+3*140, height-30-5,
338                                            buttonsGap, height-30-5, scaleV(18,16),
339                                            2*buttonsGap+140, height-30-5, scaleV(18,16),
340                                            3*buttonsGap+2*140, height-30-5, scaleV(18,16),
341                                            4*buttonsGap+3*140, height-30-5, scaleV(18,16),
342                                            )
343                 Screen.__init__(self, session)
344                 self["from"] = Label(decodeHeader(_("From") +": %s" %self._email.get('from', _('no from'))))
345                 msgdate = email.utils.parsedate_tz(self._email.get("date", ""))
346                 self["date"] = Label(_("Date") +": %s" % (time.ctime(email.utils.mktime_tz(msgdate)) if msgdate else _("no date")))
347                 self["subject"] = Label(decodeHeader(_("Subject") +": %s" %self._email.get('subject', _('no subject'))))
348                 self["body"] = ScrollLabel(_(self._email.messagebodys[0].getData()))
349                 self["buttonred"] = Button("")
350                 self["buttongreen"] = Button("")
351                 self["buttonyellow"] = Button(_("leave unread"))
352                 if '\\Deleted' in flags:
353                         self["buttonblue"] = Button(_("undelete"))
354                 else:
355                         self["buttonblue"] = Button(_("delete"))
356                 self["actions"] = ActionMap(["WizardActions", "DirectionActions", "MenuActions", "ShortcutActions"],
357                         {
358                          "back": self.close,
359                          "up": self["body"].pageUp,
360                          "down": self["body"].pageDown,
361                          # TODO: perhaps better use left/right for previous/next message
362                          "left": self["body"].pageUp,
363                          "right": self["body"].pageDown,
364                          "red": self._selectBody,
365                          "green": self._selectAttachment,
366                          "yellow": self._markUnread,
367                          "blue": self._delete,
368
369                          }, -1)
370                 self._uid = uid
371                 self._flags = flags
372                 self.onLayoutFinish.append(self._updateButtons)
373
374         def _delete(self):
375                 if '\\Deleted' in self._flags:
376                         self.session.openWithCallback(self._deleteCB, MessageBox, _("really undelete mail?"), type=MessageBox.TYPE_YESNO, timeout=config.plugins.emailimap.timeout.value)
377                 else:
378                         self.session.openWithCallback(self._deleteCB, MessageBox, _("really delete mail?"), type=MessageBox.TYPE_YESNO, timeout=config.plugins.emailimap.timeout.value)
379
380         def _deleteCB(self, returnValue):
381                 if returnValue:
382                         if '\\Deleted' in self._flags:
383                                 if not self._account.undelete(self._uid):
384                                         self.session.open(MessageBox, _("account not connected"), type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
385                         else:
386                                 if not self._account.delete(self._uid):
387                                         self.session.open(MessageBox, _("account not connected"), type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
388                         debug("[ScreenMailView] deleteCB: %s"  %repr(self._email))
389                         self.close()
390
391         def _markUnread(self):
392                 if not self._account.unread(self._uid):
393                         self.session.open(MessageBox, _("account not connected"), type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
394                 self.close()
395
396         def _updateButtons(self):
397                 if len(self._email.messagebodys):
398                         self["buttonred"].setText(_("Bodys"))
399                 else:
400                         self["buttonred"].setText("")
401                 if len(self._email.attachments):
402                         self["buttongreen"].setText(_("Attachments"))
403                 else:
404                         self["buttongreen"].setText("")
405
406         def _selectBody(self):
407                 if len(self._email.messagebodys):
408                         mylist = []
409                         for a in self._email.messagebodys:
410                                 mylist.append((a.getContenttype(), a))
411                         self.session.openWithCallback(self._selectBodyCB, ChoiceBox, _("select Body"), mylist)
412
413         def _selectBodyCB(self, choice):
414                 if choice is not None:
415                         self["body"].setText(choice[1].getData())
416
417         def _selectAttachment(self):
418                 if len(self._email.attachments):
419                         mylist = []
420                         for a in self._email.attachments:
421                                 name = a.getFilename()
422                                 if name:
423                                         mylist.append((a.getFilename(), a))
424                                 else:
425                                         mylist.append((_("no filename"), a))
426                         debug("[ScreenMailView] selectAttachment : " + repr(mylist))
427                         self.session.openWithCallback(self._selectAttachmentCB, ChoiceBox, _("select Attachment"), mylist)
428
429         def _selectAttachmentCB(self, choice):
430                 if choice:
431                         if choice[1].getFilename():
432                                 debug("[ScreenMailView] Attachment selected: " + choice[1].getFilename())
433                         else:
434                                 debug("[ScreenMailView] Attachment with no filename selected")
435                         # nothing happens here. What shall we do now with the attachment?
436
437 ############
438 class EmailBody:
439         def __init__(self, data):
440                 self.data = data
441
442         def getEncoding(self):
443                 return self.data.get_content_charset()
444
445         def getData(self):
446                 text = self.data.get_payload(decode=True)
447                 if self.getEncoding():
448                         try:
449                                 text = text.decode(self.getEncoding())
450                         except UnicodeDecodeError:
451                                 pass    
452                 # debug('EmailBody/getData text: ' +  text)
453                 #=======================================================================
454                 # if self.getEncoding():
455                 #       text = text.decode(self.getEncoding())
456                 #=======================================================================
457                 if self.getContenttype() == "text/html":
458                         debug("[EmailBody] stripping html")
459                         text = strip_readable(text)
460                         # debug('EmailBody/getData text: ' +  text)
461
462                 try:
463                         return text.encode('utf-8')
464                 except UnicodeDecodeError:
465                         return text
466                 
467
468         def getContenttype(self):
469                 return self.data.get_content_type()
470
471 ############
472 class EmailAttachment:
473         def __init__(self, filename, contenttype, data):
474                 self.filename = filename
475                 self.contenttype = contenttype
476                 self.data = data
477
478         def save(self, folder):
479                 try:
480                         fp = open(folder+"/"+self.getFilename(),"wb")
481                         fp.write(self.data)
482                         fp.close()
483                 except Exception, e:
484                         debug("[EmailAttachment] save %s" %str(e))
485                         return False
486                 return True
487
488         def getFilename(self):
489                 return self.filename
490
491         def getContenttype(self):
492                 return self.contenttype
493
494         def getData(self):
495                 return self.data
496
497 def UTF7toUTF8(string): # pylint: disable-msg=C0103
498         return imap4.decoder(string)[0]
499
500 def UTF8toUTF7(string): # pylint: disable-msg=C0103
501         return imap4.encoder(string.decode('utf-8'))[0]
502
503
504 class CheckMail:
505         def __init__(self, acc):
506                 '''
507                 Mail checker object for one account
508                 @param acc: the account to be checked periodically, each account has
509                                         at most one checker
510                 @type acc: EmailAccount
511                 '''
512                 # pylint: disable-msg=W0212
513                 import random
514                 self._name = acc._name
515                 self._account = acc
516                 self._timer = eTimer()
517                 self._timer.callback.append(self._checkMail)
518                 # I guess, users tend to use identical intervals, so make them a bit different :-)
519                 # constant stolen from ReconnectingFactory
520                 self._interval = int(self._account._interval)*60*1000
521                 self._interval = int(random.normalvariate(self._interval, self._interval * 0.11962656472))
522                 debug("[CheckMail] %(name)s: __init__: checking all %(interval)s seconds"
523                         %{'name':self._name, 'interval':self._interval/1000})
524                 self._timer.start(self._interval) # it is minutes
525                 self._unseenList = None
526                 self._checkMail()
527
528         def exit(self):
529                 debug("[CheckMail] %s: exit" %(self._name))
530                 self._timer.stop()
531
532         def stopChecking(self):
533                 '''
534                 Just stop the timer, don't empty the unseenList.
535                 '''
536                 debug("[CheckMail] %s: stopChecking" %(self._name))
537                 self._timer.stop()
538
539         def reStartChecking(self):
540                 '''
541                 Start the timer again and immediately do a check.
542                 '''
543                 debug("[CheckMail] %s: reStartChecking" %(self._name))
544                 self._timer.start(self._interval)
545                 self._checkMail()
546
547         def _checkMail(self):
548                 # debug("[CheckMail] _checkMail for %s" %self._name)
549                 self._account.getUnseenHeaders(self._filterNewUnseen)
550
551         def _filterNewUnseen(self, newUnseenList):
552                 '''
553                 Main method in this class: get the list of unseen messages
554                 and check them against the last list. New unseen messages
555                 are then displayed via _onHeaderList
556                 @param newUnseenList: new list of unseen messages
557                 '''
558                 debug('[CheckMail] %s: _filterNewUnseen: %s' %(self._name, repr(newUnseenList)))
559                 if self._unseenList is None:
560                         debug('[CheckMail] %s: _filterNewUnseen: init' %(self._name))
561                         # Notifications.AddNotification(MessageBox, str(len(newUnseenList)) + ' ' + _("unread messages in mailbox %s") %self._name, type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
562                 else:
563                         newMessages = filter(lambda x: x not in self._unseenList, newUnseenList)
564                         if newMessages:
565                                 debug("[CheckMail] %s: _filterNewUnseen: new message(s): %s" %(self._name, repr(newMessages)))
566                                 # construct MessageSet from list of message numbers
567                                 # newMessageSet = reduce(lambda x,y: y.add(x), newMessages, imap4.MessageSet())
568                                 newMessageSet = imap4.MessageSet()
569                                 for i in newMessages:
570                                         newMessageSet.add(i)
571                                 if not self._account.getHeaders(self._onHeaderList, newMessageSet):
572                                         debug("[CheckMail] %s: _filterNewUnseen: could not get Headers" %(self._name))
573
574                 self._unseenList = newUnseenList
575
576         def _onHeaderList(self, headers):
577                 '''
578                 Notify about the list of headers.
579                 @param headers: list of headers
580                 '''
581                 # debug("[CheckMail] _onHeaderList headers: %s" %repr(headers))
582                 message = _("New mail arrived for account %s:\n\n") %self._name
583                 for h in headers:
584                         m = MessageHeader(h, headers[h]['RFC822.HEADER'])
585                         message += m.getSenderString() + '\n' + m.getSubject() + '\n\n'
586                 Notifications.AddNotification(MessageBox, message, type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
587
588 class MessageHeader(object):
589         def __init__(self, uid, message):
590                 self.uid = uid #must be int
591                 self.message = email.Parser.HeaderParser().parsestr(message) #@UndefinedVariable # pylint: disable-msg=E1101
592
593         def getSenderString(self):
594                 return decodeHeader(self.get("from"), _("no sender"))
595
596         def getSubject(self):
597                 return decodeHeader(self.get("subject"), _("no subject"))
598
599         def getLocalDateTimeString(self):
600                 msgdate = email.utils.parsedate_tz(self.get("date", ""))
601                 if msgdate:
602                         return time.ctime(email.utils.mktime_tz(msgdate))
603                 else:
604                         return self.get("date", _("no date"))
605
606         def getTimestampUTC(self):
607                 ts = 0
608                 msgdate = email.utils.parsedate_tz(self.get("date", ''))
609                 if msgdate:
610                         ts = email.utils.mktime_tz(msgdate)
611                 return ts
612
613         def get(self, key, default=None):
614                 return self.message.get(key, failobj=default)
615
616         def __str__(self):
617                 return "<MessageHeader uid="+str(self.uid)+", subject="+self.getSubject()+">"
618
619 class EmailAccount():
620         '''
621         Principal class to hold an account.
622         '''
623         implements(imap4.IMailboxListener)
624
625         def __init__(self, params, afterInit=None):
626                 '''
627                 Principal class to hold an account.
628                 @param params: (name, server, port, user, password, interval, maxmail)
629                 @param afterInit: to be called, when init is done. Needed to writeAccounts AFTER this one is added
630                 '''
631                 # TODO: decrypt password
632                 (self._name, self._server, self._port, self._user, self._password, self._interval, self._maxmail, listall) = params
633                 # debug("[EmailAccount] %s: __init__: %s" %(self._name, repr(params)))
634                 self._listall = (listall==1)
635                 self._factory = createFactory(self, self._user, self._server, int(self._port))
636                 self._proto = None
637                 self._mailChecker = None
638                 self.inboxPos = 0
639                 self.mailboxList = None
640                 self._failureReason = ""
641                 self._connectCallback = None 
642                 mailAccounts.append(self)
643                 if afterInit:
644                         afterInit()
645
646         def exit(self):
647                 mailAccounts.remove(self)
648                 # stop checker and get rid of it
649                 self.stopChecker()
650                 self._mailChecker = None
651                 # stop factory and get rid of it
652                 if self._factory:
653                         self._factory.stopTrying()
654                 self._factory = None # I am not sure to stop the factory, though...
655                 # if we still have a proto, logout and dump it
656                 if self._proto:
657                         self._proto.logout()
658                 self._proto = None
659
660         def isConnected(self):
661                 return self._proto is not None and self.mailboxList is not None
662
663         def forceRetry(self, connectCallback):
664                 '''
665                 reset delays and retry
666                 @param connectCallback: call this function on successful connect, used by EmailAccountList
667                 '''
668                 self._connectCallback = connectCallback 
669                 if self._factory and self._factory.connector:
670                         self._factory.resetDelay()
671                         self._factory.retry()
672                 else:
673                         self._factory = createFactory(self, self._user, self._server, int(self._port))
674
675         def removeCallback(self):
676                 self._connectCallback = None 
677
678         def getConfig(self):
679                 # TODO: encrypt passwd
680                 return (self._name, self._server, self._port, self._user, self._password, self._interval, self._maxmail, (1 if self._listall else 0))
681
682         def _ebNotify(self, result, where, what):
683                 debug("[EmailAccount] %s: _ebNotify error in %s: %s: %s" %(self._name, where, what, result.getErrorMessage()))
684                 if config.plugins.emailimap.verbose.value:
685                         Notifications.AddNotification(MessageBox, "EmailClient for %(account)s:\n\n%(error)s" %{'account': self._name, 'error':what}, type=MessageBox.TYPE_ERROR, timeout=config.plugins.emailimap.timeout.value)
686
687         def startChecker(self):
688                 # debug("[EmailAccount] %s: startChecker?" %self._name)
689                 if int(self._interval) != 0:
690                         if self._mailChecker:
691                                 # so, we already have seen an unseenList
692                                 # debug("[EmailAccount] %s: startChecker again" %self._name)
693                                 self._mailChecker.reStartChecking()
694                         else:
695                                 # debug("[EmailAccount] %s: startChecker new" %self._name)
696                                 self._mailChecker = CheckMail(self)
697
698         def stopChecker(self):
699                 if self._mailChecker:
700                         self._mailChecker.stopChecking()
701
702         def undelete(self, uid):
703                 '''
704                 undelete a message
705                 @param uid: uid of message
706                 '''
707                 if self._proto:
708                         self._proto.removeFlags(uid, ["\\Deleted"])
709                         return True
710                 else:
711                         return False
712
713         def delete(self, uid):
714                 '''
715                 mark message as deleted 
716                 @param uid: uid of message
717                 '''
718                 if self._proto:
719                         self._proto.addFlags(uid, ["\\Deleted"])
720                         return True
721                 else:
722                         return False
723
724         def unread(self, uid):
725                 '''
726                 mark message as unread, remove \\Seen
727                 @param uid: uis of message
728                 '''
729                 if self._proto:
730                         self._proto.removeFlags(uid, ["\\Seen"])
731                         return True
732                 else:
733                         return False
734
735         def getUnseenHeaders(self, callback):
736                 # debug('[EmailAccount] %s: getUnseenHeaders' %self._name)
737                 if self._proto:
738                         self._proto.examine('inbox').addCallback(self._doSearchUnseen, callback).addErrback(self._ebNotify, "getUnseenHeaders", _("cannot access inbox"))
739                         return True
740                 else:
741                         return False
742
743         def _doSearchUnseen(self, result, callback): #@UnusedVariable # pylint: disable-msg=W0613
744                 # debug('[EmailAccount] %s: _doSearchUnseen' %(self._name))
745                 self._proto.search(imap4.Query(unseen=1)).addCallback(callback).addErrback(self._ebNotify, '_doSearchUnseen', _("cannot get list of new messages"))
746
747         def getMessageList(self, callback, mbox):
748                 if self._proto:
749                         self._proto.select(mbox.decode('utf-8')).addCallback(self._onSelect, callback).addErrback(self._onSelectFailed, callback, mbox)
750                         return True
751                 else:
752                         return False
753
754         def _onSelect(self, result, callback):
755                 # debug("[EmailAccount] _onExamine: " + str(result))
756                 numMessagesinFolder = int(result['EXISTS'])
757                 if numMessagesinFolder <= 0:
758                         callback([], [])
759                 else:
760                         if int(self._maxmail) > 0:
761                                 maxMessagesToFetch = int(self._maxmail)
762                                 startmsg = numMessagesinFolder-maxMessagesToFetch+1
763                                 if startmsg <= 0:
764                                         startmsg = 1
765                                 rangeToFetch = [startmsg, numMessagesinFolder]
766                         else:
767                                 rangeToFetch = [1, numMessagesinFolder]
768                         try:
769                                 self._proto.fetchFlags('%i:%i'%(rangeToFetch[0], rangeToFetch[1])       #'1:*'
770                                                    ).addCallback(self._onFlagsList, callback, rangeToFetch)
771
772                         except imap4.IllegalServerResponse, e:
773                                 debug("[EmailAccount] _onExamine exception: " + str(e))
774                                 callback([], [])
775
776         def _onSelectFailed(self, failure, callback, mboxname):
777                 debug("[EmailAccount] %s: _onSelectFailed: %s %s" %(self._name, mboxname, str(failure)))
778                 callback([], [])
779
780         def _onFlagsList(self, flagsList, callback, rangeToFetch):
781                 self._proto.fetchHeaders('%i:%i'%(rangeToFetch[0], rangeToFetch[1])     #'1:*'
782                                    ).addCallback(callback, flagsList)
783
784         def getMessage(self, message, callback, errCallback):
785                 debug("[EmailAccount] %s: getMessage: %s" %(self._name, str(message)))
786                 if self._proto:
787                         self._proto.fetchSize(message.uid
788                                 ).addCallback(self._onMessageSizeLoaded, message, callback, errCallback 
789                                 ).addErrback(self._onMessageLoadFailed, message, errCallback
790                                 )
791                         return True
792                 else:
793                         return False
794
795         def _onMessageSizeLoaded(self, result, message, callback, errCallback):
796                 debug("[EmailAccount] %s: _onMessageSizeLoaded: %s %s" %(self._name, str(result), str(message)))
797                 size = int(result[message.uid]['RFC822.SIZE'])
798                 if size >= 4000000:
799                         #ask here to open message
800                         debug("[EmailAccount] _onMessageSizeLoaded: message to large to open (size=%d)" %size)
801                         errCallback('', _("message too large"))
802                 else:
803                         self._proto.fetchMessage(message.uid
804                                 ).addCallback(callback, message,
805                                 ).addErrback(self._onMessageLoadFailed, message, errCallback
806                                 )
807
808         def _onMessageLoadFailed(self, failure, message, errCallback):
809                 debug("[EmailAccount] %s: onMessageLoadFailed: %s %s" %(self._name, str(failure), str(message)))
810                 errCallback('', _("failed to load message") + ': ' + failure.getErrorMessage())
811
812         def getHeaders(self, callback, messageSet):
813                 debug('[EmailAccount] %s: getHeaders' %self._name)
814                 if self._proto:
815                         self._proto.fetchHeaders(messageSet).addCallback(callback).addErrback(self._ebNotify, 'getHeaders', _("cannot get headers of new messages"))
816                         return True
817                 else:
818                         return False
819
820         def onConnect(self, proto):
821                 debug("[EmailAccount] %s: %s@%s:%s: onConnect" %(self._name, self._user, self._server, self._port))
822                 self._factory.resetDelay()
823                 self._proto = proto
824                 self._failureReason = ""
825                 if self._connectCallback:
826                         self._connectCallback()
827                         self._connectCallback = None
828                 proto.getCapabilities().addCallback(self._cbCapabilities).addErrback(self._ebCapabilities)
829
830         def onConnectionFailed(self, reason):
831                 debug("[EmailAccount] %s@%s:%s: onConnectFailed: %s" %(self._user, self._server, self._port, reason.getErrorMessage()))
832                 reasonString = reason.getErrorMessage()
833                 if reasonString != self._failureReason:
834                         self._ebNotify(reason, 'onConnectionFailed', _("connection failed - retrying")+'\n'+reason.getErrorMessage())
835                         self._failureReason = reasonString
836                 self._proto = None
837                 # don't retry, if we do not check this account
838                 if int(self._interval) == 0 and self._factory:
839                         self._factory.stopTrying()
840                 # self.stopChecker() not necessary, because we don't have an active connection...
841
842         def onConnectionLost(self, reason):
843                 debug("[EmailAccount] %s@%s:%s: onConnectFailed: %s" %(self._user, self._server, self._port, reason.getErrorMessage()))
844                 # too noisy... self._ebNotify(reason, 'onConnectionLost', _("connection lost - retrying"))
845                 self._proto = None
846                 self.stopChecker()
847                 # don't retry, if we do not check this account
848                 if int(self._interval) == 0 and self._factory:
849                         self._factory.stopTrying()
850
851         def _cbCapabilities(self, reason):
852                 debug(_("[EmailAccount] %(name)s: _cbCapabilities:\n\
853 ####################################################################################################\n\
854 # If you have problems to log into your imap-server, please send me the output of the following line\n\
855 # cbCapabilities: %(capa)s\n\
856 ####################################################################################################\n")
857                         %{'name':self._name, 'capa':str(reason)})
858                 self._doLogin()
859
860         def _ebCapabilities(self, reason):
861                 debug("[EmailAccount] %s: _ebCapabilities: %s" %(self._name, str(reason)))
862
863         def _doLogin(self):
864                 debug("[EmailAccount] %s: _doLogin secure" %(self._name))
865                 d = self._proto.authenticate(self._password)
866                 d.addCallback(self._onAuthentication)
867                 d.addErrback(self._onAuthenticationFailed)
868                 return d
869
870         def _onAuthentication(self, result):
871                 # better use LSUB here to get only the subscribed to mailboxes
872                 debug("[EmailAccount] %s: _onAuthentication: %s" %(self._name, str(result)))
873                 self.startChecker()
874                 self.getMailboxList()
875                 
876         def getMailboxList(self):
877                 if self._listall:
878                         debug("[EmailAccount] %s: getMailboxList list" %(self._name))
879                         self._proto.list("", "*").addCallback(self._onMailboxList)
880                 else:
881                         debug("[EmailAccount] %s: getMailboxList lsub" %(self._name))
882                         self._proto.lsub("", "*").addCallback(self._onMailboxList)
883
884         def _onAuthenticationFailed(self, failure):
885                 # If it failed because no SASL mechanisms match
886                 debug("[EmailAccount] %s: onAuthenticationFailed: %s" %(self._name, failure.getErrorMessage()))
887                 try:
888                         failure.trap(imap4.NoSupportedAuthentication)
889                         self._doLoginInsecure()
890                 except Exception, e:
891                         debug("[EmailAccount] %s: _onAuthenticationFailed: %s" %(self._name, e.message))
892                         print e, e.message
893
894         def _doLoginInsecure(self):
895                 debug("[EmailAccount] %s: _doLoginInsecure" %(self._name))
896                 self._proto.login(self._user, self._password).addCallback(self._onAuthentication).addErrback(self._onInsecureAuthenticationFailed)
897
898         def _onInsecureAuthenticationFailed(self, failure):
899                 debug("[EmailAccount] %s: _onInsecureAuthenticationFailed: %s" %(self._name, failure.getErrorMessage()))
900                 self._proto = None
901                 #=======================================================================
902                 # Notifications.AddNotification(
903                 #       MessageBox,
904                 #       _("error logging %(who)s in:\n%(failure)s")
905                 #               %{
906                 #               'who':"%s@%s" %(self._user, self._server),
907                 #               'failure':failure.getErrorMessage()
908                 #               }, type=MessageBox.TYPE_ERROR, timeout=config.plugins.emailimap.timeout.value)
909                 #=======================================================================
910                 self._ebNotify(failure, "_onInsecureAuthenticationFailed",
911                                         _("error logging %(who)s in:\n%(failure)s")
912                                         %{
913                                         'who':"%s@%s" %(self._user, self._server),
914                                         'failure':failure.getErrorMessage()
915                                         })
916
917         def _onMailboxList(self, result):
918                 mylist = [UTF7toUTF8(mb[2]).encode('utf-8') for mb in result if '\\Noselect' not in mb[0]]
919                 debug("[EmailAccount] %s: onMailboxList: %s selectable mailboxes" %(self._name, len(mylist)))
920                 # debug("[EmailAccount] %s: onMailboxList:\n%s" %(self._name, str(mylist)))
921                 mylist.sort()
922                 try:
923                         self.inboxPos = map(lambda x: x.lower(), mylist).index('inbox')+1
924                 except ValueError:
925                         debug("[EmailAccount] onMailboxList: no inbox?!?!")
926                         mylist = ['INBOX']
927                         self.inboxPos = 1
928                 self.mailboxList = mylist
929
930 class EmailAccountList(Screen):
931         # pylint: disable-msg=W0212
932         def __init__(self, session):
933                 '''
934                 Entry screen holding the list of accounts.
935                 Offering to add, edit or remove one. Also configuration through <menu> 
936                 '''
937                 debug("[EmailAccountList] __init__")
938                 noButtons = 3
939                 width = max(noButtons*140+35+100, DESKTOP_WIDTH/3)
940                 self.width = width
941                 height = max(5*30+50, DESKTOP_HEIGHT/3)
942                 buttonsGap = (width-(noButtons)*140-35)/(noButtons+2)
943                 self.skin = """
944                         <screen position="%d,%d" size="%d,%d" title="Accounts list" >
945                         <widget name="accounts" position="0,0" size="%d,%d" scrollbarMode="showOnDemand" />
946                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
947                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
948                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
949                         <ePixmap position="%d,%d"       size="35,25" pixmap="skin_default/buttons/key_menu.png"         alphatest="on" />
950                         <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" />
951                         <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" />
952                         <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" />
953                         </screen>""" % (
954                                                 (DESKTOP_WIDTH-width)/2, (DESKTOP_HEIGHT-height)/2, width, height,
955                                                 width, height,  # config
956                                                 buttonsGap, height-45,
957                                                 2*buttonsGap+140, height-45,
958                                                 3*buttonsGap+2*140, height-45,
959                                                 4*buttonsGap+3*140, height-38,
960                                                 buttonsGap, height-45, scaleV(22,18),
961                                                 2*buttonsGap+140, height-45, scaleV(22,18),
962                                                 3*buttonsGap+2*140, height-45, scaleV(22,18)
963                                                 )
964                 Screen.__init__(self, session)
965                 self["buttonred"] = Label(_("remove"))
966                 self["buttongreen"] = Label(_("add"))
967                 self["buttonyellow"] = Label(_("edit"))
968                 self["setupActions"] = ActionMap(["ColorActions", "OkCancelActions", "MenuActions"],
969                 {
970                         "menu": self._config,
971                         "red": self._remove,
972                         "green": self._add,
973                         "yellow": self._edit,
974                         "cancel": self._exit,
975                         "ok": self._action,
976                 }, -2)
977                 for acc in mailAccounts:
978                         if not acc.isConnected():
979                                 acc.forceRetry(self._layoutFinish)
980                 self["accounts"] = MenuList([], content=eListboxPythonMultiContent)
981                 self["accounts"].l.setItemHeight(scaleV(20, 18)+5)
982                 self["accounts"].l.setFont(0, gFont("Regular", scaleV(20, 18)))
983                 self.onLayoutFinish.append(self._layoutFinish)
984
985         def _layoutFinish(self):
986                 self.setTitle(_("Accounts list"))
987                 accList = []
988                 for acc in mailAccounts:
989                         if acc.isConnected():
990                                 color = 0x00FFFFFF
991                         else:
992                                 color = 0x00888888
993                         accList.append([acc, MultiContentEntryText(pos=(0, 0), size=(self.width, scaleV(20, 18)+5), text=acc._name, color=color, color_sel=color)])
994                 self["accounts"].l.setList(accList)
995
996         def _config(self):
997                 debug("[EmailAccountList] _config")
998                 self.session.open(EmailConfigOptions, "Rev " + "$Revision$"[11: - 1] + "$Date$"[7:23])
999
1000         def _action(self):
1001                 if self["accounts"].getCurrent():
1002                         debug("[EmailAccountList] _action: %s" %self["accounts"].getCurrent()[0]._name)
1003                         account = self["accounts"].getCurrent()[0]
1004                         if account and account.isConnected():
1005                                 self.session.open(EmailScreen, account)
1006                                 self._layoutFinish()
1007                         else:
1008                                 self.session.open(MessageBox,
1009                                                                 _("account %s is not connected") %self["accounts"].getCurrent()[0]._name,
1010                                                                 type=MessageBox.TYPE_INFO,
1011                                                                 timeout=config.plugins.emailimap.timeout.value)
1012                 else:
1013                         debug("[EmailAccountList] _action: no account selected")
1014                         self.session.open(MessageBox,
1015                                                         _("no account selected"),
1016                                                         type=MessageBox.TYPE_ERROR,
1017                                                         timeout=config.plugins.emailimap.timeout.value)
1018
1019         def _add(self):
1020                 debug("[EmailAccountList] _add")
1021                 self.session.openWithCallback(self._cbAdd, EmailConfigAccount)
1022
1023         def _cbAdd(self, params):
1024                 if params:
1025                         # TODO: encrypt passwd
1026                         EmailAccount(params, writeAccounts)
1027                 self.close()
1028
1029         def _edit(self):
1030                 debug("[EmailAccountList] _edit")
1031                 if self["accounts"].getCurrent():
1032                         self.session.openWithCallback(self._cbEdit, EmailConfigAccount, self["accounts"].getCurrent()[0].getConfig())
1033                 else:
1034                         self.session.openWithCallback(self._cbAdd, EmailConfigAccount)
1035
1036         def _cbEdit(self, params):
1037                 if params:
1038                         self["accounts"].getCurrent()[0].exit()
1039                         # TODO: encrypt passwd
1040                         EmailAccount(params, writeAccounts)
1041                 self.close()
1042                 
1043         def _remove(self):
1044                 debug("[EmailAccountList] _remove")
1045                 if self["accounts"].getCurrent():
1046                         self.session.openWithCallback(
1047                                 self._cbRemove,
1048                                 MessageBox,
1049                                 _("Really delete account %s?") % self["accounts"].getCurrent()[0]._name)
1050                 else:
1051                         self.session.open(MessageBox,
1052                                                         _("no account selected"),
1053                                                         type=MessageBox.TYPE_ERROR,
1054                                                         timeout=config.plugins.emailimap.timeout.value)
1055
1056         def _cbRemove(self, ret):
1057                 if ret:
1058                         self["accounts"].getCurrent()[0].exit()
1059                         writeAccounts()
1060                 self._layoutFinish()
1061
1062         def _exit(self):
1063                 for acc in mailAccounts:
1064                         acc.removeCallback()
1065                 self.close()
1066
1067 from Tools.Directories import resolveFilename, SCOPE_SYSETC, SCOPE_CONFIG, SCOPE_PLUGINS
1068 import csv
1069 MAILCONF = resolveFilename(SCOPE_CONFIG, "EmailClient.csv")
1070
1071 #
1072 # we need versioning on the config data
1073 #
1074 CONFIG_VERSION = 1
1075 def writeAccounts():
1076         fd = open(MAILCONF, 'w')
1077         fd.write(str(CONFIG_VERSION)+'\n')
1078         out = csv.writer(fd, quotechar='"', lineterminator='\n')
1079         for acc in mailAccounts:
1080                 out.writerow(acc.getConfig())
1081         fd.close()
1082
1083 def getAccounts():
1084         debug("[] getAccounts")
1085
1086         if not os.path.exists(MAILCONF):
1087                 fMAILCONF_XML = resolveFilename(SCOPE_SYSETC, "mailconf.xml")
1088                 debug("[] getAccounts: check for %s" %fMAILCONF_XML)
1089                 if os.path.exists(fMAILCONF_XML):
1090                         from xml.dom.minidom import parse
1091                         Notifications.AddNotification(MessageBox, _("importing configurations from %s") %fMAILCONF_XML, type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
1092                         maildom = parse(fMAILCONF_XML)
1093                         for top in maildom.getElementsByTagName("list"):
1094                                 for acc in top.getElementsByTagName("account"):
1095                                         name = str(acc.getElementsByTagName("name")[0].childNodes[0].data)
1096                                         server = str(acc.getElementsByTagName("server")[0].childNodes[0].data)
1097                                         port = str(acc.getElementsByTagName("port")[0].childNodes[0].data)
1098                                         user = str(acc.getElementsByTagName("user")[0].childNodes[0].data)
1099                                         password = str(acc.getElementsByTagName("pass")[0].childNodes[0].data)
1100                                         interval = str(acc.getElementsByTagName("interval")[0].childNodes[0].data)
1101                                         maxmail = str(acc.getElementsByTagName("MaxMail")[0].childNodes[0].data)
1102                                         debug("[EmailClient] - Autostart: import account %s" %acc(name, server, port, user, password, interval, maxmail))
1103                                         EmailAccount((name, server, port, user, password, interval, maxmail, 0))
1104                 else:
1105                         debug("[] getAccounts: no file found, exiting")
1106         else:
1107                 debug("[] getAccounts: reading %s" %MAILCONF)
1108                 fd = open(MAILCONF)
1109                 accounts = csv.reader(fd, quotechar='"')
1110                 version = 0
1111                 for acc in accounts:
1112                         if len(acc) == 1:
1113                                 version = int(acc[0])
1114                                 continue
1115                         debug("[EmailClient] - Autostart: add account %s" %acc[0])
1116                         if version == 0:
1117                                 # add listall param at the end to get version 1
1118                                 (name, server, port, user, password, interval, maxmail) = acc
1119                                 acc = (name, server, port, user, password, interval, maxmail, 0)
1120                         EmailAccount(acc)
1121                 fd.close()
1122                 if version != CONFIG_VERSION:
1123                         writeAccounts()
1124
1125 def main(session, **kwargs): #@UnusedVariable kwargs # pylint: disable-msg=W0613
1126         session.open(EmailAccountList)
1127
1128 def autostart(reason, **kwargs): #@UnusedVariable reason
1129         debug("[EmailClient] - Autostart reason: %d kwargs: %s" %(reason, repr(kwargs)))
1130         debug("[EmailClient] " + "$Revision$"[1:-1]     + "$Date$"[7:23] + " starting")
1131         import shutil
1132         if os.path.isdir('/usr/lib/python2.6') and not os.path.isfile('/usr/lib/python2.6/uu.py'):
1133                 shutil.copy(resolveFilename(SCOPE_PLUGINS, "Extensions/EmailClient/uu.py"), '/usr/lib/python2.6/uu.py')
1134         elif os.path.isdir('/usr/lib/python2.5') and not os.path.isfile('/usr/lib/python2.5/uu.py'):
1135                 shutil.copy(resolveFilename(SCOPE_PLUGINS, "Extensions/EmailClient/uu.py"), '/usr/lib/python2.5/uu.py')
1136
1137         if reason == 0:
1138                 getAccounts()
1139         else:
1140                 for acc in mailAccounts:
1141                         acc.exit()
1142
1143 initLog()
1144
1145 def Plugins(path, **kwargs): #@UnusedVariable kwargs # pylint: disable-msg=W0613,C0103
1146         return [
1147                          PluginDescriptor(name=_("Email Client"), description=_("view Emails via IMAP4"),
1148                          where = PluginDescriptor.WHERE_PLUGINMENU,
1149                          fnc = main,
1150                          icon="plugin.png"
1151                          ),
1152                          PluginDescriptor(where=PluginDescriptor.WHERE_SESSIONSTART, fnc=autostart)
1153                 ]