def _process_qualified_mails(mail_bodies, pipeline): def handle_error(failure): logger.error('failed to process qualified mails - {}'.format(failure)) processed = imap4.MessageSet() for body in mail_bodies.values(): uid = int(body[0][1]) summary = parse_mail_attachment(body[0][4]) if summary.title.find('CANON') and summary.payload_start_index > 0: processed += imap4.MessageSet(uid) path = pathlib.Path(pipeline.local_store, summary.filename) with open(path, 'wb') as f: f.write( base64.urlsafe_b64decode( body[0][4][summary.payload_start_index:])) logger.info('Successfully downloaded {}'.format(path)) logger.info('Processed {} scanned files'.format(len(processed))) if len(processed) == 0: return _next_round(None, pipeline) return pipeline.protocol.addFlags(processed, ['\\Seen'], uid=True) \ .addCallback(_next_round, pipeline) \ .addErrback(handle_error)
def testFullAppend(self): """ Test appending a full message to the mailbox """ infile = os.path.join(HERE, '..', 'rfc822.message') message = open(infile) acc = self.server.theAccount mailbox_name = "appendmbox/subthing" def add_mailbox(): return acc.addMailbox(mailbox_name) def login(): return self.client.login(TEST_USER, TEST_PASSWD) def append(): return self.client.append( mailbox_name, message, ('\\SEEN', '\\DELETED'), 'Tue, 17 Jun 2003 11:22:16 -0600 (MDT)', ) d1 = self.connected.addCallback(strip(add_mailbox)) d1.addCallback(strip(login)) d1.addCallbacks(strip(append), self._ebGeneral) d1.addCallbacks(self._cbStopClient, self._ebGeneral) d2 = self.loopback() d = defer.gatherResults([d1, d2]) d.addCallback(lambda _: acc.getMailbox(mailbox_name)) d.addCallback(lambda mb: mb.fetch(imap4.MessageSet(start=1), True)) return d.addCallback(self._cbTestFullAppend, infile)
def _findChandlerMessages(self, msgUIDs, start, total): if __debug__: trace("_findChandlerMessages") for msgUID in msgUIDs: # Add all messages that match the # search criteria to the foundUIDs # list. self.vars.foundUIDs.append(msgUID) mset = self._getSearchMessageSet() if mset: query = imap4.Query(header=('X-Chandler-Mailer', 'True'), undeleted=True, uid=mset) end = start + len(mset) d = self.proto.search(query, uid=1) d.addCallback(self._findChandlerMessages, end, total) d.addErrback(self.catchErrors) self._printSearchNum(start, end, total) return None # This point is reached when self._getSearchMessageSet() # returns None indicating that there are no more message # uids to search for Chandler Headers. if len(self.vars.foundUIDs) == 0: # If the search returned no message uids # then have the worker commit the last seen UID # for the folder to prevent searching the same # messages again. return self._getNextFolder() # Search found one or more messages containing the # Chandler Headers. msgSet = imap4.MessageSet() for uid in self.vars.foundUIDs: msgSet.add(uid) # FYI: Since there are messages to download the incrementing of # the lastUID to highest uid of the searched messages # will automatically get commited. d = self.proto.fetchFlags(msgSet, uid=True) # The True argument indicates the message UIDs were # retrieved from an IMAP Search. This flag # tells the _getMessageFlagsUID method to # perform logic specific to the results of a # search d.addCallback(self._getMessagesFlagsUID, True) d.addErrback(self.catchErrors) return None
def testPartialAppend(self): """ Test partially appending a message to the mailbox """ # TODO this test sometimes will fail because of the notify_just_mdoc infile = util.sibpath(__file__, 'rfc822.message') acc = self.server.theAccount def add_mailbox(): return acc.addMailbox('PARTIAL/SUBTHING') def login(): return self.client.login(TEST_USER, TEST_PASSWD) def append(): message = file(infile) return self.client.sendCommand( imap4.Command( 'APPEND', 'PARTIAL/SUBTHING (\\SEEN) "Right now" ' '{%d}' % os.path.getsize(infile), (), self.client._IMAP4Client__cbContinueAppend, message)) d1 = self.connected.addCallback(strip(add_mailbox)) d1.addCallback(strip(login)) d1.addCallbacks(strip(append), self._ebGeneral) d1.addCallbacks(self._cbStopClient, self._ebGeneral) d2 = self.loopback() d = defer.gatherResults([d1, d2]) d.addCallback(lambda _: acc.getMailbox("PARTIAL/SUBTHING")) d.addCallback(lambda mb: mb.fetch(imap4.MessageSet(start=1), True)) return d.addCallback(self._cbTestPartialAppend, infile)
def _fetch_qualified_mails(uids, pipeline): def handle_error(failure): logger.error('IMAP4 fetch failed because of {}'.format(failure)) if len(uids) == 0: logger.info('Found no qualified mails.') return _next_round(None, pipeline) logger.info('Found {} preliminary qualified mails.'.format(len(uids))) messages = reduce(lambda x, y: x + imap4.MessageSet(y), uids, imap4.MessageSet()) # Set peek = True to avoid accidentally flag an irrelevant message. return pipeline.protocol.fetchSpecific(messages, uid=True, headerType='TEXT', peek=True) \ .addCallback(_process_qualified_mails, pipeline) \ .addErrback(handle_error)
def cbGotSearch(self, results): if results: ms = imap4.MessageSet() for n in results: ms.add(n) self.fetchUID(ms).addCallback(self.cbGotUIDs) else: self.cbClosed(None)
def _addDeleteCallback(self, d, folderPath): msgSet = imap4.MessageSet(1, None) d.addCallback(lambda x: self.proto.select(folderPath)) d.addCallback( lambda x: self.proto.addFlags(msgSet, ("\\Deleted", ), uid=True)) d.addCallback(lambda x: self.proto.expunge()) d.addCallback(lambda x: self.proto.close()) d.addCallback(lambda x: self.proto.delete(folderPath))
def __checkForNewMessages(self, msgs): if __debug__: self.printCurrentView("checkForNewMessages") #XXX: Need to store and compare UIDVALIDITY #if not msgs['UIDVALIDITY']: # print "server: %s has no UUID Validity:\n%s" % (self.account.host, msgs) if msgs['EXISTS'] == 0: utils.NotifyUIAsync(constants.DOWNLOAD_NO_MESSAGES) return self._actionCompleted() if self.__getNextUID() == 0: msgSet = imap4.MessageSet(1, None) else: msgSet = imap4.MessageSet(self.__getNextUID(), None) d = self.proto.fetchFlags(msgSet, uid=True) d.addCallback(self.__getMessagesFlagsUID).addErrback(self.catchErrors) return d
def _checkForNewMessages(self, msgs): if __debug__: trace("_checkForNewMessages") #XXX: Need to store and compare UIDVALIDITY. # The issue is what does Chandler do if the # UIDVALIDITY has changed. Not being a # traditional mail client we could not # just refetch the messages since the # Message Items may have changed dramatically # i.e. unstamped as Mail and stamped as an Event # and shared with other users. Or altered as # part of an Edit / Update workflow. # For now UIDVALIDITY will be ignored :( #if not msgs['UIDVALIDITY']: # print "server: %s has no UUID Validity:\n%s" % (self.account.host, msgs) if self.cancel: return self._actionCompleted() if msgs['EXISTS'] == 0: return self._getNextFolder() # Check that we have not already downloaded the max # number of messages for the folder max = self.vars.folderItem.downloadMax downloaded = self.vars.folderItem.downloaded if max > 0 and max == downloaded: if __debug__: trace("Max number of messages %s reached. No new mail will be \ downloaded from '%s'" % (max, self.vars.folderItem.displayName)) return self._getNextFolder() self.vars.lastUID = self.vars.folderItem.lastMessageUID if not self.vars.lastUID > 0: self.vars.lastUID = 1 msgSet = imap4.MessageSet(self.vars.lastUID, None) if self.vars.folderItem.folderType == "CHANDLER_HEADERS": return self.proto.fetchUID(msgSet, uid=1).addCallbacks( self._searchForChandlerMessages, self.catchErrors) else: return self.proto.fetchFlags(msgSet, uid=True).addCallback( self._getMessagesFlagsUID).addErrback(self.catchErrors)
def main(reactor, username=b"alice", password=b"secret", strport="tls:example.com:993"): endpoint = endpoints.clientFromString(reactor, strport) factory = protocol.Factory.forProtocol(imap4.IMAP4Client) try: client = yield endpoint.connect(factory) yield client.login(username, password) yield client.select('INBOX') info = yield client.fetchEnvelope(imap4.MessageSet(1)) print('First message subject:', info[1]['ENVELOPE'][1]) except Exception: print("IMAP4 client interaction failed") failure.Failure().printTraceback()
def fetchNextMessage(self): # self.log.debug("IMAP in fetchnextmessage") if self.messageUIDs: nextUID = self.messageUIDs.pop(0) messageListToFetch = imap4.MessageSet(nextUID) self.log.debug("Downloading message %d of %d (%s)" % (self.messageCount - len(self.messageUIDs), self.messageCount, nextUID)) self.fetchMessage(messageListToFetch, True).addCallback( self.cbGotMessage, messageListToFetch).addErrback(self.ebLogError) else: self.log.debug("Seeing if anything new has arrived") # Go back and see if any more messages have come in self.expunge().addCallback(self.cbInboxSelected)
def fetchNextMessage(self): # self.log.debug("IMAP in fetchnextmessage") if self.messageUIDs: nextUID = self.messageUIDs.pop(0) messageListToFetch = imap4.MessageSet(nextUID) self.log.debug("Downloading message {count} of {total} ({next})", count=self.messageCount - len(self.messageUIDs), total=self.messageCount, next=nextUID) self.fetchMessage(str(messageListToFetch), True).addCallback( self.cbGotMessage, messageListToFetch).addErrback(self.ebLogError) else: # We're done for this polling interval self.expunge()
def main(reactor, username="******", password="******", strport="ssl:host=173.37.183.72:port=993"): endpoint = endpoints.clientFromString(reactor, strport) factory = protocol.Factory() factory.protocol = imap4.IMAP4Client try: client = yield endpoint.connect(factory) yield client.login(username, password) yield client.select('INBOX') info = yield client.fetchEnvelope(imap4.MessageSet(1)) print 'First message subject:', info[1]['ENVELOPE'][1] except: print "IMAP4 client interaction failed" failure.Failure().printTraceback()
def cbRecent(result, proto): """ Callback invoked when search command completes. Retrieve the subject header of every message in the result. """ if result: s = imap4.MessageSet(zip(result, result)) return proto.fetchSpecific( s, headerType='HEADER.FIELDS', headerArgs=['FROM', 'SUBJECT', 'DATE'], ).addCallback(cbFetch, proto) else: proto.display("No recent messages; Goodbye\n") return proto.logout()
def expunge(self, messageSet=imap4.MessageSet()): files = set() self.maildir.lock() if not messageSet.last: for k in self.maildir.keys(): messageSet.add(k) files.add(self.maildir.get_file(k)._file) self.maildir.clear() try: return [ messageNum for messageNum in messageSet if not self.maildir.discard(messageNum) ] finally: self.maildir.unlock() for f in files: os.unlink(f.name)
def _getSearchMessageSet(self): size = len(self.vars.searchUIDs) if size == 0: return None num = size > constants.MAX_IMAP_SEARCH_NUM and \ constants.MAX_IMAP_SEARCH_NUM or \ size mset = imap4.MessageSet() for i in xrange(0, num): mset.add(self.vars.searchUIDs[i]) # Reduce the list removing all uids that have been # added to the message set self.vars.searchUIDs = self.vars.searchUIDs[num:] return mset
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: 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(lambda x: x not in self._unseenList, newUnseenList) if 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 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