From 505543262f0da48c248ef65b926198aec2712f49 Mon Sep 17 00:00:00 2001 From: David Rousselie Date: Tue, 7 Feb 2006 22:33:33 +0100 Subject: [PATCH] Check mail new logic - use RECENT flag for search on IMAP server with write access so RECENT flag is deleted on new messages and fetch read only so SEEN flag is not set on fetched messages. - It might work on Exchange 2003 that do not support STORE flag UNSEEN - This change the get_mail loop so now each MailConnection type implement get_next_mail_index because logic is different from IMAP to POP3 connection. darcs-hash:20060207213333-86b55-9c2ccf31fb9ae9e6dd454c907f3188bc6080b817.gz --- jabber/component.py | 28 ++++++------------- jabber/mailconnection.py | 46 +++++++++++++++++++++++++----- tests/dummy_server.py | 54 ++++++++++++++++++++---------------- tests/test_mailconnection.py | 20 +++++-------- 4 files changed, 85 insertions(+), 63 deletions(-) diff --git a/jabber/component.py b/jabber/component.py index 85e7b03..50e4f62 100644 --- a/jabber/component.py +++ b/jabber/component.py @@ -850,17 +850,10 @@ class MailComponent(Component): + "@" + account.host) account.connect() mail_list = account.get_mail_list() - if not mail_list or mail_list[0] == '': - num = 0 - else: - num = len(mail_list) - # unseen mails checked by external client - # TODO : better test to find - if num < account.lastmail: - account.lastmail = 0 if action == mailconnection.RETRIEVE: - while account.lastmail < num: - (body, email_from) = account.get_mail(int(mail_list[account.lastmail])) + mail_index = account.get_next_mail_index(mail_list) + while mail_index is not None: + (body, email_from) = account.get_mail(mail_index) mesg = Message(from_jid = name + "@" + \ unicode(self.jid), \ to_jid = jid, \ @@ -868,15 +861,16 @@ class MailComponent(Component): subject = account.default_lang_class.new_mail_subject % (email_from), \ body = body) self.stream.send(mesg) - account.lastmail += 1 + account.get_next_mail_index(mail_list) else: body = "" new_mail_count = 0 - while account.lastmail < num: + mail_index = account.get_next_mail_index(mail_list) + while mail_index: (tmp_body, from_email) = \ - account.get_mail_summary(int(mail_list[account.lastmail])) + account.get_mail_summary(mail_index) body += tmp_body + "\n----------------------------------\n" - account.lastmail += 1 + mail_index = account.get_next_mail_index(mail_list) new_mail_count += 1 if body != "": mesg = Message(from_jid = name + "@" + \ @@ -915,11 +909,7 @@ class MailComponent(Component): return try: account.connect() - mail_list = account.get_mail_list() - if not mail_list or mail_list[0] == '': - account.lastmail = 0 - else: - account.lastmail = len(mail_list) + account.mark_all_as_read() account.disconnect() account.in_error = False except Exception,e: diff --git a/jabber/mailconnection.py b/jabber/mailconnection.py index 0744055..c7ba8f7 100644 --- a/jabber/mailconnection.py +++ b/jabber/mailconnection.py @@ -157,7 +157,6 @@ class MailConnection(object): self.host = host self.port = port self.ssl = ssl - self.lastmail = 0 self.status = "offline" self.connection = None self.chat_action = RETRIEVE @@ -306,6 +305,13 @@ class MailConnection(object): def get_mail_summary(self, index): return None + def get_next_mail_index(self, mail_list): + pass + + # Does not modify server state but just internal JMC state + def mark_all_as_read(self): + pass + def get_action(self): mapping = {"online": self.online_action, "chat": self.chat_action, @@ -366,29 +372,35 @@ class IMAPConnection(MailConnection): def get_mail_list(self): IMAPConnection._logger.debug("Getting mail list") typ, data = self.connection.select(self.mailbox) - typ, data = self.connection.search(None, 'UNSEEN') + typ, data = self.connection.search(None, 'RECENT') if typ == 'OK': return data[0].split(' ') return None def get_mail(self, index): IMAPConnection._logger.debug("Getting mail " + str(index)) - typ, data = self.connection.select(self.mailbox) + typ, data = self.connection.select(self.mailbox, True) typ, data = self.connection.fetch(index, '(RFC822)') - self.connection.store(index, "FLAGS", "UNSEEN") if typ == 'OK': return self.format_message(email.message_from_string(data[0][1])) return u"Error while fetching mail " + str(index) def get_mail_summary(self, index): IMAPConnection._logger.debug("Getting mail summary " + str(index)) - typ, data = self.connection.select(self.mailbox) + typ, data = self.connection.select(self.mailbox, True) typ, data = self.connection.fetch(index, '(RFC822)') - self.connection.store(index, "FLAGS", "UNSEEN") if typ == 'OK': return self.format_message_summary(email.message_from_string(data[0][1])) return u"Error while fetching mail " + str(index) + def get_next_mail_index(self, mail_list): + if not mail_list or mail_list[0] == '': + return None + return mail_list.pop(0) + + def mark_all_as_read(self): + self.get_mail_list() + type = property(get_type) class POP3Connection(MailConnection): @@ -402,6 +414,8 @@ class POP3Connection(MailConnection): else: port = 110 MailConnection.__init__(self, login, password, host, port, ssl) + self.__nb_mail = 0 + self.__lastmail = 0 def get_type(self): if self.ssl: @@ -431,7 +445,12 @@ class POP3Connection(MailConnection): def get_mail_list(self): POP3Connection._logger.debug("Getting mail list") count, size = self.connection.stat() - return [str(i) for i in range(1, count + 1)] + result = [str(i) for i in range(1, count + 1)] + if not result or result[0] == '': + self.__nb_mail = 0 + else: + self.__nb_mail = len(result) + return result def get_mail(self, index): POP3Connection._logger.debug("Getting mail " + str(index)) @@ -447,4 +466,17 @@ class POP3Connection(MailConnection): return self.format_message_summary(email.message_from_string('\n'.join(data))) return u"Error while fetching mail " + str(index) + def get_next_mail_index(self, mail_list): + if self.__nb_mail == self.__lastmail: + return None + if self.__nb_mail < self.__lastmail: + self.__lastmail = 0 + result = self.__lastmail + self.__lastmail += 1 + return result + + def mark_all_as_read(self): + self.get_mail_list() + self.__lastmail = self.__nb_mail + type = property(get_type) diff --git a/tests/dummy_server.py b/tests/dummy_server.py index 04d055e..4d9dca7 100644 --- a/tests/dummy_server.py +++ b/tests/dummy_server.py @@ -56,29 +56,34 @@ class DummyServer: self.real_queries = [] def serve(self): - conn, addr = self.socket.accept() - rfile = conn.makefile('rb', -1) - if self.responses: - data = None - for idx in range(len(self.responses)): - # if response is a function apply it (it must return a string) - # it is given previous received data - if isinstance(self.responses[idx], types.FunctionType): - response = self.responses[idx](data) - else: - response = self.responses[idx] - if response is not None: -# print >>sys.stderr, 'Sending : ', response - conn.send(response) - data = rfile.readline() - if not data: - break - else: - self.real_queries.append(data) -# print >>sys.stderr, 'Receive : ', data - conn.close() - self.socket.close() - self.socket = None + try: + conn, addr = self.socket.accept() + rfile = conn.makefile('rb', -1) + if self.responses: + data = None + for idx in range(len(self.responses)): + # if response is a function apply it (it must return a string) + # it is given previous received data + if isinstance(self.responses[idx], types.FunctionType): + response = self.responses[idx](data) + else: + response = self.responses[idx] + if response is not None: + #print >>sys.stderr, 'Sending : ', response + conn.send(response) + data = rfile.readline() + if not data: + break + else: + self.real_queries.append(data) + #print >>sys.stderr, 'Receive : ', data + conn.close() + self.socket.close() + self.socket = None + except: + type, value, stack = sys.exc_info() + print >>sys.stderr, "".join(traceback.format_exception + (type, value, stack, 5)) def verify_queries(self): result = True @@ -95,7 +100,8 @@ class DummyServer: else: result = False print >>sys.stderr, "Expected " + str(queries_len) + \ - " queries, got " + str(len(self.real_queries)) + " queries, got " + str(len(self.real_queries)) + \ + "\t" + str(self.real_queries) return result class XMLDummyServer(DummyServer): diff --git a/tests/test_mailconnection.py b/tests/test_mailconnection.py index d5d6a65..7a5aec0 100644 --- a/tests/test_mailconnection.py +++ b/tests/test_mailconnection.py @@ -236,7 +236,7 @@ class IMAPConnection_TestCase(unittest.TestCase): lambda data: "* SEARCH 9 10 \n" + \ data.split()[0] + " OK SEARCH completed\n"], \ ["^[^ ]* SELECT INBOX", \ - "^[^ ]* SEARCH UNSEEN"], \ + "^[^ ]* SEARCH RECENT"], \ lambda self: \ self.assertEquals(self.imap_connection.get_mail_list(), ['9', '10'])) @@ -247,12 +247,9 @@ class IMAPConnection_TestCase(unittest.TestCase): " OK [READ-WRITE] SELECT completed\r\n", \ lambda data: "* 1 FETCH ((RFC822) {12}\r\nbody" + \ " text\r\n)\r\n" + \ - data.split()[0] + " OK FETCH completed\r\n", \ - lambda data: "* 1 FETCH (FLAGS (\UNSEEN))\r\n" + \ - data.split()[0] + " OK STORE completed\r\n"], \ - ["^[^ ]* SELECT INBOX", \ - "^[^ ]* FETCH 1 \(RFC822\)", \ - "^[^ ]* STORE 1 FLAGS \(UNSEEN\)"], \ + data.split()[0] + " OK FETCH completed\r\n"], \ + ["^[^ ]* EXAMINE INBOX", \ + "^[^ ]* FETCH 1 \(RFC822\)"], \ lambda self: self.assertEquals(self.imap_connection.get_mail_summary(1), \ (u"From : None\nSubject : None\n\n", \ u"None"))) @@ -264,12 +261,9 @@ class IMAPConnection_TestCase(unittest.TestCase): " OK [READ-WRITE] SELECT completed\r\n", \ lambda data: "* 1 FETCH ((RFC822) {11}\r\nbody" + \ " text\r\n)\r\n" + \ - data.split()[0] + " OK FETCH completed\r\n", \ - lambda data: "* 1 FETCH (FLAGS (\UNSEEN))\r\n" + \ - data.split()[0] + " OK STORE completed\r\n"], \ - ["^[^ ]* SELECT INBOX", \ - "^[^ ]* FETCH 1 \(RFC822\)", \ - "^[^ ]* STORE 1 FLAGS \(UNSEEN\)"], \ + data.split()[0] + " OK FETCH completed\r\n"], \ + ["^[^ ]* EXAMINE INBOX", \ + "^[^ ]* FETCH 1 \(RFC822\)",], \ lambda self: self.assertEquals(self.imap_connection.get_mail(1), \ (u"From : None\nSubject : None\n\nbody text\r\n\n", \ u"None")))