diff options
Diffstat (limited to 'src/leap')
37 files changed, 378 insertions, 488 deletions
diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py index 391aa8df..674634d9 100644 --- a/src/leap/bitmask/bonafide/_protocol.py +++ b/src/leap/bitmask/bonafide/_protocol.py @@ -38,8 +38,6 @@ from twisted.logger import Logger # TODO [ ] enable-disable services # TODO [ ] read provider info -logger = Logger() - COMMANDS = 'signup', 'authenticate', 'logout', 'stats' _preffix = get_path_prefix() @@ -53,6 +51,8 @@ class BonafideProtocol(object): _apis = defaultdict(None) _sessions = defaultdict(None) + log = Logger() + def _get_api(self, provider): # TODO should get deferred if provider.domain in self._apis: @@ -85,7 +85,7 @@ class BonafideProtocol(object): # Service public methods def do_signup(self, full_id, password, invite=None, autoconf=False): - logger.debug('SIGNUP for %s' % full_id) + self.log.debug('SIGNUP for %s' % full_id) _, provider_id = config.get_username_and_provider(full_id) provider = config.Provider(provider_id, autoconf=autoconf) @@ -132,7 +132,7 @@ class BonafideProtocol(object): # TODO -- turn this into JSON response return str(_session.token), str(_session.uuid) - logger.debug('AUTH for %s' % full_id) + self.log.debug('AUTH for %s' % full_id) # XXX get deferred? session = self._get_session(provider, full_id, password) @@ -143,7 +143,7 @@ class BonafideProtocol(object): def do_logout(self, full_id): # XXX use the AVATAR here - logger.debug('LOGOUT for %s' % full_id) + self.log.debug('LOGOUT for %s' % full_id) if (full_id not in self._sessions or not self._sessions[full_id].is_authenticated): return fail(RuntimeError("There is no session for such user")) @@ -162,7 +162,7 @@ class BonafideProtocol(object): return users def do_change_password(self, full_id, current_password, new_password): - logger.debug('change password for %s' % full_id) + self.log.debug('Change password for %s' % full_id) if (full_id not in self._sessions or not self._sessions[full_id].is_authenticated): return fail(RuntimeError("There is no session for such user")) @@ -208,7 +208,7 @@ class BonafideProtocol(object): pass def do_stats(self): - logger.debug('calculating Bonafide Service STATS') + self.log.debug('Calculating Bonafide Service STATS') mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss return {'sessions': len(self._sessions), 'mem': '%s KB' % (mem / 1024)} diff --git a/src/leap/bitmask/bonafide/config.py b/src/leap/bitmask/bonafide/config.py index 1002edbd..b3c35ba1 100644 --- a/src/leap/bitmask/bonafide/config.py +++ b/src/leap/bitmask/bonafide/config.py @@ -40,8 +40,6 @@ from leap.common.check import leap_assert from leap.common.config import get_path_prefix as common_get_path_prefix from leap.common.files import mkdir_p -logger = Logger() - APPNAME = "bonafide" if platform.system() == 'Windows': @@ -158,6 +156,7 @@ def delete_provider(domain): class Provider(object): + # TODO add validation SERVICES_MAP = { @@ -168,6 +167,8 @@ class Provider(object): ongoing_bootstrap = defaultdict(None) stuck_bootstrap = defaultdict(None) + log = Logger() + def __init__(self, domain, autoconf=False, basedir=None, check_certificate=True): # TODO: I need a way to know if it was already configured @@ -196,7 +197,7 @@ class Provider(object): if not is_configured: if autoconf: - logger.debug( + self.log.debug( 'provider %s not configured: downloading files...' % domain) self.bootstrap() @@ -204,7 +205,7 @@ class Provider(object): raise NotConfiguredError("Provider %s is not configured" % (domain,)) else: - logger.debug('provider already initialized') + self.log.debug('Provider already initialized') self.first_bootstrap[self._domain] = defer.succeed( 'already_initialized') self.ongoing_bootstrap[self._domain] = defer.succeed( @@ -251,10 +252,10 @@ class Provider(object): def bootstrap(self): domain = self._domain - logger.debug("bootstrapping provider %s" % domain) + self.log.debug('Bootstrapping provider %s' % domain) ongoing = self.ongoing_bootstrap.get(domain) if ongoing: - logger.debug('already bootstrapping this provider...') + self.log.debug('Already bootstrapping this provider...') return self.first_bootstrap[self._domain] = defer.Deferred() @@ -359,7 +360,7 @@ class Provider(object): # See: # https://leap.se/code/issues/7906 def further_bootstrap_needs_auth(ignored): - logger.warn('cannot download services config yet, need auth') + self.log.warn('Cannot download services config yet, need auth') pending_deferred = defer.Deferred() self.stuck_bootstrap[self._domain] = pending_deferred return defer.succeed('ok for now') @@ -382,9 +383,8 @@ class Provider(object): def workaround_for_config_fetch(failure): # FIXME --- configs.json raises 500, see #7914. # This is a workaround until that's fixed. - logger.error(failure) - logger.debug( - "COULD NOT VERIFY CONFIGS.JSON, WORKAROUND: DIRECT DOWNLOAD") + self.log.debug( + 'COULD NOT VERIFY CONFIGS.JSON, WORKAROUND: DIRECT DOWNLOAD') if 'mx' in self._provider_config.services: soledad_uri = '/1/config/soledad-service.json' @@ -412,7 +412,6 @@ class Provider(object): if stuck: d = self._get_config_for_all_services(session) d.addCallback(lambda _: stuck.callback('continue!')) - d.addErrback(logger.error) return d if not self.has_fetched_services_config(): @@ -497,7 +496,7 @@ class Provider(object): def _load_provider_json(self): path = self._get_provider_json_path() if not is_file(path): - logger.debug("cannot LOAD provider config path %s" % path) + self.log.debug('cannot LOAD provider config path %s' % path) return with open(path, 'r') as config: @@ -535,7 +534,7 @@ class Provider(object): return services_dict def _fetch_provider_configs_unauthenticated(self, uri, path): - logger.info('downloading config for %s...' % uri) + self.log.info('Downloading config for %s...' % uri) d = downloadPage(uri, path, method='GET') return d diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py index 0f9c6281..b9aaf1af 100644 --- a/src/leap/bitmask/bonafide/service.py +++ b/src/leap/bitmask/bonafide/service.py @@ -30,12 +30,13 @@ from twisted.internet import defer from twisted.logger import Logger -logger = Logger() _preffix = get_path_prefix() class BonafideService(HookableService): + log = Logger() + def __init__(self, basedir=None): if not basedir: basedir = os.path.join(_preffix, 'leap') @@ -44,7 +45,7 @@ class BonafideService(HookableService): self.service_hooks = defaultdict(list) def startService(self): - logger.debug('starting Bonafide Service') + self.log.debug('Starting Bonafide Service') super(BonafideService, self).startService() # Commands @@ -58,7 +59,7 @@ class BonafideService(HookableService): def notify_bonafide_auth(result): if not result: msg = "authentication hook did not return anything" - logger.debug(msg) + self.log.debug(msg) raise RuntimeError(msg) token, uuid = result diff --git a/src/leap/bitmask/bonafide/session.py b/src/leap/bitmask/bonafide/session.py index 732afe53..551bfa2e 100644 --- a/src/leap/bitmask/bonafide/session.py +++ b/src/leap/bitmask/bonafide/session.py @@ -24,7 +24,6 @@ from leap.bitmask.bonafide import _srp from leap.bitmask.bonafide import provider from leap.bitmask.bonafide._http import httpRequest, cookieAgentFactory -logger = Logger() OK = 'ok' @@ -45,6 +44,8 @@ def _auth_required(func): class Session(object): + log = Logger() + def __init__(self, credentials, api, provider_cert): # TODO check if an anonymous credentials is passed. # TODO move provider_cert to api object. @@ -91,7 +92,7 @@ class Session(object): def authenticate(self): uri = self._api.get_handshake_uri() met = self._api.get_handshake_method() - logger.debug("%s to %s" % (met, uri)) + self.log.debug('%s to %s' % (met, uri)) params = self._srp_auth.get_handshake_params() handshake = yield self._request(self._agent, uri, values=params, @@ -101,7 +102,7 @@ class Session(object): uri = self._api.get_authenticate_uri(login=self.username) met = self._api.get_authenticate_method() - logger.debug("%s to %s" % (met, uri)) + self.log.debug('%s to %s' % (met, uri)) params = self._srp_auth.get_authentication_params() auth = yield self._request(self._agent, uri, values=params, @@ -119,9 +120,7 @@ class Session(object): def logout(self): uri = self._api.get_logout_uri() met = self._api.get_logout_method() - auth = yield self._request(self._agent, uri, method=met) - print 'AUTH', auth - print 'resetting user/pass' + yield self._request(self._agent, uri, method=met) self.username = None self.password = None self._initialize_session() @@ -211,6 +210,8 @@ if __name__ == "__main__": import sys from twisted.cred.credentials import UsernamePassword + log = Logger() + if len(sys.argv) != 4: print "Usage:", sys.argv[0], "provider", "username", "password" sys.exit() @@ -229,7 +230,7 @@ if __name__ == "__main__": reactor.stop() def auth_eb(failure): - logger.error(failure) + log.error(failure) d = session.authenticate() d.addCallback(print_result) diff --git a/src/leap/bitmask/core/_session.py b/src/leap/bitmask/core/_session.py index 9b22f154..ae992f4a 100644 --- a/src/leap/bitmask/core/_session.py +++ b/src/leap/bitmask/core/_session.py @@ -27,9 +27,6 @@ from twisted.logger import Logger from leap.bitmask.hooks import HookableService -logger = Logger() - - class SessionService(HookableService): """ @@ -49,6 +46,7 @@ class SessionService(HookableService): """ name = 'sessions' + log = Logger() def __init__(self, basedir, tokens): service.Service.__init__(self) @@ -56,7 +54,7 @@ class SessionService(HookableService): self._tokens = tokens def startService(self): - logger.info('starting Session Service') + self.log.info('Starting Session Service') super(SessionService, self).startService() def stopService(self): diff --git a/src/leap/bitmask/core/_zmq.py b/src/leap/bitmask/core/_zmq.py index 30ac743d..af1da9ff 100644 --- a/src/leap/bitmask/core/_zmq.py +++ b/src/leap/bitmask/core/_zmq.py @@ -29,7 +29,7 @@ from leap.bitmask.core import ENDPOINT from leap.bitmask.core.dispatcher import CommandDispatcher -logger = Logger() +log = Logger() class ZMQServerService(service.Service): @@ -64,8 +64,8 @@ class _DispatcherREPConnection(ZmqREPConnection): reactor.callLater(0, self.reply, msgId, str(response)) def log_err(self, failure, msgId): - logger.error(failure) + log.failure('Error on dispatcher') self.defer_reply("ERROR: %r" % failure, msgId) def do_greet(self): - logger.info('starting ZMQ dispatcher') + log.info('Starting ZMQ dispatcher') diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py index fc17b76a..187ebfd5 100644 --- a/src/leap/bitmask/core/dispatcher.py +++ b/src/leap/bitmask/core/dispatcher.py @@ -35,7 +35,7 @@ from leap.common.events import catalog from .api import APICommand, register_method -logger = Logger() +log = Logger() class DispatchError(Exception): @@ -546,11 +546,9 @@ def _format_error(failure): expected = getattr(failure.value, 'expected', False) if not expected: - try: - logger.failure('[DISPATCHER] Unexpected error:') - except: - logger.debug('[DISPATCHER] Unexpected error: %r' % failure.value) - logger.warn(failure.getTraceback()) + log.error('[DISPATCHER] Unexpected error!') + log.error('{0!r}'.format(failure.value)) + log.error(failure.getTraceback()) # if needed, we could add here the exception type as an extra field return json.dumps({'error': failure.value.message, 'result': None}) diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py index 70e7b490..eefbbc5c 100644 --- a/src/leap/bitmask/core/mail_services.py +++ b/src/leap/bitmask/core/mail_services.py @@ -54,9 +54,6 @@ from leap.bitmask.core.uuid_map import UserMap from leap.bitmask.core.configurable import DEFAULT_BASEDIR -logger = Logger() - - class Container(object): def __init__(self, service=None): @@ -152,12 +149,14 @@ def is_service_ready(service, provider): class SoledadService(HookableService): + log = Logger() + def __init__(self, basedir): service.Service.__init__(self) self._basedir = basedir def startService(self): - logger.info('starting Soledad Service') + self.log.info('Starting Soledad Service') self._container = SoledadContainer(service=self) super(SoledadService, self).startService() @@ -174,12 +173,12 @@ class SoledadService(HookableService): password = kw.get('password') uuid = kw.get('uuid') container = self._container - logger.debug("on_passphrase_entry: " - "New Soledad Instance: %s" % userid) + self.log.debug('On_passphrase_entry: ' + 'New Soledad Instance: %s' % userid) if not container.get_instance(userid): container.add_instance(userid, password, uuid=uuid, token=None) else: - logger.debug('service MX is not ready...') + self.log.debug('Service MX is not ready...') def hook_on_bonafide_auth(self, **kw): userid = kw['username'] @@ -195,10 +194,10 @@ class SoledadService(HookableService): container = self._container if container.get_instance(userid): - logger.debug("passing a new SRP Token to Soledad: %s" % userid) + self.log.debug('Passing a new SRP Token to Soledad: %s' % userid) container.set_remote_auth_token(userid, token) else: - logger.debug("adding a new Soledad Instance: %s" % userid) + self.log.debug('Adding a new Soledad Instance: %s' % userid) container.add_instance( userid, password, uuid=uuid, token=token) @@ -210,19 +209,21 @@ class SoledadService(HookableService): password = kw['password'] soledad = self._container.get_instance(userid) if soledad is not None: - logger.info("Change soledad passphrase for %s" % userid) + self.log.info('Change soledad passphrase for %s' % userid) soledad.change_passphrase(unicode(password)) class KeymanagerContainer(Container): + log = Logger() + def __init__(self, service=None, basedir=DEFAULT_BASEDIR): self._basedir = os.path.expanduser(basedir) self._status = {} super(KeymanagerContainer, self).__init__(service=service) def add_instance(self, userid, token, uuid, soledad): - logger.debug("adding Keymanager instance for: %s" % userid) + self.log.debug('Adding Keymanager instance for: %s' % userid) self._set_status(userid, "starting") keymanager = self._create_keymanager_instance( userid, token, uuid, soledad) @@ -252,15 +253,15 @@ class KeymanagerContainer(Container): def _get_or_generate_keys(self, keymanager, userid): def _get_key(_): - logger.info("looking up private key for %s" % userid) + self.log.info('Looking up private key for %s' % userid) return keymanager.get_key(userid, private=True, fetch_remote=False) def _found_key(key): - logger.info("found key: %r" % key) + self.log.info('Found key: %r' % key) def _if_not_found_generate(failure): failure.trap(KeyNotFound) - logger.info("key not found, generating key for %s" % (userid,)) + self.log.info('Key not found, generating key for %s' % (userid,)) self._set_status(userid, "starting", keys="generating") d = keymanager.gen_key() d.addCallbacks(_send_key, _log_key_error("generating")) @@ -272,25 +273,25 @@ class KeymanagerContainer(Container): # but this hasn't been successfully uploaded. How do we know that? # XXX Should this be a method of bonafide instead? # ----------------------------------------------------------------- - logger.info("key generated for %s" % userid) + self.log.info('Key generated for %s' % userid) if not keymanager.token: - logger.debug( - "token not available, scheduling " - "a new key sending attempt...") + self.log.debug( + 'Token not available, scheduling ' + 'a new key sending attempt...') return task.deferLater(reactor, 5, _send_key, None) - logger.info("sending public key to server") + self.log.info('Sending public key to server') d = keymanager.send_key() d.addCallbacks( - lambda _: logger.info("key sent to server"), + lambda _: self.log.info('Key sent to server'), _log_key_error("sending")) return d def _log_key_error(step): def log_error(failure): - logger.error("Error while %s key!" % step) - logger.error(failure) + self.log.error('Error while %s key!' % step) + self.log.failure('error!') error = "Error generating key: %s" % failure.getErrorMessage() self._set_status(userid, "failure", error=error) return failure @@ -298,22 +299,22 @@ class KeymanagerContainer(Container): def _sync_if_never_synced(ever_synced): if ever_synced: - logger.debug("soledad has synced in the past") + self.log.debug('Soledad has synced in the past') return defer.succeed(None) - logger.debug("soledad has never synced") + self.log.debug('Soledad has never synced') if not keymanager.token: - logger.debug("no token to sync now, scheduling a new check") + self.log.debug('No token to sync now, scheduling a new check') d = task.deferLater(reactor, 5, keymanager.ever_synced) d.addCallback(_sync_if_never_synced) return d - logger.debug("syncing soledad for the first time...") + self.log.debug('Syncing soledad for the first time...') self._set_status(userid, "starting", keys="sync") return keymanager._soledad.sync() - logger.debug("checking if soledad has ever synced...") + self.log.debug('Checking if soledad has ever synced...') d = keymanager.ever_synced() d.addCallback(_sync_if_never_synced) d.addCallback(_get_key) @@ -355,13 +356,15 @@ class KeymanagerContainer(Container): class KeymanagerService(HookableService): + log = Logger() + def __init__(self, basedir=DEFAULT_BASEDIR): service.Service.__init__(self) self._basedir = basedir self._container = None def startService(self): - logger.debug('starting Keymanager Service') + self.log.debug('Starting Keymanager Service') self._container = KeymanagerContainer(self._basedir) self._container.service = self self.tokens = {} @@ -376,7 +379,7 @@ class KeymanagerService(HookableService): uuid = kw['uuid'] soledad = kw['soledad'] if not container.get_instance(user): - logger.debug('Adding a new Keymanager instance for %s' % user) + self.log.debug('Adding a new Keymanager instance for %s' % user) if not token: token = self.tokens.get(user) container.add_instance(user, token, uuid, soledad) @@ -393,12 +396,12 @@ class KeymanagerService(HookableService): container = self._container if container.get_instance(userid): - logger.debug( - 'passing a new SRP Token ' + self.log.debug( + 'Passing a new SRP Token ' 'to Keymanager: %s' % userid) container.set_remote_auth_token(userid, token) else: - logger.debug('storing the keymanager token... %s ' % token) + self.log.debug('Storing the keymanager token... %s ' % token) self.tokens[userid] = token # commands @@ -464,6 +467,7 @@ class StandardMailService(service.MultiService, HookableService): """ name = 'mail' + log = Logger() # TODO factor out Mail Service to inside mail package. @@ -487,11 +491,11 @@ class StandardMailService(service.MultiService, HookableService): self.addService(IncomingMailService(self)) def startService(self): - logger.info('starting mail service') + self.log.info('Starting Mail Service') super(StandardMailService, self).startService() def stopService(self): - logger.info('stopping mail service') + self.log.info('Stopping Mail service') super(StandardMailService, self).stopService() def startInstance(self, userid, soledad, keymanager): @@ -541,14 +545,14 @@ class StandardMailService(service.MultiService, HookableService): """ try to turn on incoming mail service for the user that just logged in """ - logger.debug( - 'looking for incoming mail service for auth: %s' % userid) + self.log.debug( + 'Looking for incoming mail service for auth: %s' % userid) multiservice = self.getServiceNamed('incoming_mail') try: incoming = multiservice.getServiceNamed(userid) incoming.startService() except KeyError: - logger.debug('no incoming service for %s' % userid) + self.log.debug('No incoming service for %s' % userid) @defer.inlineCallbacks def _maybe_fetch_smtp_certificate(self, userid): @@ -579,7 +583,7 @@ class StandardMailService(service.MultiService, HookableService): except KeyError: incoming = None if incoming: - logger.debug( + self.log.debug( 'looking for incoming mail service ' 'for logout: %s' % username) incoming.stopService() @@ -624,8 +628,8 @@ class StandardMailService(service.MultiService, HookableService): try: shutil.rmtree(tokens_folder) except OSError as e: - logger.warn("Can't remove tokens folder %s: %s" - % (tokens_folder, e)) + self.log.warn("Can't remove tokens folder %s: %s" + % (tokens_folder, e)) return os.mkdir(tokens_folder, 0700) @@ -646,6 +650,7 @@ class StandardMailService(service.MultiService, HookableService): class IMAPService(service.Service): name = 'imap' + log = Logger() def __init__(self, soledad_sessions): self._soledad_sessions = soledad_sessions @@ -654,7 +659,7 @@ class IMAPService(service.Service): super(IMAPService, self).__init__() def startService(self): - logger.info('starting imap service') + self.log.info('Starting IMAP Service') port, factory = imap_service.run_service( self._soledad_sessions, factory=self._factory) self._port = port @@ -662,7 +667,7 @@ class IMAPService(service.Service): super(IMAPService, self).startService() def stopService(self): - logger.info("stopping imap service") + self.log.info('Stopping IMAP Service') if self._port: self._port.stopListening() self._port = None @@ -680,6 +685,7 @@ class IMAPService(service.Service): class SMTPService(service.Service): name = 'smtp' + log = Logger() def __init__(self, soledad_sessions, keymanager_sessions, sendmail_opts, basedir=DEFAULT_BASEDIR): @@ -693,7 +699,7 @@ class SMTPService(service.Service): super(SMTPService, self).__init__() def startService(self): - logger.info('starting smtp service') + self.log.info('starting smtp service') port, factory = smtp_service.run_service( self._soledad_sessions, self._keymanager_sessions, @@ -704,7 +710,7 @@ class SMTPService(service.Service): super(SMTPService, self).startService() def stopService(self): - logger.info('stopping smtp service') + self.log.info('Stopping SMTP Service') if self._port: self._port.stopListening() self._port = None @@ -725,6 +731,7 @@ class IncomingMailService(service.MultiService): """ name = 'incoming_mail' + log = Logger() def __init__(self, mail_service): super(IncomingMailService, self).__init__() @@ -732,7 +739,7 @@ class IncomingMailService(service.MultiService): self._status = {} def startService(self): - logger.info('starting incoming mail service') + self.log.info('Starting Incoming Mail Service') super(IncomingMailService, self).startService() def stopService(self): @@ -745,7 +752,7 @@ class IncomingMailService(service.MultiService): soledad = self._mail.get_soledad_session(userid) keymanager = self._mail.get_keymanager_session(userid) - logger.info('setting up incoming mail service for %s' % userid) + self.log.info('Setting up Incoming Mail Service for %s' % userid) self._start_incoming_mail_instance( keymanager, soledad, userid) @@ -773,7 +780,7 @@ class IncomingMailService(service.MultiService): keymanager, soledad, inbox, userid, check_period=INCOMING_CHECK_PERIOD) - logger.debug('setting incoming mail service for %s' % userid) + self.log.debug('Setting Incoming Mail Service for %s' % userid) incoming_mail.setName(userid) self.addService(incoming_mail) @@ -791,7 +798,7 @@ class IncomingMailService(service.MultiService): def _errback(self, failure, userid): self._set_status(userid, "failure", error=str(failure)) - logger.error(str(failure)) + self.log.failure('failure!') # -------------------------------------------------------------------- # diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index c06a5343..2972a51c 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -37,9 +37,6 @@ from leap.bitmask.core.web.service import HTTPDispatcherService from leap.bitmask.vpn.service import VPNService from leap.common.events import server as event_server -logger = Logger() - - backend = flags.BACKEND if backend == 'default': @@ -52,6 +49,9 @@ else: raise RuntimeError('Backend not supported') +log = Logger() + + class BitmaskBackend(configurable.ConfigurableService): """ @@ -68,7 +68,8 @@ class BitmaskBackend(configurable.ConfigurableService): # The global token is used for authenticating some of the channels that # expose the dispatcher. For the moment being, this is the REST API. self.global_tokens = [uuid.uuid4().hex] - logger.info('Global token: {0}'.format(self.global_tokens[0])) + log.debug( + 'Global token: {0}'.format(self.global_tokens[0])) self._touch_token_file() # These tokens are user-session tokens. Implemented and rolled back, @@ -129,13 +130,13 @@ class BitmaskBackend(configurable.ConfigurableService): sessions.setServiceParent(self) def _start_child_service(self, name): - logger.debug('starting backend child service: %s' % name) + log.debug('Starting backend child service: %s' % name) service = self.getServiceNamed(name) if service: service.startService() def _stop_child_service(self, name): - logger.debug('stopping backend child service: %s' % name) + log.debug('Stopping backend child service: %s' % name) service = self.getServiceNamed(name) if service: service.stopService() @@ -203,7 +204,6 @@ class BitmaskBackend(configurable.ConfigurableService): try: service = self.getServiceNamed(label) except KeyError: - logger.debug("initializing service: %s" % label) service = klass(*args, **kw) service.setName(label) service.setServiceParent(self) @@ -282,8 +282,6 @@ class BackendCommands(object): return {'version_core': __version__} def do_stats(self): - print "DO STATS" - logger.debug('BitmaskCore Service STATS') mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss return {'mem_usage': '%s MB' % (mem / 1024)} diff --git a/src/leap/bitmask/core/web/api.py b/src/leap/bitmask/core/web/api.py index 01c65bae..7f871711 100644 --- a/src/leap/bitmask/core/web/api.py +++ b/src/leap/bitmask/core/web/api.py @@ -40,7 +40,8 @@ class Api(Resource): d = self.dispatcher.dispatch(command) d.addCallback(self._write_response, request) - d.addErrback(log.error) + d.addErrback( + lambda f: log.error('Error on POST: {0!r}'.format(f))) return NOT_DONE_YET def _write_response(self, response, request): diff --git a/src/leap/bitmask/hooks.py b/src/leap/bitmask/hooks.py index fb9771e9..3e9324e0 100644 --- a/src/leap/bitmask/hooks.py +++ b/src/leap/bitmask/hooks.py @@ -25,7 +25,7 @@ from twisted.logger import Logger from zope.interface import implementer -logger = Logger() +log = Logger() @implementer(IService) @@ -47,7 +47,7 @@ class HookableService(Service): def register_hook(self, name, listener): if not hasattr(self, 'event_listeners'): self.event_listeners = defaultdict(list) - logger.debug('registering hook %s->%s' % (name, listener)) + log.debug('registering hook %s->%s' % (name, listener)) self.event_listeners[name].append(listener) def trigger_hook(self, name, **data): @@ -55,8 +55,8 @@ class HookableService(Service): def react_to_hook(listener, name, **kw): try: getattr(listener, 'hook_' + name)(**kw) - except AttributeError as exc: - logger.failure('Error while triggering hook') + except AttributeError: + log.failure('Error while triggering hook') raise RuntimeError( "Tried to notify a hook, but the listener " "service class %s does not seem to have " diff --git a/src/leap/bitmask/keymanager/__init__.py b/src/leap/bitmask/keymanager/__init__.py index fb4668c7..190e26ff 100644 --- a/src/leap/bitmask/keymanager/__init__.py +++ b/src/leap/bitmask/keymanager/__init__.py @@ -40,12 +40,6 @@ from leap.bitmask.keymanager.refresher import RandomRefreshPublicKey from leap.bitmask.keymanager.validation import ValidationLevels, can_upgrade from leap.bitmask.keymanager.openpgp import OpenPGPScheme -logger = Logger() - - -# -# The Key Manager -# class KeyManager(object): @@ -53,6 +47,8 @@ class KeyManager(object): # server's key storage constants # + log = Logger() + OPENPGP_KEY = 'openpgp' PUBKEY_KEY = "user[public_key]" @@ -94,7 +90,7 @@ class KeyManager(object): try: self._combined_ca_bundle = combined_ca_bundle or create() except Exception: - logger.warn('error while creating combined ca bundle') + self.log.warn('Error while creating combined ca bundle') self._combined_ca_bundle = '' self._async_client = HTTPClient(self._combined_ca_bundle) @@ -155,17 +151,17 @@ class KeyManager(object): except keymanager_errors.KeyNotFound: raise except IOError as e: - logger.warn("HTTP error retrieving key: %r" % (e,)) - logger.warn("%s" % (content,)) + self.log.warn("HTTP error retrieving key: %r" % (e,)) + self.log.warn("%s" % (content,)) raise keymanager_errors.KeyNotFound(e.message), \ None, sys.exc_info()[2] except ValueError as v: - logger.warn("invalid JSON data from key: %s" % (uri,)) + self.log.warn("Invalid JSON data from key: %s" % (uri,)) raise keymanager_errors.KeyNotFound(v.message + ' - ' + uri), \ None, sys.exc_info()[2] except Exception as e: - logger.warn("error retrieving key: %r" % (e,)) + self.log.warn("Error retrieving key: %r" % (e,)) raise keymanager_errors.KeyNotFound(e.message), \ None, sys.exc_info()[2] # Responses are now text/plain, although it's json anyway, but @@ -190,7 +186,7 @@ class KeyManager(object): def check_404(response): if response.code == NOT_FOUND: message = '%s: %s key not found.' % (response.code, address) - logger.warn(message) + self.log.warn(message) raise KeyNotFound(message), None, sys.exc_info()[2] return response @@ -218,7 +214,7 @@ class KeyManager(object): try: content = yield self._async_client.request(str(uri), 'GET') except Exception as e: - logger.warn("There was a problem fetching key: %s" % (e,)) + self.log.warn("There was a problem fetching key: %s" % (e,)) raise keymanager_errors.KeyNotFound(uri) if not content: raise keymanager_errors.KeyNotFound(uri) @@ -307,8 +303,7 @@ class KeyManager(object): :raise UnsupportedKeyTypeError: if invalid key type """ - logger.debug("getting key for %s" % (address,)) - + self.log.debug('Getting key for %s' % (address,)) emit_async(catalog.KEYMANAGER_LOOKING_FOR_KEY, address) def key_found(key): @@ -455,7 +450,6 @@ class KeyManager(object): :raise UnsupportedKeyTypeError: if invalid key type """ - @defer.inlineCallbacks def encrypt(keys): pubkey, signkey = keys @@ -726,7 +720,7 @@ class KeyManager(object): :raise UnsupportedKeyTypeError: if invalid key type """ - logger.info("fetch key for %s from %s" % (address, uri)) + self.log.info('Fetch key for %s from %s' % (address, uri)) key_content = yield self._get_with_combined_ca_bundle(uri) # XXX parse binary keys diff --git a/src/leap/bitmask/keymanager/keys.py b/src/leap/bitmask/keymanager/keys.py index 622c1c68..54609c05 100644 --- a/src/leap/bitmask/keymanager/keys.py +++ b/src/leap/bitmask/keymanager/keys.py @@ -30,7 +30,7 @@ from leap.bitmask.keymanager.wrapper import TempGPGWrapper from leap.bitmask.keymanager.validation import ValidationLevels from leap.bitmask.keymanager import documents as doc -logger = Logger() +log = Logger() # @@ -71,9 +71,9 @@ def build_key_from_dict(key, active=None): try: validation = ValidationLevels.get(active[doc.KEY_VALIDATION_KEY]) except ValueError: - logger.error("Not valid validation level (%s) for key %s", - (active[doc.KEY_VALIDATION_KEY], - active[doc.KEY_FINGERPRINT_KEY])) + log.error('Not valid validation level (%s) for key %s', + (active[doc.KEY_VALIDATION_KEY], + active[doc.KEY_FINGERPRINT_KEY])) last_audited_at = _to_datetime(active[doc.KEY_LAST_AUDITED_AT_KEY]) encr_used = active[doc.KEY_ENCR_USED_KEY] sign_used = active[doc.KEY_SIGN_USED_KEY] @@ -112,6 +112,7 @@ def _to_unix_time(date): class OpenPGPKey(object): + """ Base class for OpenPGP keys. """ @@ -120,6 +121,7 @@ class OpenPGPKey(object): 'private', 'length', 'expiry_date', 'validation', 'last_audited_at', 'refreshed_at', 'encr_used', 'sign_used', '_index', '_gpgbinary') + log = Logger() def __init__(self, address=None, gpgbinary=None, uids=[], fingerprint="", key_data="", private=False, length=0, expiry_date=None, @@ -184,7 +186,7 @@ class OpenPGPKey(object): def merge(self, newkey): if newkey.fingerprint != self.fingerprint: - logger.critical( + self.log.critical( "Can't put a key whith the same key_id and different " "fingerprint: %s, %s" % (newkey.fingerprint, self.fingerprint)) diff --git a/src/leap/bitmask/keymanager/nicknym.py b/src/leap/bitmask/keymanager/nicknym.py index 7c738da0..8e3a08f9 100644 --- a/src/leap/bitmask/keymanager/nicknym.py +++ b/src/leap/bitmask/keymanager/nicknym.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # nicknym.py -# Copyright (C) 2016 LEAP +# Copyright (C) 2016-2017 LEAP # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -17,10 +17,10 @@ import json import sys -import logging import urllib from twisted.internet import defer +from twisted.logger import Logger from twisted.web import client from twisted.web._responses import NOT_FOUND @@ -30,14 +30,14 @@ from leap.common.http import HTTPClient from leap.common.decorators import memoized_method -logger = logging.getLogger(__name__) - - class Nicknym(object): + """ Responsible for communication to the nicknym server. """ + log = Logger() + PUBKEY_KEY = "user[public_key]" def __init__(self, nickserver_uri, ca_cert_path, token): @@ -83,12 +83,12 @@ class Nicknym(object): body=str(data), headers=headers) except Exception as e: - logger.warning("Error uploading key: %r" % (e,)) + self.log.warn('Error uploading key: %r' % (e,)) raise e if 'error' in res: # FIXME: That's a workaround for 500, # we need to implement a readBody to assert response code - logger.warning("Error uploading key: %r" % (res,)) + self.log.warn('Error uploading key: %r' % (res,)) raise Exception(res) @defer.inlineCallbacks @@ -105,19 +105,17 @@ class Nicknym(object): try: content = yield self._fetch_and_handle_404_from_nicknym(uri) json_content = json.loads(content) - except KeyNotFound: raise except IOError as e: - logger.warning("HTTP error retrieving key: %r" % (e,)) - logger.warning("%s" % (content,)) + self.log.warn('HTTP error retrieving key: %r' % (e,)) + self.log.warn("%s" % (content,)) raise KeyNotFound(e.message), None, sys.exc_info()[2] except ValueError as v: - logger.warning("Invalid JSON data from key: %s" % (uri,)) + self.log.warn('Invalid JSON data from key: %s' % (uri,)) raise KeyNotFound(v.message + ' - ' + uri), None, sys.exc_info()[2] - except Exception as e: - logger.warning("Error retrieving key: %r" % (e,)) + self.log.warn('Error retrieving key: %r' % (e,)) raise KeyNotFound(e.message), None, sys.exc_info()[2] # Responses are now text/plain, although it's json anyway, but # this will fail when it shouldn't @@ -141,7 +139,7 @@ class Nicknym(object): if response.code == NOT_FOUND: message = ' %s: Key not found. Request: %s' \ % (response.code, uri) - logger.warning(message) + self.log.warn(message) raise KeyNotFound(message), None, sys.exc_info()[2] return response diff --git a/src/leap/bitmask/keymanager/openpgp.py b/src/leap/bitmask/keymanager/openpgp.py index b72c62ce..8c9dc1e8 100644 --- a/src/leap/bitmask/keymanager/openpgp.py +++ b/src/leap/bitmask/keymanager/openpgp.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # openpgp.py -# Copyright (C) 2013-2016 LEAP +# Copyright (C) 2013-2017 LEAP # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,11 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. + """ Infrastructure for using OpenPGP keys in Key Manager. """ + import os import re import tempfile @@ -74,13 +76,6 @@ except ImportError: return bool(result.key) -logger = Logger() - - -# -# A temporary GPG keyring wrapped to provide OpenPGP functionality. -# - # This function will be used to call blocking GPG functions outside # of Twisted reactor and match the concurrent calls to the amount of CPU cores cpu_core_semaphore = defer.DeferredSemaphore(cpu_count()) @@ -91,16 +86,22 @@ def from_thread(func, *args, **kwargs): return cpu_core_semaphore.run(call) +log = Logger() + + # # The OpenPGP wrapper # class OpenPGPScheme(object): + """ A wrapper for OpenPGP keys management and use (encryption, decyption, signing and verification). """ + log = Logger() + # type used on the soledad documents KEY_TYPE = OpenPGPKey.__name__ ACTIVE_TYPE = KEY_TYPE + KEYMANAGER_ACTIVE_TYPE @@ -186,11 +187,11 @@ class OpenPGPScheme(object): name_real=address, name_email=address, name_comment='') - logger.info("About to generate keys... " - "This might take SOME time.") + self.log.info('About to generate keys... ' + 'This might take SOME time.') yield from_thread(gpg.gen_key, params) - logger.info("Keys for %s have been successfully " - "generated." % (address,)) + self.log.info('Keys for %s have been successfully ' + 'generated.' % (address,)) pubkeys = gpg.list_keys() # assert for new key characteristics @@ -509,8 +510,8 @@ class OpenPGPScheme(object): if len(docs) == 0: raise errors.KeyNotFound(key) elif len(docs) > 1: - logger.warn("There is more than one key for fingerprint %s" - % key.fingerprint) + self.log.warn('There is more than one key for fingerprint %s' + % key.fingerprint) has_deleted = False deferreds = [] @@ -559,7 +560,7 @@ class OpenPGPScheme(object): """ stderr = getattr(result, 'stderr', None) if stderr: - logger.debug("%s" % (stderr,)) + log.debug("%s" % (stderr,)) if getattr(result, 'ok', None) is not True: raise errors.GPGError( 'Failed to encrypt/decrypt: %s' % stderr) @@ -612,7 +613,7 @@ class OpenPGPScheme(object): self._assert_gpg_result_ok(result) defer.returnValue(result.data) except errors.GPGError as e: - logger.warn('Failed to encrypt: %s.' % str(e)) + self.log.warn('Failed to encrypt: %s.' % str(e)) raise errors.EncryptError() @defer.inlineCallbacks @@ -658,7 +659,7 @@ class OpenPGPScheme(object): defer.returnValue((result.data, sign_valid)) except errors.GPGError as e: - logger.warn('Failed to decrypt: %s.' % str(e)) + self.log.warn('Failed to decrypt: %s.' % str(e)) raise errors.DecryptError(str(e)) def is_encrypted(self, data): @@ -803,8 +804,8 @@ class OpenPGPScheme(object): :rtype: Deferred """ def log_key_doc(doc): - logger.error("\t%s: %s" % (doc.content[KEY_UIDS_KEY], - doc.content[KEY_FINGERPRINT_KEY])) + self.log.error("\t%s: %s" % (doc.content[KEY_UIDS_KEY], + doc.content[KEY_FINGERPRINT_KEY])) def cmp_key(d1, d2): return cmp(d1.content[KEY_REFRESHED_AT_KEY], @@ -833,8 +834,8 @@ class OpenPGPScheme(object): pass def log_active_doc(doc): - logger.error("\t%s: %s" % (doc.content[KEY_ADDRESS_KEY], - doc.content[KEY_FINGERPRINT_KEY])) + self.log.error("\t%s: %s" % (doc.content[KEY_ADDRESS_KEY], + doc.content[KEY_FINGERPRINT_KEY])) def cmp_active(d1, d2): # XXX: for private keys it will be nice to check which key is known @@ -857,9 +858,9 @@ class OpenPGPScheme(object): defer.returnValue(doc) def _repair_docs(self, doclist, cmp_func, log_func): - logger.error("BUG ---------------------------------------------------") - logger.error("There is more than one doc of type %s:" - % (doclist[0].content[KEY_TYPE_KEY],)) + self.log.error("BUG -------------------------------------------------") + self.log.error("There is more than one doc of type %s:" + % (doclist[0].content[KEY_TYPE_KEY],)) doclist.sort(cmp=cmp_func, reverse=True) log_func(doclist[0]) @@ -869,9 +870,8 @@ class OpenPGPScheme(object): d = self._soledad.delete_doc(doc) deferreds.append(d) - logger.error("") - logger.error(traceback.extract_stack()) - logger.error("BUG (please report above info) ------------------------") + self.log.error('Error repairing') + self.log.error("BUG (please report above info) ----------------------") d = defer.gatherResults(deferreds, consumeErrors=True) d.addCallback(lambda _: doclist[0]) return d diff --git a/src/leap/bitmask/keymanager/refresher.py b/src/leap/bitmask/keymanager/refresher.py index 694a537f..d1ae9f84 100644 --- a/src/leap/bitmask/keymanager/refresher.py +++ b/src/leap/bitmask/keymanager/refresher.py @@ -28,18 +28,18 @@ from random import choice, randrange DEBUG_STOP_REFRESH = "Stop to refresh the key directory ..." DEBUG_START_REFRESH = "Start to refresh the key directory ..." -ERROR_UNEQUAL_FINGERPRINTS = "[WARNING] Your provider might be cheat " \ - "on you, and gave a wrong key back. " \ - "Fingerprints are unequal, old %s new %s " +ERROR_UNEQUAL_FINGERPRINTS = "[WARNING] Your provider *might* be cheating " \ + "on you, and given a wrong key back. " \ + "Fingerprints do not match: old-> %s, new-> %s " MIN_RANDOM_INTERVAL_RANGE = 4 * 60 # four minutes MAX_RANDOM_INTERVAL_RANGE = 6 * 60 # six minutes -logger = Logger() - class RandomRefreshPublicKey(object): + log = Logger() + def __init__(self, openpgp, keymanager): """ Initialize the RandomRefreshPublicKey. @@ -59,14 +59,14 @@ class RandomRefreshPublicKey(object): :rtype: A deferred. """ self._loop.start(self._get_random_interval_to_refresh(), False) - logger.debug(DEBUG_START_REFRESH) + self.log.debug(DEBUG_START_REFRESH) def stop(self): """ Stop the looping call with random interval. """ self._loop.stop() - logger.debug(DEBUG_STOP_REFRESH) + self.log.debug(DEBUG_STOP_REFRESH) @defer.inlineCallbacks def _get_random_key(self): @@ -110,7 +110,7 @@ class RandomRefreshPublicKey(object): fetch_key_with_fingerprint(old_key.fingerprint) if old_updated_key.fingerprint != old_key.fingerprint: - logger.error(ERROR_UNEQUAL_FINGERPRINTS % + self.log.error(ERROR_UNEQUAL_FINGERPRINTS % (old_key.fingerprint, old_updated_key.fingerprint)) defer.returnValue(None) diff --git a/src/leap/bitmask/keymanager/wrapper.py b/src/leap/bitmask/keymanager/wrapper.py index ef8e6640..eb5074f3 100644 --- a/src/leap/bitmask/keymanager/wrapper.py +++ b/src/leap/bitmask/keymanager/wrapper.py @@ -35,7 +35,6 @@ except ImportError: GNUPG_NG = False -logger = Logger() class TempGPGWrapper(object): @@ -43,6 +42,7 @@ class TempGPGWrapper(object): A context manager that wraps a temporary GPG keyring which only contains the keys given at object creation. """ + log = Logger() def __init__(self, keys=None, gpgbinary=None): """ @@ -160,6 +160,6 @@ class TempGPGWrapper(object): homedir = os.path.normpath(homedir).replace("\\", "/") homedir = str(homedir.replace("c:/", "c://")) if platform.system() == "Windows": - logger.error("BUG! Not erasing folder in Windows") + self.log.error("BUG! Not erasing folder in Windows") return shutil.rmtree(homedir) diff --git a/src/leap/bitmask/mail/adaptors/soledad.py b/src/leap/bitmask/mail/adaptors/soledad.py index 6a057d65..a3e011a3 100644 --- a/src/leap/bitmask/mail/adaptors/soledad.py +++ b/src/leap/bitmask/mail/adaptors/soledad.py @@ -41,8 +41,6 @@ from leap.soledad.common import l2db from leap.soledad.common.document import SoledadDocument -logger = Logger() - # TODO # [ ] Convenience function to create mail specifying subject, date, etc? @@ -67,6 +65,7 @@ def cleanup_deferred_locks(): class SoledadDocumentWrapper(models.DocumentWrapper): + """ A Wrapper object that can be manipulated, passed around, and serialized in a format that the Soledad Store understands. @@ -74,6 +73,9 @@ class SoledadDocumentWrapper(models.DocumentWrapper): It ensures atomicity of the document operations on creation, update and deletion. """ + + log = Logger() + # TODO we could also use a _dirty flag (in models) # TODO add a get_count() method ??? -- that is extended over l2db. @@ -186,7 +188,7 @@ class SoledadDocumentWrapper(models.DocumentWrapper): # error, we should mark them in the copy so there is no attempt to # create/update them. failure.trap(l2db.errors.RevisionConflict) - logger.debug("got conflict while putting %s" % doc_id) + self.log.debug('Got conflict while putting %s' % doc_id) def delete(self, store): """ @@ -506,7 +508,7 @@ class MessageWrapper(object): for (key, doc) in cdocs.items()]) for doc_id, cdoc in zip(self.mdoc.cdocs, self.cdocs.values()): if cdoc.raw == "": - logger.warn("Empty raw field in cdoc %s" % doc_id) + self.log.warn('Empty raw field in cdoc %s' % doc_id) cdoc.set_future_doc_id(doc_id) def create(self, store, notify_just_mdoc=False, pending_inserts_dict=None): @@ -588,7 +590,7 @@ class MessageWrapper(object): self.d.append(cdoc.create(store)) def log_all_inserted(result): - logger.debug("All parts inserted for msg!") + self.log.debug('All parts inserted for msg!') return result self.all_inserted_d = defer.gatherResults(self.d, consumeErrors=True) @@ -850,6 +852,8 @@ class SoledadMailAdaptor(SoledadIndexMixin): mboxwrapper_klass = MailboxWrapper + log = Logger() + def __init__(self): SoledadIndexMixin.__init__(self) @@ -948,15 +952,15 @@ class SoledadMailAdaptor(SoledadIndexMixin): # See https://leap.se/code/issues/7495. # This avoids blocks, but the real cause still needs to be # isolated (0.9.0rc3) -- kali - logger.debug("BUG ------------------------------------------") - logger.debug( + self.log.debug("BUG ------------------------------------------") + self.log.debug( "BUG: Error while retrieving part docs for mdoc id %s" % mdoc_id) - logger.debug("BUG (please report above info) ---------------") + self.log.debug("BUG (please report above info) ---------------") return [] def _err_log_cannot_find_msg(failure): - logger.error("BUG: Error while getting msg (uid=%s)" % uid) + self.log.error('BUG: Error while getting msg (uid=%s)' % uid) return None if get_cdocs: @@ -1089,7 +1093,7 @@ class SoledadMailAdaptor(SoledadIndexMixin): unseen_index = indexes.TYPE_MBOX_SEEN_IDX d = store.get_count_from_index(unseen_index, type_, uuid, "0") - d.addErrback(self._errback) + d.addErrback(lambda f: self.log.error('Error on count_unseen')) return d def get_count_recent(self, store, mbox_uuid): @@ -1106,7 +1110,7 @@ class SoledadMailAdaptor(SoledadIndexMixin): recent_index = indexes.TYPE_MBOX_RECENT_IDX d = store.get_count_from_index(recent_index, type_, uuid, "1") - d.addErrback(self._errback) + d.addErrback(lambda f: self.log.error('Error on count_recent')) return d # search api @@ -1124,7 +1128,7 @@ class SoledadMailAdaptor(SoledadIndexMixin): def get_mdoc_id(hdoc): if not hdoc: - logger.warn("Could not find a HDOC with MSGID %s" % msgid) + self.log.warn("Could not find a HDOC with MSGID %s" % msgid) return None hdoc = hdoc[0] mdoc_id = hdoc.doc_id.replace("H-", "M-%s-" % uuid) @@ -1175,9 +1179,6 @@ class SoledadMailAdaptor(SoledadIndexMixin): """ return MailboxWrapper.get_all(store) - def _errback(self, failure): - logger.failure() - def _split_into_parts(raw): # TODO signal that we can delete the original message!----- diff --git a/src/leap/bitmask/mail/imap/account.py b/src/leap/bitmask/mail/imap/account.py index 63d214e4..9b26dbaa 100644 --- a/src/leap/bitmask/mail/imap/account.py +++ b/src/leap/bitmask/mail/imap/account.py @@ -32,16 +32,6 @@ from leap.bitmask.mail.mail import Account from leap.bitmask.mail.imap.mailbox import IMAPMailbox, normalize_mailbox from leap.soledad.client import Soledad -logger = Logger() - -PROFILE_CMD = os.environ.get('LEAP_PROFILE_IMAPCMD', False) - -if PROFILE_CMD: - def _debugProfiling(result, cmdname, start): - took = (time.time() - start) * 1000 - logger.debug("CMD " + cmdname + " TOOK: " + str(took) + " msec") - return result - ####################################### # Soledad IMAP Account @@ -49,6 +39,7 @@ if PROFILE_CMD: class IMAPAccount(object): + """ An implementation of an imap4 Account that is backed by Soledad Encrypted Documents. @@ -57,6 +48,7 @@ class IMAPAccount(object): implements(imap4.IAccount, imap4.INamespacePresenter) selected = None + log = Logger() def __init__(self, store, user_id, d=defer.Deferred()): """ @@ -246,7 +238,7 @@ class IMAPAccount(object): def check_it_exists(mailboxes): if name not in mailboxes: - logger.warn('SELECT: No such mailbox!') + self.log.warn('SELECT: No such mailbox!') return None return name diff --git a/src/leap/bitmask/mail/imap/mailbox.py b/src/leap/bitmask/mail/imap/mailbox.py index 836b38fd..cc5d5df6 100644 --- a/src/leap/bitmask/mail/imap/mailbox.py +++ b/src/leap/bitmask/mail/imap/mailbox.py @@ -39,38 +39,11 @@ from leap.common.check import leap_assert_type from leap.bitmask.mail.constants import INBOX_NAME, MessageFlags from leap.bitmask.mail.imap.messages import IMAPMessage -logger = Logger() - # TODO LIST -# [ ] Restore profile_cmd instrumentation # [ ] finish the implementation of IMailboxListener # [ ] implement the rest of ISearchableMailbox -""" -If the environment variable `LEAP_SKIPNOTIFY` is set, we avoid -notifying clients of new messages. Use during stress tests. -""" -NOTIFY_NEW = not os.environ.get('LEAP_SKIPNOTIFY', False) -PROFILE_CMD = os.environ.get('LEAP_PROFILE_IMAPCMD', False) - -if PROFILE_CMD: - - def _debugProfiling(result, cmdname, start): - took = (time.time() - start) * 1000 - logger.debug("CMD " + cmdname + " TOOK: " + str(took) + " msec") - return result - - def do_profile_cmd(d, name): - """ - Add the profiling debug to the passed callback. - :param d: deferred - :param name: name of the command - :type name: str - """ - d.addCallback(_debugProfiling, name, time.time()) - d.addErrback(lambda f: logger.error(f.getTraceback())) - INIT_FLAGS = (MessageFlags.SEEN_FLAG, MessageFlags.ANSWERED_FLAG, MessageFlags.FLAGGED_FLAG, MessageFlags.DELETED_FLAG, MessageFlags.DRAFT_FLAG, MessageFlags.RECENT_FLAG, @@ -107,6 +80,7 @@ def make_collection_listener(mailbox): class IMAPMailbox(object): + """ A Soledad-backed IMAP mailbox. @@ -115,6 +89,7 @@ class IMAPMailbox(object): MessageCollection class. We receive an instance of it and it is made accessible in the `collection` attribute. """ + implements( imap4.IMailbox, imap4.IMailboxInfo, @@ -133,6 +108,8 @@ class IMAPMailbox(object): CMD_UIDVALIDITY = "UIDVALIDITY" CMD_UNSEEN = "UNSEEN" + log = Logger() + # TODO we should turn this into a datastructure with limited capacity _listeners = defaultdict(set) @@ -184,12 +161,10 @@ class IMAPMailbox(object): :param listener: listener to add :type listener: an object that implements IMailboxListener """ - if not NOTIFY_NEW: - return listeners = self.listeners - logger.debug('adding mailbox listener: %s. Total: %s' % ( - listener, len(listeners))) + self.log.debug('Adding mailbox listener: %s. Total: %s' % ( + listener, len(listeners))) listeners.add(listener) def removeListener(self, listener): @@ -407,7 +382,8 @@ class IMAPMailbox(object): d = self.collection.add_msg(message, flags, date=date, notify_just_mdoc=notify_just_mdoc) - d.addErrback(lambda failure: logger.error(failure)) + d.addErrback( + lambda failure: self.log.failure('Error while adding msg')) return d def notify_new(self, *args): @@ -432,7 +408,7 @@ class IMAPMailbox(object): d = self._get_notify_count() d.addCallback(cbNotifyNew) d.addCallback(self.collection.cb_signal_unread_to_ui) - d.addErrback(lambda failure: logger.error(failure)) + d.addErrback(lambda failure: self.log.failure('Error while notify')) def _get_notify_count(self): """ @@ -451,7 +427,7 @@ class IMAPMailbox(object): def log_num_msg(result): exists, recent = tuple(result) - logger.debug("NOTIFY (%r): there are %s messages, %s recent" % ( + self.log.debug("NOTIFY (%r): there are %s messages, %s recent" % ( self.mbox_name, exists, recent)) return result @@ -515,7 +491,8 @@ class IMAPMailbox(object): d = self._bound_seq(messages_asked, uid) if uid: d.addCallback(get_range) - d.addErrback(lambda f: logger.error(f)) + d.addErrback( + lambda f: self.log.failure('Error getting msg range')) return d def _bound_seq(self, messages_asked, uid): @@ -619,12 +596,15 @@ class IMAPMailbox(object): d = defer.gatherResults(d_msg, consumeErrors=True) d.addCallback(_get_imap_msg) d.addCallback(_zip_msgid) - d.addErrback(lambda failure: logger.error(failure)) + d.addErrback( + lambda failure: self.log.error( + 'Error getting msg for range')) return d d = self._get_messages_range(messages_asked, uid) d.addCallback(get_imap_messages_for_range) - d.addErrback(lambda failure: logger.error(failure)) + d.addErrback( + lambda failure: self.log.failure('Error on fetch')) return d def fetch_flags(self, messages_asked, uid): @@ -802,17 +782,15 @@ class IMAPMailbox(object): read-write. """ if not self.isWriteable(): - logger.info('read only mailbox!') + self.log.info('Read only mailbox!') raise imap4.ReadOnlyMailbox d = defer.Deferred() reactor.callLater(0, self._do_store, messages_asked, flags, mode, uid, d) - if PROFILE_CMD: - do_profile_cmd(d, "STORE") d.addCallback(self.collection.cb_signal_unread_to_ui) - d.addErrback(lambda f: logger.error(f)) + d.addErrback(lambda f: self.log.error('Error on store')) return d def _do_store(self, messages_asked, flags, mode, uid, observer): @@ -888,14 +866,14 @@ class IMAPMailbox(object): if len(query) > 2: if query[1] == 'HEADER' and query[2].lower() == "message-id": msgid = str(query[3]).strip() - logger.debug("Searching for %s" % (msgid,)) + self.log.debug('Searching for %s' % (msgid,)) d = self.collection.get_uid_from_msgid(str(msgid)) d.addCallback(lambda result: [result]) return d # nothing implemented for any other query - logger.warn("Cannot process query: %s" % (query,)) + self.log.warn('Cannot process query: %s' % (query,)) return [] # IMessageCopier diff --git a/src/leap/bitmask/mail/imap/server.py b/src/leap/bitmask/mail/imap/server.py index 897e1f05..9eef6e83 100644 --- a/src/leap/bitmask/mail/imap/server.py +++ b/src/leap/bitmask/mail/imap/server.py @@ -32,10 +32,8 @@ from twisted.mail.imap4 import LiteralString, LiteralFile from leap.common.events import emit_async, catalog -logger = Logger() - - def _getContentType(msg): + """ Return a two-tuple of the main and subtype of the given message. """ @@ -62,16 +60,21 @@ def _getContentType(msg): major = minor = None return major, minor, attrs + # Monkey-patch _getContentType to avoid bug that passes lower-case boundary in # BODYSTRUCTURE response. + imap4._getContentType = _getContentType class LEAPIMAPServer(imap4.IMAP4Server): + """ An IMAP4 Server with a LEAP Storage Backend. """ + log = Logger() + ############################################################# # # Twisted imap4 patch to workaround bad mime rendering in TB. @@ -167,7 +170,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): msg = line[:7] + " [...]" else: msg = copy(line) - logger.debug('rcv (%s): %s' % (self.state, msg)) + self.log.debug('rcv (%s): %s' % (self.state, msg)) imap4.IMAP4Server.lineReceived(self, line) def close_server_connection(self): @@ -452,7 +455,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): def _subscribeEb(failure): m = failure.value - logger.error() + self.log.error('Error on SUBSCRIBE') if failure.check(imap4.MailboxException): self.sendNegativeResponse(tag, str(m)) else: @@ -477,7 +480,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): def _unsubscribeEb(failure): m = failure.value - logger.error() + self.log.error('Error on UNSUBSCRIPBE') if failure.check(imap4.MailboxException): self.sendNegativeResponse(tag, str(m)) else: @@ -512,7 +515,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): elif failure.check(imap4.MailboxException): self.sendNegativeResponse(tag, str(m)) else: - logger.error() + self.log.error('Error on RENAME') self.sendBadResponse( tag, "Server error encountered while " @@ -539,7 +542,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): if failure.check(imap4.MailboxException): self.sendNegativeResponse(tag, str(c)) else: - logger.error() + self.log.error('Error on CREATE') self.sendBadResponse( tag, "Server error encountered while creating mailbox") @@ -564,7 +567,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): if failure.check(imap4.MailboxException): self.sendNegativeResponse(tag, str(m)) else: - logger.error() + self.log.error('Error on DELETE') self.sendBadResponse( tag, "Server error encountered while deleting mailbox") @@ -600,7 +603,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): def _ebAppendGotMailbox(self, failure, tag): self.sendBadResponse( tag, "Server error encountered while opening mailbox.") - logger.error(failure) + self.log.failure('Error appending') def __cbAppend(self, result, tag, mbox): diff --git a/src/leap/bitmask/mail/imap/service/__init__.py b/src/leap/bitmask/mail/imap/service/__init__.py index 880ff238..b3673c2e 100644 --- a/src/leap/bitmask/mail/imap/service/__init__.py +++ b/src/leap/bitmask/mail/imap/service/__init__.py @@ -37,7 +37,7 @@ from leap.bitmask.mail.imap.server import LEAPIMAPServer # TODO: leave only an implementor of IService in here -logger = Logger() +log = Logger() DO_MANHOLE = os.environ.get("LEAP_MAIL_MANHOLE", None) if DO_MANHOLE: @@ -137,23 +137,15 @@ class LeapIMAPFactory(ServerFactory): :param addr: remote ip address :type addr: str """ - # TODO should reject anything from addr != localhost, - # just in case. - logger.debug("Building protocol for connection %s" % addr) imapProtocol = self.protocol(self._soledad_sessions) self._connections[addr] = imapProtocol return imapProtocol def stopFactory(self): - # say bye! for conn, proto in self._connections.items(): - logger.debug("Closing connections for %s" % conn) proto.close_server_connection() def doStop(self): - """ - Stops imap service (fetcher, factory and port). - """ return ServerFactory.doStop(self) @@ -182,10 +174,10 @@ def run_service(soledad_sessions, port=IMAP_PORT, factory=None): tport = reactor.listenTCP(port, factory, interface=interface) except CannotListenError: - logger.error("IMAP Service failed to start: " - "cannot listen in port %s" % (port,)) + log.error('IMAP Service failed to start: ' + 'cannot listen in port %s' % (port,)) except Exception as exc: - logger.error("Error launching IMAP service: %r" % (exc,)) + log.error("Error launching IMAP service: %r" % (exc,)) else: # all good. @@ -198,7 +190,7 @@ def run_service(soledad_sessions, port=IMAP_PORT, factory=None): # TODO use Endpoints !!! reactor.listenTCP(manhole.MANHOLE_PORT, manhole_factory, interface="127.0.0.1") - logger.debug("IMAP4 Server is RUNNING in port %s" % (port,)) + log.debug('IMAP4 Server is RUNNING in port %s' % (port,)) emit_async(catalog.IMAP_SERVICE_STARTED, str(port)) # FIXME -- change service signature diff --git a/src/leap/bitmask/mail/incoming/service.py b/src/leap/bitmask/mail/incoming/service.py index 9ed76a09..45d0e398 100644 --- a/src/leap/bitmask/mail/incoming/service.py +++ b/src/leap/bitmask/mail/incoming/service.py @@ -164,7 +164,8 @@ class IncomingMail(Service): Calls a deferred that will execute the fetch callback. """ def _sync_errback(failure): - self.log.failure('Error while fetching incoming mail') + self.log.error( + 'Error while fetching incoming mail {0!r}'.format(failure)) def syncSoledadCallback(_): # XXX this should be moved to adaptors @@ -219,7 +220,8 @@ class IncomingMail(Service): # synchronize incoming mail def _errback(self, failure): - self.log.failure('Error while processing incoming mail') + self.log.error( + 'Error while processing incoming mail {0!r}'.format(failure)) def _sync_soledad(self): """ @@ -234,7 +236,7 @@ class IncomingMail(Service): def _handle_invalid_auth_token_error(failure): failure.trap(InvalidAuthTokenError) - self.log.warn('sync failed because token is invalid: %r' % failure) + self.log.warn('Sync failed because token is invalid: %r' % failure) self.stopService() emit_async(catalog.SOLEDAD_INVALID_AUTH_TOKEN, self._userid) @@ -338,7 +340,7 @@ class IncomingMail(Service): return self._process_decrypted_doc(doc, decrdata) def log_doc_id_and_call_errback(failure): - self.log.failure( + self.log.error( '_decrypt_doc: Error decrypting document with ' 'ID %s' % doc.doc_id) @@ -368,8 +370,10 @@ class IncomingMail(Service): # the deferreds that would process an individual document try: msg = json_loads(data) - except (UnicodeError, ValueError): - self.log.failure("Error while decrypting %s" % (doc.doc_id,)) + except (UnicodeError, ValueError) as exc: + self.log.error('Error while decrypting %s' % + (doc.doc_id,)) + self.log.error(str(exc)) # we flag the message as "with decrypting errors", # to avoid further decryption attempts during sync @@ -764,12 +768,13 @@ class IncomingMail(Service): % (url,)) elif failure.check(keymanager_errors.KeyAttributesDiffer): self.log.warn("Key from OpenPGP header url %s didn't " - "match the from address %s" - % (url, address)) + "match the from address %s" + % (url, address)) else: - self.log.warn("An error has ocurred adding key from " - "OpenPGP header url %s for %s: %s" % - (url, address, failure.getErrorMessage())) + self.log.warn( + "An error has ocurred adding key from " + "OpenPGP header url %s for %s: %s" % + (url, address, failure.getErrorMessage())) return False d = self._keymanager.fetch_key(address, url) @@ -804,7 +809,7 @@ class IncomingMail(Service): def failed_put_key(failure): self.log.info("An error has ocurred adding attached key for %s: %s" - % (address, failure.getErrorMessage())) + % (address, failure.getErrorMessage())) return False deferreds = [] diff --git a/src/leap/bitmask/mail/mail.py b/src/leap/bitmask/mail/mail.py index 9f60ff00..8cc01e27 100644 --- a/src/leap/bitmask/mail/mail.py +++ b/src/leap/bitmask/mail/mail.py @@ -43,7 +43,7 @@ from leap.bitmask.mail.plugins import soledad_sync_hooks from leap.bitmask.mail.utils import find_charset, CaseInsensitiveDict from leap.bitmask.mail.utils import lowerdict -logger = Logger() +log = Logger() # TODO LIST @@ -98,7 +98,7 @@ def _encode_payload(payload, ctype=""): if isinstance(payload, unicode): payload = payload.encode(charset) except UnicodeError as exc: - logger.error( + log.error( "Unicode error, using 'replace'. {0!r}".format(exc)) payload = payload.encode(charset, 'replace') return payload @@ -122,13 +122,17 @@ def _unpack_headers(headers_dict): class MessagePart(object): + # TODO This class should be better abstracted from the data model. # TODO support arbitrarily nested multiparts (right now we only support # the trivial case) + """ Represents a part of a multipart MIME Message. """ + log = Logger() + def __init__(self, part_map, cdocs=None, nested=False): """ :param part_map: a dictionary mapping the subparts for @@ -191,7 +195,7 @@ class MessagePart(object): try: part_map = sub_pmap[str(part)] except KeyError: - logger.debug("getSubpart for %s: KeyError" % (part,)) + self.log.debug('get_subpart for %s: KeyError' % (part,)) raise IndexError return MessagePart(part_map, cdocs=self._cdocs, nested=True) @@ -203,6 +207,7 @@ class MessagePart(object): class Message(object): + """ Represents a single message, and gives access to all its attributes. """ @@ -325,6 +330,7 @@ class Flagsmode(object): class MessageCollection(object): + """ A generic collection of messages. It can be messages sharing the same mailbox, tag, the result of a given query, or just a bunch of ids for @@ -340,6 +346,8 @@ class MessageCollection(object): instance or proxy, for instance). """ + log = Logger() + # TODO LIST # [ ] look at IMessageSet methods # [ ] make constructor with a per-instance deferredLock to use on @@ -438,7 +446,7 @@ class MessageCollection(object): d.addCallback( lambda uid: self.get_message_by_uid( uid, get_cdocs=get_cdocs)) - d.addErrback(logger.error) + d.addErrback(self.log.error('Error getting msg by seq')) return d def get_message_by_uid(self, uid, absolute=True, get_cdocs=False): @@ -611,7 +619,7 @@ class MessageCollection(object): headers = lowerdict(msg.get_headers()) moz_draft_hdr = "X-Mozilla-Draft-Info" if moz_draft_hdr.lower() in headers: - logger.debug("setting fast notify to False, Draft detected") + self.log.debug('Setting fast notify to False, Draft detected') notify_just_mdoc = False if notify_just_mdoc: @@ -635,9 +643,9 @@ class MessageCollection(object): # --- BUG ----------------------------------------- # XXX watch out, sometimes mdoc doesn't have doc_id # but it has future_id. Should be solved already. - logger.error("BUG: (please report) Null doc_id for " - "document %s" % - (wrapper.mdoc.serialize(),)) + self.log.error('BUG: (please report) Null doc_id for ' + 'document %s' % + (wrapper.mdoc.serialize(),)) return defer.succeed("mdoc_id not inserted") # XXX BUG ----------------------------------------- @@ -656,7 +664,7 @@ class MessageCollection(object): d.addCallback(insert_mdoc_id, wrapper) d.addCallback(self.cb_signal_unread_to_ui) d.addCallback(self.notify_new_to_listeners) - d.addErrback(logger.error) + d.addErrback(lambda f: self.log.error('Error adding msg!')) return d @@ -743,7 +751,6 @@ class MessageCollection(object): def insert_doc(_, mbox_uuid, doc_id): d = self.mbox_indexer.get_uid_from_doc_id(mbox_uuid, doc_id) d.addCallback(insert_conditionally, mbox_uuid, doc_id) - d.addErrback(logger.error) d.addCallback(log_result) return d @@ -801,7 +808,6 @@ class MessageCollection(object): self.store, self.mbox_uuid) mdocs_deleted.addCallback(get_uid_list) mdocs_deleted.addCallback(delete_uid_entries) - mdocs_deleted.addErrback(logger.error) return mdocs_deleted # TODO should add a delete-by-uid to collection? diff --git a/src/leap/bitmask/mail/outgoing/service.py b/src/leap/bitmask/mail/outgoing/service.py index bd578ed2..a3b8d005 100644 --- a/src/leap/bitmask/mail/outgoing/service.py +++ b/src/leap/bitmask/mail/outgoing/service.py @@ -55,11 +55,6 @@ from leap.bitmask.mail.rfc3156 import PGPEncrypted # [ ] rename this module to something else, service should be the implementor # of IService -logger = Logger() - - -logger = Logger() - class SSLContextFactory(ssl.ClientContextFactory): def __init__(self, cert, key): @@ -99,6 +94,8 @@ class OutgoingMail(object): Sends Outgoing Mail, encrypting and signing if needed. """ + log = Logger() + def __init__(self, from_address, keymanager, cert, key, host, port, bouncer=None): """ @@ -167,7 +164,7 @@ class OutgoingMail(object): """ dest_addrstr = smtp_sender_result[1][0][0] fromaddr = self._from_address - logger.info('Message sent from %s to %s' % (fromaddr, dest_addrstr)) + self.log.info('Message sent from %s to %s' % (fromaddr, dest_addrstr)) emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, fromaddr, dest_addrstr) @@ -190,7 +187,7 @@ class OutgoingMail(object): # temporal error. We might want to notify the permanent errors # differently. - logger.error(failure) + self.log.error('Error while sending: {0!r}'.format(failure)) if self._bouncer: self._bouncer.bounce_message( @@ -208,7 +205,7 @@ class OutgoingMail(object): :type encrypt_and_sign_result: tuple """ message, recipient = encrypt_and_sign_result - logger.info( + self.log.info( 'Connecting to SMTP server %s:%s' % (self._host, self._port)) msg = message.as_string(False) @@ -302,7 +299,7 @@ class OutgoingMail(object): def if_key_not_found_send_unencrypted(failure, message): failure.trap(KeyNotFound, KeyAddressMismatch) - logger.info('Will send unencrypted message to %s.' % to_address) + self.log.info('Will send unencrypted message to %s.' % to_address) emit_async(catalog.SMTP_START_SIGN, self._from_address, to_address) d = self._sign(message, from_address) d.addCallback(signal_sign) @@ -312,8 +309,8 @@ class OutgoingMail(object): emit_async(catalog.SMTP_END_SIGN, self._from_address) return newmsg, recipient - logger.info("Will encrypt the message with %s and sign with %s." - % (to_address, from_address)) + self.log.info("Will encrypt the message with %s and sign with %s." + % (to_address, from_address)) emit_async(catalog.SMTP_START_ENCRYPT_AND_SIGN, self._from_address, "%s,%s" % (self._from_address, to_address)) diff --git a/src/leap/bitmask/mail/rfc3156.py b/src/leap/bitmask/mail/rfc3156.py index e602b6b6..cb592689 100644 --- a/src/leap/bitmask/mail/rfc3156.py +++ b/src/leap/bitmask/mail/rfc3156.py @@ -34,7 +34,7 @@ from email.generator import ( _make_boundary, ) -logger = Logger() +log = Logger() # # A generator that solves http://bugs.python.org/issue14983 @@ -166,7 +166,7 @@ def encode_base64(msg): except KeyError: msg['Content-Transfer-Encoding'] = 'base64' elif encoding is not 'base64': - logger.error('Unknown content-transfer-encoding: %s' % encoding) + log.error('Unknown content-transfer-encoding: %s' % encoding) def encode_base64_rec(msg): diff --git a/src/leap/bitmask/mail/smtp/__init__.py b/src/leap/bitmask/mail/smtp/__init__.py index 90baf668..e69de29b 100644 --- a/src/leap/bitmask/mail/smtp/__init__.py +++ b/src/leap/bitmask/mail/smtp/__init__.py @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -# __init__.py -# Copyright (C) 2013-2016 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -""" -SMTP gateway helper function. -""" -import os - -from twisted.internet import reactor -from twisted.internet.error import CannotListenError -from twisted.logger import Logger - -from leap.common.events import emit_async, catalog -from leap.bitmask.mail.smtp.gateway import SMTPFactory - -logger = Logger() - -SMTP_PORT = 2013 - - -def run_service(soledad_sessions, keymanager_sessions, sendmail_opts, - port=SMTP_PORT, factory=None): - """ - Main entry point to run the service from the client. - - :param soledad_sessions: a dict-like object, containing instances - of a Store (soledad instances), indexed by userid. - :param keymanager_sessions: a dict-like object, containing instances - of Keymanager, indexed by userid. - :param sendmail_opts: a dict-like object of sendmailOptions. - - :returns: the port as returned by the reactor when starts listening, and - the factory for the protocol. - :rtype: tuple - """ - if not factory: - factory = SMTPFactory(soledad_sessions, keymanager_sessions, - sendmail_opts) - - try: - interface = "localhost" - # don't bind just to localhost if we are running on docker since we - # won't be able to access smtp from the host - if os.environ.get("LEAP_DOCKERIZED"): - interface = '' - - # TODO Use Endpoints instead -------------------------------- - tport = reactor.listenTCP(port, factory, interface=interface) - emit_async(catalog.SMTP_SERVICE_STARTED, str(port)) - - return tport, factory - except CannotListenError: - logger.error("SMTP Service failed to start: " - "cannot listen in port %s" % port) - emit_async(catalog.SMTP_SERVICE_FAILED_TO_START, str(port)) - except Exception as exc: - logger.error("Unhandled error while launching smtp gateway service") - logger.error('%r' % exc) diff --git a/src/leap/bitmask/mail/smtp/gateway.py b/src/leap/bitmask/mail/smtp/gateway.py index b1fafadd..08d59e11 100644 --- a/src/leap/bitmask/mail/smtp/gateway.py +++ b/src/leap/bitmask/mail/smtp/gateway.py @@ -56,8 +56,6 @@ generator.Generator = RFC3156CompliantGenerator LOCAL_FQDN = "bitmask.local" -logger = Logger() - @implementer(IRealm) class LocalSMTPRealm(object): @@ -221,10 +219,13 @@ class SMTPFactory(protocol.ServerFactory): @implementer(smtp.IMessageDelivery) class SMTPDelivery(object): + """ Validate email addresses and handle message delivery. """ + log = Logger() + def __init__(self, userid, keymanager, encrypted_only, outgoing_mail): """ Initialize the SMTP delivery object. @@ -298,7 +299,7 @@ class SMTPDelivery(object): # verify if recipient key is available in keyring def found(_): - logger.debug("Accepting mail for %s..." % user.dest.addrstr) + self.log.debug('Accepting mail for %s...' % user.dest.addrstr) emit_async(catalog.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED, self._userid, user.dest.addrstr) @@ -310,7 +311,7 @@ class SMTPDelivery(object): emit_async(catalog.SMTP_RECIPIENT_REJECTED, self._userid, user.dest.addrstr) raise smtp.SMTPBadRcpt(user.dest.addrstr) - logger.warn( + self.log.warn( 'Warning: will send an unencrypted message (because ' '"encrypted_only" is set to False).') emit_async( @@ -344,7 +345,7 @@ class SMTPDelivery(object): # accept mail from anywhere. To reject an address, raise # smtp.SMTPBadSender here. if str(origin) != str(self._userid): - logger.error( + self.log.error( "Rejecting sender {0}, expected {1}".format(origin, self._userid)) raise smtp.SMTPBadSender(origin) @@ -362,6 +363,7 @@ class EncryptedMessage(object): recipient. """ implements(smtp.IMessage) + log = Logger() def __init__(self, user, outgoing_mail): """ @@ -396,7 +398,7 @@ class EncryptedMessage(object): :returns: a deferred """ - logger.debug("Message data complete.") + self.log.debug('Message data complete.') self._lines.append('') # add a trailing newline raw_mail = '\r\n'.join(self._lines) @@ -406,8 +408,7 @@ class EncryptedMessage(object): """ Log an error when the connection is lost. """ - logger.error("Connection lost unexpectedly!") - logger.error() + self.log.error('Connection lost unexpectedly!') emit_async(catalog.SMTP_CONNECTION_LOST, self._userid, self._user.dest.addrstr) # unexpected loss of connection; don't save diff --git a/src/leap/bitmask/mail/smtp/service.py b/src/leap/bitmask/mail/smtp/service.py index 48e6865b..6fd000ce 100644 --- a/src/leap/bitmask/mail/smtp/service.py +++ b/src/leap/bitmask/mail/smtp/service.py @@ -26,7 +26,7 @@ from twisted.logger import Logger from leap.common.events import emit_async, catalog from leap.bitmask.mail.smtp.gateway import SMTPFactory -logger = Logger() +log = Logger() SMTP_PORT = 2013 @@ -65,9 +65,9 @@ def run_service(soledad_sessions, keymanager_sessions, sendmail_opts, return tport, factory except CannotListenError: - logger.error("STMP Service failed to start: " - "cannot listen in port %s" % port) + log.error('STMP Service failed to start: ' + 'cannot listen in port %s' % port) emit_async(catalog.SMTP_SERVICE_FAILED_TO_START, str(port)) except Exception as exc: - logger.error("Unhandled error while launching smtp gateway service") - logger.error('%r' % exc) + log.error('Unhandled error while launching smtp gateway service') + log.error('%r' % exc) diff --git a/src/leap/bitmask/mail/sync_hooks.py b/src/leap/bitmask/mail/sync_hooks.py index de3a0c1a..e8f25e88 100644 --- a/src/leap/bitmask/mail/sync_hooks.py +++ b/src/leap/bitmask/mail/sync_hooks.py @@ -14,12 +14,14 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. + """ Soledad PostSync Hooks. Process every new document of interest after every soledad synchronization, using the hooks that soledad exposes via plugins. """ + from re import compile as regex_compile from zope.interface import implements @@ -30,7 +32,7 @@ from twisted.logger import Logger from leap.bitmask.mail import constants from leap.soledad.client.interfaces import ISoledadPostSyncPlugin -logger = Logger() +log = Logger() def _get_doc_type_preffix(s): @@ -38,6 +40,7 @@ def _get_doc_type_preffix(s): class MailProcessingPostSyncHook(object): + implements(IPlugin, ISoledadPostSyncPlugin) META_DOC_PREFFIX = _get_doc_type_preffix(constants.METAMSGID) @@ -56,7 +59,7 @@ class MailProcessingPostSyncHook(object): for doc_id in doc_id_list: if _get_doc_type_preffix(doc_id) in self.watched_doc_types: - logger.info("Mail post-sync hook: processing %s" % doc_id) + log.info("Mail post-sync hook: processing %s" % doc_id) process_fun(doc_id) return defer.gatherResults(self._processing_deferreds) @@ -77,7 +80,7 @@ class MailProcessingPostSyncHook(object): mbox_uuid = _get_mbox_uuid(mdoc_id) if mbox_uuid: chash = _get_chash_from_mdoc(mdoc_id) - logger.debug('making index table for %s:%s' % (mbox_uuid, chash)) + log.debug('Making index table for %s:%s' % (mbox_uuid, chash)) index_docid = constants.METAMSGID.format( mbox_uuid=mbox_uuid.replace('-', '_'), chash=chash) @@ -91,7 +94,7 @@ class MailProcessingPostSyncHook(object): def _process_queued_docs(self): assert(self._has_configured_account()) pending = self._pending_docs - logger.info("Mail post-sync hook: processing queued docs") + log.info('Mail post-sync hook: processing queued docs') def remove_pending_docs(res): self._pending_docs = [] diff --git a/src/leap/bitmask/mail/testing/imap.py b/src/leap/bitmask/mail/testing/imap.py index 92bf4d34..fa4c08eb 100644 --- a/src/leap/bitmask/mail/testing/imap.py +++ b/src/leap/bitmask/mail/testing/imap.py @@ -38,7 +38,7 @@ from leap.bitmask.mail.testing.common import SoledadTestMixin TEST_USER = "testuser@leap.se" TEST_PASSWD = "1234" -logger = Logger() +log = Logger() # @@ -182,7 +182,7 @@ class IMAP4HelperMixin(SoledadTestMixin): self.client.transport.loseConnection() self.server.transport.loseConnection() if hasattr(self, 'function'): - logger.error(failure, "Problem with %r" % (self.function,)) + log.error("Problem with %r" % (self.function,)) def loopback(self): return loopback.loopbackAsync(self.server, self.client) diff --git a/src/leap/bitmask/util.py b/src/leap/bitmask/util.py index 05f1c5b3..c5154cd0 100644 --- a/src/leap/bitmask/util.py +++ b/src/leap/bitmask/util.py @@ -28,7 +28,7 @@ from leap.common.files import which STANDALONE = getattr(sys, 'frozen', False) -logger = Logger() +log = Logger() def here(module=None): @@ -108,7 +108,7 @@ def get_gpg_bin_path(): gpgbin = path break except IndexError as e: - logger.debug("couldn't find the gpg binary!: %s" % (e,)) + log.debug("Couldn't find the gpg binary!: %s" % (e,)) if gpgbin is not None: return gpgbin @@ -124,8 +124,8 @@ def get_gpg_bin_path(): gpgbin = opt break except IndexError as e: - logger.debug("couldn't find the gpg1 binary!: %s" % (e,)) + log.debug("Couldn't find the gpg1 binary!: %s" % (e,)) if gpgbin is None: - logger.debug("Could not find gpg1 binary") + log.debug("Could not find gpg1 binary") return gpgbin diff --git a/src/leap/bitmask/vpn/_control.py b/src/leap/bitmask/vpn/_control.py index 6d4db965..85e4911c 100644 --- a/src/leap/bitmask/vpn/_control.py +++ b/src/leap/bitmask/vpn/_control.py @@ -8,7 +8,7 @@ from twisted.logger import Logger from .process import VPNProcess from .constants import IS_MAC -logger = Logger() +log = Logger() # NOTE: We need to set a bigger poll time in OSX because it seems # openvpn malfunctions when you ask it a lot of things in a short @@ -47,7 +47,7 @@ class VPNControl(object): self._port = socket_port def start(self): - logger.debug('VPN: start') + log.debug('VPN: start') self._user_stopped = False self._stop_pollers() @@ -58,13 +58,13 @@ class VPNControl(object): restartfun=self.restart) if vpnproc.get_openvpn_process(): - logger.info("Another vpn process is running. Will try to stop it.") + log.info('Another vpn process is running. Will try to stop it.') vpnproc.stop_if_already_running() try: cmd = vpnproc.getCommand() except Exception as e: - logger.error("Error while getting vpn command... {0!r}".format(e)) + log.error('Error while getting vpn command... {0!r}'.format(e)) raise env = os.environ @@ -118,7 +118,7 @@ class VPNControl(object): reactor.callLater( self.TERMINATE_WAIT, self._kill_if_left_alive) else: - logger.debug("VPN is not running.") + log.debug('VPN is not running.') return True @@ -153,7 +153,7 @@ class VPNControl(object): """ self._stop_pollers() if self._vpnproc is None: - logger.debug("There's no vpn process running to kill.") + log.debug("There's no vpn process running to kill.") else: self._vpnproc.aborted = True self._vpnproc.killProcess() @@ -168,10 +168,9 @@ class VPNControl(object): """ while tries < self.TERMINATE_MAXTRIES: if self._vpnproc.transport.pid is None: - logger.debug("Process has been happily terminated.") return else: - logger.debug("Process did not die, waiting...") + log.debug('Process did not die, waiting...') tries += 1 reactor.callLater(self.TERMINATE_WAIT, @@ -179,11 +178,11 @@ class VPNControl(object): return # after running out of patience, we try a killProcess - logger.debug("Process did not died. Sending a SIGKILL.") + log.debug('Process did not die. Sending a SIGKILL.') try: self._killit() except OSError: - logger.error("Could not kill process!") + log.error('Could not kill process!') def _start_pollers(self): """ diff --git a/src/leap/bitmask/vpn/_management.py b/src/leap/bitmask/vpn/_management.py index 8bf3cc78..453b042c 100644 --- a/src/leap/bitmask/vpn/_management.py +++ b/src/leap/bitmask/vpn/_management.py @@ -18,9 +18,6 @@ except ImportError: from ._telnet import UDSTelnet -logger = Logger() - - class OpenVPNAlreadyRunning(Exception): message = ("Another openvpn instance is already running, and could " "not be stopped.") @@ -41,6 +38,7 @@ class VPNManagement(object): zcat `dpkg -L openvpn | grep management` """ + log = Logger() # Timers, in secs CONNECTION_RETRY_TIME = 1 @@ -56,7 +54,7 @@ class VPNManagement(object): try: self._tn.read_eager() except EOFError: - logger.debug("Could not read from socket. Assuming it died.") + self.log.debug('Could not read from socket. Assuming it died.') return def _send_command(self, command, until=b"END"): @@ -89,23 +87,21 @@ class VPNManagement(object): except socket.error: # XXX should get a counter and repeat only # after mod X times. - logger.warn('socket error (command was: "%s")' % (command,)) + self.log.warn('Socket error (command was: "%s")' % (command,)) self._close_management_socket(announce=False) - logger.debug('trying to connect to management again') + self.log.debug('Trying to connect to management again') self.try_to_connect_to_management(max_retries=5) return [] - # XXX should move this to a errBack! except Exception as e: - logger.warn("Error sending command %s: %r" % - (command, e)) + self.log.warn("Error sending command %s: %r" % + (command, e)) return [] def _close_management_socket(self, announce=True): """ Close connection to openvpn management interface. """ - logger.debug('closing socket') if announce: self._tn.write("quit\n") self._tn.read_all() @@ -141,9 +137,8 @@ class VPNManagement(object): if self._tn: self._tn.read_eager() - # XXX move this to the Errback except Exception as e: - logger.warn("Could not connect to OpenVPN yet: %r" % (e,)) + self.log.warn('Could not connect to OpenVPN yet: %r' % (e,)) self._tn = None def _connectCb(self, *args): @@ -152,10 +147,8 @@ class VPNManagement(object): :param args: not used """ - if self._tn: - logger.info('Connected to management') - else: - logger.debug('Cannot connect to management...') + if not self._tn: + self.log.warn('Cannot connect to management...') def _connectErr(self, failure): """ @@ -163,7 +156,7 @@ class VPNManagement(object): :param failure: Failure """ - logger.warn(failure) + self.log.failure('Error while connecting to management!') def connect_to_management(self, host, port): """ @@ -200,17 +193,17 @@ class VPNManagement(object): :type retry: int """ if max_retries and retry > max_retries: - logger.warn("Max retries reached while attempting to connect " - "to management. Aborting.") + self.log.warn( + 'Max retries reached while attempting to connect ' + 'to management. Aborting.') self.aborted = True return # _alive flag is set in the VPNProcess class. if not self._alive: - logger.debug('Tried to connect to management but process is ' - 'not alive.') + self.log.debug('Tried to connect to management but process is ' + 'not alive.') return - logger.debug('trying to connect to management') if not self.aborted and not self.is_connected(): self.connect_to_management(self._socket_host, self._socket_port) reactor.callLater( @@ -268,7 +261,7 @@ class VPNManagement(object): try: text, value = parts except ValueError: - logger.debug("Could not parse %s" % parts) + self.log.debug('Could not parse %s' % parts) return # text can be: # "TUN/TAP read bytes" @@ -329,13 +322,14 @@ class VPNManagement(object): under normal circumstances, we should be able to delete. """ if self._socket_port == "unix": - logger.debug('cleaning socket file temp folder') + self.log.debug('Cleaning socket file temp folder') tempfolder = _first(os.path.split(self._socket_host)) if tempfolder and os.path.isdir(tempfolder): try: shutil.rmtree(tempfolder) except OSError: - logger.error('could not delete tmpfolder %s' % tempfolder) + self.log.error( + 'Could not delete tmpfolder %s' % tempfolder) def get_openvpn_process(self): """ @@ -380,11 +374,11 @@ class VPNManagement(object): """ process = self.get_openvpn_process() if not process: - logger.debug('Could not find openvpn process while ' - 'trying to stop it.') + self.log.debug('Could not find openvpn process while ' + 'trying to stop it.') return - logger.debug("OpenVPN is already running, trying to stop it...") + self.log.debug('OpenVPN is already running, trying to stop it...') cmdline = process.cmdline manag_flag = "--management" @@ -400,16 +394,16 @@ class VPNManagement(object): return "leap" in s and "providers" in s if not any(map(smellslikeleap, cmdline)): - logger.debug("We cannot stop this instance since we do not " - "recognise it as a leap invocation.") + self.log.debug("We cannot stop this instance since we do not " + "recognise it as a leap invocation.") raise AlienOpenVPNAlreadyRunning try: index = cmdline.index(manag_flag) host = cmdline[index + 1] port = cmdline[index + 2] - logger.debug("Trying to connect to %s:%s" - % (host, port)) + self.log.debug("Trying to connect to %s:%s" + % (host, port)) self.connect_to_management(host, port) # XXX this has a problem with connections to different @@ -421,19 +415,18 @@ class VPNManagement(object): # However, that should be a rare case right now. self._send_command("signal SIGTERM") self._close_management_socket(announce=True) - except (Exception, AssertionError) as e: - logger.warn("Problem trying to terminate OpenVPN: %r" - % (e,)) + except (Exception, AssertionError): + self.log.failure('Problem trying to terminate OpenVPN') else: - logger.debug("Could not find the expected openvpn command line.") + self.log.debug('Could not find the expected openvpn command line.') process = self.get_openvpn_process() if process is None: - logger.debug("Successfully finished already running " - "openvpn process.") + self.log.debug('Successfully finished already running ' + 'openvpn process.') return True else: - logger.warn("Unable to terminate OpenVPN") + self.log.warn('Unable to terminate OpenVPN') raise OpenVPNAlreadyRunning diff --git a/src/leap/bitmask/vpn/launcher.py b/src/leap/bitmask/vpn/launcher.py index 3006d6da..fa9923a8 100644 --- a/src/leap/bitmask/vpn/launcher.py +++ b/src/leap/bitmask/vpn/launcher.py @@ -33,7 +33,7 @@ from leap.bitmask.vpn.constants import IS_LINUX from leap.bitmask.vpn.utils import force_eval -logger = Logger() +log = Logger() flags_STANDALONE = False @@ -62,18 +62,18 @@ def _has_updown_scripts(path, warn=True): """ is_file = os.path.isfile(path) if warn and not is_file: - logger.error("Could not find up/down script %s. " - "Might produce DNS leaks." % (path,)) + log.error('Could not find up/down script %s. ' + 'Might produce DNS leaks.' % (path,)) # XXX check if applies in win is_exe = False try: is_exe = (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] != 0) except OSError as e: - logger.warn("%s" % (e,)) + log.warn("%s" % (e,)) if warn and not is_exe: - logger.error("Up/down script %s is not executable. " - "Might produce DNS leaks." % (path,)) + log.error('Up/down script %s is not executable. ' + 'Might produce DNS leaks.' % (path,)) return is_file and is_exe @@ -91,8 +91,8 @@ def _has_other_files(path, warn=True): """ is_file = os.path.isfile(path) if warn and not is_file: - logger.warning("Could not find file during checks: %s. " % ( - path,)) + log.warn('Could not find file during checks: %s. ' % ( + path,)) return is_file @@ -109,6 +109,7 @@ class VPNLauncher(object): PREFERRED_PORTS = ("443", "80", "53", "1194") + # FIXME -- dead code? @classmethod @abstractmethod def get_gateways(kls, vpnconfig, providerconfig): @@ -138,7 +139,7 @@ class VPNLauncher(object): gws = [gateway_conf] if not gws: - logger.error('No gateway was found!') + log.error('No gateway was found!') raise VPNLauncherException('No gateway was found!') for idx, gw in enumerate(gws): @@ -156,7 +157,7 @@ class VPNLauncher(object): gateways.append((gw, the_port)) - logger.debug("Using gateways (ip, port): {0!r}".format(gateways)) + log.debug('Using gateways (ip, port): {0!r}'.format(gateways)) return gateways @classmethod @@ -198,8 +199,8 @@ class VPNLauncher(object): openvpn_path = force_eval(kls.OPENVPN_BIN_PATH) if not os.path.isfile(openvpn_path): - logger.warning("Could not find openvpn bin in path %s" % ( - openvpn_path)) + log.warn('Could not find openvpn bin in path %s' % ( + openvpn_path)) raise OpenVPNNotFoundException() args = [] @@ -321,11 +322,6 @@ class VPNLauncher(object): :rtype: list """ - # leap_assert(kls.OTHER_FILES is not None, - # "Need to define OTHER_FILES for this particular " - # "auncher before calling this method") - - # TODO assert vs except? if kls.OTHER_FILES is None: raise Exception( "Need to define OTHER_FILES for this particular " diff --git a/src/leap/bitmask/vpn/privilege.py b/src/leap/bitmask/vpn/privilege.py index 135acbb8..9666a943 100644 --- a/src/leap/bitmask/vpn/privilege.py +++ b/src/leap/bitmask/vpn/privilege.py @@ -31,7 +31,7 @@ from abc import ABCMeta, abstractmethod from twisted.logger import Logger from twisted.python.procutils import which -logger = Logger() +log = Logger() flags_STANDALONE = False @@ -71,8 +71,8 @@ def is_missing_policy_permissions(): if not policy_checker: # it is true that we miss permission to escalate # privileges without asking for password each time. - logger.debug("we could not find a policy checker implementation " - "for %s" % (_system,)) + log.debug('we could not find a policy checker implementation ' + 'for %s' % (_system,)) return True return policy_checker().is_missing_policy_permissions() @@ -146,18 +146,15 @@ class LinuxPolicyChecker(PolicyChecker): time.sleep(2) if self.is_up(): pkexec_possibilities = which(self.PKEXEC_BIN) - # leap_assert(len(pkexec_possibilities) > 0, - # "We couldn't find pkexec") if not pkexec_possibilities: - logger.error("We couldn't find pkexec") raise Exception("We couldn't find pkexec") return pkexec_possibilities else: - logger.warn("No polkit auth agent found. pkexec " + - "will use its own auth agent.") + log.warn('No polkit auth agent found. pkexec ' + + 'will use its own auth agent.') raise NoPolkitAuthAgentAvailable() else: - logger.warn("System has no pkexec") + log.warn('System has no pkexec') raise NoPkexecAvailable() @classmethod @@ -178,11 +175,11 @@ class LinuxPolicyChecker(PolicyChecker): # will do "sh -c 'foo'", so if we do not quoute it we'll end # up with a invocation to the python interpreter. And that # is bad. - logger.debug("Trying to launch polkit agent") + log.debug('Trying to launch polkit agent') subprocess.call(["python -m leap.bitmask.util.polkit_agent"], shell=True, env=env) - except Exception as exc: - logger.error(str(exc)) + except Exception: + log.failure('Error while launching vpn') @classmethod def is_up(self): diff --git a/src/leap/bitmask/vpn/process.py b/src/leap/bitmask/vpn/process.py index 6096a473..e136e5bb 100644 --- a/src/leap/bitmask/vpn/process.py +++ b/src/leap/bitmask/vpn/process.py @@ -32,20 +32,21 @@ from leap.bitmask.vpn.utils import get_vpn_launcher from leap.bitmask.vpn import _status from leap.bitmask.vpn import _management -logger = Logger() - # OpenVPN verbosity level - from flags.py OPENVPN_VERBOSITY = 1 class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): + """ A ProcessProtocol class that can be used to spawn a process that will launch openvpn and connect to its management interface to control it programmatically. """ + log = Logger() + # TODO do we really need the vpnconfig/providerconfig objects in here??? def __init__(self, vpnconfig, providerconfig, socket_host, socket_port, @@ -123,7 +124,7 @@ class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): line = data[:-1] if 'SIGTERM[soft,ping-restart]' in line: self.restarting = True - logger.info(line) + self.log.info(line) self._status.watch(line) def processExited(self, failure): @@ -140,9 +141,9 @@ class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): elif err == internet_error.ProcessTerminated: status, errmsg = 'failure', failure.value.exitCode if errmsg: - logger.debug("processExited, status %d" % (errmsg,)) + self.log.debug('processExited, status %d' % (errmsg,)) else: - logger.warn('%r' % failure.value) + self.log.warn('%r' % failure.value) self._status.set_status(status, errmsg) self._alive = False @@ -156,9 +157,9 @@ class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): """ exit_code = reason.value.exitCode if isinstance(exit_code, int): - logger.debug("processEnded, status %d" % (exit_code,)) + self.log.debug('processEnded, status %d' % (exit_code,)) if self.restarting: - logger.debug('Restarting VPN process') + self.log.debug('Restarting VPN process') reactor.callLater(2, self._restartfun) # polling @@ -202,8 +203,8 @@ class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): if not isinstance(c, str): command[i] = c.encode(encoding) - logger.debug("Running VPN with command: ") - logger.debug("{0}".format(" ".join(command))) + self.log.debug("Running VPN with command: ") + self.log.debug("{0}".format(" ".join(command))) return command def getGateways(self): @@ -227,4 +228,4 @@ class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): try: self.transport.signalProcess('KILL') except internet_error.ProcessExitedAlready: - logger.debug('Process Exited Already') + self.log.debug('Process Exited Already') |