comparison sat/plugins/plugin_misc_imap.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 56f94936df1e
children
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SàT plugin for managing imap server 4 # SàT plugin for managing imap server
5 # Copyright (C) 2011 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2011 Jérôme Poisson (goffi@goffi.org)
6 6
27 from twisted.cred import error as cred_error 27 from twisted.cred import error as cred_error
28 from twisted.mail import imap4 28 from twisted.mail import imap4
29 from twisted.python import failure 29 from twisted.python import failure
30 from email.parser import Parser 30 from email.parser import Parser
31 import os 31 import os
32 from cStringIO import StringIO 32 from io import StringIO
33 from twisted.internet import reactor 33 from twisted.internet import reactor
34 34
35 from zope.interface import implements 35 from zope.interface import implementer
36 36
37 PLUGIN_INFO = { 37 PLUGIN_INFO = {
38 C.PI_NAME: "IMAP server Plugin", 38 C.PI_NAME: "IMAP server Plugin",
39 C.PI_IMPORT_NAME: "IMAP", 39 C.PI_IMPORT_NAME: "IMAP",
40 C.PI_TYPE: "Misc", 40 C.PI_TYPE: "Misc",
73 73
74 self.server_factory = ImapServerFactory(self.host) 74 self.server_factory = ImapServerFactory(self.host)
75 reactor.listenTCP(port, self.server_factory) 75 reactor.listenTCP(port, self.server_factory)
76 76
77 77
78 @implementer(imap4.IMessage)
78 class Message(object): 79 class Message(object):
79 implements(imap4.IMessage)
80 80
81 def __init__(self, uid, flags, mess_fp): 81 def __init__(self, uid, flags, mess_fp):
82 log.debug("Message Init") 82 log.debug("Message Init")
83 self.uid = uid 83 self.uid = uid
84 self.flags = flags 84 self.flags = flags
110 @param names: The names of the headers to retrieve or omit. 110 @param names: The names of the headers to retrieve or omit.
111 @param negate: If True, indicates that the headers listed in names 111 @param negate: If True, indicates that the headers listed in names
112 should be omitted from the return value, rather than included. 112 should be omitted from the return value, rather than included.
113 @return: A mapping of header field names to header field values 113 @return: A mapping of header field names to header field values
114 """ 114 """
115 log.debug(u"getHeaders %s - %s" % (negate, names)) 115 log.debug("getHeaders %s - %s" % (negate, names))
116 final_dict = {} 116 final_dict = {}
117 to_check = [name.lower() for name in names] 117 to_check = [name.lower() for name in names]
118 for header in self.message.keys(): 118 for header in list(self.message.keys()):
119 if (negate and not header.lower() in to_check) or ( 119 if (negate and not header.lower() in to_check) or (
120 not negate and header.lower() in to_check 120 not negate and header.lower() in to_check
121 ): 121 ):
122 final_dict[header] = self.message[header] 122 final_dict[header] = self.message[header]
123 return final_dict 123 return final_dict
148 """ 148 """
149 log.debug("getSubPart") 149 log.debug("getSubPart")
150 return TypeError 150 return TypeError
151 151
152 152
153 @implementer(imap4.IMailbox)
153 class SatMailbox(object): 154 class SatMailbox(object):
154 implements(imap4.IMailbox)
155 155
156 def __init__(self, host, name, profile): 156 def __init__(self, host, name, profile):
157 self.host = host 157 self.host = host
158 self.listeners = set() 158 self.listeners = set()
159 log.debug(u"Mailbox init (%s)" % name) 159 log.debug("Mailbox init (%s)" % name)
160 if name != "INBOX": 160 if name != "INBOX":
161 raise imap4.MailboxException("Only INBOX is managed for the moment") 161 raise imap4.MailboxException("Only INBOX is managed for the moment")
162 self.mailbox = self.host.plugins["Maildir"].accessMessageBox( 162 self.mailbox = self.host.plugins["Maildir"].accessMessageBox(
163 name, self.messageNew, profile 163 name, self.messageNew, profile
164 ) 164 )
185 def getUID(self, message): 185 def getUID(self, message):
186 """Return the UID of a message in the mailbox 186 """Return the UID of a message in the mailbox
187 @param message: The message sequence number 187 @param message: The message sequence number
188 @return: The UID of the message. 188 @return: The UID of the message.
189 """ 189 """
190 log.debug(u"getUID (%i)" % message) 190 log.debug("getUID (%i)" % message)
191 # return self.mailbox.getUid(message-1) #XXX: it seems that this method get uid and not message sequence number 191 # return self.mailbox.getUid(message-1) #XXX: it seems that this method get uid and not message sequence number
192 return message 192 return message
193 193
194 def getMessageCount(self): 194 def getMessageCount(self):
195 """Return the number of messages in this mailbox. 195 """Return the number of messages in this mailbox.
241 241
242 @type listener: Any object which implements C{IMailboxListener} 242 @type listener: Any object which implements C{IMailboxListener}
243 @param listener: An object to add to the set of those which will 243 @param listener: An object to add to the set of those which will
244 be notified when the contents of this mailbox change. 244 be notified when the contents of this mailbox change.
245 """ 245 """
246 log.debug(u"addListener %s" % listener) 246 log.debug("addListener %s" % listener)
247 self.listeners.add(listener) 247 self.listeners.add(listener)
248 248
249 def removeListener(self, listener): 249 def removeListener(self, listener):
250 """Remove a mailbox change listener 250 """Remove a mailbox change listener
251 251
286 """Retrieve one or more messages. 286 """Retrieve one or more messages.
287 @param messages: The identifiers of messages to retrieve information 287 @param messages: The identifiers of messages to retrieve information
288 about 288 about
289 @param uid: If true, the IDs specified in the query are UIDs; 289 @param uid: If true, the IDs specified in the query are UIDs;
290 """ 290 """
291 log.debug(u"fetch (%s, %s)" % (messages, uid)) 291 log.debug("fetch (%s, %s)" % (messages, uid))
292 if uid: 292 if uid:
293 messages.last = self.mailbox.getMaxUid() 293 messages.last = self.mailbox.getMaxUid()
294 messages.getnext = self.mailbox.getNextExistingUid 294 messages.getnext = self.mailbox.getNextExistingUid
295 for mess_uid in messages: 295 for mess_uid in messages:
296 if mess_uid is None: 296 if mess_uid is None:
410 410
411 def _emptyMailbox(self, name, id): 411 def _emptyMailbox(self, name, id):
412 return SatMailbox(self.host, name, self.profile) 412 return SatMailbox(self.host, name, self.profile)
413 413
414 414
415 @implementer(portal.IRealm)
415 class ImapRealm(object): 416 class ImapRealm(object):
416 implements(portal.IRealm)
417 417
418 def __init__(self, host): 418 def __init__(self, host):
419 self.host = host 419 self.host = host
420 420
421 def requestAvatar(self, avatarID, mind, *interfaces): 421 def requestAvatar(self, avatarID, mind, *interfaces):
422 log.debug("requestAvatar") 422 log.debug("requestAvatar")
423 profile = avatarID.decode("utf-8") 423 profile = avatarID
424 if imap4.IAccount not in interfaces: 424 if imap4.IAccount not in interfaces:
425 raise NotImplementedError 425 raise NotImplementedError
426 return imap4.IAccount, ImapSatAccount(self.host, profile), lambda: None 426 return imap4.IAccount, ImapSatAccount(self.host, profile), lambda: None
427 427
428 428
429 @implementer(checkers.ICredentialsChecker)
429 class SatProfileCredentialChecker(object): 430 class SatProfileCredentialChecker(object):
430 """ 431 """
431 This credential checker check against SàT's profile and associated jabber's password 432 This credential checker check against SàT's profile and associated jabber's password
432 Check if the profile exists, and if the password is OK 433 Check if the profile exists, and if the password is OK
433 Return the profile as avatarId 434 Return the profile as avatarId
434 """ 435 """
435 436
436 implements(checkers.ICredentialsChecker)
437 credentialInterfaces = ( 437 credentialInterfaces = (
438 credentials.IUsernamePassword, 438 credentials.IUsernamePassword,
439 credentials.IUsernameHashedPassword, 439 credentials.IUsernameHashedPassword,
440 ) 440 )
441 441
468 468
469 def startedConnecting(self, connector): 469 def startedConnecting(self, connector):
470 log.debug(_("IMAP server connection started")) 470 log.debug(_("IMAP server connection started"))
471 471
472 def clientConnectionLost(self, connector, reason): 472 def clientConnectionLost(self, connector, reason):
473 log.debug(_(u"IMAP server connection lost (reason: %s)"), reason) 473 log.debug(_("IMAP server connection lost (reason: %s)"), reason)
474 474
475 def buildProtocol(self, addr): 475 def buildProtocol(self, addr):
476 log.debug("Building protocol") 476 log.debug("Building protocol")
477 prot = protocol.ServerFactory.buildProtocol(self, addr) 477 prot = protocol.ServerFactory.buildProtocol(self, addr)
478 prot.portal = portal.Portal(ImapRealm(self.host)) 478 prot.portal = portal.Portal(ImapRealm(self.host))