From e52f9a239a146c06a0683e47eaf9d53f4deb332b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 11 Feb 2015 23:46:42 -0400 Subject: enable --offline mode for email again --- src/leap/bitmask/app.py | 17 +-- src/leap/bitmask/backend/backend_proxy.py | 3 + src/leap/bitmask/backend/components.py | 5 +- src/leap/bitmask/backend/settings.py | 27 +++-- src/leap/bitmask/config/leapsettings.py | 32 ------ src/leap/bitmask/gui/mainwindow.py | 10 +- .../services/soledad/soledadbootstrapper.py | 116 +++++++++++---------- src/leap/bitmask/util/leap_argparse.py | 41 ++++---- 8 files changed, 117 insertions(+), 134 deletions(-) (limited to 'src/leap/bitmask') diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 9056d2a6..72c03cb9 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -127,9 +127,7 @@ def start_app(): } flags.STANDALONE = opts.standalone - # XXX Disabled right now since it's not tested after login refactor - # flags.OFFLINE = opts.offline - flags.OFFLINE = False + flags.OFFLINE = opts.offline flags.MAIL_LOGFILE = opts.mail_log_file flags.APP_VERSION_CHECK = opts.app_version_check flags.API_VERSION_CHECK = opts.api_version_check @@ -139,16 +137,19 @@ def start_app(): flags.CA_CERT_FILE = opts.ca_cert_file replace_stdout = True - if opts.repair or opts.import_maildir: - # We don't want too much clutter on the comand mode - # this could be more generic with a Command class. - replace_stdout = False + + # XXX mail repair commands disabled for now + # if opts.repair or opts.import_maildir: + # We don't want too much clutter on the comand mode + # this could be more generic with a Command class. + # replace_stdout = False logger = create_logger(opts.debug, opts.log_file, replace_stdout) # ok, we got logging in place, we can satisfy mail plumbing requests # and show logs there. it normally will exit there if we got that path. - do_mail_plumbing(opts) + # XXX mail repair commands disabled for now + # do_mail_plumbing(opts) try: event_server.ensure_server(event_server.SERVER_PORT) diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py index 3e79289f..e9ad9b4d 100644 --- a/src/leap/bitmask/backend/backend_proxy.py +++ b/src/leap/bitmask/backend/backend_proxy.py @@ -28,6 +28,7 @@ import time import zmq from leap.bitmask.backend.api import API, STOP_REQUEST, PING_REQUEST +from leap.bitmask.backend.settings import Settings from leap.bitmask.backend.utils import generate_zmq_certificates_if_needed from leap.bitmask.backend.utils import get_backend_certificates @@ -54,6 +55,8 @@ class BackendProxy(object): self._socket = None + self.settings = Settings() + # initialize ZMQ stuff: context = zmq.Context() logger.debug("Connecting to server...") diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index 2ae796f6..a843147e 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -779,6 +779,8 @@ class Soledad(object): """ provider_config = ProviderConfig.get_provider_config(domain) if provider_config is not None: + # XXX FIXME --- remove defer-to-thread or at least put it in a + # separate threadpool. self._soledad_defer = threads.deferToThread( self._soledad_bootstrapper.run_soledad_setup_checks, provider_config, username, password, @@ -816,8 +818,9 @@ class Soledad(object): Signaler.soledad_offline_finished Signaler.soledad_offline_failed """ - self._soledad_bootstrapper.load_offline_soledad( + d = self._soledad_bootstrapper.load_offline_soledad( username, password, uuid) + d.addCallback(self._set_proxies_cb) def cancel_bootstrap(self): """ diff --git a/src/leap/bitmask/backend/settings.py b/src/leap/bitmask/backend/settings.py index 5cb4c616..ed70ca6b 100644 --- a/src/leap/bitmask/backend/settings.py +++ b/src/leap/bitmask/backend/settings.py @@ -122,37 +122,36 @@ class Settings(object): self._settings.set(provider, self.GATEWAY_KEY, gateway) self._save() - def get_uuid(self, username): + def get_uuid(self, full_user_id): """ Gets the uuid for a given username. - :param username: the full user identifier in the form user@provider - :type username: basestring + :param full_user_id: the full user identifier in the form user@provider + :type full_user_id: basestring """ - leap_assert("@" in username, + leap_assert("@" in full_user_id, "Expected username in the form user@provider") - user, provider = username.split('@') + username, provider = full_user_id.split('@') + return self._get_value(provider, full_user_id, "") - return self._get_value(provider, username, "") - - def set_uuid(self, username, value): + def set_uuid(self, full_user_id, value): """ Sets the uuid for a given username. - :param username: the full user identifier in the form user@provider - :type username: str or unicode + :param full_user_id: the full user identifier in the form user@provider + :type full_user_id: str or unicode :param value: the uuid to save or None to remove it :type value: str or unicode or None """ - leap_assert("@" in username, + leap_assert("@" in full_user_id, "Expected username in the form user@provider") - user, provider = username.split('@') + user, provider = full_user_id.split('@') if value is None: - self._settings.remove_option(provider, username) + self._settings.remove_option(provider, full_user_id) else: leap_assert(len(value) > 0, "We cannot save an empty uuid") self._add_section(provider) - self._settings.set(provider, username, value) + self._settings.set(provider, full_user_id, value) self._save() diff --git a/src/leap/bitmask/config/leapsettings.py b/src/leap/bitmask/config/leapsettings.py index 13a1e99e..fd3e1592 100644 --- a/src/leap/bitmask/config/leapsettings.py +++ b/src/leap/bitmask/config/leapsettings.py @@ -353,35 +353,3 @@ class LeapSettings(object): """ leap_assert_type(skip, bool) self._settings.setValue(self.SKIPFIRSTRUN_KEY, skip) - - def get_uuid(self, username): - """ - Gets the uuid for a given username. - - :param username: the full user identifier in the form user@provider - :type username: basestring - """ - leap_assert("@" in username, - "Expected username in the form user@provider") - user, provider = username.split('@') - return self._settings.value( - self.UUIDFORUSER_KEY % (provider, user), "") - - def set_uuid(self, username, value): - """ - Sets the uuid for a given username. - - :param username: the full user identifier in the form user@provider - :type username: str or unicode - :param value: the uuid to save or None to remove it - :type value: str or unicode or None - """ - leap_assert("@" in username, - "Expected username in the form user@provider") - user, provider = username.split('@') - key = self.UUIDFORUSER_KEY % (provider, user) - if value is None: - self._settings.remove(key) - else: - leap_assert(len(value) > 0, "We cannot save an empty uuid") - self._settings.setValue(key, value) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 808b0410..35253ebe 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -126,6 +126,7 @@ class MainWindow(QtGui.QMainWindow, SignalTracker): self._backend = self.app.backend self._leap_signaler = self.app.signaler self._settings = self.app.settings + self._backend_settings = self._backend.settings # Login Widget self._login_widget = LoginWidget(self._backend, @@ -1191,8 +1192,9 @@ class MainWindow(QtGui.QMainWindow, SignalTracker): user = self._login_widget.get_logged_user() # XXX the widget now gives us the full user id. # this is confusing. - #domain = self._providers.get_selected_provider() - #full_user_id = make_address(user, domain) + # domain = self._providers.get_selected_provider() + # full_user_id = make_address(user, domain) + # XXX the casting to str (needed by smtp gateway) should be done # in a better place. self._mail_conductor.userid = str(user) @@ -1313,10 +1315,10 @@ class MainWindow(QtGui.QMainWindow, SignalTracker): if flags.OFFLINE: full_user_id = make_address(username, provider_domain) - uuid = self._settings.get_uuid(full_user_id) + uuid = self._backend_settings.get_uuid(full_user_id) self._mail_conductor.userid = full_user_id - if uuid is None: + if not uuid: # We don't need more visibility at the moment, # this is mostly for internal use/debug for now. logger.warning("Sorry! Log-in at least one time.") diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index 045b2e19..e8946fa4 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -28,6 +28,7 @@ from sqlite3 import ProgrammingError as sqlite_ProgrammingError from u1db import errors as u1db_errors from twisted.internet import threads, defer +import zope.proxy from zope.proxy import sameProxiedObjects from pysqlcipher.dbapi2 import ProgrammingError as sqlcipher_ProgrammingError @@ -59,31 +60,6 @@ They should not be needed after we allow a null remote initialization in the soledad client, and a switch to remote sync-able mode during runtime. """ - -class Mock(object): - """ - A generic simple mock class - """ - def __init__(self, return_value=None): - self._return = return_value - - def __call__(self, *args, **kwargs): - return self._return - - -class MockSharedDB(object): - """ - Mocked SharedDB object to replace in soledad before - instantiating it in offline mode. - """ - get_doc = Mock() - put_doc = Mock() - lock = Mock(return_value=('atoken', 300)) - unlock = Mock(return_value=True) - - def __call__(self): - return self - # TODO these exceptions could be moved to soledad itself # after settling this down. @@ -146,7 +122,6 @@ class SoledadBootstrapper(AbstractBootstrapper): self._provider_config = None self._soledad_config = None - self._keymanager = None self._download_if_needed = False self._user = "" @@ -155,7 +130,9 @@ class SoledadBootstrapper(AbstractBootstrapper): self._uuid = "" self._srpauth = None - self._soledad = None + + self._soledad = zope.proxy.ProxyBase(None) + self._keymanager = zope.proxy.ProxyBase(None) @property def keymanager(self): @@ -190,9 +167,13 @@ class SoledadBootstrapper(AbstractBootstrapper): self._password = password self._uuid = uuid try: - self.load_and_sync_soledad(uuid, offline=True) - self._signaler.signal(self._signaler.soledad_offline_finished) + d = self.load_and_sync_soledad(uuid, offline=True) + d.addCallback( + lambda _: self._signaler.signal( + self._signaler.soledad_offline_finished)) + return d except Exception as e: + # XXX convert exception into failure.trap # TODO: we should handle more specific exceptions in here logger.exception(e) self._signaler.signal(self._signaler.soledad_offline_failed) @@ -248,7 +229,7 @@ class SoledadBootstrapper(AbstractBootstrapper): # warned the user in _do_soledad_sync() def _do_soledad_init(self, uuid, secrets_path, local_db_path, - server_url, cert_file, token): + server_url, cert_file, token, syncable): """ Initialize soledad, retry if necessary and raise an exception if we can't succeed. @@ -274,7 +255,7 @@ class SoledadBootstrapper(AbstractBootstrapper): logger.debug("Trying to init soledad....") self._try_soledad_init( uuid, secrets_path, local_db_path, - server_url, cert_file, token) + server_url, cert_file, token, syncable) logger.debug("Soledad has been initialized.") return except Exception as exc: @@ -296,6 +277,8 @@ class SoledadBootstrapper(AbstractBootstrapper): :type uuid: unicode, or None. :param offline: whether to instantiate soledad for offline use. :type offline: bool + + :rtype: defer.Deferred """ local_param = self._get_soledad_local_params(uuid, offline) remote_param = self._get_soledad_server_params(uuid, offline) @@ -303,21 +286,26 @@ class SoledadBootstrapper(AbstractBootstrapper): secrets_path, local_db_path, token = local_param server_url, cert_file = remote_param - try: - self._do_soledad_init(uuid, secrets_path, local_db_path, - server_url, cert_file, token) - except SoledadInitError: - # re-raise the exceptions from try_init, - # we're currently handling the retries from the - # soledad-launcher in the gui. - raise - - leap_assert(not sameProxiedObjects(self._soledad, None), - "Null soledad, error while initializing") + if offline: + return self._load_soledad_nosync( + uuid, secrets_path, local_db_path, cert_file, token) - if flags.OFFLINE: - self._init_keymanager(self._address, token) else: + # We assume online operation from here on. + # XXX split in subfunction + syncable = True + try: + self._do_soledad_init(uuid, secrets_path, local_db_path, + server_url, cert_file, token, syncable) + except SoledadInitError: + # re-raise the exceptions from try_init, + # we're currently handling the retries from the + # soledad-launcher in the gui. + raise + + leap_assert(not sameProxiedObjects(self._soledad, None), + "Null soledad, error while initializing") + address = make_address( self._user, self._provider_config.get_domain()) @@ -335,9 +323,21 @@ class SoledadBootstrapper(AbstractBootstrapper): address, openpgp.OpenPGPKey, private=True, fetch_remote=False)) d.addCallbacks( + # XXX WTF --- FIXME remove this call to thread, soledad already + # does the sync in its own threadpool. -- kali lambda _: threads.deferToThread(self._do_soledad_sync), key_not_found) d.addErrback(self._soledad_sync_errback) + return d + + def _load_soledad_nosync(self, uuid, secrets_path, local_db_path, + cert_file, token): + + syncable = False + self._do_soledad_init(uuid, secrets_path, local_db_path, + "", cert_file, token, syncable) + d = self._init_keymanager(self._address, token) + return d def _pick_server(self, uuid): """ @@ -410,7 +410,7 @@ class SoledadBootstrapper(AbstractBootstrapper): raise SoledadSyncError() def _try_soledad_init(self, uuid, secrets_path, local_db_path, - server_url, cert_file, auth_token): + server_url, cert_file, auth_token, syncable): """ Try to initialize soledad. @@ -433,9 +433,6 @@ class SoledadBootstrapper(AbstractBootstrapper): # (issue #3309) encoding = sys.getfilesystemencoding() - # XXX We should get a flag in soledad itself - if flags.OFFLINE is True: - Soledad._shared_db = MockSharedDB() try: self._soledad = Soledad( uuid, @@ -445,7 +442,8 @@ class SoledadBootstrapper(AbstractBootstrapper): server_url=server_url, cert_file=cert_file.encode(encoding), auth_token=auth_token, - defer_encryption=True) + defer_encryption=True, + syncable=syncable) # XXX All these errors should be handled by soledad itself, # and return a subclass of SoledadInitializationFailed @@ -668,7 +666,9 @@ class SoledadBootstrapper(AbstractBootstrapper): self._user = user self._password = password - if flags.OFFLINE: + is_offline = flags.OFFLINE + + if is_offline: signal_finished = self._signaler.soledad_offline_finished signal_failed = self._signaler.soledad_offline_failed else: @@ -676,17 +676,23 @@ class SoledadBootstrapper(AbstractBootstrapper): signal_failed = self._signaler.soledad_bootstrap_failed try: - self._download_config() + if not is_offline: + # XXX FIXME make this async too! (use txrequests) + # Also, why the fuck would we want to download it *every time*? + # We should be fine by using last-time config, or at least + # trying it. + self._download_config() - # soledad config is ok, let's proceed to load and sync soledad - uuid = self.srpauth.get_uuid() - self.load_and_sync_soledad(uuid) + # soledad config is ok, let's proceed to load and sync soledad + uuid = self.srpauth.get_uuid() + self.load_and_sync_soledad(uuid) - if not flags.OFFLINE: d = self._gen_key() d.addCallback(lambda _: self._signaler.signal(signal_finished)) + return d else: self._signaler.signal(signal_finished) + return defer.succeed(True) except Exception as e: # TODO: we should handle more specific exceptions in here self._soledad = None diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index 346caed5..12fd9736 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -74,26 +74,27 @@ def build_parser(): help='Verbosity level for openvpn logs [1-6]') # mail stuff - # XXX Disabled right now since it's not tested after login refactor - # parser.add_argument('-o', '--offline', action="store_true", - # help='Starts Bitmask in offline mode: will not ' - # 'try to sync with remote replicas for email.') - - parser.add_argument('--acct', metavar="user@provider", - nargs='?', - action="store", dest="acct", - help='Manipulate mailboxes for this account') - parser.add_argument('-r', '--repair-mailboxes', default=False, - action="store_true", dest="repair", - help='Repair mailboxes for a given account. ' - 'Use when upgrading versions after a schema ' - 'change. Use with --acct') - parser.add_argument('--import-maildir', metavar="/path/to/Maildir", - nargs='?', - action="store", dest="import_maildir", - help='Import the given maildir. Use with the ' - '--to-mbox flag to import to folders other ' - 'than INBOX. Use with --acct') + parser.add_argument('-o', '--offline', action="store_true", + help='Starts Bitmask in offline mode: will not ' + 'try to sync with remote replicas for email.') + + # XXX not yet updated to new mail api for mail 0.4.0 + + # parser.add_argument('--acct', metavar="user@provider", + #nargs='?', + #action="store", dest="acct", + #help='Manipulate mailboxes for this account') + # parser.add_argument('-r', '--repair-mailboxes', default=False, + #action="store_true", dest="repair", + #help='Repair mailboxes for a given account. ' + #'Use when upgrading versions after a schema ' + #'change. Use with --acct') + # parser.add_argument('--import-maildir', metavar="/path/to/Maildir", + #nargs='?', + #action="store", dest="import_maildir", + #help='Import the given maildir. Use with the ' + #'--to-mbox flag to import to folders other ' + #'than INBOX. Use with --acct') if not IS_RELEASE_VERSION: help_text = ("Bypasses the certificate check during provider " -- cgit v1.2.3