diff options
-rw-r--r-- | run_tests.py | 2 | ||||
-rw-r--r-- | src/jmc/jabber/component.py | 98 | ||||
-rw-r--r-- | src/jmc/jabber/tests/component.py | 206 | ||||
-rw-r--r-- | src/jmc/model/account.py | 61 | ||||
-rw-r--r-- | src/jmc/model/tests/account.py | 9 | ||||
-rw-r--r-- | src/jmc/runner.py | 3 | ||||
-rw-r--r-- | src/jmc/tests/runner.py | 3 |
7 files changed, 365 insertions, 17 deletions
diff --git a/run_tests.py b/run_tests.py index edd7d95..29e2f65 100644 --- a/run_tests.py +++ b/run_tests.py @@ -44,7 +44,7 @@ def suite(): if __name__ == '__main__': logger = logging.getLogger() logger.addHandler(logging.StreamHandler()) - logger.setLevel(logging.INFO) + logger.setLevel(logging.CRITICAL) coverage.erase() coverage.start() diff --git a/src/jmc/jabber/component.py b/src/jmc/jabber/component.py index fd66f06..504abdd 100644 --- a/src/jmc/jabber/component.py +++ b/src/jmc/jabber/component.py @@ -1,4 +1,4 @@ -# -*- coding: UTF-8 -*- +# -*- coding: utf-8 -*- ## ## component.py ## Login : David Rousselie <dax@happycoders.org> @@ -22,13 +22,18 @@ ## import logging +import re +import sys + +from sqlobject import * from pyxmpp.message import Message -from jcl.model.account import PresenceAccount +from jcl.model.account import Account, PresenceAccount +from jcl.jabber.component import Handler, DefaultSubscribeHandler, DefaultUnsubscribeHandler, DefaultPresenceHandler from jcl.jabber.feeder import FeederComponent, Feeder, MessageSender, HeadlineSender -from jmc.model.account import MailAccount, IMAPAccount, POP3Account +from jmc.model.account import MailAccount, IMAPAccount, POP3Account, SMTPAccount from jmc.lang import Lang class MailComponent(FeederComponent): @@ -53,7 +58,16 @@ class MailComponent(FeederComponent): lang = lang) self.feeder = MailFeeder(self) self.sender = MailSender(self) - self.account_manager.account_classes = (IMAPAccount, POP3Account) + self.account_manager.account_classes = (IMAPAccount, POP3Account, SMTPAccount) + + def authenticated(self): + """Register message handlers""" + FeederComponent.authenticated(self) + self.msg_handlers += [SendMailMessageHandler(), RootSendMailMessageHandler()] + self.subscribe_handlers += [MailSubscribeHandler()] + self.unsubscribe_handlers += [MailUnsubscribeHandler()] + self.available_handlers += [DefaultPresenceHandler()] + self.unavailable_handlers += [DefaultPresenceHandler()] class MailFeeder(Feeder): """Email check""" @@ -159,4 +173,80 @@ class MailSender(MessageSender, HeadlineSender): MessageSender.send(self, to_account, subject, body) elif to_account.action == MailAccount.DIGEST: HeadlineSender.send(self, to_account, subject, body) + +class MailHandler(Handler): + """Define filter for email address in JID""" + + def __init__(self): + Handler.__init__(self) + self.dest_jid_regexp = re.compile(".*%.*") + + def filter(self, stanza): + """Return empty array if JID match '.*%.*@componentJID'""" + if self.dest_jid_regexp.match(stanza.get_to().node): + bare_from_jid = unicode(stanza.get_from().bare()) + accounts = Account.select(Account.q.user_jid == bare_from_jid) + if accounts.count() == 0: + raise Exception() + else: + default_account = accounts.newClause(SMTPAccount.q.default_account == True) + if default_account.count() > 0: + return default_account + else: + return accounts + return None + +class SendMailMessageHandler(MailHandler): + def __init__(self): + MailHandler.__init__(self) + self.__logger = logging.getLogger("jmc.jabber.component.SendMailMessageHandler") + + def handle(self, message, lang, accounts): + to_email = replace(message.get_to().node, '%', '@', 1) + accounts[0].send_email(to_email, message.get_subject(), message.get_body()) + +class RootSendMailMessageHandler(SendMailMessageHandler): + def __init__(self): + SendMailMessageHandler.__init__(self) + self.__logger = logging.getLogger("jmc.jabber.component.RootSendMailMessageHandler") + + def filter(self, message): + name = message.get_to().node + bare_from_jid = unicode(message.get_from().bare()) + accounts = Account.select(\ + AND(Account.q.name == name, \ + Account.q.user_jid == bare_from_jid)) + if accounts.count() != 1: + self.__logger.error("Account " + name + " for user " + bare_from_jid + " must be uniq") + return accounts + + def handle(self, message, lang, accounts): + # TODO : parse "headers", or advanced addressing + to_email = "" + accounts[0].send_email(to_email, message.get_subject(), message.get_body()) + +class MailSubscribeHandler(DefaultSubscribeHandler, MailHandler): + """Use DefaultSubscribeHandler handle method and MailHandler filter""" + + def __init__(self): + DefaultSubscribeHandler.__init__(self) + MailHandler.__init__(self) + + def filter(self, stanza): + return MailHandler.filter(self, stanza) + + def handle(self, stanza, lang, accounts): + return DefaultSubscribeHandler.handle(self, stanza, lang, accounts) + +class MailUnsubscribeHandler(DefaultUnsubscribeHandler, MailHandler): + """Use DefaultUnsubscribeHandler handle method and MailHandler filter""" + + def __init__(self): + DefaultUnsubscribeHandler.__init__(self) + MailHandler.__init__(self) + def filter(self, stanza): + return MailHandler.filter(self, stanza) + + def handle(self, stanza, lang, accounts): + return DefaultUnsubscribeHandler.handle(self, stanza, lang, accounts) diff --git a/src/jmc/jabber/tests/component.py b/src/jmc/jabber/tests/component.py index 1c47536..d435c2f 100644 --- a/src/jmc/jabber/tests/component.py +++ b/src/jmc/jabber/tests/component.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- ## ## test_component.py ## Login : <dax@happycoders.org> @@ -27,17 +28,21 @@ import sys from sqlobject import * from sqlobject.dbconnection import TheURIOpener +from pyxmpp.presence import Presence +from pyxmpp.message import Message + from jcl.model import account from jcl.model.account import Account, PresenceAccount +from jcl.jabber.tests.component import DefaultSubscribeHandler_TestCase, DefaultUnsubscribeHandler_TestCase -from jmc.model.account import MailAccount, IMAPAccount, POP3Account -from jmc.jabber.component import MailComponent +from jmc.model.account import MailAccount, IMAPAccount, POP3Account, SMTPAccount +from jmc.jabber.component import MailComponent, SendMailMessageHandler, RootSendMailMessageHandler, MailHandler, MailSubscribeHandler, MailUnsubscribeHandler if sys.platform == "win32": - DB_PATH = "/c|/temp/test.db" + DB_PATH = "/c|/temp/jmc_test.db" else: - DB_PATH = "/tmp/test.db" + DB_PATH = "/tmp/jmc_test.db" DB_URL = DB_PATH# + "?debug=1&debugThreading=1" class MockStream(object): @@ -148,6 +153,7 @@ class MailComponent_TestCase(unittest.TestCase): MailAccount.createTable(ifNotExists = True) IMAPAccount.createTable(ifNotExists = True) POP3Account.createTable(ifNotExists = True) + SMTPAccount.createTable(ifNotExists = True) MockIMAPAccount.createTable(ifNotExists = True) MockPOP3Account.createTable(ifNotExists = True) del account.hub.threadConnection @@ -156,6 +162,7 @@ class MailComponent_TestCase(unittest.TestCase): account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) MockPOP3Account.dropTable(ifExists = True) MockIMAPAccount.dropTable(ifExists = True) + SMTPAccount.dropTable(ifExists = True) POP3Account.dropTable(ifExists = True) IMAPAccount.dropTable(ifExists = True) MailAccount.dropTable(ifExists = True) @@ -502,9 +509,196 @@ class MailComponent_TestCase(unittest.TestCase): self.assertTrue(account11.has_connected) self.assertTrue(account11.marked_all_as_read) del account.hub.threadConnection - + +class SendMailMessageHandler_TestCase(unittest.TestCase): + def setUp(self): + self.handler = SendMailMessageHandler() + + def test_handle(self): + # TODO + pass + +class RootSendMailMessageHandler_TestCase(unittest.TestCase): + def setUp(self): + self.handler = RootSendMailMessageHandler() + if os.path.exists(DB_PATH): + os.unlink(DB_PATH) + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + Account.createTable(ifNotExists = True) + SMTPAccount.createTable(ifNotExists = True) + del account.hub.threadConnection + + def tearDown(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + SMTPAccount.dropTable(ifExists = True) + Account.dropTable(ifExists = True) + del TheURIOpener.cachedURIs['sqlite://' + DB_URL] + account.hub.threadConnection.close() + del account.hub.threadConnection + if os.path.exists(DB_PATH): + os.unlink(DB_PATH) + + def test_filter(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account12 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + message = Message(from_jid = "user1@test.com", \ + to_jid = "account11@jcl.test.com", \ + stanza_type = "normal", \ + body = "message") + accounts = self.handler.filter(message) + self.assertEquals(accounts.count(), 1) + del account.hub.threadConnection + + def test_filter_wrong_dest(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account12 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + message = Message(from_jid = "user1@test.com", \ + to_jid = "user2%test.com@jcl.test.com", \ + stanza_type = "normal", \ + body = "message") + accounts = self.handler.filter(message) + self.assertEquals(accounts.count(), 0) + del account.hub.threadConnection + + def test_filter_wrong_user(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account12 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + message = Message(from_jid = "user2@test.com", \ + to_jid = "account11@jcl.test.com", \ + stanza_type = "normal", \ + body = "message") + accounts = self.handler.filter(message) + self.assertEquals(accounts.count(), 0) + del account.hub.threadConnection + +class MailHandler_TestCase(unittest.TestCase): + def setUp(self): + self.handler = MailHandler() + if os.path.exists(DB_PATH): + os.unlink(DB_PATH) + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + Account.createTable(ifNotExists = True) + SMTPAccount.createTable(ifNotExists = True) + del account.hub.threadConnection + + def tearDown(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + SMTPAccount.dropTable(ifExists = True) + Account.dropTable(ifExists = True) + del TheURIOpener.cachedURIs['sqlite://' + DB_URL] + account.hub.threadConnection.close() + del account.hub.threadConnection + if os.path.exists(DB_PATH): + os.unlink(DB_PATH) + + def test_filter(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account11.default_account = True + account12 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + message = Message(from_jid = "user1@test.com", \ + to_jid = "user2%test.com@jcl.test.com", \ + stanza_type = "normal", \ + body = "message") + accounts = self.handler.filter(message) + self.assertNotEquals(accounts, None) + self.assertEquals(accounts.count(), 1) + self.assertEquals(accounts[0].name, "account11") + del account.hub.threadConnection + + def test_filter_no_default(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account12 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + message = Message(from_jid = "user1@test.com", \ + to_jid = "user2%test.com@jcl.test.com", \ + stanza_type = "normal", \ + body = "message") + accounts = self.handler.filter(message) + self.assertNotEquals(accounts, None) + self.assertEquals(accounts.count(), 2) + self.assertEquals(accounts[0].name, "account11") + del account.hub.threadConnection + + def test_filter_wrong_dest(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account12 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + message = Message(from_jid = "user1@test.com", \ + to_jid = "user2test.com@jcl.test.com", \ + stanza_type = "normal", \ + body = "message") + accounts = self.handler.filter(message) + self.assertEquals(accounts, None) + del account.hub.threadConnection + + def test_filter_wrong_account(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account11", \ + jid = "account11@jcl.test.com") + account12 = SMTPAccount(user_jid = "user1@test.com", \ + name = "account12", \ + jid = "account12@jcl.test.com") + message = Message(from_jid = "user3@test.com", \ + to_jid = "user2%test.com@jcl.test.com", \ + stanza_type = "normal", \ + body = "message") + try: + accounts = self.handler.filter(message) + except Exception, e: + self.assertNotEquals(e, None) + return + finally: + del account.hub.threadConnection + self.fail("No exception catched") + +class MailSubscribeHandler_TestCase(DefaultSubscribeHandler_TestCase, MailHandler_TestCase): + def setUp(self): + MailHandler_TestCase.setUp(self) + self.handler = MailSubscribeHandler() + +class MailUnsubscribeHandler_TestCase(DefaultUnsubscribeHandler_TestCase, MailHandler_TestCase): + def setUp(self): + MailHandler_TestCase.setUp(self) + self.handler = MailUnsubscribeHandler() + def suite(): - return unittest.makeSuite(MailComponent_TestCase, 'test') + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(MailComponent_TestCase, 'test')) + suite.addTest(unittest.makeSuite(SendMailMessageHandler_TestCase, 'test')) + suite.addTest(unittest.makeSuite(RootSendMailMessageHandler_TestCase, 'test')) + suite.addTest(unittest.makeSuite(MailHandler_TestCase, 'test')) + suite.addTest(unittest.makeSuite(MailUnsubscribeHandler_TestCase, 'test')) + suite.addTest(unittest.makeSuite(MailSubscribeHandler_TestCase, 'test')) + return suite if __name__ == '__main__': unittest.main(defaultTest='suite') diff --git a/src/jmc/model/account.py b/src/jmc/model/account.py index 247755c..65bb99a 100644 --- a/src/jmc/model/account.py +++ b/src/jmc/model/account.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- ## ## account.py ## Login : <dax@happycoders.org> @@ -158,7 +159,7 @@ class MailAccount(PresenceAccount): self.__logger = logging.getLogger("jmc.model.account.MailAccount") self.connection = None self.connected = False - self.default_lang_class = Lang.en # TODO: use String + self.default_lang_class = Lang.en def _get_register_fields(cls, real_class = None): """See Account._get_register_fields @@ -338,7 +339,7 @@ class MailAccount(PresenceAccount): raise NotImplementedError class IMAPAccount(MailAccount): - mailbox = StringCol(default = "INBOX") # TODO : set default INBOX in reg_form (use get_register_fields last field ?) + mailbox = StringCol(default = "INBOX") def _get_register_fields(cls, real_class = None): """See Account._get_register_fields @@ -515,3 +516,59 @@ class POP3Account(MailAccount): self.get_mail_list() self.lastmail = self.nb_mail + +class SMTPAccount(Account): + """Send email account""" + + login = StringCol(default = "") + password = StringCol(default = None) + host = StringCol(default = "localhost") + port = IntCol(default = 110) + ssl = BoolCol(default = False) + store_password = BoolCol(default = True) + waiting_password_reply = BoolCol(default = False) + default_from = StringCol(default = "nobody@localhost") + default_account = BoolCol(default = False) + + def _init(self, *args, **kw): + """SMTPAccount init + Initialize class attributes""" + Account._init(self, *args, **kw) + self.__logger = logging.getLogger("jmc.model.account.SMTPAccount") + + def _get_register_fields(cls, real_class = None): + """See Account._get_register_fields + """ + def password_post_func(password, default_func): + if password is None or password == "": + return None + return password + + if real_class is None: + real_class = cls + return Account.get_register_fields(real_class) + \ + [("login", "text-single", None, \ + lambda field_value, default_func: account.mandatory_field(field_value), \ + lambda : ""), \ + ("password", "text-private", None, password_post_func, \ + lambda : ""), \ + ("host", "text-single", None, \ + lambda field_value, default_func: account.mandatory_field(field_value), \ + lambda : ""), \ + ("port", "text-single", None, \ + account.int_post_func, \ + lambda : real_class.get_default_port()), \ + ("ssl", "boolean", None, \ + account.default_post_func, \ + lambda : False), \ + ("default_from", "text-single", None, \ + lambda field_value, default_func: account.mandatory_field(field_value), \ + lambda : ""), \ + ("store_password", "boolean", None, \ + account.default_post_func, \ + lambda : True)] + + get_register_fields = classmethod(_get_register_fields) + + def send_email(self, to_email, subject, body): + pass diff --git a/src/jmc/model/tests/account.py b/src/jmc/model/tests/account.py index 64f77cc..fa5e96c 100644 --- a/src/jmc/model/tests/account.py +++ b/src/jmc/model/tests/account.py @@ -31,9 +31,9 @@ from sqlobject.dbconnection import TheURIOpener from jcl.model import account from jcl.model.account import Account, PresenceAccount -from jmc.model.account import MailAccount, POP3Account, IMAPAccount +from jmc.model.account import MailAccount, POP3Account, IMAPAccount, SMTPAccount -from jcl.model.tests.account import PresenceAccount_TestCase +from jcl.model.tests.account import Account_TestCase, PresenceAccount_TestCase from jmc.model.tests import email_generator, server if sys.platform == "win32": @@ -388,12 +388,17 @@ class IMAPAccount_TestCase(unittest.TestCase): register_fields = IMAPAccount.get_register_fields() self.assertEquals(len(register_fields), 16) +class SMTPAccount_TestCase(Account_TestCase): + def test_get_register_fields(self): + register_fields = SMTPAccount.get_register_fields() + self.assertEquals(len(register_fields), 7) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(MailAccount_TestCase, 'test')) suite.addTest(unittest.makeSuite(POP3Account_TestCase, 'test')) suite.addTest(unittest.makeSuite(IMAPAccount_TestCase, 'test')) + suite.addTest(unittest.makeSuite(SMTPAccount_TestCase, 'test')) return suite if __name__ == '__main__': diff --git a/src/jmc/runner.py b/src/jmc/runner.py index c103e9f..9d418e1 100644 --- a/src/jmc/runner.py +++ b/src/jmc/runner.py @@ -22,7 +22,7 @@ from jcl.runner import JCLRunner -from jmc.model.account import MailAccount, IMAPAccount, POP3Account +from jmc.model.account import MailAccount, IMAPAccount, POP3Account, SMTPAccount from jmc.jabber.component import MailComponent from jmc.lang import Lang @@ -48,6 +48,7 @@ class JMCRunner(JCLRunner): MailAccount.createTable(ifNotExists = True) IMAPAccount.createTable(ifNotExists = True) POP3Account.createTable(ifNotExists = True) + SMTPAccount.createTable(ifNotExists = True) def run(self): def run_func(): diff --git a/src/jmc/tests/runner.py b/src/jmc/tests/runner.py index 6f818c4..9ae0c3e 100644 --- a/src/jmc/tests/runner.py +++ b/src/jmc/tests/runner.py @@ -33,7 +33,7 @@ from jcl.model.account import Account, PresenceAccount import jmc from jmc.runner import JMCRunner -from jmc.model.account import MailAccount, IMAPAccount, POP3Account +from jmc.model.account import MailAccount, IMAPAccount, POP3Account, SMTPAccount if sys.platform == "win32": DB_PATH = "/c|/temp/test.db" @@ -136,6 +136,7 @@ class JMCRunner_TestCase(JCLRunner_TestCase): MailAccount.dropTable() IMAPAccount.dropTable() POP3Account.dropTable() + SMTPAccount.dropTable() del account.hub.threadConnection os.unlink(DB_PATH) self.assertFalse(os.access("/tmp/jmc.pid", os.F_OK)) |