Mercurial > libervia-backend
comparison src/plugins/plugin_misc_imap.py @ 594:e629371a28d3
Fix pep8 support in src/plugins.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Fri, 18 Jan 2013 17:55:35 +0100 |
parents | beaf6bec2fcd |
children | 84a6e83157c2 |
comparison
equal
deleted
inserted
replaced
593:70bae685d05c | 594:e629371a28d3 |
---|---|
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 """ | 20 """ |
21 | 21 |
22 from logging import debug, info, error | 22 from logging import debug, info, error |
23 import warnings | 23 import warnings |
24 from twisted.internet import protocol,defer | 24 from twisted.internet import protocol, defer |
25 from twisted.words.protocols.jabber import error as jab_error | 25 from twisted.words.protocols.jabber import error as jab_error |
26 from twisted.cred import portal,checkers,credentials | 26 from twisted.cred import portal, checkers, credentials |
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 email.message | 31 import email.message |
32 import os,os.path | 32 import os |
33 from cStringIO import StringIO | 33 from cStringIO import StringIO |
34 from twisted.internet import reactor | 34 from twisted.internet import reactor |
35 import pdb | 35 import pdb |
36 | 36 |
37 | |
38 from zope.interface import implements | 37 from zope.interface import implements |
39 | 38 |
40 | |
41 PLUGIN_INFO = { | 39 PLUGIN_INFO = { |
42 "name": "IMAP server Plugin", | 40 "name": "IMAP server Plugin", |
43 "import_name": "IMAP", | 41 "import_name": "IMAP", |
44 "type": "Misc", | 42 "type": "Misc", |
45 "protocols": [], | 43 "protocols": [], |
46 "dependencies": ["Maildir"], | 44 "dependencies": ["Maildir"], |
47 "main": "IMAP_server", | 45 "main": "IMAP_server", |
48 "handler": "no", | 46 "handler": "no", |
49 "description": _("""Create an Imap server that you can use to read your "normal" type messages""") | 47 "description": _("""Create an Imap server that you can use to read your "normal" type messages""") |
50 } | 48 } |
49 | |
51 | 50 |
52 class IMAP_server(object): | 51 class IMAP_server(object): |
53 #TODO: connect profile on mailbox request, once password is accepted | 52 #TODO: connect profile on mailbox request, once password is accepted |
54 | 53 |
55 params = """ | 54 params = """ |
73 info(_("Launching IMAP server on port %d"), port) | 72 info(_("Launching IMAP server on port %d"), port) |
74 | 73 |
75 self.server_factory = ImapServerFactory(self.host) | 74 self.server_factory = ImapServerFactory(self.host) |
76 reactor.listenTCP(port, self.server_factory) | 75 reactor.listenTCP(port, self.server_factory) |
77 | 76 |
77 | |
78 class Message(object): | 78 class Message(object): |
79 implements(imap4.IMessage) | 79 implements(imap4.IMessage) |
80 | 80 |
81 def __init__(self, uid, flags, mess_fp): | 81 def __init__(self, uid, flags, mess_fp): |
82 debug('Message Init') | 82 debug('Message Init') |
83 self.uid=uid | 83 self.uid = uid |
84 self.flags=flags | 84 self.flags = flags |
85 self.mess_fp=mess_fp | 85 self.mess_fp = mess_fp |
86 self.message=Parser().parse(mess_fp) | 86 self.message = Parser().parse(mess_fp) |
87 | 87 |
88 def getUID(self): | 88 def getUID(self): |
89 """Retrieve the unique identifier associated with this message. | 89 """Retrieve the unique identifier associated with this message. |
90 """ | 90 """ |
91 debug('getUID (message)') | 91 debug('getUID (message)') |
102 """Retrieve the date internally associated with this message. | 102 """Retrieve the date internally associated with this message. |
103 @return: An RFC822-formatted date string. | 103 @return: An RFC822-formatted date string. |
104 """ | 104 """ |
105 debug('getInternalDate') | 105 debug('getInternalDate') |
106 return self.message['Date'] | 106 return self.message['Date'] |
107 | |
108 | 107 |
109 def getHeaders(self, negate, *names): | 108 def getHeaders(self, negate, *names): |
110 """Retrieve a group of message headers. | 109 """Retrieve a group of message headers. |
111 @param names: The names of the headers to retrieve or omit. | 110 @param names: The names of the headers to retrieve or omit. |
112 @param negate: If True, indicates that the headers listed in names | 111 @param negate: If True, indicates that the headers listed in names |
113 should be omitted from the return value, rather than included. | 112 should be omitted from the return value, rather than included. |
114 @return: A mapping of header field names to header field values | 113 @return: A mapping of header field names to header field values |
115 """ | 114 """ |
116 debug('getHeaders %s - %s' % (negate, names)) | 115 debug('getHeaders %s - %s' % (negate, names)) |
117 final_dict={} | 116 final_dict = {} |
118 to_check=[name.lower() for name in names] | 117 to_check = [name.lower() for name in names] |
119 for header in self.message.keys(): | 118 for header in self.message.keys(): |
120 if (negate and not header.lower() in to_check) or \ | 119 if (negate and not header.lower() in to_check) or \ |
121 (not negate and header.lower() in to_check): | 120 (not negate and header.lower() in to_check): |
122 final_dict[header]=self.message[header] | 121 final_dict[header] = self.message[header] |
123 return final_dict | 122 return final_dict |
124 | 123 |
125 def getBodyFile(self): | 124 def getBodyFile(self): |
126 """Retrieve a file object containing only the body of this message. | 125 """Retrieve a file object containing only the body of this message. |
127 """ | 126 """ |
130 | 129 |
131 def getSize(self): | 130 def getSize(self): |
132 """Retrieve the total size, in octets, of this message. | 131 """Retrieve the total size, in octets, of this message. |
133 """ | 132 """ |
134 debug('getSize') | 133 debug('getSize') |
135 self.mess_fp.seek(0,os.SEEK_END) | 134 self.mess_fp.seek(0, os.SEEK_END) |
136 return self.mess_fp.tell() | 135 return self.mess_fp.tell() |
137 | |
138 | 136 |
139 def isMultipart(self): | 137 def isMultipart(self): |
140 """Indicate whether this message has subparts. | 138 """Indicate whether this message has subparts. |
141 """ | 139 """ |
142 debug('isMultipart') | 140 debug('isMultipart') |
143 return False | 141 return False |
144 | 142 |
145 def getSubPart(self,part): | 143 def getSubPart(self, part): |
146 """Retrieve a MIME sub-message | 144 """Retrieve a MIME sub-message |
147 @param part: The number of the part to retrieve, indexed from 0. | 145 @param part: The number of the part to retrieve, indexed from 0. |
148 @return: The specified sub-part. | 146 @return: The specified sub-part. |
149 """ | 147 """ |
150 debug('getSubPart') | 148 debug('getSubPart') |
152 | 150 |
153 | 151 |
154 class SatMailbox(object): | 152 class SatMailbox(object): |
155 implements(imap4.IMailbox) | 153 implements(imap4.IMailbox) |
156 | 154 |
157 def __init__(self,host,name,profile): | 155 def __init__(self, host, name, profile): |
158 self.host = host | 156 self.host = host |
159 self.listeners=set() | 157 self.listeners = set() |
160 debug ('Mailbox init (%s)', name) | 158 debug('Mailbox init (%s)', name) |
161 if name!="INBOX": | 159 if name != "INBOX": |
162 raise imap4.MailboxException("Only INBOX is managed for the moment") | 160 raise imap4.MailboxException("Only INBOX is managed for the moment") |
163 self.mailbox=self.host.plugins["Maildir"].accessMessageBox(name,self.newMessage, profile) | 161 self.mailbox = self.host.plugins["Maildir"].accessMessageBox(name, self.newMessage, profile) |
164 | 162 |
165 def newMessage(self): | 163 def newMessage(self): |
166 """Called when a new message is in the mailbox""" | 164 """Called when a new message is in the mailbox""" |
167 debug ("newMessage signal received") | 165 debug("newMessage signal received") |
168 nb_messages=self.getMessageCount() | 166 nb_messages = self.getMessageCount() |
169 for listener in self.listeners: | 167 for listener in self.listeners: |
170 listener.newMessages(nb_messages,None) | 168 listener.newMessages(nb_messages, None) |
171 | 169 |
172 def getUIDValidity(self): | 170 def getUIDValidity(self): |
173 """Return the unique validity identifier for this mailbox. | 171 """Return the unique validity identifier for this mailbox. |
174 """ | 172 """ |
175 debug ('getUIDValidity') | 173 debug('getUIDValidity') |
176 return 0 | 174 return 0 |
177 | 175 |
178 def getUIDNext(self): | 176 def getUIDNext(self): |
179 """Return the likely UID for the next message added to this mailbox. | 177 """Return the likely UID for the next message added to this mailbox. |
180 """ | 178 """ |
181 debug ('getUIDNext') | 179 debug('getUIDNext') |
182 return self.mailbox.getNextUid() | 180 return self.mailbox.getNextUid() |
183 | 181 |
184 def getUID(self,message): | 182 def getUID(self, message): |
185 """Return the UID of a message in the mailbox | 183 """Return the UID of a message in the mailbox |
186 @param message: The message sequence number | 184 @param message: The message sequence number |
187 @return: The UID of the message. | 185 @return: The UID of the message. |
188 """ | 186 """ |
189 debug ('getUID (%i)' % message) | 187 debug('getUID (%i)' % message) |
190 #return self.mailbox.getUid(message-1) #XXX: it seems that this method get uid and not message sequence number | 188 #return self.mailbox.getUid(message-1) #XXX: it seems that this method get uid and not message sequence number |
191 return message | 189 return message |
192 | 190 |
193 def getMessageCount(self): | 191 def getMessageCount(self): |
194 """Return the number of messages in this mailbox. | 192 """Return the number of messages in this mailbox. |
206 | 204 |
207 def getUnseenCount(self): | 205 def getUnseenCount(self): |
208 """Return the number of messages with the 'Unseen' flag. | 206 """Return the number of messages with the 'Unseen' flag. |
209 """ | 207 """ |
210 debug('getUnseenCount') | 208 debug('getUnseenCount') |
211 return self.getMessageCount()-len(self.mailbox.getMessageIdsWithFlag('\\SEEN')) | 209 return self.getMessageCount() - len(self.mailbox.getMessageIdsWithFlag('\\SEEN')) |
212 | 210 |
213 def isWriteable(self): | 211 def isWriteable(self): |
214 """Get the read/write status of the mailbox. | 212 """Get the read/write status of the mailbox. |
215 @return: A true value if write permission is allowed, a false value otherwise. | 213 @return: A true value if write permission is allowed, a false value otherwise. |
216 """ | 214 """ |
219 | 217 |
220 def destroy(self): | 218 def destroy(self): |
221 """Called before this mailbox is deleted, permanently. | 219 """Called before this mailbox is deleted, permanently. |
222 """ | 220 """ |
223 debug('destroy') | 221 debug('destroy') |
224 | |
225 | 222 |
226 def requestStatus(self, names): | 223 def requestStatus(self, names): |
227 """Return status information about this mailbox. | 224 """Return status information about this mailbox. |
228 @param names: The status names to return information regarding. | 225 @param names: The status names to return information regarding. |
229 The possible values for each name are: MESSAGES, RECENT, UIDNEXT, | 226 The possible values for each name are: MESSAGES, RECENT, UIDNEXT, |
260 if listener in self.listeners: | 257 if listener in self.listeners: |
261 self.listeners.remove(listener) | 258 self.listeners.remove(listener) |
262 else: | 259 else: |
263 raise imap4.MailboxException('Trying to remove an unknown listener') | 260 raise imap4.MailboxException('Trying to remove an unknown listener') |
264 | 261 |
265 def addMessage(self, message, flags = (), date = None): | 262 def addMessage(self, message, flags=(), date=None): |
266 """Add the given message to this mailbox. | 263 """Add the given message to this mailbox. |
267 @param message: The RFC822 formatted message | 264 @param message: The RFC822 formatted message |
268 @param flags: The flags to associate with this message | 265 @param flags: The flags to associate with this message |
269 @param date: If specified, the date to associate with this | 266 @param date: If specified, the date to associate with this |
270 @return: A deferred whose callback is invoked with the message | 267 @return: A deferred whose callback is invoked with the message |
286 """Retrieve one or more messages. | 283 """Retrieve one or more messages. |
287 @param messages: The identifiers of messages to retrieve information | 284 @param messages: The identifiers of messages to retrieve information |
288 about | 285 about |
289 @param uid: If true, the IDs specified in the query are UIDs; | 286 @param uid: If true, the IDs specified in the query are UIDs; |
290 """ | 287 """ |
291 debug('fetch (%s, %s)'%(messages,uid)) | 288 debug('fetch (%s, %s)' % (messages, uid)) |
292 if uid: | 289 if uid: |
293 messages.last = self.mailbox.getMaxUid() | 290 messages.last = self.mailbox.getMaxUid() |
294 messages.getnext = self.mailbox.getNextExistingUid | 291 messages.getnext = self.mailbox.getNextExistingUid |
295 for mess_uid in messages: | 292 for mess_uid in messages: |
296 if mess_uid == None: | 293 if mess_uid is None: |
297 debug ('stopping iteration') | 294 debug('stopping iteration') |
298 raise StopIteration | 295 raise StopIteration |
299 try: | 296 try: |
300 yield (mess_uid,Message(mess_uid,self.mailbox.getFlagsUid(mess_uid), self.mailbox.getMessageUid(mess_uid))) | 297 yield (mess_uid, Message(mess_uid, self.mailbox.getFlagsUid(mess_uid), self.mailbox.getMessageUid(mess_uid))) |
301 except IndexError: | 298 except IndexError: |
302 continue | 299 continue |
303 else: | 300 else: |
304 messages.last = self.getMessageCount() | 301 messages.last = self.getMessageCount() |
305 for mess_idx in messages: | 302 for mess_idx in messages: |
306 if mess_idx>self.getMessageCount(): | 303 if mess_idx > self.getMessageCount(): |
307 raise StopIteration | 304 raise StopIteration |
308 yield (mess_idx,Message(mess_idx,self.mailbox.getFlags(mess_idx),self.mailbox.getMessage(mess_idx-1))) | 305 yield (mess_idx, Message(mess_idx, self.mailbox.getFlags(mess_idx), self.mailbox.getMessage(mess_idx - 1))) |
309 | 306 |
310 def store(self, messages, flags, mode, uid): | 307 def store(self, messages, flags, mode, uid): |
311 """Set the flags of one or more messages. | 308 """Set the flags of one or more messages. |
312 @param messages: The identifiers of the messages to set the flags of. | 309 @param messages: The identifiers of the messages to set the flags of. |
313 @param flags: The flags to set, unset, or add. | 310 @param flags: The flags to set, unset, or add. |
322 been performed, or a Deferred whose callback will be invoked with | 319 been performed, or a Deferred whose callback will be invoked with |
323 such a dict. | 320 such a dict. |
324 """ | 321 """ |
325 debug('store') | 322 debug('store') |
326 | 323 |
327 flags=[flag.upper() for flag in flags] | 324 flags = [flag.upper() for flag in flags] |
328 | 325 |
329 def updateFlags(getF,setF): | 326 def updateFlags(getF, setF): |
330 ret = {} | 327 ret = {} |
331 for mess_id in messages: | 328 for mess_id in messages: |
332 if (uid and mess_id == None) or (not uid and mess_id>self.getMessageCount()): | 329 if (uid and mess_id is None) or (not uid and mess_id > self.getMessageCount()): |
333 break | 330 break |
334 _flags=set(getF(mess_id) if mode else []) | 331 _flags = set(getF(mess_id) if mode else []) |
335 if mode==-1: | 332 if mode == -1: |
336 _flags.difference_update(set(flags)) | 333 _flags.difference_update(set(flags)) |
337 else: | 334 else: |
338 _flags.update(set(flags)) | 335 _flags.update(set(flags)) |
339 new_flags=list(_flags) | 336 new_flags = list(_flags) |
340 setF(mess_id, new_flags) | 337 setF(mess_id, new_flags) |
341 ret[mess_id] = tuple(new_flags) | 338 ret[mess_id] = tuple(new_flags) |
342 return ret | 339 return ret |
343 | 340 |
344 if uid: | 341 if uid: |
345 messages.last = self.mailbox.getMaxUid() | 342 messages.last = self.mailbox.getMaxUid() |
346 messages.getnext = self.mailbox.getNextExistingUid | 343 messages.getnext = self.mailbox.getNextExistingUid |
347 ret = updateFlags(self.mailbox.getFlagsUid,self.mailbox.setFlagsUid) | 344 ret = updateFlags(self.mailbox.getFlagsUid, self.mailbox.setFlagsUid) |
348 for listener in self.listeners: | 345 for listener in self.listeners: |
349 listener.flagsChanged(ret) | 346 listener.flagsChanged(ret) |
350 return ret | 347 return ret |
351 | 348 |
352 else: | 349 else: |
353 messages.last = self.getMessageCount() | 350 messages.last = self.getMessageCount() |
354 ret = updateFlags(self.mailbox.getFlags,self.mailbox.setFlags) | 351 ret = updateFlags(self.mailbox.getFlags, self.mailbox.setFlags) |
355 newFlags={} | 352 newFlags = {} |
356 for idx in ret: | 353 for idx in ret: |
357 #we have to convert idx to uid for the listeners | 354 #we have to convert idx to uid for the listeners |
358 newFlags[self.mailbox.getUid(idx)] = ret[idx] | 355 newFlags[self.mailbox.getUid(idx)] = ret[idx] |
359 for listener in self.listeners: | 356 for listener in self.listeners: |
360 listener.flagsChanged(newFlags) | 357 listener.flagsChanged(newFlags) |
364 """Return the flags defined in this mailbox | 361 """Return the flags defined in this mailbox |
365 Flags with the \\ prefix are reserved for use as system flags. | 362 Flags with the \\ prefix are reserved for use as system flags. |
366 @return: A list of the flags that can be set on messages in this mailbox. | 363 @return: A list of the flags that can be set on messages in this mailbox. |
367 """ | 364 """ |
368 debug('getFlags') | 365 debug('getFlags') |
369 return ['\\SEEN','\\ANSWERED','\\FLAGGED','\\DELETED','\\DRAFT'] #TODO: add '\\RECENT' | 366 return ['\\SEEN', '\\ANSWERED', '\\FLAGGED', '\\DELETED', '\\DRAFT'] # TODO: add '\\RECENT' |
370 | 367 |
371 def getHierarchicalDelimiter(self): | 368 def getHierarchicalDelimiter(self): |
372 """Get the character which delimits namespaces for in this mailbox. | 369 """Get the character which delimits namespaces for in this mailbox. |
373 """ | 370 """ |
374 debug('getHierarchicalDelimiter') | 371 debug('getHierarchicalDelimiter') |
375 return '.' | 372 return '.' |
376 | 373 |
374 | |
377 class ImapSatAccount(imap4.MemoryAccount): | 375 class ImapSatAccount(imap4.MemoryAccount): |
378 #implements(imap4.IAccount) | 376 #implements(imap4.IAccount) |
379 | 377 |
380 def __init__(self, host, profile): | 378 def __init__(self, host, profile): |
381 debug("ImapAccount init") | 379 debug("ImapAccount init") |
382 self.host=host | 380 self.host = host |
383 self.profile=profile | 381 self.profile = profile |
384 imap4.MemoryAccount.__init__(self,profile) | 382 imap4.MemoryAccount.__init__(self, profile) |
385 self.addMailbox("Inbox") #We only manage Inbox for the moment | 383 self.addMailbox("Inbox") # We only manage Inbox for the moment |
386 debug ('INBOX added') | 384 debug('INBOX added') |
387 | 385 |
388 def _emptyMailbox(self, name, id): | 386 def _emptyMailbox(self, name, id): |
389 return SatMailbox(self.host,name,self.profile) | 387 return SatMailbox(self.host, name, self.profile) |
390 | 388 |
391 | 389 |
392 class ImapRealm(object): | 390 class ImapRealm(object): |
393 implements(portal.IRealm) | 391 implements(portal.IRealm) |
394 | 392 |
395 def __init__(self,host): | 393 def __init__(self, host): |
396 self.host = host | 394 self.host = host |
397 | 395 |
398 def requestAvatar(self, avatarID, mind, *interfaces): | 396 def requestAvatar(self, avatarID, mind, *interfaces): |
399 debug('requestAvatar') | 397 debug('requestAvatar') |
400 profile=avatarID.decode('utf-8') | 398 profile = avatarID.decode('utf-8') |
401 if imap4.IAccount not in interfaces: | 399 if imap4.IAccount not in interfaces: |
402 raise NotImplementedError | 400 raise NotImplementedError |
403 return imap4.IAccount, ImapSatAccount(self.host,profile), lambda:None | 401 return imap4.IAccount, ImapSatAccount(self.host, profile), lambda: None |
402 | |
404 | 403 |
405 class SatProfileCredentialChecker(object): | 404 class SatProfileCredentialChecker(object): |
406 """ | 405 """ |
407 This credential checker check against SàT's profile and associated jabber's password | 406 This credential checker check against SàT's profile and associated jabber's password |
408 Check if the profile exists, and if the password is OK | 407 Check if the profile exists, and if the password is OK |
410 """ | 409 """ |
411 implements(checkers.ICredentialsChecker) | 410 implements(checkers.ICredentialsChecker) |
412 credentialInterfaces = (credentials.IUsernamePassword, | 411 credentialInterfaces = (credentials.IUsernamePassword, |
413 credentials.IUsernameHashedPassword) | 412 credentials.IUsernameHashedPassword) |
414 | 413 |
415 | |
416 def __init__(self, host): | 414 def __init__(self, host): |
417 self.host = host | 415 self.host = host |
418 | 416 |
419 def _cbPasswordMatch(self, matched, profile): | 417 def _cbPasswordMatch(self, matched, profile): |
420 if matched: | 418 if matched: |
429 d = self.host.memory.asyncGetParamA("Password", "Connection", profile_key=credentials.username) | 427 d = self.host.memory.asyncGetParamA("Password", "Connection", profile_key=credentials.username) |
430 d.addCallback(lambda password: credentials.checkPassword(password)) | 428 d.addCallback(lambda password: credentials.checkPassword(password)) |
431 d.addCallback(self._cbPasswordMatch, credentials.username) | 429 d.addCallback(self._cbPasswordMatch, credentials.username) |
432 return d | 430 return d |
433 | 431 |
432 | |
434 class ImapServerFactory(protocol.ServerFactory): | 433 class ImapServerFactory(protocol.ServerFactory): |
435 protocol = imap4.IMAP4Server | 434 protocol = imap4.IMAP4Server |
436 | 435 |
437 def __init__(self, host): | 436 def __init__(self, host): |
438 self.host=host | 437 self.host = host |
439 | 438 |
440 def startedConnecting(self, connector): | 439 def startedConnecting(self, connector): |
441 debug (_("IMAP server connection started")) | 440 debug(_("IMAP server connection started")) |
442 | 441 |
443 def clientConnectionLost(self, connector, reason): | 442 def clientConnectionLost(self, connector, reason): |
444 debug (_("IMAP server connection lost (reason: %s)"), reason) | 443 debug(_("IMAP server connection lost (reason: %s)"), reason) |
445 | 444 |
446 def buildProtocol(self, addr): | 445 def buildProtocol(self, addr): |
447 debug ("Building protocol") | 446 debug("Building protocol") |
448 prot = protocol.ServerFactory.buildProtocol(self, addr) | 447 prot = protocol.ServerFactory.buildProtocol(self, addr) |
449 prot.portal = portal.Portal(ImapRealm(self.host)) | 448 prot.portal = portal.Portal(ImapRealm(self.host)) |
450 prot.portal.registerChecker(SatProfileCredentialChecker(self.host)) | 449 prot.portal.registerChecker(SatProfileCredentialChecker(self.host)) |
451 return prot | 450 return prot |