From 511e4916c3f5b2b013fb1bd8d5ba35b7520ee756 Mon Sep 17 00:00:00 2001 From: David Rousselie Date: Wed, 31 Oct 2007 17:55:47 +0100 Subject: [PATCH] Store mailbox with "/" delimiter and discover when populating account what is the real delimiter darcs-hash:20071031165547-86b55-50bb56e0db9ff77385656b126d7f0f2a02ff3d3a.gz --- src/jmc/jabber/component.py | 10 ++- src/jmc/lang.py | 22 +++---- src/jmc/model/account.py | 41 ++++++++++-- src/jmc/model/tests/account.py | 116 +++++++++++++++++++++++++++------ 4 files changed, 150 insertions(+), 39 deletions(-) diff --git a/src/jmc/jabber/component.py b/src/jmc/jabber/component.py index 0f2c703..b6f8cb2 100644 --- a/src/jmc/jabber/component.py +++ b/src/jmc/jabber/component.py @@ -56,9 +56,11 @@ class MailAccountManager(AccountManager): from_jid, account_type, lang_class): - """Handle get_register on an IMAP account. + """ + Handle get_register on an IMAP account. Return a preinitialized form. - account_type contains 'account_type + imap_dir'""" + account_type contains 'account_type + imap_dir' + """ splitted_node = account_type.split("/") splitted_node_len = len(splitted_node) if splitted_node_len == 1 or \ @@ -71,6 +73,7 @@ class MailAccountManager(AccountManager): else: info_query = info_query.make_result_response() model.db_connect() + # TODO : "/" is default, "." could be imap_dir = "/".join(splitted_node[1:]) bare_from_jid = from_jid.bare() _account = account.get_account_filter(\ @@ -80,17 +83,20 @@ class MailAccountManager(AccountManager): account_class=IMAPAccount) query = info_query.new_query("jabber:iq:register") if _account is not None: + # update account result = self.generate_registration_form_init(lang_class, _account) else: _account = account.get_account(bare_from_jid, name, IMAPAccount) if _account is not None: + # create new account based on current one result = self.generate_registration_form_init(lang_class, _account) result["name"].value = None result["name"].type = "text-single" else: + # create new account from scratch result = self.generate_registration_form(\ lang_class, IMAPAccount, diff --git a/src/jmc/lang.py b/src/jmc/lang.py index 3ae3a2c..7b5a179 100644 --- a/src/jmc/lang.py +++ b/src/jmc/lang.py @@ -137,17 +137,17 @@ class Lang(jcl.lang.Lang): send_mail_ok_body = u"Votre email a été envoyé à %s." help_message_body = u"Pour envoyer un email avec JMC, vous avez le choix " \ - + "entre :\n" \ - + " - Envoyer un message à la passerelle JMC: le sujet de l'email" \ - + " sera le sujet du message Jabber ou le sujet spécifié avec la " \ - + "syntaxe suivant dans le corps du message Jabber :\n" \ - + "\tSubject: votre sujet\n" \ - + " Pour spécifier les destinataires de l'email, il faut ajouter une" \ - + " ligne au corps du message Jabber avec la syntaxe suivante:\n" \ - + "\tTo: to_email@test.com\n\n" \ - + " - Ajouter un contact à votre roster, avec comme JID " \ - + "to_email\%test.com@jmc.test.com, où to_email@test.com est " \ - + "l'adresse du destinataire." + + u"entre :\n" \ + + u" - Envoyer un message à la passerelle JMC: le sujet de l'email" \ + + u" sera le sujet du message Jabber ou le sujet spécifié avec la " \ + + u"syntaxe suivant dans le corps du message Jabber :\n" \ + + u"\tSubject: votre sujet\n" \ + + u" Pour spécifier les destinataires de l'email, il faut ajouter une" \ + + u" ligne au corps du message Jabber avec la syntaxe suivante:\n" \ + + u"\tTo: to_email@test.com\n\n" \ + + u" - Ajouter un contact à votre roster, avec comme JID " \ + + u"to_email\%test.com@jmc.test.com, où to_email@test.com est " \ + + u"l'adresse du destinataire." class nl(jcl.lang.Lang.nl): # TODO: when finish, delete this line and uncomment in tests/lang.py the makeSuite(Language_nl_TestCase, 'test') line diff --git a/src/jmc/model/account.py b/src/jmc/model/account.py index abdcc02..7346eae 100644 --- a/src/jmc/model/account.py +++ b/src/jmc/model/account.py @@ -373,6 +373,7 @@ class MailAccount(PresenceAccount): class IMAPAccount(MailAccount): mailbox = StringCol(default="INBOX") + delimiter = StringCol(default=".") def _get_register_fields(cls, real_class=None): """See Account._get_register_fields @@ -397,13 +398,19 @@ class IMAPAccount(MailAccount): self.__logger = logging.getLogger("jmc.IMAPConnection") self._regexp_list = re.compile("\((.*)\) \"(.)\" \"?([^\"]*)\"?$") self.__cached_folders = {} - self.default_delimiter = "." def get_type(self): if self.ssl: return "imaps" return "imap" + def _get_real_mailbox(self): + """ + mailbox attribute is stored with "/" to delimit folders. + real mailbox is the folder with the delimiter used by the IMAP server + """ + return self.mailbox.replace("/", self.delimiter) + def get_status(self): return MailAccount.get_status(self) + "/" + self.mailbox @@ -427,7 +434,7 @@ class IMAPAccount(MailAccount): def get_mail_list(self): self.__logger.debug("Getting mail list") - typ, data = self.connection.select(self.mailbox) + typ, data = self.connection.select(self._get_real_mailbox()) typ, data = self.connection.search(None, 'RECENT') if typ == 'OK': return data[0].split(' ') @@ -476,8 +483,8 @@ class IMAPAccount(MailAccount): for line in data: match = self._regexp_list.match(line) if match is not None: - self.default_delimiter = match.group(2) - subdir = match.group(3).split(self.default_delimiter) + delimiter = match.group(2) + subdir = match.group(3).split(delimiter) self._add_full_path_to_cache(subdir) return self.__cached_folders @@ -485,7 +492,6 @@ class IMAPAccount(MailAccount): """ imap_dir: IMAP directory to list. subdirs must be delimited by '/' """ - # TODO : implement with cache (ie. only one connection) self.__logger.debug("Listing IMAP dir '" + str(imap_dir) + "'") if self.__cached_folders == {}: if not self.connected: @@ -504,6 +510,31 @@ class IMAPAccount(MailAccount): current_folder = current_folder[folder] return current_folder.keys() + def populate_handler(self): + """ + Handler called when populating account + """ + # Get delimiter + if not self.connected: + self.connect() + typ, data = self.connection.list(self.mailbox) + if typ == 'OK': + line = data[0] + match = self._regexp_list.match(line) + if match is not None: + self.delimiter = match.group(2) + else: + self.disconnect() + raise Exception("Cannot find mailbox " + self.mailbox) + else: + self.disconnect() + raise Exception("Cannot find mailbox " + self.mailbox) + self.disconnect() + # replace any previous delimiter in self.mailbox by "/" + if self.delimiter != "/": + self.mailbox = self.mailbox.replace(self.delimiter, "/") + + class POP3Account(MailAccount): nb_mail = IntCol(default=0) lastmail = IntCol(default=0) diff --git a/src/jmc/model/tests/account.py b/src/jmc/model/tests/account.py index a99b88d..5df4d5e 100644 --- a/src/jmc/model/tests/account.py +++ b/src/jmc/model/tests/account.py @@ -242,8 +242,8 @@ class IMAPAccount_TestCase(InheritableAccount_TestCase): self.imap_account.ssl = False self.account_class = IMAPAccount - def make_test(responses=None, queries=None, core=None): - def inner(self): + def make_test(self, responses=None, queries=None, core=None): + def inner(): self.server = server.DummyServer("localhost", 1143) thread.start_new_thread(self.server.serve, ()) self.server.responses = ["* OK [CAPABILITY IMAP4 LOGIN-REFERRALS " + \ @@ -261,20 +261,25 @@ class IMAPAccount_TestCase(InheritableAccount_TestCase): if queries: self.server.queries += queries self.server.queries += ["^[^ ]* LOGOUT"] - self.imap_account.connect() + if not self.imap_account.connected: + self.imap_account.connect() self.failUnless(self.imap_account.connection, \ "Cannot establish connection") if core: model.db_connect() core(self) model.db_disconnect() - self.imap_account.disconnect() + if self.imap_account.connected: + self.imap_account.disconnect() self.failUnless(self.server.verify_queries()) return inner - test_connection = make_test() + def test_connection(self): + test_func = self.make_test() + test_func() - test_get_mail_list = make_test([lambda data: "* 42 EXISTS\n* 1 RECENT\n* OK" +\ + def test_get_mail_list(self): + test_func = self.make_test([lambda data: "* 42 EXISTS\n* 1 RECENT\n* OK" +\ " [UNSEEN 9]\n* FLAGS (\Deleted \Seen\*)\n*" +\ " OK [PERMANENTFLAGS (\Deleted \Seen\*)\n" + \ data.split()[0] + \ @@ -285,22 +290,60 @@ class IMAPAccount_TestCase(InheritableAccount_TestCase): "^[^ ]* SEARCH RECENT"], \ lambda self: \ self.assertEquals(self.imap_account.get_mail_list(), ['9', '10'])) + test_func() - test_get_mail_summary = make_test([lambda data: "* 42 EXISTS\r\n* 1 RECENT\r\n* OK" +\ - " [UNSEEN 9]\r\n* FLAGS (\Deleted \Seen\*)\r\n*" +\ - " OK [PERMANENTFLAGS (\Deleted \Seen\*)\r\n" + \ - data.split()[0] + \ - " 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"], \ - ["^[^ ]* EXAMINE INBOX", \ - "^[^ ]* FETCH 1 \(RFC822\)"], \ - lambda self: self.assertEquals(self.imap_account.get_mail_summary(1), \ - (u"From : None\nSubject : None\n\n", \ - u"None"))) + def test_get_mail_list_delimiter1(self): + self.imap_account.mailbox = "INBOX/dir1/subdir2" + self.imap_account.delimiter = "." + test_func = self.make_test( \ + [lambda data: "* 42 EXISTS\n* 1 RECENT\n* OK" + \ + " [UNSEEN 9]\n* FLAGS (\Deleted \Seen\*)\n*" + \ + " OK [PERMANENTFLAGS (\Deleted \Seen\*)\n" + \ + data.split()[0] + \ + " OK [READ-WRITE] SELECT completed\n", \ + lambda data: "* SEARCH 9 10 \n" + \ + data.split()[0] + " OK SEARCH completed\n"], \ + ["^[^ ]* SELECT \"?INBOX\.dir1\.subdir2\"?", + "^[^ ]* SEARCH RECENT"], \ + lambda self: \ + self.assertEquals(self.imap_account.get_mail_list(), ['9', '10'])) + test_func() - test_get_mail = make_test([lambda data: "* 42 EXISTS\r\n* 1 RECENT\r\n* OK" +\ + def test_get_mail_list_delimiter2(self): + self.imap_account.mailbox = "INBOX/dir1/subdir2" + self.imap_account.delimiter = "/" + test_func = self.make_test( \ + [lambda data: "* 42 EXISTS\n* 1 RECENT\n* OK" + \ + " [UNSEEN 9]\n* FLAGS (\Deleted \Seen\*)\n*" + \ + " OK [PERMANENTFLAGS (\Deleted \Seen\*)\n" + \ + data.split()[0] + \ + " OK [READ-WRITE] SELECT completed\n", \ + lambda data: "* SEARCH 9 10 \n" + \ + data.split()[0] + " OK SEARCH completed\n"], \ + ["^[^ ]* SELECT \"?INBOX/dir1/subdir2\"?", + "^[^ ]* SEARCH RECENT"], \ + lambda self: \ + self.assertEquals(self.imap_account.get_mail_list(), ['9', '10'])) + test_func() + + def test_get_mail_summary(self): + test_func = self.make_test([lambda data: "* 42 EXISTS\r\n* 1 RECENT\r\n* OK" +\ + " [UNSEEN 9]\r\n* FLAGS (\Deleted \Seen\*)\r\n*" +\ + " OK [PERMANENTFLAGS (\Deleted \Seen\*)\r\n" + \ + data.split()[0] + \ + " 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"], + ["^[^ ]* EXAMINE INBOX", + "^[^ ]* FETCH 1 \(RFC822\)"], + lambda self: self.assertEquals(self.imap_account.get_mail_summary(1), + (u"From : None\nSubject : None\n\n", + u"None"))) + test_func() + + def test_get_mail(self): + test_func = self.make_test([lambda data: "* 42 EXISTS\r\n* 1 RECENT\r\n* OK" +\ " [UNSEEN 9]\r\n* FLAGS (\Deleted \Seen\*)\r\n*" +\ " OK [PERMANENTFLAGS (\Deleted \Seen\*)\r\n" + \ data.split()[0] + \ @@ -313,8 +356,10 @@ class IMAPAccount_TestCase(InheritableAccount_TestCase): lambda self: self.assertEquals(self.imap_account.get_mail(1), \ (u"From : None\nSubject : None\n\nbody text\r\n\n", \ u"None"))) + test_func() - test_build_folder_cache = make_test(\ + def test_build_folder_cache(self): + test_func = self.make_test(\ [lambda data: '* LIST () "." "INBOX"\r\n' + \ '* LIST () "." "INBOX.dir1"\r\n' + \ '* LIST () "." "INBOX.dir1.subdir1"\r\n' + \ @@ -328,6 +373,7 @@ class IMAPAccount_TestCase(InheritableAccount_TestCase): {"subdir1": {}, "subdir2": {}}, "dir2": {}}})) + test_func() def test_ls_dir_base(self): self.test_build_folder_cache() @@ -356,6 +402,34 @@ class IMAPAccount_TestCase(InheritableAccount_TestCase): self.assertEquals(result, ["subdir1", "subdir2"]) + def test_populate_handler(self): + self.assertEquals(".", self.imap_account.delimiter) + self.imap_account.mailbox = "INBOX.dir1.subdir2" + def call_func(self): + self.imap_account.populate_handler() + self.assertEquals("INBOX/dir1/subdir2", self.imap_account.mailbox) + test_func = self.make_test(\ + [lambda data: '* LIST () "." "INBOX.dir1.subdir2"\r\n' + \ + data.split()[0] + ' OK LIST completed\r\n'], + ["^[^ ]* LIST \"?INBOX.dir1.subdir2\"? \*"], + call_func) + test_func() + + def test_populate_handler_wrong_mailbox(self): + self.assertEquals(".", self.imap_account.delimiter) + self.imap_account.mailbox = "INBOX.dir1.subdir2" + def call_func(self): + try: + self.imap_account.populate_handler() + except Exception, e: + return + self.fail("Exception should have been raised") + test_func = self.make_test(\ + [lambda data: data.split()[0] + ' ERR LIST completed\r\n'], + ["^[^ ]* LIST \"?INBOX.dir1.subdir2\"? \*"], + call_func) + test_func() + class SMTPAccount_TestCase(Account_TestCase): def setUp(self): JCLTestCase.setUp(self, tables=[Account, ExampleAccount, User,