diff --git a/run_test.py b/run_tests.py similarity index 69% rename from run_test.py rename to run_tests.py index 1823549..c9efa64 100644 --- a/run_test.py +++ b/run_tests.py @@ -35,11 +35,13 @@ sys.setdefaultencoding('utf8') del sys.setdefaultencoding import tests +from tests.jmc.jabber.test_component import * from tests.jmc.model.test_account import * +from tests.jmc.test_lang import * import jmc import jmc.jabber -#import jmc.jabber.component +import jmc.jabber.component if __name__ == '__main__': logger = logging.getLogger() @@ -49,18 +51,26 @@ if __name__ == '__main__': mail_account_suite = unittest.makeSuite(MailAccount_TestCase, "test") imap_account_suite = unittest.makeSuite(IMAPAccount_TestCase, "test") pop3_account_suite = unittest.makeSuite(POP3Account_TestCase, "test") + lang_suite = unittest.makeSuite(Lang_TestCase, "test") + mail_component_suite = unittest.makeSuite(MailComponent_TestCase, "test") - jmc_suite = unittest.TestSuite() - jmc_suite = unittest.TestSuite((mail_account_suite, \ +# jmc_suite = unittest.TestSuite((mail_component_suite)) +# jmc_suite = unittest.TestSuite() +# jmc_suite.addTest(MailAccount_TestCase('test_get_register_fields')) + jmc_suite = unittest.TestSuite((lang_suite, \ + mail_account_suite, \ imap_account_suite, \ - pop3_account_suite)) + pop3_account_suite, \ + mail_component_suite)) test_support.run_suite(jmc_suite) coverage.stop() -#coverage.analysis(jmc.jabber.component) +coverage.analysis(jmc.jabber.component) +coverage.analysis(jmc.lang) coverage.analysis(jmc.model.account) -coverage.report([ +coverage.report([jmc.jabber.component, \ + jmc.lang, \ jmc.model.account]) diff --git a/src/jmc.py b/src/jmc.py index caf5029..229f0d1 100755 --- a/src/jmc.py +++ b/src/jmc.py @@ -30,17 +30,31 @@ del sys.setdefaultencoding from sqlobject import * from pyxmpp.message import Message +from jcl.model import account +from jcl.model.account import Account, PresenceAccount + from jmc.jabber.component import MailComponent -from jmc.model.account import MailPresenceAccount +from jmc.model.account import MailAccount, IMAPAccount, POP3Account + +DB_PATH = "/tmp/jmc.db" +DB_URL = DB_PATH# + "?debug=1&debugThreading=1" logger = logging.getLogger() logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.DEBUG) + +account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) +Account.createTable(ifNotExists = True) +PresenceAccount.createTable(ifNotExists = True) +MailAccount.createTable(ifNotExists = True) +IMAPAccount.createTable(ifNotExists = True) +POP3Account.createTable(ifNotExists = True) +del account.hub.threadConnection + component = MailComponent("jmc.localhost", \ "secret", \ "127.0.0.1", \ 5349, \ - "sqlite:///tmp/jmc_test.db") -component.account_class = MailPresenceAccount + "sqlite://" + DB_URL) component.run() logger.debug("JMC is exiting") diff --git a/src/jmc/__init__.py b/src/jmc/__init__.py index 7c37883..cd76290 100644 --- a/src/jmc/__init__.py +++ b/src/jmc/__init__.py @@ -1,2 +1,5 @@ """JMC module""" __revision__ = "" + +version = "0.3.0" + diff --git a/src/jmc/jabber/component.py b/src/jmc/jabber/component.py index 58d4b02..3229987 100644 --- a/src/jmc/jabber/component.py +++ b/src/jmc/jabber/component.py @@ -21,925 +21,145 @@ ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## -import re -import signal -import threading -import thread import logging -import sys -import anydbm -import os -import time -import traceback -import jmc.email.mailconnection as mailconnection -from jmc.email.mailconnection import * -from jmc.jabber.x import * -from jmc.utils.storage import * -from jmc.utils.lang import Lang -import jmc.email.mailconnection_factory - -import pyxmpp.jabberd -from pyxmpp.presence import Presence from pyxmpp.message import Message -from pyxmpp.streambase import StreamError, FatalStreamError -from pyxmpp.jid import JID -from pyxmpp.jabber.disco import DiscoItems, DiscoItem, DiscoInfo, DiscoIdentity -from pyxmpp.jabberd.component import Component -VERSION="0.2.2" +from jcl.model.account import PresenceAccount +from jcl.jabber.feeder import FeederComponent, Feeder, Sender -class ComponentFatalError(RuntimeError): - pass +from jmc.model.account import MailAccount, IMAPAccount, POP3Account +from jmc.lang import Lang + +class MailComponent(FeederComponent): + """Jabber Mail Component main implementation""" -class MailComponent(Component): def __init__(self, jid, secret, server, port, - default_lang, - check_interval, - spool_dir, - storage, - name): - Component.__init__(self, \ - JID(jid), \ - secret, \ - server, \ - port, \ - disco_category = "gateway", \ - disco_type = "headline") - self.__logger = logging.getLogger("jmc.jabber.Component") - self.__shutdown = 0 - self.__lang = Lang(default_lang) - self.__name = name - - signal.signal(signal.SIGINT, self.signal_handler) - signal.signal(signal.SIGTERM, self.signal_handler) - - self.__interval = check_interval - spool_dir += "/" + jid + db_connection_str, + lang = Lang()): + """Use FeederComponent behavior and setup feeder and sender + attributes + """ + FeederComponent.__init__(self, \ + jid, \ + secret, \ + server, \ + port, \ + db_connection_str, + lang = lang) + self.name = "Jabber Mail Component" + self.feeder = MailFeeder(self) + self.sender = MailSender(self) + self.account_classes = [IMAPAccount, POP3Account] + +class MailFeeder(Feeder): + """Email check""" + + def __init__(self, component): + """MailFeeder constructor""" + Feeder.__init__(self, component) + self.__logger = logging.getLogger("jmc.jabber.component.MailFeeder") + + def initialize_live_email(self, _account): + """For live email checking account, mark emails received while + offline as read. + Return a boolean to continue mail checking or not (if waiting for password)""" + if _account.password is None: + if not _account.waiting_password_reply: + self.component.ask_password(_account, + _account.default_lang_class) + return False try: - self.__storage = globals()[storage \ - + "Storage"](2, spool_dir = spool_dir) - except: - print >>sys.stderr, "Cannot find " \ - + storage + "Storage class" - sys.exit(1) - # dump registered accounts (save) every hour - self.__count = 60 - self.running = False - - def __del__(self): - logging.shutdown() - - """ Register Form creator """ - def get_reg_form(self, lang_class): - reg_form = X() - reg_form.xmlns = "jabber:x:data" - reg_form.title = lang_class.register_title - reg_form.instructions = lang_class.register_instructions - reg_form.type = "form" - - reg_form.add_field(type = "text-single", \ - label = lang_class.account_name, \ - var = "name") - - reg_form.add_field(type = "text-single", \ - label = lang_class.account_login, \ - var = "login") - - reg_form.add_field(type = "text-private", \ - label = lang_class.account_password, \ - var = "password") - - reg_form.add_field(type = "boolean", \ - label = lang_class.account_password_store, \ - var = "store_password", - value = "1") - - reg_form.add_field(type = "text-single", \ - label = lang_class.account_host, \ - var = "host") - - reg_form.add_field(type = "text-single", \ - label = lang_class.account_port, \ - var = "port") - - field = reg_form.add_field(type = "list-single", \ - label = lang_class.account_type, \ - var = "type") - field.add_option(label = "POP3", \ - value = "pop3") - field.add_option(label = "POP3S", \ - value = "pop3s") - field.add_option(label = "IMAP", \ - value = "imap") - field.add_option(label = "IMAPS", \ - value = "imaps") - - reg_form.add_field(type = "text-single", \ - label = lang_class.account_mailbox, \ - var = "mailbox", \ - value = "INBOX") - - field = reg_form.add_field(type = "list-single", \ - label = lang_class.account_ffc_action, \ - var = "chat_action", \ - value = str(mailconnection.RETRIEVE)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form.add_field(type = "list-single", \ - label = lang_class.account_online_action, \ - var = "online_action", \ - value = str(mailconnection.RETRIEVE)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form.add_field(type = "list-single", \ - label = lang_class.account_away_action, \ - var = "away_action", \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form.add_field(type = "list-single", \ - label = lang_class.account_xa_action, \ - var = "xa_action", \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form.add_field(type = "list-single", \ - label = lang_class.account_dnd_action, \ - var = "dnd_action", \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form.add_field(type = "list-single", \ - label = lang_class.account_offline_action, \ - var = "offline_action", \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - # default interval in config file - reg_form.add_field(type = "text-single", \ - label = lang_class.account_check_interval, \ - var = "interval", \ - value = unicode(self.__interval)) - - reg_form.add_field(type = "boolean", \ - label = lang_class.account_live_email_only, \ - var = "live_email_only") - - return reg_form - - """ Register Form modifier for existing accounts """ - def get_reg_form_init(self, lang_class, jid, name): - if not self.__storage.has_key((jid, name)): - return None - account = self.__storage[(jid, name)] - reg_form_init = X() - reg_form_init.xmlns = "jabber:x:data" - reg_form_init.title = lang_class.update_title - reg_form_init.instructions = lang_class.update_instructions % \ - (name,) - reg_form_init.type = "form" - - reg_form_init.add_field(type = "hidden", \ - label = lang_class.account_name, \ - var = "name", \ - value = name) - - reg_form_init.add_field(type = "text-single", \ - label = lang_class.account_login, \ - var = "login", \ - value = account.login) - - reg_form_init.add_field(type = "text-private", \ - label = lang_class.account_password, \ - var = "password", \ - value = account.password) - - reg_form_init.add_field(type = "boolean", \ - label = lang_class.account_password_store, \ - var = "store_password", \ - value = str(account.store_password).lower()) - - reg_form_init.add_field(type = "text-single", \ - label = lang_class.account_host, \ - var = "host", \ - value = account.host) - - reg_form_init.add_field(type = "text-single", \ - label = lang_class.account_port, \ - var = "port", \ - value = unicode(account.port)) - - field = reg_form_init.add_field(type = "list-single", \ - label = lang_class.account_type, \ - var = "type", \ - value = account.get_type()) - if account.get_type()[0:4] == "imap": - field.add_option(label = "IMAP", \ - value = "imap") - field.add_option(label = "IMAPS", \ - value = "imaps") - field = reg_form_init.add_field(type = "text-single", \ - label = lang_class.account_mailbox, \ - var = "mailbox") - field.value = account.mailbox - else: - field.add_option(label = "POP3", \ - value = "pop3") - field.add_option(label = "POP3S", \ - value = "pop3s") - - - field = reg_form_init.add_field(type = "list-single", \ - label = lang_class.account_ffc_action, \ - var = "chat_action", \ - value = str(account.chat_action)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form_init.add_field(type = "list-single", \ - label = lang_class.account_online_action, \ - var = "online_action", \ - value = str(account.online_action)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form_init.add_field(type = "list-single", \ - label = lang_class.account_away_action, \ - var = "away_action", \ - value = str(account.away_action)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form_init.add_field(type = "list-single", \ - label = lang_class.account_xa_action, \ - var = "xa_action", \ - value = str(account.xa_action)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form_init.add_field(type = "list-single", \ - label = lang_class.account_dnd_action, \ - var = "dnd_action", \ - value = str(account.dnd_action)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - field = reg_form_init.add_field(type = "list-single", \ - label = lang_class.account_offline_action, \ - var = "offline_action", \ - value = str(account.offline_action)) - field.add_option(label = lang_class.action_nothing, \ - value = str(mailconnection.DO_NOTHING)) - field.add_option(label = lang_class.action_digest, \ - value = str(mailconnection.DIGEST)) - field.add_option(label = lang_class.action_retrieve, \ - value = str(mailconnection.RETRIEVE)) - - reg_form_init.add_field(type = "text-single", \ - label = lang_class.account_check_interval, \ - var = "interval", \ - value = str(account.interval)) - - reg_form_init.add_field(type = "boolean", \ - label = lang_class.account_live_email_only, \ - var = "live_email_only", \ - value = str(account.live_email_only).lower()) - - return reg_form_init - - """ Looping method """ - def run(self, timeout): - self.connect() - self.running = True - thread.start_new_thread(self.time_handler, ()) - try: - while (not self.__shutdown and self.stream - and not self.stream.eof and self.stream.socket is not None): + _account.connect() + _account.mark_all_as_read() + _account.disconnect() + _account.first_check = False + _account.in_error = False + return True + except Exception, e: + if _account.connected: try: - self.stream.loop_iter(timeout) - except (KeyboardInterrupt, SystemExit, FatalStreamError, \ - StreamError): - raise + _account.disconnect() except: - self.__logger.exception("Exception cought:") - finally: - ## TODO : for jid in self.__storage.keys(()) - ## for name in self.__storage.keys((jid,)) - self.running = False - if self.stream: - for jid in self.__storage.keys(()): - p = Presence(from_jid = unicode(self.jid), to_jid = jid, \ - stanza_type = "unavailable") - self.stream.send(p) - for jid, name in self.__storage.keys(): - if self.__storage[(jid, name)].status != "offline": - p = Presence(from_jid = name + "@" + unicode(self.jid), \ - to_jid = jid, \ - stanza_type = "unavailable") - self.stream.send(p) - threads = threading.enumerate() - for th in threads: + # We have done everything we could + _account.connected = False + self.component.send_error(_account, e) + return False + + def feed(self, _account): + """Check for new emails for given MailAccount and return a list of + those emails or a summary""" + self.__logger.debug("MailFeeder.feed") + result = [] + if _account.first_check and _account.live_email_only: + continue_checking = self.initialize_live_email(_account) + if not continue_checking: + return [] + _account.lastcheck += 1 + if _account.lastcheck == _account.interval: + _account.lastcheck = 0 + action = _account.action + if action != PresenceAccount.DO_NOTHING: try: - th.join(10 * timeout) - except: - pass - for th in threads: - try: - th.join(timeout) - except: - pass - self.disconnect() - del self.__storage - self.__logger.debug("Exitting normally") + if _account.password is None: + self.component.ask_password(_account, + _account.default_lang_class) + return [] + self.__logger.debug("Checking " + _account.name) + self.__logger.debug("\t" + _account.login \ + + "@" + _account.host) + _account.connect() + mail_list = _account.get_mail_list() + if action == MailAccount.RETRIEVE: + # TODO : use generator (yield) + mail_index = _account.get_next_mail_index(mail_list) + while mail_index is not None: + (body, email_from) = _account.get_mail(mail_index) + result.append(Message(from_jid = _account.jid, \ + to_jid = _account.user_jid, \ + subject = _account.default_lang_class.new_mail_subject % (email_from), \ + body = body)) + mail_index = _account.get_next_mail_index(mail_list) + elif action == MailAccount.DIGEST: + body = "" + new_mail_count = 0 + mail_index = _account.get_next_mail_index(mail_list) + while mail_index is not None: + (tmp_body, from_email) = \ + _account.get_mail_summary(mail_index) + body += tmp_body + "\n----------------------------------\n" + mail_index = _account.get_next_mail_index(mail_list) + new_mail_count += 1 + if body != "": + result.append(Message(from_jid = _account.jid, \ + to_jid = _account.user_jid, \ + stanza_type = "headline", \ + subject = _account.default_lang_class.new_digest_subject % (new_mail_count), \ + body = body)) + else: + raise Exception("Unkown action: " + str(action) + + "\nPlease reconfigure account.") + _account.disconnect() + _account.in_error = False + self.__logger.debug("\nCHECK_MAIL ends " + _account.jid) + except Exception, e: + if _account.connected: + try: + _account.disconnect() + except: + # We have done everything we could + _account.connected = False + self.component.send_error(_account, e) + return result - """ Stop method handler """ - def signal_handler(self, signum, frame): - self.__logger.debug("Signal %i received, shutting down..." % (signum,)) - self.__shutdown = 1 - - """ SIGALRM signal handler """ - def time_handler(self): - self.__logger.debug("Check mail thread started...") - while self.running: - self.check_all_mail() - self.__logger.debug("Resetting alarm signal") - if self.__count == 0: - self.__logger.debug("Dumping registered accounts Database") - self.__storage.sync() - self.__count = 60 - else: - self.__count -= 1 - time.sleep(60) - - """ Component authentication handler """ - def authenticated(self): - self.__logger.debug("AUTHENTICATED") - Component.authenticated(self) - for jid in self.__storage.keys(()): - p = Presence(from_jid = unicode(self.jid), \ - to_jid = jid, stanza_type = "probe") - self.stream.send(p) - for jid, name in self.__storage.keys(): - p = Presence(from_jid = name + "@" + unicode(self.jid), \ - to_jid = jid, stanza_type = "probe") - self.stream.send(p) - - self.stream.set_iq_get_handler("query", "jabber:iq:version", \ - self.get_version) - self.stream.set_iq_get_handler("query", "jabber:iq:register", \ - self.get_register) - self.stream.set_iq_set_handler("query", "jabber:iq:register", \ - self.set_register) - - self.stream.set_presence_handler("available", \ - self.presence_available) - - self.stream.set_presence_handler("probe", \ - self.presence_available) - - self.stream.set_presence_handler("unavailable", \ - self.presence_unavailable) - - self.stream.set_presence_handler("unsubscribe", \ - self.presence_unsubscribe) - self.stream.set_presence_handler("unsubscribed", \ - self.presence_unsubscribed) - self.stream.set_presence_handler("subscribe", \ - self.presence_subscribe) - self.stream.set_presence_handler("subscribed", \ - self.presence_subscribed) - - self.stream.set_message_handler("normal", \ - self.message) +class MailSender(Sender): + """Send emails messages to jabber users""" + + def send(self, to_account, data): + """Send given emails (in data) as Jabber messages""" + pass - def stream_state_changed(self,state,arg): - self.__logger.debug("*** State changed: %s %r ***" % (state,arg)) - - """ Discovery get info handler """ - def disco_get_info(self, node, iq): - self.__logger.debug("DISCO_GET_INFO") - di = DiscoInfo() - if node is None: - di.add_feature("jabber:iq:version") - di.add_feature("jabber:iq:register") - DiscoIdentity(di, self.__name, "headline", "newmail") - else: - di.add_feature("jabber:iq:register") - return di - - """ Discovery get nested nodes handler """ - def disco_get_items(self, node, iq): - self.__logger.debug("DISCO_GET_ITEMS") - lang_class = self.__lang.get_lang_class_from_node(iq.get_node()) - base_from_jid = unicode(iq.get_from().bare()) - di = DiscoItems() - if not node: - for name in self.__storage.keys((base_from_jid,)): - account = self.__storage[(base_from_jid, name)] - str_name = lang_class.connection_label % (account.get_type().upper(), name) - if account.get_type()[0:4] == "imap": - str_name += " (" + account.mailbox + ")" - DiscoItem(di, JID(name + "@" + unicode(self.jid)), \ - name, str_name) - return di - - """ Get Version handler """ - def get_version(self, iq): - self.__logger.debug("GET_VERSION") - iq = iq.make_result_response() - q = iq.new_query("jabber:iq:version") - q.newTextChild(q.ns(), "name", self.__name) - q.newTextChild(q.ns(), "version", VERSION) - self.stream.send(iq) - return 1 - - """ Send back register form to user """ - def get_register(self, iq): - self.__logger.debug("GET_REGISTER") - lang_class = self.__lang.get_lang_class_from_node(iq.get_node()) - base_from_jid = unicode(iq.get_from().bare()) - to = iq.get_to() - iq = iq.make_result_response() - q = iq.new_query("jabber:iq:register") - if to and to != self.jid: - self.get_reg_form_init(lang_class, - base_from_jid, - to.node).attach_xml(q) - else: - self.get_reg_form(lang_class).attach_xml(q) - self.stream.send(iq) - return 1 - - """ Handle user registration response """ - def set_register(self, iq): - self.__logger.debug("SET_REGISTER") - lang_class = self.__lang.get_lang_class_from_node(iq.get_node()) - to = iq.get_to() - from_jid = iq.get_from() - base_from_jid = unicode(from_jid.bare()) - remove = iq.xpath_eval("r:query/r:remove", \ - {"r" : "jabber:iq:register"}) - if remove: - for name in self.__storage.keys((base_from_jid,)): - self.__logger.debug("Deleting " + name + " for " + base_from_jid) - p = Presence(from_jid = name + "@" + unicode(self.jid), \ - to_jid = from_jid, \ - stanza_type = "unsubscribe") - self.stream.send(p) - p = Presence(from_jid = name + "@" + unicode(self.jid), \ - to_jid = from_jid, \ - stanza_type = "unsubscribed") - self.stream.send(p) - del self.__storage[(base_from_jid, name)] - p = Presence(from_jid = self.jid, to_jid = from_jid, \ - stanza_type = "unsubscribe") - self.stream.send(p) - p = Presence(from_jid = self.jid, to_jid = from_jid, \ - stanza_type = "unsubscribed") - self.stream.send(p) - return 1 - - query = iq.get_query() - x = X() - x.from_xml(query.children) - - if x.fields.has_key("name"): - name = x.fields["name"].value.lower() - else: - name = u"" - - if x.fields.has_key("login"): - login = x.fields["login"].value - else: - login = u"" - - if x.fields.has_key("password") and x.fields["password"].value != "": - password = x.fields["password"].value - else: - password = None - - store_password = x.fields.has_key("store_password") \ - and (x.fields["store_password"].value == "1" - or x.fields["store_password"].value.lower() == "true") - - if x.fields.has_key("host"): - host = x.fields["host"].value - else: - host = u"" - - if x.fields.has_key("mailbox"): - mailbox = x.fields["mailbox"].value - else: - mailbox = u"" - - if x.fields.has_key("type"): - type = x.fields["type"].value - else: - type = u"pop3" - - if x.fields.has_key("port") and x.fields["port"].value != "": - port = int(x.fields["port"].value) - else: - port = None - - if x.fields.has_key("chat_action") and x.fields["chat_action"].value != "": - chat_action = int(x.fields["chat_action"].value) - else: - chat_action = mailconnection.DO_NOTHING - - if x.fields.has_key("online_action") and x.fields["online_action"].value != "": - online_action = int(x.fields["online_action"].value) - else: - online_action = mailconnection.DO_NOTHING - - if x.fields.has_key("away_action") and x.fields["away_action"].value != "": - away_action = int(x.fields["away_action"].value) - else: - away_action = mailconnection.DO_NOTHING - - if x.fields.has_key("xa_action") and x.fields["xa_action"].value != "": - xa_action = int(x.fields["xa_action"].value) - else: - xa_action = mailconnection.DO_NOTHING - - if x.fields.has_key("dnd_action") and x.fields["dnd_action"].value != "": - dnd_action = int(x.fields["dnd_action"].value) - else: - dnd_action = mailconnection.DO_NOTHING - - if x.fields.has_key("offline_action") and x.fields["offline_action"].value != "": - offline_action = int(x.fields["offline_action"].value) - else: - offline_action = mailconnection.DO_NOTHING - - if x.fields.has_key("interval") and x.fields["interval"].value != "": - interval = int(x.fields["interval"].value) - else: - interval = None - - live_email_only = x.fields.has_key("live_email_only") \ - and (x.fields["live_email_only"].value == "1" - or x.fields["live_email_only"].value == "true") - - self.__logger.debug(u"New Account: %s, %s, %s, %s, %s, %s, %s, %s %i %i %i %i %i %i %i %s" \ - % (name, login, password, str(store_password), host, str(port), \ - mailbox, type, chat_action, online_action, away_action, \ - xa_action, dnd_action, offline_action, interval, str(live_email_only))) - - iq = iq.make_result_response() - self.stream.send(iq) - - if not self.__storage.has_key((base_from_jid,)): - p = Presence(from_jid = self.jid, to_jid = base_from_jid, \ - stanza_type="subscribe") - self.stream.send(p) - - ## Update account - if port != None: - socket = host + ":" + str(port) - else: - socket = host - if self.__storage.has_key((base_from_jid, name)): - account = self.__storage[(base_from_jid, name)] - m = Message(from_jid = self.jid, to_jid = from_jid, \ - stanza_type = "normal", \ - subject = lang_class.update_account_message_subject \ - % (type, name), \ - body = lang_class.update_account_message_body \ - % (login, password, socket)) - self.stream.send(m) - else: - account = mailconnection_factory.get_new_mail_connection(type) - m = Message(from_jid = self.jid, to_jid = from_jid, \ - stanza_type = "normal", \ - subject = lang_class.new_account_message_subject \ - % (type, name), \ - body = lang_class.new_account_message_body \ - % (login, password, socket)) - self.stream.send(m) - p = Presence(from_jid = name + "@" + unicode(self.jid), \ - to_jid = base_from_jid, \ - stanza_type="subscribe") - self.stream.send(p) - account.login = login - account.password = password - account.store_password = store_password - account.host = host - account.chat_action = chat_action - account.online_action = online_action - account.away_action = away_action - account.xa_action = xa_action - account.dnd_action = dnd_action - account.offline_action = offline_action - account.interval = interval - account.live_email_only = live_email_only - - if port: - account.port = port - - if type[0:4] == "imap": - account.mailbox = mailbox - self.__storage[(base_from_jid, name)] = account - - return 1 - - def __send_presence_available(self, name, to_account, to_jid, show, lang_class): - to_account.default_lang_class = lang_class - to_account.first_check = True - old_status = to_account.status - # Make available to receive mail only when online - if show is None: - to_account.status = "online" # TODO get real status = (not show) - else: - to_account.status = show - p = Presence(from_jid = name + "@" + \ - unicode(self.jid), \ - to_jid = to_jid, \ - status = to_account.get_status_msg(), \ - show = show, \ - stanza_type = "available") - self.stream.send(p) - if to_account.store_password == False \ - and old_status == "offline": - self.__ask_password(name, to_jid, lang_class, to_account) - - """ Handle presence availability """ - def presence_available(self, stanza): - self.__logger.debug("PRESENCE_AVAILABLE") - from_jid = stanza.get_from() - base_from_jid = unicode(from_jid.bare()) - name = stanza.get_to().node - lang_class = self.__lang.get_lang_class_from_node(stanza.get_node()) - show = stanza.get_show() - self.__logger.debug("SHOW : " + str(show)) - if name: - self.__logger.debug("TO : " + name + " " + base_from_jid) - if not name and self.__storage.has_key((base_from_jid,)): - p = Presence(from_jid = self.jid, \ - to_jid = from_jid, \ - status = \ - str(len(self.__storage.keys((base_from_jid,)))) \ - + " accounts registered.", \ - show = show, \ - stanza_type = "available") - self.stream.send(p) - for name in self.__storage.keys((base_from_jid,)): - account = self.__storage[(base_from_jid, name)] - self.__send_presence_available(name, account, from_jid, show, lang_class) - elif self.__storage.has_key((base_from_jid, name)): - account = self.__storage[(base_from_jid, name)] - self.__send_presence_available(name, account, from_jid, show, lang_class) - return 1 - - def __ask_password(self, name, from_jid, lang_class, account): - if not account.waiting_password_reply \ - and account.status != "offline": - account.waiting_password_reply = True - msg = Message(from_jid = name + "@" + unicode(self.jid), \ - to_jid = from_jid, \ - stanza_type = "normal", \ - subject = u"[PASSWORD] " + lang_class.ask_password_subject, \ - body = lang_class.ask_password_body % \ - (account.host, account.login)) - self.stream.send(msg) - - """ handle presence unavailability """ - def presence_unavailable(self, stanza): - self.__logger.debug("PRESENCE_UNAVAILABLE") - from_jid = stanza.get_from() - base_from_jid = unicode(from_jid.bare()) - if stanza.get_to() == unicode(self.jid): - for name in self.__storage.keys((base_from_jid,)): - account = self.__storage[(base_from_jid, name)] - account.status = "offline" - account.waiting_password_reply = False - if account.store_password == False: - self.__logger.debug("Forgetting password") - account.password = None - p = Presence(from_jid = name + "@" + unicode(self.jid), \ - to_jid = from_jid, \ - stanza_type = "unavailable") - self.stream.send(p) - - p = Presence(from_jid = stanza.get_to(), to_jid = from_jid, \ - stanza_type = "unavailable") - self.stream.send(p) - return 1 - - """ handle subscribe presence from user """ - def presence_subscribe(self, stanza): - self.__logger.debug("PRESENCE_SUBSCRIBE") - p = stanza.make_accept_response() - self.stream.send(p) - return 1 - - """ handle subscribed presence from user """ - def presence_subscribed(self, stanza): - self.__logger.debug("PRESENCE_SUBSCRIBED") - name = stanza.get_to().node - from_jid = stanza.get_from() - base_from_jid = unicode(from_jid.bare()) - if name is not None and self.__storage.has_key((base_from_jid, name)): - account = self.__storage[(base_from_jid, name)] - account.status = "online" # TODO retrieve real status - p = Presence(from_jid = stanza.get_to(), to_jid = from_jid, \ - status = account.get_status_msg(), \ - stanza_type = "available") - self.stream.send(p) - return 1 - - """ handle unsubscribe presence from user """ - def presence_unsubscribe(self, stanza): - self.__logger.debug("PRESENCE_UNSUBSCRIBE") - name = stanza.get_to().node - from_jid = stanza.get_from() - base_from_jid = unicode(from_jid.bare()) - if name is not None and self.__storage.has_key((base_from_jid, name)): - del self.__storage[(base_from_jid, name)] - p = Presence(from_jid = stanza.get_to(), to_jid = from_jid, \ - stanza_type = "unsubscribe") - self.stream.send(p) - p = stanza.make_accept_response() - self.stream.send(p) - return 1 - - """ handle unsubscribed presence from user """ - def presence_unsubscribed(self, stanza): - self.__logger.debug("PRESENCE_UNSUBSCRIBED") - p = Presence(from_jid = stanza.get_to(), to_jid = stanza.get_from(), \ - stanza_type = "unavailable") - self.stream.send(p) - self.__storage.sync() - return 1 - - """ Handle new message """ - def message(self, message): - self.__logger.debug("MESSAGE: " + message.get_body()) - lang_class = self.__lang.get_lang_class_from_node(message.get_node()) - name = message.get_to().node - base_from_jid = unicode(message.get_from().bare()) - if re.compile("\[PASSWORD\]").search(message.get_subject()) is not None \ - and self.__storage.has_key((base_from_jid, name)): - account = self.__storage[(base_from_jid, name)] - account.password = message.get_body() - account.waiting_password_reply = False - msg = Message(from_jid = name + "@" + unicode(self.jid), \ - to_jid = message.get_from(), \ - stanza_type = "normal", \ - subject = lang_class.password_saved_for_session, \ - body = lang_class.password_saved_for_session) - self.stream.send(msg) - return 1 - - """ Check mail account """ - def check_mail(self, jid, name): - self.__logger.debug("\nCHECK_MAIL " + unicode(jid) + " " + name) - account = self.__storage[(jid, name)] - action = account.action - - if action != mailconnection.DO_NOTHING: - try: - if account.password is None: - self.__ask_password(name, jid, account.default_lang_class, account) - return - self.__logger.debug("Checking " + name) - self.__logger.debug("\t" + account.login \ - + "@" + account.host) - account.connect() - mail_list = account.get_mail_list() - if action == mailconnection.RETRIEVE: - 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, \ - stanza_type = "normal", \ - subject = account.default_lang_class.new_mail_subject % (email_from), \ - body = body) - self.stream.send(mesg) - mail_index = account.get_next_mail_index(mail_list) - else: - body = "" - new_mail_count = 0 - mail_index = account.get_next_mail_index(mail_list) - while mail_index: - (tmp_body, from_email) = \ - account.get_mail_summary(mail_index) - body += tmp_body + "\n----------------------------------\n" - mail_index = account.get_next_mail_index(mail_list) - new_mail_count += 1 - if body != "": - mesg = Message(from_jid = name + "@" + \ - unicode(self.jid), \ - to_jid = jid, \ - stanza_type = "headline", \ - subject = account.default_lang_class.new_digest_subject % (new_mail_count), \ - body = body) - self.stream.send(mesg) - account.disconnect() - account.in_error = False - self.__logger.debug("\nCHECK_MAIL ends " + unicode(jid) + " " + name) - except Exception,e: - if account.in_error == False: - account.in_error = True - msg = Message(from_jid = name + "@" + unicode(self.jid), \ - to_jid = jid, \ - stanza_type = "error", \ - subject = account.default_lang_class.check_error_subject, \ - body = account.default_lang_class.check_error_body \ - % (e)) - self.stream.send(msg) - type, value, stack = sys.exc_info() - self.__logger.debug("Error while checking mail : %s\n%s" \ - % (e, "".join(traceback.format_exception - (type, value, stack, 5)))) - - """ check mail handler """ - def check_all_mail(self): - self.__logger.debug("CHECK_ALL_MAIL") - for jid, name in self.__storage.keys(): - account = self.__storage[(jid, name)] - if account.first_check and account.live_email_only: - account.first_check = False - if account.password is None: - self.__ask_password(name, jid, account.default_lang_class, account) - return - try: - account.connect() - account.mark_all_as_read() - account.disconnect() - account.in_error = False - except Exception,e: - if account.in_error == False: - account.in_error = True - msg = Message(from_jid = name + "@" + unicode(self.jid), \ - to_jid = jid, \ - stanza_type = "error", \ - subject = account.default_lang_class.check_error_subject, \ - body = account.default_lang_class.check_error_body \ - % (e)) - self.stream.send(msg) - type, value, stack = sys.exc_info() - self.__logger.debug("Error while first checking mail : %s\n%s" \ - % (e, "".join(traceback.format_exception - (type, value, stack, 5)))) - account.lastcheck += 1 - if account.lastcheck == account.interval: - account.lastcheck = 0 - self.check_mail(jid, name) diff --git a/src/jmc/lang.py b/src/jmc/lang.py index 6afdb8b..38591ac 100644 --- a/src/jmc/lang.py +++ b/src/jmc/lang.py @@ -21,31 +21,11 @@ ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## -class Lang: - def __init__(self, default_lang = "en"): - self.default_lang = default_lang +import jcl.lang - def get_lang_from_node(self, node): - lang = node.getLang() - if lang is None: - print "Using default lang " + self.default_lang - lang = self.default_lang - return lang - def get_lang_class(self, lang): - if lang is not None: - lang = lang[:2] - if hasattr(Lang, lang): - return getattr(Lang, lang) - return getattr(Lang, self.default_lang) - - def get_lang_class_from_node(self, node): - return self.get_lang_class(self.get_lang_from_node(node)) - - class en: - register_title = u"Jabber Mail connection registration" - register_instructions = u"Enter connection parameters" - account_name = u"Connection name" +class Lang(jcl.lang.Lang): + class en(jcl.lang.Lang.en): account_login = u"Login" account_password = u"Password" account_password_store = u"Store password on Jabber server?" @@ -70,15 +50,6 @@ class Lang: update_account_message_subject = u"Updated %s connection '%s'" update_account_message_body = u"Registered with username '%s' and " \ "password '%s' on '%s'" - new_account_message_subject = u"New %s connection '%s' created" - new_account_message_body = u"Registered with " \ - "username '%s' and password '%s' on '%s'" - ask_password_subject = u"Password request" - ask_password_body = u"Reply to this message with the password " \ - "for the following account: \n" \ - "\thost = %s\n" \ - "\tlogin = %s\n" - password_saved_for_session = u"Password will be kept during your Jabber session" check_error_subject = u"Error while checking emails." check_error_body = u"An error appears while checking emails:\n\t%s" new_mail_subject = u"New email from %s" diff --git a/src/jmc/model/account.py b/src/jmc/model/account.py index 496423d..3e79ad7 100644 --- a/src/jmc/model/account.py +++ b/src/jmc/model/account.py @@ -33,7 +33,8 @@ import socket from sqlobject.inheritance import InheritableSQLObject from sqlobject.col import StringCol, IntCol, BoolCol -from jcl.model.account import PresenceAccount +from jcl.model import account +from jcl.model.account import Account, PresenceAccount from jmc.lang import Lang IMAP4_TIMEOUT = 10 @@ -148,15 +149,15 @@ class MailAccount(PresenceAccount): lastcheck = IntCol(default = 0) waiting_password_reply = BoolCol(default = False) - in_error = BoolCol(default = False) first_check = BoolCol(default = True) def _init(self, *args, **kw): """MailAccount init Initialize class attributes""" - InheritableSQLObject._init(self, *args, **kw) + PresenceAccount._init(self, *args, **kw) self.__logger = logging.getLogger("jmc.model.account.MailAccount") self.connection = None + self.connected = False self.default_lang_class = Lang.en # TODO: use String def _get_register_fields(cls): @@ -167,7 +168,7 @@ class MailAccount(PresenceAccount): return None return password - return Account.get_register_fields() + \ + return PresenceAccount.get_register_fields() + \ [("login", "text-single", None, account.string_not_null_post_func, \ account.mandatory_field), \ ("password", "text-private", None, password_post_func, \ @@ -314,22 +315,12 @@ class MailAccount(PresenceAccount): def get_next_mail_index(self, mail_list): raise NotImplementedError + def is_mail_list_valid(self, mail_list): + return (mail_list and mail_list != [] and mail_list[0] != '') + # Does not modify server state but just internal JMC state def mark_all_as_read(self): raise NotImplementedError - - def get_action(self): - mapping = {"online": self.online_action, - "chat": self.chat_action, - "away": self.away_action, - "xa": self.xa_action, - "dnd": self.dnd_action, - "offline": self.offline_action} - if mapping.has_key(self.status): - return mapping[self.status] - return PresenceAccount.DO_NOTHING - - action = property(get_action) class IMAPAccount(MailAccount): mailbox = StringCol(default = "INBOX") # TODO : set default INBOX in reg_form (use get_register_fields last field ?) @@ -344,11 +335,7 @@ class IMAPAccount(MailAccount): return MailAccount.get_register_fields() + \ [("mailbox", "text-single", None, account.string_not_null_post_func, \ - (lambda field_name: "INBOX")), \ - ("password", "text-private", None, password_post_func, \ - (lambda field_name: None)), \ - ("store_password", "boolean", None, account.boolean_post_func, \ - lambda field_name: True)] + (lambda field_name: "INBOX"))] get_register_fields = classmethod(_get_register_fields) @@ -375,11 +362,13 @@ class IMAPAccount(MailAccount): else: self.connection = MYIMAP4(self.host, self.port) self.connection.login(self.login, self.password) + self.connected = True def disconnect(self): self.__logger.debug("Disconnecting from IMAP server " \ + self.host) self.connection.logout() + self.connected = False def get_mail_list(self): self.__logger.debug("Getting mail list") @@ -406,9 +395,10 @@ class IMAPAccount(MailAccount): return u"Error while fetching mail " + str(index) def get_next_mail_index(self, mail_list): - if not mail_list or mail_list[0] == '': + if self.is_mail_list_valid(mail_list): + return mail_list.pop(0) + else: return None - return mail_list.pop(0) def mark_all_as_read(self): self.get_mail_list() @@ -443,12 +433,14 @@ class POP3Account(MailAccount): except: self.connection.user(self.login) self.connection.pass_(self.password) + self.connected = True def disconnect(self): self.__logger.debug("Disconnecting from POP3 server " \ + self.host) self.connection.quit() + self.connected = False def get_mail_list(self): self.__logger.debug("Getting mail list") @@ -479,13 +471,16 @@ class POP3Account(MailAccount): return u"Error while fetching mail " + str(index) def get_next_mail_index(self, mail_list): - if self.nb_mail == self.lastmail: + if self.is_mail_list_valid(mail_list): + if self.nb_mail == self.lastmail: + return None + if self.nb_mail < self.lastmail: + self.lastmail = 0 + result = int(mail_list[self.lastmail]) + self.lastmail += 1 + return result + else: return None - if self.nb_mail < self.lastmail: - self.lastmail = 0 - result = int(mail_list[self.lastmail]) - self.lastmail += 1 - return result def mark_all_as_read(self): self.get_mail_list() diff --git a/tests/jmc/dummy_server.py b/tests/jmc/dummy_server.py index 7f48481..1b939aa 100644 --- a/tests/jmc/dummy_server.py +++ b/tests/jmc/dummy_server.py @@ -58,6 +58,7 @@ class DummyServer: try: s = socket.socket(af, socktype, proto) except socket.error, msg: + print >>sys.stderr, msg s = None raise socket.error try: @@ -65,6 +66,7 @@ class DummyServer: s.bind(sa) s.listen(1) except socket.error, msg: + print >>sys.stderr, msg s.close() s = None raise socket.error diff --git a/tests/jmc/jabber/test_component.py b/tests/jmc/jabber/test_component.py index 118ce52..6bcfb73 100644 --- a/tests/jmc/jabber/test_component.py +++ b/tests/jmc/jabber/test_component.py @@ -21,6 +21,471 @@ ## import unittest +import os + +from sqlobject import * +from sqlobject.dbconnection import TheURIOpener + +from jcl.model import account +from jcl.model.account import Account, PresenceAccount + +from jmc.model.account import MailAccount, IMAPAccount, POP3Account +from jmc.jabber.component import MailComponent + + +DB_PATH = "/tmp/test_jmc.db" +DB_URL = DB_PATH# + "?debug=1&debugThreading=1" + +class MockStream(object): + def __init__(self, \ + jid = "", + secret = "", + server = "", + port = "", + keepalive = True): + self.sent = [] + self.connection_started = False + self.connection_stopped = False + self.eof = False + self.socket = [] + + def send(self, iq): + self.sent.append(iq) + + def set_iq_set_handler(self, iq_type, ns, handler): + if not iq_type in ["query"]: + raise Exception("IQ type unknown: " + iq_type) + if not ns in ["jabber:iq:version", \ + "jabber:iq:register", \ + "http://jabber.org/protocol/disco#items", \ + "http://jabber.org/protocol/disco#info"]: + raise Exception("Unknown namespace: " + ns) + if handler is None: + raise Exception("Handler must not be None") + + set_iq_get_handler = set_iq_set_handler + + def set_presence_handler(self, status, handler): + if not status in ["available", \ + "unavailable", \ + "probe", \ + "subscribe", \ + "subscribed", \ + "unsubscribe", \ + "unsubscribed"]: + raise Exception("Status unknown: " + status) + if handler is None: + raise Exception("Handler must not be None") + + def set_message_handler(self, msg_type, handler): + if not msg_type in ["normal"]: + raise Exception("Message type unknown: " + msg_type) + if handler is None: + raise Exception("Handler must not be None") + + def connect(self): + self.connection_started = True + + def disconnect(self): + self.connection_stopped = True + + def loop_iter(self, timeout): + time.sleep(timeout) + + def close(self): + pass + +class MockMailAccount(): + def _init(self): + self.connected = False + self.has_connected = False + self.marked_all_as_read = False + self._action = PresenceAccount.DO_NOTHING + + def connect(self): + self.connected = True + self.has_connected = True + + def mark_all_as_read(self): + self.marked_all_as_read = True + + def disconnect(self): + self.connected = False + + def get_action(self): + return self._action + + action = property(get_action) + +class MockIMAPAccount(MockMailAccount, IMAPAccount): + def _init(self, *args, **kw): + IMAPAccount._init(self, *args, **kw) + MockMailAccount._init(self) + +class MockPOP3Account(MockMailAccount, POP3Account): + def _init(self, *args, **kw): + IMAPAccount._init(self, *args, **kw) + MockMailAccount._init(self) class MailComponent_TestCase(unittest.TestCase): - pass + def setUp(self): + if os.path.exists(DB_PATH): + os.unlink(DB_PATH) + self.comp = MailComponent("jmc.test.com", + "password", + "localhost", + "5347", + 'sqlite://' + DB_URL) + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + Account.createTable(ifNotExists = True) + PresenceAccount.createTable(ifNotExists = True) + MailAccount.createTable(ifNotExists = True) + IMAPAccount.createTable(ifNotExists = True) + POP3Account.createTable(ifNotExists = True) + MockIMAPAccount.createTable(ifNotExists = True) + MockPOP3Account.createTable(ifNotExists = True) + del account.hub.threadConnection + + def tearDown(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + MockPOP3Account.dropTable(ifExists = True) + MockIMAPAccount.dropTable(ifExists = True) + POP3Account.dropTable(ifExists = True) + IMAPAccount.dropTable(ifExists = True) + MailAccount.dropTable(ifExists = True) + PresenceAccount.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) + + ########################################################################### + # 'feed' test methods + ########################################################################### + def test_feed_live_email_init_no_password(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11.status = account.ONLINE + self.assertTrue(account11.first_check) + self.assertFalse(account11.in_error) + self.assertFalse(account11.waiting_password_reply) + account11.live_email_only = True + account11.password = None + result = self.comp.feeder.feed(account11) + self.assertEquals(result, []) + self.assertTrue(account11.first_check) + self.assertTrue(account11.waiting_password_reply) + self.assertFalse(account11.in_error) + self.assertFalse(account11.connected) + self.assertFalse(account11.has_connected) + self.assertFalse(account11.marked_all_as_read) + self.assertEquals(len(self.comp.stream.sent), 1) + del account.hub.threadConnection + + def test_feed_live_email_init_no_password2(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11.status = account.ONLINE + self.assertTrue(account11.first_check) + self.assertFalse(account11.in_error) + account11.waiting_password_reply = True + account11.live_email_only = True + account11.password = None + result = self.comp.feeder.feed(account11) + self.assertEquals(result, []) + self.assertTrue(account11.first_check) + self.assertTrue(account11.waiting_password_reply) + self.assertFalse(account11.in_error) + self.assertFalse(account11.connected) + self.assertFalse(account11.has_connected) + self.assertFalse(account11.marked_all_as_read) + self.assertEquals(len(self.comp.stream.sent), 0) + del account.hub.threadConnection + + def test_feed_interval_no_check(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = PresenceAccount.DO_NOTHING + account11.first_check = False + self.assertEquals(account11.lastcheck, 0) + account11.interval = 2 + result = self.comp.feeder.feed(account11) + self.assertEquals(result, []) + self.assertEquals(account11.lastcheck, 1) + del account.hub.threadConnection + + def test_feed_interval_check(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = PresenceAccount.DO_NOTHING + account11.first_check = False + account11.lastcheck = 1 + account11.interval = 2 + result = self.comp.feeder.feed(account11) + self.assertEquals(result, []) + self.assertEquals(account11.lastcheck, 0) + del account.hub.threadConnection + + def test_feed_no_password(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = MailAccount.RETRIEVE + account11.status = account.ONLINE + account11.first_check = False + account11.lastcheck = 1 + account11.interval = 2 + account11.password = None + self.assertFalse(account11.waiting_password_reply) + result = self.comp.feeder.feed(account11) + self.assertFalse(account11.in_error) + self.assertEquals(result, []) + self.assertEquals(account11.lastcheck, 0) + self.assertFalse(account11.connected) + self.assertFalse(account11.has_connected) + self.assertEquals(len(self.comp.stream.sent), 1) + del account.hub.threadConnection + + def test_feed_unknown_action(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = 42 # Unknown action + account11.status = account.ONLINE + account11.first_check = False + account11.lastcheck = 1 + account11.interval = 2 + account11.password = "password" + account11.get_mail_list = lambda: [] + result = self.comp.feeder.feed(account11) + self.assertTrue(account11.in_error) + self.assertEquals(result, []) + self.assertEquals(account11.lastcheck, 0) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertEquals(len(self.comp.stream.sent), 1) + del account.hub.threadConnection + + def test_feed_retrieve_no_mail(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = MailAccount.RETRIEVE + account11.status = account.ONLINE + account11.first_check = False + account11.lastcheck = 1 + account11.interval = 2 + account11.password = "password" + account11.get_mail_list = lambda: [] + result = self.comp.feeder.feed(account11) + self.assertFalse(account11.in_error) + self.assertEquals(result, []) + self.assertEquals(account11.lastcheck, 0) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertEquals(len(self.comp.stream.sent), 0) + del account.hub.threadConnection + + def test_feed_retrieve_mail(self): + def mock_get_mail(index): + return [("body1", "from1@test.com"), \ + ("body2", "from2@test.com")][index] + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = MailAccount.RETRIEVE + account11.status = account.ONLINE + account11.first_check = False + account11.lastcheck = 1 + account11.interval = 2 + account11.password = "password" + account11.get_mail_list = lambda: [0, 1] + account11.get_mail = mock_get_mail + result = self.comp.feeder.feed(account11) + self.assertFalse(account11.in_error) + self.assertEquals(account11.lastcheck, 0) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertEquals(len(self.comp.stream.sent), 0) + self.assertEquals(len(result), 2) + self.assertEquals(result[0].get_from(), "account11@jmc.test.com") + self.assertEquals(result[0].get_to(), "test1@test.com") + self.assertEquals(result[0].stanza_type, "message") + self.assertEquals(result[0].get_subject(), \ + account11.default_lang_class.new_mail_subject \ + % ("from1@test.com")) + self.assertEquals(result[0].get_body(), "body1") + self.assertEquals(result[1].get_from(), "account11@jmc.test.com") + self.assertEquals(result[1].get_to(), "test1@test.com") + self.assertEquals(result[1].stanza_type, "message") + self.assertEquals(result[1].get_subject(), \ + account11.default_lang_class.new_mail_subject \ + % ("from2@test.com")) + self.assertEquals(result[1].get_body(), "body2") + del account.hub.threadConnection + + def test_feed_digest_no_mail(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = MailAccount.DIGEST + account11.status = account.ONLINE + account11.first_check = False + account11.lastcheck = 1 + account11.interval = 2 + account11.password = "password" + account11.get_mail_list = lambda: [] + result = self.comp.feeder.feed(account11) + self.assertFalse(account11.in_error) + self.assertEquals(result, []) + self.assertEquals(account11.lastcheck, 0) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertEquals(len(self.comp.stream.sent), 0) + del account.hub.threadConnection + + def test_feed_digest_mail(self): + def mock_get_mail_summary(index): + return [("body1", "from1@test.com"), \ + ("body2", "from2@test.com")][index] + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11._action = MailAccount.DIGEST + account11.status = account.ONLINE + account11.first_check = False + account11.lastcheck = 1 + account11.interval = 2 + account11.password = "password" + account11.get_mail_list = lambda: [0, 1] + account11.get_mail_summary = mock_get_mail_summary + result = self.comp.feeder.feed(account11) + self.assertFalse(account11.in_error) + self.assertEquals(account11.lastcheck, 0) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertEquals(len(self.comp.stream.sent), 0) + self.assertEquals(len(result), 1) + self.assertEquals(result[0].get_from(), "account11@jmc.test.com") + self.assertEquals(result[0].get_to(), "test1@test.com") + self.assertEquals(result[0].stanza_type, "message") + self.assertEquals(result[0].get_subject(), \ + account11.default_lang_class.new_digest_subject \ + % (2)) + self.assertEquals(result[0].get_body(), \ + "body1\n----------------------------------\nbody2\n----------------------------------\n") + del account.hub.threadConnection + + ########################################################################### + # 'initialize_live_email' test methods + ########################################################################### + def test_initialize_live_email(self): + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11.status = account.ONLINE + self.assertTrue(account11.first_check) + self.assertFalse(account11.in_error) + account11.live_email_only = True + account11.password = "password" + result = self.comp.feeder.initialize_live_email(account11) + self.assertEquals(result, True) + self.assertFalse(account11.first_check) + self.assertFalse(account11.waiting_password_reply) + self.assertFalse(account11.in_error) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertTrue(account11.marked_all_as_read) + self.assertEquals(len(self.comp.stream.sent), 0) + del account.hub.threadConnection + + def test_initialize_live_email_connection_error(self): + def raiser(): + raise Exception + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11.connect = raiser + account11.status = account.ONLINE + self.assertTrue(account11.first_check) + self.assertFalse(account11.in_error) + account11.live_email_only = True + account11.password = "password" + result = self.comp.feeder.initialize_live_email(account11) + self.assertEquals(result, False) + self.assertTrue(account11.first_check) + self.assertFalse(account11.waiting_password_reply) + self.assertTrue(account11.in_error) + self.assertFalse(account11.connected) + self.assertFalse(account11.has_connected) + self.assertFalse(account11.marked_all_as_read) + self.assertEquals(len(self.comp.stream.sent), 1) + del account.hub.threadConnection + + def test_initialize_live_email_mark_as_read_error(self): + def raiser(): + raise Exception + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11.mark_all_as_read = raiser + account11.status = account.ONLINE + self.assertTrue(account11.first_check) + self.assertFalse(account11.in_error) + account11.live_email_only = True + account11.password = "password" + result = self.comp.feeder.initialize_live_email(account11) + self.assertEquals(result, False) + self.assertTrue(account11.first_check) + self.assertFalse(account11.waiting_password_reply) + self.assertTrue(account11.in_error) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertFalse(account11.marked_all_as_read) + self.assertEquals(len(self.comp.stream.sent), 1) + del account.hub.threadConnection + + def test_initialize_live_email_disconnection_error(self): + def raiser(): + raise Exception + account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) + account11 = MockIMAPAccount(user_jid = "test1@test.com", \ + name = "account11", \ + jid = "account11@jmc.test.com") + account11.disconnect = raiser + account11.status = account.ONLINE + self.assertTrue(account11.first_check) + self.assertFalse(account11.in_error) + account11.live_email_only = True + account11.password = "password" + result = self.comp.feeder.initialize_live_email(account11) + self.assertEquals(result, False) + self.assertTrue(account11.first_check) + self.assertFalse(account11.waiting_password_reply) + self.assertTrue(account11.in_error) + self.assertFalse(account11.connected) + self.assertTrue(account11.has_connected) + self.assertTrue(account11.marked_all_as_read) + self.assertEquals(len(self.comp.stream.sent), 1) + del account.hub.threadConnection diff --git a/tests/jmc/model/test_account.py b/tests/jmc/model/test_account.py index 2f7d4c3..bf69c39 100644 --- a/tests/jmc/model/test_account.py +++ b/tests/jmc/model/test_account.py @@ -139,11 +139,12 @@ class MailAccount_TestCase(unittest.TestCase): u"Encoded multipart3 with no charset (éàê)\n", \ u"encoded from (éàê)")) + def test_get_register_fields(self): + register_fields = MailAccount.get_register_fields() + self.assertEquals(len(register_fields), 14) class POP3Account_TestCase(unittest.TestCase): def setUp(self): - self.server = dummy_server.DummyServer("localhost", 1110) - thread.start_new_thread(self.server.serve, ()) if os.path.exists(DB_PATH): os.unlink(DB_PATH) account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) @@ -177,6 +178,8 @@ class POP3Account_TestCase(unittest.TestCase): def make_test(responses = None, queries = None, core = None): def inner(self): + self.server = dummy_server.DummyServer("localhost", 1110) + thread.start_new_thread(self.server.serve, ()) self.server.responses = ["+OK connected\r\n", \ "+OK name is a valid mailbox\r\n", \ "+OK pass\r\n"] @@ -266,11 +269,12 @@ class POP3Account_TestCase(unittest.TestCase): u"mymessage\n", \ u"user@test.com"))) + def test_get_register_fields(self): + register_fields = POP3Account.get_register_fields() + self.assertEquals(len(register_fields), 14) class IMAPAccount_TestCase(unittest.TestCase): def setUp(self): - self.server = dummy_server.DummyServer("localhost", 1143) - thread.start_new_thread(self.server.serve, ()) if os.path.exists(DB_PATH): os.unlink(DB_PATH) account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) @@ -304,6 +308,8 @@ class IMAPAccount_TestCase(unittest.TestCase): def make_test(responses = None, queries = None, core = None): def inner(self): + self.server = dummy_server.DummyServer("localhost", 1143) + thread.start_new_thread(self.server.serve, ()) self.server.responses = ["* OK [CAPABILITY IMAP4 LOGIN-REFERRALS " + \ "AUTH=PLAIN]\n", \ lambda data: "* CAPABILITY IMAP4 " + \ @@ -372,3 +378,6 @@ class IMAPAccount_TestCase(unittest.TestCase): (u"From : None\nSubject : None\n\nbody text\r\n\n", \ u"None"))) + def test_get_register_fields(self): + register_fields = IMAPAccount.get_register_fields() + self.assertEquals(len(register_fields), 15) diff --git a/tests/jmc/test_lang.py b/tests/jmc/test_lang.py index 1c35a4a..af47ce5 100644 --- a/tests/jmc/test_lang.py +++ b/tests/jmc/test_lang.py @@ -22,7 +22,7 @@ ## import unittest -from jmc.utils.lang import Lang +from jmc.lang import Lang from pyxmpp.iq import Iq