+# -*- coding: utf-8 -*-
+#===============================================================================
+# $Author$
+# $Revision$
+# $Date$
+# $Id$
+#==============================
from Components.ActionMap import ActionMap
from Components.Label import Label
from Screens.MessageBox import MessageBox
from Components.MultiContent import MultiContentEntryText
from Components.ScrollLabel import ScrollLabel
from Components.Button import Button
-from Components.config import config, ConfigSubsection, ConfigInteger, ConfigText, ConfigEnableDisable
-from EmailConfig import EmailConfigScreen
+from Components.config import config, ConfigSubsection, ConfigInteger, ConfigEnableDisable
from Plugins.Plugin import PluginDescriptor
from Screens.ChoiceBox import ChoiceBox
from Screens.Screen import Screen
from Tools import Notifications
-from enigma import eListboxPythonMultiContent, gFont, eTimer
+from enigma import eListboxPythonMultiContent, gFont, eTimer #@UnresolvedImport
from twisted.mail import imap4
from zope.interface import implements
-import email, time
+import email, re, os
from email.header import decode_header
from TagStrip import strip_readable
from protocol import createFactory
-from . import _, initLog, debug
+from . import _, initLog, debug, scaleH, scaleV, DESKTOP_WIDTH, DESKTOP_HEIGHT #@UnresolvedImport
+mailAccounts = [] # contains all EmailAccount objects
+from EmailConfig import EmailConfigOptions, EmailConfigAccount
config.plugins.emailimap = ConfigSubsection()
-config.plugins.emailimap.username = ConfigText("user", fixed_size=False)
-config.plugins.emailimap.password = ConfigText("password", fixed_size=False)
-config.plugins.emailimap.server = ConfigText("please.config.first", fixed_size=False)
-config.plugins.emailimap.port = ConfigInteger(default=143, limits = (1, 65536))
config.plugins.emailimap.showDeleted = ConfigEnableDisable(default=False)
config.plugins.emailimap.checkForNewMails = ConfigEnableDisable(default=True)
-config.plugins.emailimap.checkPeriod = ConfigInteger(default=60, limits=(0, 900)) # in minutes
config.plugins.emailimap.timeout = ConfigInteger(default=0, limits=(0, 90)) # in seconds
-# 0= fetch all header , 10= fetch only the last 10 headers/messages of a mailbox
-config.plugins.emailimap.maxheadertoload = ConfigInteger(0, limits = (1, 100))
config.plugins.emailimap.debug = ConfigEnableDisable(default=False)
-from enigma import getDesktop
-DESKTOP_WIDTH = getDesktop(0).size().width()
-DESKTOP_HEIGHT = getDesktop(0).size().height()
-#
-# this is pure magic.
-# It returns the first value, if HD (1280x720),
-# the second if SD (720x576),
-# else something scaled accordingly
-# if one of the parameters is -1, scale proportionally
-#
-def scaleH(y2, y1):
- if y2 == -1:
- y2 = y1*1280/720
- elif y1 == -1:
- y1 = y2*720/1280
- return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
-def scaleV(y2, y1):
- if y2 == -1:
- y2 = y1*720/576
- elif y1 == -1:
- y1 = y2*576/720
- return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
-def scale(y2, y1, x2, x1, x):
- return (y2 - y1) * (x - x1) / (x2 - x1) + y1
-
def decodeHeader(text, default=''):
if text is None:
return _(default)
text = text.replace('\r',' ').replace('\n',' ').replace('\t',' ')
- while text.find(' ') != -1:
- text = text.replace(' ',' ')
+ text = re.sub('\s\s+', ' ', text)
textNew = ""
for part in decode_header(text):
(content, charset) = part
IS_SEEN = 1
IS_DELETED = 2
-class EmailHandler:
- def __init__(self):
- pass
- def onConnect(self, proto):
- pass
-
-class EmailScreen(Screen, EmailHandler):
- implements(imap4.IMailboxListener)
+class EmailScreen(Screen):
+ '''
+ This is the main screen for interacting with the user.
+ It contains the list of mailboxes (boxlist) on the left and
+ the list of messages (messagelist) on the right.
+ At the bottom we have a line for info messages.
+ It is specific for one account.
+ '''
width = scaleH(-1,530)
height = scaleV(-1,430)
0, height-infolabelHeight, width, infolabelHeight, scaleV(20,18)
)
- currentmailbox = None
- proto = None
-
- def __init__(self, session, args = 0):
- EmailHandler.__init__(self)
- self.session = session
-
+ def __init__(self, session, account):
+ '''
+ This is the main screen for interacting with the user.
+ It contains the list of mailboxes (boxlist) on the left and
+ the list of messages (messagelist) on the right.
+ At the bottom we have a line for info messages.
+ It is specific for one account.
+
+ @param session: session in which this screen is running
+ @param account: account for which mailboxes are shown
+ '''
+ self._session = session
+ self._account = account
self.skin = EmailScreen.skin
Screen.__init__(self, session)
- self.factory = createFactory(self, config.plugins.emailimap.username.value, config.plugins.emailimap.server.value, config.plugins.emailimap.port.value)
self["actions"] = ActionMap(["InfobarChannelSelection", "WizardActions", "DirectionActions", "MenuActions", "ShortcutActions", "GlobalActions", "HelpActions", "NumberActions", "ChannelSelectBaseActions"],
{
- "ok": self.action_ok,
- "back": self.action_exit,
- "historyNext": self.selectMessagelist,
- "historyBack": self.selectBoxlist,
- "nextBouquet": self.selectMessagelist,
- "prevBouquet": self.selectBoxlist,
- "down": self.down,
- "up": self.up,
- "left": self.left,
- "right": self.right,
- "menu": self.action_menu,
+ "ok": self._ok,
+ "back": self._exit,
+ "historyNext": self._selectMessagelist,
+ "historyBack": self._selectBoxlist,
+ "nextBouquet": self._selectMessagelist,
+ "prevBouquet": self._selectBoxlist,
+ "down": self._down,
+ "up": self._up,
+ "left": self._left,
+ "right": self._right,
}, -1)
- self["boxlist"] = MenuList([])
- self["messagelist"] = MailList([])
- self["infolabel"] = Label("")
- self.onLayoutFinish.append(self.selectBoxlist)
+ self["messagelist"] = MenuList([], content=eListboxPythonMultiContent)
+ self["messagelist"].l.setItemHeight(scaleV(70,60))
+ self["messagelist"].l.setFont(0, gFont("Regular", scaleV(20,18))) # new
+ self["messagelist"].l.setFont(1, gFont("Regular", scaleV(18,16))) # deleted
+ self["messagelist"].l.setFont(2, gFont("Regular", scaleV(18,16))) # seen
+
+ if self._account.isConnected():
+ self["infolabel"] = Label("")
+ self["boxlist"] = MenuList(self._account.mailboxList)
+ self.onLayoutFinish.append(self._finishBoxlist)
+ else:
+ self["infolabel"] = Label(_("account not connected"))
+ self["boxlist"] = MenuList([])
- def action_menu(self):
- self.session.open(EmailConfigScreen).onHide.append(self.onBoxSelected)
+ def _exit(self):
+ self.close()
- def selectBoxlist(self):
+ def _finishBoxlist(self):
+ self.setTitle(_("%(name)s (%(user)s@%(server)s)")
+ %{
+ 'name':self._account._name,
+ 'user':self._account._user,
+ 'server':self._account._server
+ })
+ self["boxlist"].moveToIndex(self._account.inboxPos-1)
+ self._selectBoxlist()
+ self._onBoxSelected()
+
+ def _selectBoxlist(self):
self.currList = "boxlist"
- self["boxlist"].selectionEnabled(1)
self["messagelist"].selectionEnabled(0)
+ self["boxlist"].selectionEnabled(1)
- def selectMessagelist(self):
+ def _selectMessagelist(self):
self.currList = "messagelist"
self["boxlist"].selectionEnabled(0)
self["messagelist"].selectionEnabled(1)
- def up(self):
+ def _up(self):
self[self.currList].up()
- def down(self):
+ def _down(self):
self[self.currList].down()
- def left(self):
+ def _left(self):
self[self.currList].pageUp()
- def right(self):
+ def _right(self):
self[self.currList].pageDown()
- def action_ok(self):
+ def _ok(self):
if self.currList == "boxlist":
- self.onBoxSelected()
+ self._onBoxSelected()
+ else:
+ self._onMessageSelected()
+
+ def _ebNotify(self, where, what):
+ '''
+ Error notification via calling back
+ @param where: location, where the error occurred
+ @param what: message, what happened
+ '''
+ debug("[EmailScreen] _ebNotify error in %s: %s" %(where, what))
+ 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)
+
+ def _onBoxSelected(self):
+ self["messagelist"].l.setList([])
+ self._onBoxSelectedNoClear()
+
+ def _onBoxSelectedNoClear(self):
+ self["infolabel"].setText(_("loading headers ..."))
+ if self["boxlist"].getCurrent():
+ if self._account.getMessageList(self._onHeaderList, self["boxlist"].getCurrent()):
+ self._selectMessagelist()
+ else:
+ self["infolabel"].setText(_("account not connected"))
else:
- self.onMessageSelected()
+ self["infolabel"].setText(_("no mailbox?!?!"))
+
- def onBoxSelected(self):
- c = self["boxlist"].getCurrent()
- if c is not None:
- self.proto.select(UTF7toUTF8(c[1][2]) # select instead of examine to get write access
- ).addCallback(self.onExamine, c[0] , self.proto
- ).addErrback(self.onExamineFailed, c[0], self.proto
- )
- # self.proto.search(imap4.Query(unseen=1)).addCallback(self.cbOk).addErrback(self.cbNotOk)
+ def _onHeaderList(self, result, flagsList):
+ '''
+
+ @param result: list of message
+ @param flagsList: list of corresponding flags
+ '''
+ debug("[EmailScreen] onHeaderList")
+ self["infolabel"].setText(_("headers loaded, now parsing ..."))
+ self._flagsList = flagsList
+ list = []
+ for m in result:
+ state = IS_UNSEEN
+ # debug("onHeaderList :" + repr(flagsList[m]['FLAGS']))
+ if '\\Seen' in flagsList[m]['FLAGS']:
+ state = IS_SEEN
+ if '\\Deleted' in flagsList[m]['FLAGS']:
+ if not config.plugins.emailimap.showDeleted.value:
+ continue
+ else:
+ state = IS_DELETED
+ try:
+ list.append(self._buildMessageListItem(MessageHeader(m, result[m]['RFC822.HEADER']), state))
+ except Exception,e:
+ try:
+ list.append(self._buildMessageListItem(MessageHeader(m, result[m]['RFC822.HEADER'].decode('iso8859-1', 'replace'), state)))
+ except:
+ # this appear to be errors in the formatting of the mail itself...
+ debug("[EmailScreen] onHeaderList error: %s (%s)" %(result[m]['RFC822.HEADER'], str(e)))
+ if list:
+ list.reverse()
+ self["messagelist"].l.setList(list)
+ self["infolabel"].setText(_("have %d messages") %(len(list)))
+ else:
+ self["messagelist"].l.setList([])
+ self["infolabel"].setText(_("have no messages"))
+ # self.onBoxSelected() # brings us into endless loop, when still deleted messages are in there...
+ self._selectBoxlist()
- def onMessageSelected(self):
+ def _onMessageSelected(self):
+ self["infolabel"].setText(_("getting message ..."))
c = self["messagelist"].getCurrent()
if c is not None:
- self.fetchMessageSize(c[0])
+ if not self._account.getMessage(c[0], self._onMessageLoaded, self._ebNotify):
+ self["infolabel"] = Label(_("account not connected"))
- def fetchMessageSize(self, message):
- debug("[EmailScreen] fetchMessageSize: " + str(message))
- self.proto.fetchSize(message.uid
- ).addCallback(self.onMessageSizeLoaded, message, self.proto
- ).addErrback(self.onMessageLoadFailed, message, self.proto
- )
-
- def onMessageSizeLoaded(self, result, message, proto):
- debug("[EmailScreen] onMessageSizeLoaded: " + str(result) + ' ' + str(message))
- size = int(result[message.uid]['RFC822.SIZE'])
- self.MAX_MESSAGE_SIZE_TO_OPEN = 4000000
- if size >= self.MAX_MESSAGE_SIZE_TO_OPEN:
- #ask here to open message
- debug("message to large to open (size=%d" %size)
- else:
- self.loadMessage(message)
-
-# def fetchBodyStructure(self, message):
-# print "fetchBodyStructure",message
-# self.proto.fetchBodyStructure(message.uid
-# ).addCallback(self.onBodystructureLoaded, message, self.proto
-# ).addErrback(self.onMessageLoadFailed, message, self.proto
-# )
-
- def loadMessage(self, message):
- debug("[EmailScreen] loadMessage: " + str(message))
- self["infolabel"].setText(_("loading message"))
-
- self.proto.fetchMessage(message.uid
- ).addCallback(self.onMessageLoaded, message, self.proto
- ).addErrback(self.onMessageLoadFailed, message, self.proto
- )
-
- def onMessageLoaded(self, result, message, proto):
- self["infolabel"].setText(_("parsing message"))
+ def _onMessageLoaded(self, result, message):
+ self["infolabel"].setText(_("parsing message ..."))
debug("[EmailScreen] onMessageLoaded") #,result,message
try:
msgstr = result[message.uid]['RFC822']
except KeyError:
- self.loadMessage(message)
+ self._account.getMessage(message, self._onMessageLoaded, self._ebNotify)
+ # self.loadMessage(message)
return
- msg = email.Parser.Parser().parsestr(msgstr)
+ msg = email.Parser.Parser().parsestr(msgstr) #@UndefinedVariable
msg.messagebodys = []
msg.attachments = []
elif part.get_content_subtype() == "plain":
msg.messagebodys.append(EmailBody(part))
else:
- debug("[EmailScreen] unkown content type= " + part.get_content_maintype() + "/", part.get_content_subtype())
+ debug("[EmailScreen] onMessageLoaded: unknown content type=%s/%s" %(str(part.get_content_maintype()), str(part.get_content_subtype())))
else:
- debug("[EmailScreen] found Attachment with " + part.get_content_type() + " and name " + part.get_filename())
+ debug("[EmailScreen] onMessageLoaded: found Attachment with %s and name %s" %(str(part.get_content_type()), str(part.get_filename())))
msg.attachments.append(EmailAttachment(part.get_filename(), part.get_content_type(), part.get_payload()))
else:
msg.messagebodys.append(EmailBody(msg))
- self.session.open(ScreenMailView, msg, message.uid, proto, self.flagsList[message.uid]['FLAGS']).onHide.append(self.onBoxSelected)
-
- def onMessageLoadFailed(self, failure, message, proto):
- debug("[EmailScreen] onMessageLoadFailed: " + str(failure) + ' ' + str(message))
- self["infolabel"].setText(_("failed to load message") + ': ' + failure.getErrorMessage())
-
- def action_exit(self):
- global mailChecker
- if not mailChecker and self.proto is not None:
- self.proto.logout().addCallback(self.onLogedOut, self.proto).addErrback(self.onLogedOut, self.proto)
- else:
- self.factory.stopFactory()
- self.close()
-
- def onLogedOut(self, result, proto):
- debug("[EmailScreen] onLogedOut: " + str(result))
- self.factory.stopFactory()()
- self.close()
-
- def onConnect(self, proto):
- self["infolabel"].setText(_("connected"))
- proto.getCapabilities(
- ).addCallback(self.cbCapabilities, proto
- ).addErrback(self.ebCapabilities, proto
- )
- self.factory.resetDelay()
-
- def cbCapabilities(self,reason,proto):
- debug("[EmailScreen] \n\
-####################################################################################################\n\
-# If you have problems to log into your imap-server, please send me the output of the following line\n\
-# cbCapabilities: " + str(reason) +"\n\
-####################################################################################################\n")
- self.doLogin(proto)
-
- def ebCapabilities(self,reason,proto):
- debug("[EmailScreen] ebCapabilities: " + str(reason))
-
- def onConnectFailed(self, reason):
- debug("[EmailScreen] onConnectFailed: " + reason.getErrorMessage())
- if self.has_key('infolabel'):
- self["infolabel"].setText(_("connection to %(server)s:%(port)d failed") %{'server':config.plugins.emailimap.server.value,'port':config.plugins.emailimap.port.value}) # + ': ' + reason.getErrorMessage()) # the messages provided by twisted are crap here
- self.action_exit()
-
- def onAuthentication(self, result, proto):
- self.proto = proto
- self["infolabel"].setText(_("logged in"))
- # better use LSUB here to get only the subscribed to mailboxes
- proto.lsub("", "*").addCallback(self.onMailboxList, proto)
-
- def doLogin(self, proto):
- debug("[EmailScreen] login secure")
- useTLS = False #True
- if useTLS:
- context = proto.context.getContext()
- d = proto.startTLS(context)
- d = d.addCallback(proto.authenticate, config.plugins.emailimap.password.value)
- else:
- d = proto.authenticate(config.plugins.emailimap.password.value)
- d.addCallback(self.onAuthentication, proto)
- d.addErrback(self.onAuthenticationFailed, proto)
- return d
-
- def onAuthenticationFailed(self, failure, proto):
- # If it failed because no SASL mechanisms match
- debug("[EmailScreen] onAuthenticationFailed: " + failure.getErrorMessage())
- self["infolabel"].setText(_("encrypted login failed, trying without encryption"))
- try:
- failure.trap(imap4.NoSupportedAuthentication)
- self.doLoginInsecure(proto)
- except Exception,e:
- print e,e.message
-
- def doLoginInsecure(self, proto):
- debug("[EmailScreen] login INSECURE")
- proto.login(config.plugins.emailimap.username.value, config.plugins.emailimap.password.value
- ).addCallback(self.onAuthentication, proto
- ).addErrback(self.onInsecureAuthenticationFailed, proto
- )
-
- def onInsecureAuthenticationFailed(self, failure, proto):
- debug("[EmailScreen] onInsecureAuthenticationFailed: " + failure.getErrorMessage())
- self["infolabel"].setText(_("login failed") + ': ' + failure.getErrorMessage())
-
- def onMailboxList(self, result, proto):
- debug("[EmailScreen] onMailboxList: " + str(result) + ' ' + str(proto))
- list = []
- inboxPos = 0
- for i in result:
- flags, hierarchy_delimiter, name = i #@UnusedVariable
- list.append((UTF7toUTF8(name).encode('utf-8'), i))
- if name.lower() == 'inbox':
- inboxPos = len(list)
- self["boxlist"].setList(list)
- self["boxlist"].moveToIndex(inboxPos-1)
-
- def onExamine(self, result, mboxname, proto):
- debug("[EmailScreen] onExamine: " + str(result) + ' ' + mboxname)
- self.setTitle(_("Mailbox")+": "+mboxname)
- self.currentmailbox = mboxname
- numMessagesinFolder = int(result['EXISTS'])
- if numMessagesinFolder <= 0:
- self["infolabel"].setText(_("Box '%s' is empty") %(mboxname))
- self["messagelist"].l.setList([])
-
- else:
- if config.plugins.emailimap.maxheadertoload.value > 0:
- maxMessagesToFetch = config.plugins.emailimap.maxheadertoload.value
- startmsg = numMessagesinFolder-maxMessagesToFetch+1
- if startmsg <= 0:
- startmsg = 1
- rangeToFetch = [startmsg, numMessagesinFolder]
- else:
- rangeToFetch = [1, numMessagesinFolder]
- self["infolabel"].setText(_("loading headers %(from)d-%(to)d of Box '%(name)s'") % {'from': rangeToFetch[0], 'to': rangeToFetch[1], 'name': mboxname})
-
- try:
-# proto.fetchEnvelope('%i:%i'%(rangeToFetch[0], rangeToFetch[1]) #'1:*'
-# ).addCallback(self.onnvelopeList, proto
-# )
- self.proto = proto
- self.rangeToFetch = rangeToFetch
- proto.fetchFlags('%i:%i'%(rangeToFetch[0], rangeToFetch[1]) #'1:*'
- ).addCallback(self.onFlagsList)
-
- except imap4.IllegalServerResponse, e:
- debug("[EmailScreen] onExamine exception: " + str(e))
- self.selectMessagelist()
-
- def onFlagsList(self, result):
- self.flagsList = result
- self.proto.fetchHeaders('%i:%i'%(self.rangeToFetch[0], self.rangeToFetch[1]) #'1:*'
- ).addCallback(self.onHeaderList, self.proto
- )
-
- def onExamineFailed(self, failure, mboxname, proto):
- debug("[EmailScreen] onExamineFailed: " + mboxname + ' ' + str(failure) + ' ' + str(proto))
- self["infolabel"].setText(_("cannot access mailbox '%(mboxname)s'") % {'mboxname':mboxname})
-
- def cbOk(self, result):
- debug("[EmailScreen] cbOk result: %s" %repr(result))
-
- def cbNotOk(self, result):
- debug("[EmailScreen] cbNotOk result: %s" %(str(result)))
-
- def onHeaderList(self, result, proto):
- debug("[EmailScreen] onHeaderList") #,result,proto
- self["infolabel"].setText(_("headers loaded, now parsing ..."))
- list = []
- for m in result:
- state = IS_UNSEEN
- # debug("onHeaderList :" + repr(self.flagsList[m]['FLAGS']))
- if '\\Seen' in self.flagsList[m]['FLAGS']:
- state = IS_SEEN
- if '\\Deleted' in self.flagsList[m]['FLAGS']:
- if not config.plugins.emailimap.showDeleted.value:
- continue
- else:
- state = IS_DELETED
- try:
- list.append(self.buildMessageListItem(MessageHeader(m, result[m]['RFC822.HEADER']), state))
- except Exception,e:
- try:
- list.append(self.buildMessageListItem(MessageHeader(m, result[m]['RFC822.HEADER'].decode('iso8859-1', 'replace'), state)))
- except:
- # this appear to be errors in the formatting of the mail itself...
- debug("[EmailScreen] onHeaderList error: %s (%s)" %(result[m]['RFC822.HEADER'], str(e)))
- if list:
- list.reverse()
- self["messagelist"].l.setList(list)
- self["infolabel"].setText(_("have %d messages") %(len(list)))
- else:
- self["messagelist"].l.setList([])
- self["infolabel"].setText(_("have no messages"))
- # self.onBoxSelected() # brings us into endless loop, when still deleted messages are in there...
- self.selectBoxlist()
-
- def buildMessageListItem(self, message, state):
+ debug("[EmailScreen] onMessageLoaded:" + str(message.uid) +';'+ repr(self._flagsList[message.uid]['FLAGS']))
+ self.session.open(ScreenMailView, self._account, msg, message.uid, self._flagsList[message.uid]['FLAGS']).onHide.append(self._onBoxSelectedNoClear)
+ self["infolabel"].setText("")
+
+ def _buildMessageListItem(self, message, state):
+ '''
+ Construct a MultiContentEntryText from parameters
+ @param message: message
+ @param state: IS_UNSEEN (grey), IS_DELETED (red) are especially colored
+ '''
if state == IS_UNSEEN:
font = 0
color = 0x00FFFFFF # white
MultiContentEntryText(pos=(5, scaleV(20,18)+1), size=(self.messagelistWidth, scaleV(20,18)+5), font=font, text=message.get('date', default=_('no date')), color=color, color_sel=color),
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)
]
- #
- # IMailboxListener methods
- #
- def modeChanged(self, writeable):
- debug("[EmailScreen] modeChanged: " + str(writeable))
-
- def flagsChanged(self, newFlags):
- debug("[EmailScreen] flagsChanged: " + str(newFlags))
-
- def newMessages(self, exists, recent):
- debug("[EmailScreen] newMessages: " + str(exists) + ' ' + str(recent))
class ScreenMailView(Screen):
skin=""
- def __init__(self, session, email, uid, proto, flags):
- self.session = session
- self.email = email
+ def __init__(self, session, account, email, uid, flags):
+ '''
+ Principal screen to show one mail message.
+ @param session:
+ @param account: mail acoount, this message is coming from
+ @param email: the message itself
+ @param uid: uid of the message, needed to (un)delete and unmark
+ @param flags: the flags of the message, needed to check, whether IS_DELETED
+ '''
+ self._session = session
+ self._email = email
+ self._account = account
# debug('ScreenMailView ' + repr(email) + ' dir: ' + repr(dir(email)))
width = max(4*140,scaleH(-1,550))
height = scaleV(-1,476)
4*buttonsGap+3*140, height-30-5, scaleV(18,16),
)
Screen.__init__(self, session)
- self["from"] = Label(decodeHeader(_("From") +": %s" %self.email.get('from', _('no from'))))
- self["date"] = Label(_("Date") +": %s" %self.email.get('date', 'no-date'))
- self["subject"] = Label(decodeHeader(_("Subject") +": %s" %self.email.get('subject', _('no subject'))))
- self["body"] = ScrollLabel(_(self.email.messagebodys[0].getData()))
- # TODO: show headers
+ self["from"] = Label(decodeHeader(_("From") +": %s" %self._email.get('from', _('no from'))))
+ self["date"] = Label(_("Date") +": %s" %self._email.get('date', 'no-date'))
+ self["subject"] = Label(decodeHeader(_("Subject") +": %s" %self._email.get('subject', _('no subject'))))
+ self["body"] = ScrollLabel(_(self._email.messagebodys[0].getData()))
self["buttonred"] = Button("")
self["buttongreen"] = Button("")
self["buttonyellow"] = Button(_("leave unread"))
# TODO: perhaps better use left/right for previous/next message
"left": self["body"].pageUp,
"right": self["body"].pageDown,
- "red": self.selectBody,
- "green": self.selectAttachment,
- "yellow": self.markUnread,
- "blue": self.delete,
+ "red": self._selectBody,
+ "green": self._selectAttachment,
+ "yellow": self._markUnread,
+ "blue": self._delete,
}, -1)
- self.flags = flags
- self.proto = proto
- self.uid = uid
- proto.fetchFlags(self.uid).addCallback(self.cbOk).addErrback(self.cbNotOk)
- self.onLayoutFinish.append(self.updateButtons)
+ self._uid = uid
+ self._flags = flags
+ self.onLayoutFinish.append(self._updateButtons)
- def cbOk(self, result):
- debug("[ScreenMailView] cbOk result: %s" %repr(result))
-
- def cbNotOk(self, result):
- debug("[ScreenMailView] cbNotOk result: %s" %(str(result)))
-
- def delete(self):
- if '\\Deleted' in self.flags:
- self.session.openWithCallback(self.deleteCB, ChoiceBox, title=_("really undelete Mail?"), list=[(_("yes"), True),(_("no"), False)])
+ def _delete(self):
+ if '\\Deleted' in self._flags:
+ self.session.openWithCallback(self._deleteCB, MessageBox, _("really undelete mail?"), type=MessageBox.TYPE_YESNO, timeout=config.plugins.emailimap.timeout.value)
else:
- self.session.openWithCallback(self.deleteCB, ChoiceBox, title=_("really delete Mail?"), list=[(_("yes"), True),(_("no"), False)])
+ self.session.openWithCallback(self._deleteCB, MessageBox, _("really delete mail?"), type=MessageBox.TYPE_YESNO, timeout=config.plugins.emailimap.timeout.value)
- def deleteCB(self, returnValue):
- if returnValue and returnValue[1] is True:
- if '\\Deleted' in self.flags:
- self.proto.removeFlags(self.uid, ["\\Deleted"]).addCallback(self.cbOk).addErrback(self.cbNotOk)
+ def _deleteCB(self, returnValue):
+ if returnValue:
+ if '\\Deleted' in self._flags:
+ if not self._account.undelete(self._uid):
+ self.session.open(MessageBox, _("account not connected"), type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
else:
- self.proto.addFlags(self.uid, ["\\Deleted"]).addCallback(self.cbOk).addErrback(self.cbNotOk)
- debug("[ScreenMailView] deleteCB: %s" %repr(self.email))
+ if not self._account.delete(self._uid):
+ self.session.open(MessageBox, _("account not connected"), type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
+ debug("[ScreenMailView] deleteCB: %s" %repr(self._email))
self.close()
- def markUnread(self):
- self.proto.removeFlags(self.uid, ["\\Seen"]).addCallback(self.cbOk).addErrback(self.cbNotOk)
+ def _markUnread(self):
+ if not self._account.unread(self._uid):
+ self.session.open(MessageBox, _("account not connected"), type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
self.close()
- def openMessagesHeaders(self):
- pass #self.session.open(ScreenMailViewHeader,self.profil,self.email)
-
- def updateButtons(self):
- self["buttonred"].setText(_("Bodys"))
- if len(self.email.attachments):
+ def _updateButtons(self):
+ if len(self._email.messagebodys):
+ self["buttonred"].setText(_("Bodys"))
+ else:
+ self["buttonred"].setText("")
+ if len(self._email.attachments):
self["buttongreen"].setText(_("Attachments"))
else:
self["buttongreen"].setText("")
- def selectBody(self):
- if len(self.email.messagebodys):
+ def _selectBody(self):
+ if len(self._email.messagebodys):
list = []
- for a in self.email.messagebodys:
+ for a in self._email.messagebodys:
list.append((a.getContenttype(), a))
- self.session.openWithCallback(self.selectBodyCB, ChoiceBox, _("select Body"), list)
+ self.session.openWithCallback(self._selectBodyCB, ChoiceBox, _("select Body"), list)
- def selectBodyCB(self, choice):
+ def _selectBodyCB(self, choice):
if choice is not None:
self["body"].setText(choice[1].getData())
- def selectAttachment(self):
- if len(self.email.attachments):
+ def _selectAttachment(self):
+ if len(self._email.attachments):
list = []
- for a in self.email.attachments:
+ for a in self._email.attachments:
name = a.getFilename()
if name:
list.append((a.getFilename(), a))
else:
list.append((_("no filename"), a))
debug("[ScreenMailView] selectAttachment : " + repr(list))
- self.session.openWithCallback(self.selectAttachmentCB, ChoiceBox, _("select Attachment"), list)
-
- def selectAttachmentCB(self, choice):
- if choice is not None:
- debug("[ScreenMailView] Attachment selected: " + choice[1].getFilename())
- #showMessageBox(self.session)
-
-class MailList(MenuList):
- def __init__(self, list, enableWrapAround = False):
- MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
- self.l.setFont(0, gFont("Regular", scaleV(20,18))) # new
- self.l.setFont(1, gFont("Regular", scaleV(18,16))) # deleted
- self.l.setFont(2, gFont("Regular", scaleV(18,16))) # seen
-
- def postWidgetCreate(self, instance):
- MenuList.postWidgetCreate(self, instance)
- instance.setItemHeight(scaleV(70,60))
-
-class MessageHeader(object):
- def __init__(self, uid, message):
- self.uid = uid #must be int
- self.message = email.Parser.Parser().parsestr(message)
-
- def getSenderString(self):
- return decodeHeader(self.get("from"), _("no sender"))
-
- def getSubject(self):
- return decodeHeader(self.get("subject"), _("no subject"))
+ self.session.openWithCallback(self._selectAttachmentCB, ChoiceBox, _("select Attachment"), list)
- def get(self, key, default=None):
- return self.message.get(key,failobj=default)
-
- def __str__(self):
- return "<MessageHeader uid="+str(self.uid)+", subject="+self.get("subject",_("no subject"))+">"
+ def _selectAttachmentCB(self, choice):
+ if choice:
+ if choice[1].getFilename():
+ debug("[ScreenMailView] Attachment selected: " + choice[1].getFilename())
+ else:
+ debug("[ScreenMailView] Attachment with no filename selected")
+ # nothing happens here. What shall we do now with the attachment?
############
class EmailBody:
def UTF8toUTF7(str):
return imap4.encoder(str.decode('utf-8'))[0]
-def main(session, **kwargs):
- import os,shutil
- if os.path.isfile('/usr/lib/python2.5/uu.py') is not True:
- shutil.copy('/usr/lib/enigma2/python/Plugins/Extensions/EmailClient/uu.py', '/usr/lib/python2.5/uu.py')
- global session2
- session2 = session
- 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)
- else:
- session.open(EmailScreen)
-
-def MessageCB(*args):
- global session2
- session2.open(EmailScreen)
class CheckMail:
- implements(imap4.IMailboxListener)
- _timer = None
-
- def __init__(self):
- debug('[CheckMail] __init__ on %s' %time.ctime())
- self.factory = createFactory(self, config.plugins.emailimap.username.value, config.plugins.emailimap.server.value, config.plugins.emailimap.port.value)
- if not self._timer:
- debug('[CheckMail] __init__ creatings timer: %s' %time.ctime())
- self._timer = eTimer()
- # self._timer.timeout.get().append(self._checkMail)
- self._timer.callback.append(self._checkMail)
- self._timer.start(config.plugins.emailimap.checkPeriod.value*60*1000) # it is minutes
+ def __init__(self, acc):
+ '''
+ Mail checker object for one account
+ @param acc: the account to be checked periodically, each account has
+ at most one checker
+ @type acc: EmailAccount
+ '''
+ import random
+ self._name = acc._name
+ self._account = acc
+ self._timer = eTimer()
+ self._timer.callback.append(self._checkMail)
+ # I guess, users tend to use identical intervals, so make them a bit different :-)
+ # constant stolen from ReconnectingFactory
+ self._interval = int(self._account._interval)*60*1000
+ self._interval = int(random.normalvariate(self._interval, self._interval * 0.11962656472))
+ debug("[CheckMail] %(name)s: __init__: checking all %(interval)s seconds"
+ %{'name':self._name, 'interval':self._interval/1000})
+ self._timer.start(self._interval) # it is minutes
self._unseenList = None
- self._proto = None
+ self._checkMail()
def exit(self):
- if self._proto:
- self._proto.logout()
- self._proto = None
+ debug("[CheckMail] %s: exit" %(self._name))
self._timer.stop()
- self.factory.stopFactory()
- def _checkMail(self):
- debug('[CheckMail] _checkMail on %s' %time.ctime())
- if self._proto:
- self._proto.search(imap4.Query(unseen=1)).addCallback(self._cbNotify).addErrback(self._ebNotify, _("cannot get list of new messages"))
- else:
- self.factory.retry()
+ def stopChecking(self):
+ '''
+ Just stop the timer, don't empty the unseenList.
+ '''
+ debug("[CheckMail] %s: stopChecking" %(self._name))
+ self._timer.stop()
- def _cbNotify(self, newUnseenList):
- def haveNotSeenBefore(messageNo): return messageNo not in self._unseenList
+ def reStartChecking(self):
+ '''
+ Start the timer again and immediately do a check.
+ '''
+ debug("[CheckMail] %s: reStartChecking" %(self._name))
+ self._timer.start(self._interval)
+ self._checkMail()
- debug("[CheckMail] _cbNotify newUnseenList: %s" %repr(newUnseenList))
+ def _checkMail(self):
+ # debug("[CheckMail] _checkMail for %s" %self._name)
+ self._account.getUnseenHeaders(self._filterNewUnseen)
+
+ def _filterNewUnseen(self, newUnseenList):
+ '''
+ Main method in this class: get the list of unseen messages
+ and check them against the last list. New unseen messages
+ are then displayed via _onHeaderList
+ @param newUnseenList: new list of unseen messages
+ '''
+ debug('[CheckMail] %s: _filterNewUnseen: %s' %(self._name, repr(newUnseenList)))
if self._unseenList is None:
- Notifications.AddNotification(MessageBox, str(len(newUnseenList)) + ' ' + _("unread messages in mailbox"), type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
+ debug('[CheckMail] %s: _filterNewUnseen: init' %(self._name))
+ # Notifications.AddNotification(MessageBox, str(len(newUnseenList)) + ' ' + _("unread messages in mailbox %s") %self._name, type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
else:
- newMessages = filter(haveNotSeenBefore, newUnseenList)
+ newMessages = filter(lambda x: x not in self._unseenList, newUnseenList)
if newMessages:
- debug("[CheckMail] _cbNotify newMessages: %s" %repr(newMessages))
+ debug("[CheckMail] %s: _filterNewUnseen: new message(s): %s" %(self._name, repr(newMessages)))
+ # construct MessageSet from list of message numbers
+ # newMessageSet = reduce(lambda x,y: y.add(x), newMessages, imap4.MessageSet())
newMessageSet = imap4.MessageSet()
- for messageNo in newMessages:
- newMessageSet.add(messageNo)
- self._proto.fetchHeaders(newMessageSet).addCallback(self._onHeaderList).addErrback(self._ebNotify, _("cannot get headers of new messages"))
+ for i in newMessages:
+ newMessageSet.add(i)
+ if not self._account.getHeaders(self._onHeaderList, newMessageSet):
+ debug("[CheckMail] %s: _filterNewUnseen: could not get Headers" %(self._name))
+
self._unseenList = newUnseenList
def _onHeaderList(self, headers):
- # debug("_onHeaderList headers: %s" %repr(headers))
- message = _("New mail arrived:\n\n")
+ '''
+ Notify about the list of headers.
+ @param headers: list of headers
+ '''
+ # debug("[CheckMail] _onHeaderList headers: %s" %repr(headers))
+ message = _("New mail arrived for account %s:\n\n") %self._name
for h in headers:
m = MessageHeader(h, headers[h]['RFC822.HEADER'])
message += m.getSenderString() + '\n' + m.getSubject() + '\n\n'
Notifications.AddNotification(MessageBox, message, type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
+class MessageHeader(object):
+ def __init__(self, uid, message):
+ self.uid = uid #must be int
+ self.message = email.Parser.Parser().parsestr(message) #@UndefinedVariable
+
+ def getSenderString(self):
+ return decodeHeader(self.get("from"), _("no sender"))
+
+ def getSubject(self):
+ return decodeHeader(self.get("subject"), _("no subject"))
+
+ def get(self, key, default=None):
+ return self.message.get(key,failobj=default)
+
+ def __str__(self):
+ return "<MessageHeader uid="+str(self.uid)+", subject="+self.getSubject()+">"
+
+class EmailAccount():
+ '''
+ Principal class to hold an account.
+ '''
+ implements(imap4.IMailboxListener)
+
+ def __init__(self, params, afterInit=None):
+ '''
+ Principal class to hold an account.
+ @param params: (name, server, port, user, password, interval, maxmail)
+ @param afterInit: to be called, when init is done. Needed to writeAccounts AFTER this one is added
+ '''
+ (self._name, self._server, self._port, self._user, self._password, self._interval, self._maxmail) = params
+ # debug("[EmailAccount] %s: __init__: %s" %(self._name, repr(params)))
+ self._factory = createFactory(self, self._user, self._server, int(self._port))
+ self._proto = None
+ self._mailChecker = None
+ self.inboxPos = 0
+ self.mailboxList = None
+ self._failureReason = ""
+ self._connectCallback = None
+ mailAccounts.append(self)
+ if afterInit:
+ afterInit()
+
+ def exit(self):
+ mailAccounts.remove(self)
+ # stop checker and get rid of it
+ self.stopChecker()
+ self._mailChecker = None
+ # stop factory and get rid of it
+ if self._factory:
+ self._factory.stopTrying()
+ self._factory = None # I am not sure to stop the factory, though...
+ # if we still have a proto, logout and dump it
+ if self._proto:
+ self._proto.logout()
+ self._proto = None
+
+ def isConnected(self):
+ return self._proto is not None and self.mailboxList is not None
+
+ def forceRetry(self, connectCallback):
+ '''
+ reset delays and retry
+ @param connectCallback: call this function on successful connect, used by EmailAccountList
+ '''
+ self._connectCallback = connectCallback
+ self._factory.resetDelay()
+ self._factory.retry()
+
+ def removeCallback(self):
+ self._connectCallback = None
+
+ def getConfig(self):
+ return (self._name, self._server, self._port, self._user, self._password, self._interval, self._maxmail)
+
def _ebNotify(self, result, where, what):
- debug("[CheckMail] _ebNotify error in %s: %s: %s" %(where, what, result.getErrorMessage()))
- Notifications.AddNotification(MessageBox, "EmailClient:\n\n" + what, type=MessageBox.TYPE_ERROR, timeout=config.plugins.emailimap.timeout.value)
- # self.exit()
+ debug("[EmailAccount] %s: _ebNotify error in %s: %s: %s" %(self._name, where, what, result.getErrorMessage()))
+ 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)
+
+ def startChecker(self):
+ # debug("[EmailAccount] %s: startChecker?" %self._name)
+ if config.plugins.emailimap.checkForNewMails.value:
+ # interval == 0 means: no checking for this account
+ if int(self._interval) != 0:
+ if self._mailChecker:
+ # so, we already have seen an unseenList
+ # debug("[EmailAccount] %s: startChecker again" %self._name)
+ self._mailChecker.reStartChecking()
+ else:
+ # debug("[EmailAccount] %s: startChecker new" %self._name)
+ self._mailChecker = CheckMail(self)
+
+ def stopChecker(self):
+ if self._mailChecker:
+ self._mailChecker.stopChecking()
+
+ def undelete(self, uid):
+ '''
+ undelete a message
+ @param uid: uid of message
+ '''
+ if self._proto:
+ self._proto.removeFlags(uid, ["\\Deleted"])
+ return True
+ else:
+ return False
+
+ def delete(self, uid):
+ '''
+ mark message as deleted
+ @param uid: uid of message
+ '''
+ if self._proto:
+ self._proto.addFlags(uid, ["\\Deleted"])
+ return True
+ else:
+ return False
+
+ def unread(self, uid):
+ '''
+ mark message as unread, remove \\Seen
+ @param uid: uis of message
+ '''
+ if self._proto:
+ self._proto.removeFlags(uid, ["\\Seen"])
+ return True
+ else:
+ return False
+
+ def getUnseenHeaders(self, callback):
+ # debug('[EmailAccount] %s: getUnseenHeaders' %self._name)
+ if self._proto:
+ self._proto.examine('inbox').addCallback(self._doSearchUnseen, callback).addErrback(self._ebNotify, "getUnseenHeaders", _("cannot access inbox"))
+ return True
+ else:
+ return False
+
+ def _doSearchUnseen(self, result, callback): #@UnusedVariable
+ # debug('[EmailAccount] %s: _doSearchUnseen' %(self._name))
+ self._proto.search(imap4.Query(unseen=1)).addCallback(callback).addErrback(self._ebNotify, '_doSearchUnseen', _("cannot get list of new messages"))
+
+ def getMessageList(self, callback, mbox):
+ if self._proto:
+ self._proto.select(mbox.decode('utf-8')).addCallback(self._onSelect, callback, mbox).addErrback(self._onSelectFailed, callback, mbox)
+ return True
+ else:
+ return False
+
+ def _onSelect(self, result, callback, mboxname):
+ # debug("[EmailAccount] _onExamine: " + str(result) + ' ' + mboxname)
+ numMessagesinFolder = int(result['EXISTS'])
+ if numMessagesinFolder <= 0:
+ callback([], [])
+ else:
+ if int(self._maxmail) > 0:
+ maxMessagesToFetch = int(self._maxmail)
+ startmsg = numMessagesinFolder-maxMessagesToFetch+1
+ if startmsg <= 0:
+ startmsg = 1
+ rangeToFetch = [startmsg, numMessagesinFolder]
+ else:
+ rangeToFetch = [1, numMessagesinFolder]
+ try:
+ self._rangeToFetch = rangeToFetch
+ self._proto.fetchFlags('%i:%i'%(rangeToFetch[0], rangeToFetch[1]) #'1:*'
+ ).addCallback(self._onFlagsList, callback)
+
+ except imap4.IllegalServerResponse, e:
+ debug("[EmailAccount] _onExamine exception: " + str(e))
+ callback([], [])
+
+ def _onSelectFailed(self, failure, callback, mboxname):
+ debug("[EmailAccount] %s: _onSelectFailed: %s %s" %(self._name, mboxname, str(failure)))
+ callback([], [])
+
+ def _onFlagsList(self, flagsList, callback):
+ self._proto.fetchHeaders('%i:%i'%(self._rangeToFetch[0], self._rangeToFetch[1]) #'1:*'
+ ).addCallback(callback, flagsList)
+
+ def getMessage(self, message, callback, errCallback):
+ debug("[EmailAccount] %s: getMessage: %s" %(self._name, str(message)))
+ if self._proto:
+ self._proto.fetchSize(message.uid
+ ).addCallback(self._onMessageSizeLoaded, message, callback, errCallback
+ ).addErrback(self._onMessageLoadFailed, message, callback, errCallback
+ )
+ return True
+ else:
+ return False
- def _cbOk(self, result):
- debug("[CheckMail] _cbOk result: %s" %repr(result))
+ def _onMessageSizeLoaded(self, result, message, callback, errCallback):
+ debug("[EmailAccount] %s: _onMessageSizeLoaded: %s %s" %(self._name, str(result), str(message)))
+ size = int(result[message.uid]['RFC822.SIZE'])
+ self.MAX_MESSAGE_SIZE_TO_OPEN = 4000000
+ if size >= self.MAX_MESSAGE_SIZE_TO_OPEN:
+ #ask here to open message
+ debug("[EmailAccount] _onMessageSizeLoaded: message to large to open (size=%d)" %size)
+ errCallback('', _("message too large"))
+ else:
+ self._proto.fetchMessage(message.uid
+ ).addCallback(callback, message,
+ ).addErrback(self._onMessageLoadFailed, message, callback, errCallback
+ )
+
+ def _onMessageLoadFailed(self, failure, message, callback, errCallback):
+ debug("[EmailAccount] %s: onMessageLoadFailed: %s %s" %(self._name, str(failure), str(message)))
+ errCallback('', _("failed to load message") + ': ' + failure.getErrorMessage())
+
+ def getHeaders(self, callback, messageSet):
+ debug('[EmailAccount] %s: getHeaders' %self._name)
+ if self._proto:
+ self._proto.fetchHeaders(messageSet).addCallback(callback).addErrback(self._ebNotify, 'getHeaders', _("cannot get headers of new messages"))
+ return True
+ else:
+ return False
def onConnect(self, proto):
- debug('[CheckMail] onConnect ' + str(proto))
+ debug("[EmailAccount] %s: %s@%s:%s: onConnect" %(self._name, self._user, self._server, self._port))
+ self._factory.resetDelay()
self._proto = proto
- proto.getCapabilities().addCallback(self._cbCapabilities).addErrback(self._ebNotify, "getCapabilities", _("cannot get capabilities of mailserver"))
+ self._failureReason = ""
+ try:
+ self._connectCallback()
+ except:
+ pass # for the unlikely case, the screen does not exist anymore
+ self._connectCallback = None
+ proto.getCapabilities().addCallback(self._cbCapabilities).addErrback(self._ebCapabilities)
+
+ def onConnectionFailed(self, reason):
+ debug("[EmailAccount] %s@%s:%s: onConnectFailed: %s" %(self._user, self._server, self._port, reason.getErrorMessage()))
+ reasonString = reason.getErrorMessage()
+ if reasonString != self._failureReason:
+ self._ebNotify(reason, 'onConnectionFailed', _("connection failed - retrying")+'\n'+reason.getErrorMessage())
+ self._failureReason = reasonString
+ self._proto = None
+ # self.stopChecker() not necessary, because we don't have an active connection...
- def onConnectFailed(self, reason):
- debug('[CheckMail] onConnectFailed: ' + reason.getErrorMessage())
- self._ebNotify(reason, "onConnectFailed", _("connection to %(server)s:%(port)d failed:\n%(reason)s") %{'server':config.plugins.emailimap.server.value,'port':config.plugins.emailimap.port.value,'reason':reason.getErrorMessage()})
+ def onConnectionLost(self, reason):
+ debug("[EmailAccount] %s@%s:%s: onConnectFailed: %s" %(self._user, self._server, self._port, reason.getErrorMessage()))
+ # too noisy... self._ebNotify(reason, 'onConnectionLost', _("connection lost - retrying"))
self._proto = None
+ self.stopChecker()
def _cbCapabilities(self,reason):
- debug("[CheckMail] _cbCapabilities\n\
+ debug(_("[EmailAccount] %(name)s: _cbCapabilities:\n\
####################################################################################################\n\
# If you have problems to log into your imap-server, please send me the output of the following line\n\
-# cbCapabilities: " + str(reason) +"\n\
+# cbCapabilities: %(capa)s\n\
####################################################################################################\n")
+ %{'name':self._name, 'capa':str(reason)})
self._doLogin()
-
+
+ def _ebCapabilities(self,reason):
+ debug("[EmailAccount] %s: _ebCapabilities: %s" %(self._name, str(reason)))
+
def _doLogin(self):
- useTLS = False #True
- if useTLS:
- #d = self._proto.startTLS().addCallback(self._proto.authenticate, config.plugins.emailimap.password.value)
- d = self._proto.startTLS().addCallback(self._proto.authenticate) # don't know, why authenticate wants no param...
- else:
- d = self._proto.authenticate(config.plugins.emailimap.password.value)
- d.addCallback(self._onAuthentication).addErrback(self._onAuthenticationFailed)
-
+ debug("[EmailAccount] %s: _doLogin secure" %(self._name))
+ d = self._proto.authenticate(self._password)
+ d.addCallback(self._onAuthentication)
+ d.addErrback(self._onAuthenticationFailed)
+ return d
+
def _onAuthentication(self, result):
- debug("[CheckMail] onAuthentication: logged in")
- self._proto.examine('inbox').addCallback(self._cbOk).addErrback(self._ebNotify, "examine", _("cannot access inbox"))
- self._checkMail()
+ # better use LSUB here to get only the subscribed to mailboxes
+ debug("[EmailAccount] %s: _onAuthentication: %s" %(self._name, str(result)))
+ self.startChecker()
+ self._proto.lsub("", "*").addCallback(self._onMailboxList)
def _onAuthenticationFailed(self, failure):
# If it failed because no SASL mechanisms match
- debug("[CheckMail] onAuthenticationFailed: " + failure.getErrorMessage())
+ debug("[EmailAccount] %s: onAuthenticationFailed: %s" %(self._name, failure.getErrorMessage()))
try:
failure.trap(imap4.NoSupportedAuthentication)
self._doLoginInsecure()
except Exception,e:
- debug("[CheckMail] onAuthenticationFailed: %s" %str(e))
+ debug("[EmailAccount] %s: _onAuthenticationFailed: %s" %(self._name, e.message))
+ print e,e.message
def _doLoginInsecure(self):
- debug("[CheckMail] doLoginInsecure")
- self._proto.login(config.plugins.emailimap.username.value, config.plugins.emailimap.password.value
- ).addCallback(self._onAuthentication).addErrback(self._ebNotify, "login", _("login failed"))
+ debug("[EmailAccount] %s: _doLoginInsecure" %(self._name))
+ self._proto.login(self._user, self._password).addCallback(self._onAuthentication).addErrback(self._onInsecureAuthenticationFailed)
+
+ def _onInsecureAuthenticationFailed(self, failure):
+ debug("[EmailAccount] %s: _onInsecureAuthenticationFailed: %s" %(self._name, failure.getErrorMessage()))
+ self._proto = None
+ Notifications.AddNotification(
+ MessageBox,
+ _("error logging %(who)s in:\n%(failure)s")
+ %{
+ 'who':"%s@%s" %(self._user, self._server),
+ 'failure':failure.getErrorMessage()
+ }, type=MessageBox.TYPE_ERROR, timeout=config.plugins.emailimap.timeout.value)
+
+ def _onMailboxList(self, result):
+ list = [UTF7toUTF8(mb[2]).encode('utf-8') for mb in result if '\\Noselect' not in mb[0]]
+ debug("[EmailAccount] %s: onMailboxList: %s selectable mailboxes" %(self._name, len(list)))
+ list.sort()
+ try:
+ self.inboxPos = map(lambda x: x.lower(), list).index('inbox')+1
+ except:
+ debug("[EmailAccount] onMailboxList: no inbox?!?!")
+ list = ['INBOX']
+ self.inboxPos = 1
+ self.mailboxList = list
+
+class EmailAccountList(Screen):
+
+ def __init__(self, session):
+ '''
+ Entry screen holding the list of accounts.
+ Offering to add, edit or remove one. Also configuration through <menu>
+ '''
+ debug("[EmailAccountList] __init__")
+ noButtons = 3
+ width = max(noButtons*140+35+100, DESKTOP_WIDTH/3)
+ self.width = width
+ height = max(5*30+50, DESKTOP_HEIGHT/3)
+ buttonsGap = (width-(noButtons)*140-35)/(noButtons+2)
+ self.skin = """
+ <screen position="%d,%d" size="%d,%d" title="Accounts list" >
+ <widget name="accounts" position="0,0" size="%d,%d" scrollbarMode="showOnDemand" />
+ <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+ <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
+ <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+ <ePixmap position="%d,%d" size="35,25" pixmap="skin_default/buttons/key_menu.png" alphatest="on" />
+ <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" />
+ <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" />
+ <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" />
+ </screen>""" %(
+ (DESKTOP_WIDTH-width)/2, (DESKTOP_HEIGHT-height)/2, width, height,
+ width, height, # config
+ buttonsGap, height-45,
+ 2*buttonsGap+140, height-45,
+ 3*buttonsGap+2*140, height-45,
+ 4*buttonsGap+3*140, height-38,
+ buttonsGap, height-45, scaleV(22,18),
+ 2*buttonsGap+140, height-45, scaleV(22,18),
+ 3*buttonsGap+2*140, height-45, scaleV(22,18)
+ )
+ Screen.__init__(self, session)
+ self["buttonred"] = Label(_("remove"))
+ self["buttongreen"] = Label(_("add"))
+ self["buttonyellow"] = Label(_("edit"))
+ self["setupActions"] = ActionMap(["ColorActions", "OkCancelActions", "MenuActions"],
+ {
+ "menu": self._config,
+ "red": self._remove,
+ "green": self._add,
+ "yellow": self._edit,
+ "cancel": self._exit,
+ "ok": self._action,
+ }, -2)
+ for acc in mailAccounts:
+ if not acc.isConnected():
+ acc.forceRetry(self._layoutFinish)
+ self["accounts"] = MenuList([], content=eListboxPythonMultiContent)
+ self["accounts"].l.setItemHeight(scaleV(20,18)+5)
+ self["accounts"].l.setFont(0, gFont("Regular", scaleV(20,18)))
+ self.onLayoutFinish.append(self._layoutFinish)
+
+ def _layoutFinish(self):
+ self.setTitle(_("Accounts list"))
+ accList = []
+ for acc in mailAccounts:
+ if acc.isConnected():
+ color = 0x00FFFFFF
+ else:
+ color = 0x00888888
+ accList.append([acc, MultiContentEntryText(pos=(0, 0), size=(self.width,scaleV(20,18)+5), text=acc._name, color=color, color_sel=color)])
+ self["accounts"].l.setList(accList)
+
+ def _config(self):
+ debug("[EmailAccountList] _config")
+ self.session.open(EmailConfigOptions, "Rev " + "$Revision$"[11: - 1] + "$Date$"[7:23])
+
+ def _action(self):
+ if self["accounts"].getCurrent():
+ debug("[EmailAccountList] _action: %s" %self["accounts"].getCurrent()[0]._name)
+ account = self["accounts"].getCurrent()[0]
+ if account and account.isConnected():
+ self.session.open(EmailScreen, account)
+ self._layoutFinish()
+ else:
+ self.session.open(MessageBox,
+ _("account %s is not connected") %self["accounts"].getCurrent()[0]._name,
+ type=MessageBox.TYPE_INFO,
+ timeout=config.plugins.emailimap.timeout.value)
+ else:
+ debug("[EmailAccountList] _action: no account selected")
+ self.session.open(MessageBox,
+ _("no account selected"),
+ type=MessageBox.TYPE_ERROR,
+ timeout=config.plugins.emailimap.timeout.value)
+
+ def _add(self):
+ debug("[EmailAccountList] _add")
+ self.session.openWithCallback(self._cbAdd, EmailConfigAccount)
+
+ def _cbAdd(self, params):
+ if params:
+ EmailAccount(params, writeAccounts)
+ self._layoutFinish()
+
+ def _edit(self):
+ debug("[EmailAccountList] _edit")
+ if self["accounts"].getCurrent():
+ self.session.openWithCallback(self._cbEdit, EmailConfigAccount, self["accounts"].getCurrent()[0].getConfig())
+ else:
+ self.session.openWithCallback(self._cbAdd, EmailConfigAccount)
-mailChecker = None
-def autostart(reason, **kwargs):
- debug("[EmailClient] - Autostart")
- global mailChecker
- if config.plugins.emailimap.checkForNewMails.value and not mailChecker:
- mailChecker = CheckMail()
+ def _cbEdit(self, params):
+ if params:
+ self["accounts"].getCurrent()[0].exit()
+ EmailAccount(params, writeAccounts)
+ self._layoutFinish()
+
+ def _remove(self):
+ debug("[EmailAccountList] _remove")
+ if self["accounts"].getCurrent():
+ self.session.openWithCallback(
+ self._cbRemove,
+ MessageBox,
+ _("Really delete account %s?") % self["accounts"].getCurrent()[0]._name)
+ else:
+ self.session.open(MessageBox,
+ _("no account selected"),
+ type=MessageBox.TYPE_ERROR,
+ timeout=config.plugins.emailimap.timeout.value)
+
+ def _cbRemove(self, ret):
+ if ret:
+ self["accounts"].getCurrent()[0].exit()
+ writeAccounts()
+ self._layoutFinish()
+
+ def _exit(self):
+ for acc in mailAccounts:
+ acc.removeCallback()
+ self.close()
+
+from Tools.Directories import resolveFilename, SCOPE_SYSETC, SCOPE_CONFIG
+import csv
+MAILCONF = resolveFilename(SCOPE_CONFIG, "EmailClient.csv")
+
+def writeAccounts():
+ global mailAccounts
+ fd = open(MAILCONF, 'w')
+ out = csv.writer(fd, quotechar='"')
+ for acc in mailAccounts:
+ out.writerow(acc.getConfig())
+ fd.close()
+
+def getAccounts():
+ debug("[] getAccounts")
+
+ mailAccountsParams = []
+ if not os.path.exists(MAILCONF):
+ MAILCONF_XML = resolveFilename(SCOPE_SYSETC, "mailconf.xml")
+ debug("[] getAccounts: check for %s" %MAILCONF_XML)
+ if os.path.exists(MAILCONF_XML):
+ from xml.dom.minidom import parse
+ Notifications.AddNotification(MessageBox, _("importing configurations from %s") %MAILCONF_XML, type=MessageBox.TYPE_INFO, timeout=config.plugins.emailimap.timeout.value)
+ maildom = parse(MAILCONF_XML)
+ for top in maildom.getElementsByTagName("list"):
+ for acc in top.getElementsByTagName("account"):
+ name = str(acc.getElementsByTagName("name")[0].childNodes[0].data)
+ server = str(acc.getElementsByTagName("server")[0].childNodes[0].data)
+ port = str(acc.getElementsByTagName("port")[0].childNodes[0].data)
+ user = str(acc.getElementsByTagName("user")[0].childNodes[0].data)
+ password = str(acc.getElementsByTagName("pass")[0].childNodes[0].data)
+ interval = str(acc.getElementsByTagName("interval")[0].childNodes[0].data)
+ maxmail = str(acc.getElementsByTagName("MaxMail")[0].childNodes[0].data)
+
+ debug("[] getAccounts: %s, %s, %s, %s, %s, %s, %s"
+ %(name, server, port, user, password, interval, maxmail)
+ )
+ mailAccountsParams.append((str(name), (name, server, port, user, password, interval, maxmail)))
+ else:
+ debug("[] getAccounts: no file found, exiting")
+ else:
+ debug("[] getAccounts: reading %s" %MAILCONF)
+ fd = open(MAILCONF)
+ accounts = csv.reader(fd, quotechar='"')
+ for acc in accounts:
+ mailAccountsParams.append(acc)
+ fd.close()
+
+ return mailAccountsParams
+
+def main(session, **kwargs): #@UnusedVariable kwargs
+ session.open(EmailAccountList)
+
+def autostart(reason, **kwargs): #@UnusedVariable reason
+ debug("[EmailClient] - Autostart reason: %d kwargs: %s" %(reason,repr(kwargs)))
+ debug("[EmailClient] " + "$Revision$"[1:-1] + "$Date$"[7:23] + " starting")
+ # ouch, this is a hack
+ if kwargs.has_key("session"):
+ global my_global_session
+ my_global_session = kwargs["session"]
+ # return
+
+ if reason == 0:
+ for acc in getAccounts():
+ debug("[EmailClient] - Autostart: add account %s" %acc[0])
+ EmailAccount(acc)
+ else:
+ for acc in mailAccounts:
+ acc.exit()
initLog()
-def Plugins(path, **kwargs):
+def Plugins(path, **kwargs): #@UnusedVariable kwargs
return [
PluginDescriptor(name=_("Email Client"), description=_("view Emails via IMAP4"),
where = PluginDescriptor.WHERE_PLUGINMENU,