From da45ed15190ea0f9c7c38b6312239f27664545a4 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 28 Apr 2016 15:46:13 -0400 Subject: [bug] do not force autobahn dependency yet websockets interface is not mature enough yet, make this dependency optional, for the case the user actively enables it. - Releases: 0.9.2 --- src/leap/bitmask/core/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index 13c8864a..ceda6353 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -26,7 +26,6 @@ from leap.bitmask import __version__ from leap.bitmask.core import configurable from leap.bitmask.core import mail_services from leap.bitmask.core import _zmq -from leap.bitmask.core import websocket from leap.bonafide.service import BonafideService from leap.common.events import server as event_server # from leap.vpn import EIPService @@ -103,6 +102,7 @@ class BitmaskBackend(configurable.ConfigurableService): zs.setServiceParent(self) def init_web(self): + from leap.bitmask.core import websocket ws = websocket.WebSocketsDispatcherService(self) ws.setServiceParent(self) -- cgit v1.2.3 From 928f547a1d1235306056be1d81e8400d4d77ecce Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Sat, 30 Apr 2016 12:15:16 -0400 Subject: [bug] fix hardcoded paths --- src/leap/bitmask/core/mail_services.py | 4 ++-- src/leap/bitmask/core/service.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py index 6472bdc2..3495aa39 100644 --- a/src/leap/bitmask/core/mail_services.py +++ b/src/leap/bitmask/core/mail_services.py @@ -302,7 +302,7 @@ class KeymanagerContainer(Container): class KeymanagerService(HookableService): - def __init__(self, basedir='~/.config/leap'): + def __init__(self, basedir=DEFAULT_BASEDIR): service.Service.__init__(self) self._basedir = basedir @@ -507,7 +507,7 @@ class SMTPService(service.Service): name = 'smtp' def __init__(self, soledad_sessions, keymanager_sessions, sendmail_opts, - basedir='~/.config/leap'): + basedir=DEFAULT_BASEDIR): self._basedir = os.path.expanduser(basedir) port, factory = smtp.run_service( diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index ceda6353..ddd86155 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -33,7 +33,7 @@ from leap.common.events import server as event_server class BitmaskBackend(configurable.ConfigurableService): - def __init__(self, basedir='~/.config/leap'): + def __init__(self, basedir=configurable.DEFAULT_BASEDIR): configurable.ConfigurableService.__init__(self, basedir) -- cgit v1.2.3 From 08da5b11103cdd132c3ac4110ba42fcc8510a78b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Sat, 30 Apr 2016 12:15:58 -0400 Subject: [refactor] pass backend to core service --- src/leap/bitmask/core/flags.py | 1 + src/leap/bitmask/core/launcher.py | 7 ++++++- src/leap/bitmask/core/service.py | 13 ++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 src/leap/bitmask/core/flags.py diff --git a/src/leap/bitmask/core/flags.py b/src/leap/bitmask/core/flags.py new file mode 100644 index 00000000..9a40c70c --- /dev/null +++ b/src/leap/bitmask/core/flags.py @@ -0,0 +1 @@ +BACKEND = 'default' diff --git a/src/leap/bitmask/core/launcher.py b/src/leap/bitmask/core/launcher.py index b2319077..b8916a1e 100644 --- a/src/leap/bitmask/core/launcher.py +++ b/src/leap/bitmask/core/launcher.py @@ -17,17 +17,22 @@ """ Run bitmask daemon. """ -from twisted.scripts.twistd import run from os.path import join from sys import argv +from twisted.scripts.twistd import run + from leap.bitmask.util import here from leap.bitmask import core +from leap.bitmask.core import flags def run_bitmaskd(): # TODO --- configure where to put the logs... (get --logfile, --logdir # from the bitmask_cli + for (index, arg) in enumerate(argv): + if arg == '--backend': + flags.BACKEND = argv[index + 1] argv[1:] = [ '-y', join(here(core), "bitmaskd.tac"), '--pidfile', '/tmp/bitmaskd.pid', diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index ddd86155..fca51048 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -17,6 +17,7 @@ """ Bitmask-core Service. """ +import json import resource from twisted.internet import reactor @@ -26,6 +27,7 @@ from leap.bitmask import __version__ from leap.bitmask.core import configurable from leap.bitmask.core import mail_services from leap.bitmask.core import _zmq +from leap.bitmask.core import flags from leap.bonafide.service import BonafideService from leap.common.events import server as event_server # from leap.vpn import EIPService @@ -126,17 +128,18 @@ class BitmaskBackend(configurable.ConfigurableService): # we may want to make this tuple a class member services = ('soledad', 'keymanager', 'mail', 'eip') - status_messages = [] + status = {} for name in services: - status = 'stopped' + _status = 'stopped' try: if self.getServiceNamed(name).running: - status = "running" + _status = 'running' except KeyError: pass - status_messages.append("[{}: {}]".format(name, status)) + status[name] = _status + status['backend'] = flags.BACKEND - return " ".join(status_messages) + return json.dumps(status) def do_version(self): version = __version__ -- cgit v1.2.3 From 1173e77cb8d635936c9730ba4ad8b88b24ad1be2 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Sun, 1 May 2016 11:09:07 -0400 Subject: [feature] pluggable backends and api registry the idea behind this mechanism (partially implemented for that) is to be able to check the backend output against some type annotations. We want to be able to detect if a given backend (real services or authoritative mocks) have diverged from what's specified in the API annotations. --- src/leap/bitmask/cli/bitmask_cli.py | 2 +- src/leap/bitmask/core/api.py | 54 +++++++ src/leap/bitmask/core/api_contract.py | 40 +++++ src/leap/bitmask/core/dispatcher.py | 286 +++++++++++++++++++++------------ src/leap/bitmask/core/dummy.py | 80 +++++++++ src/leap/bitmask/core/mail_services.py | 10 +- src/leap/bitmask/core/service.py | 86 ++++++---- 7 files changed, 420 insertions(+), 138 deletions(-) create mode 100644 src/leap/bitmask/core/api.py create mode 100644 src/leap/bitmask/core/api_contract.py create mode 100644 src/leap/bitmask/core/dummy.py diff --git a/src/leap/bitmask/cli/bitmask_cli.py b/src/leap/bitmask/cli/bitmask_cli.py index c5bb1b15..c2b1ba71 100755 --- a/src/leap/bitmask/cli/bitmask_cli.py +++ b/src/leap/bitmask/cli/bitmask_cli.py @@ -325,7 +325,7 @@ def send_command(cli): s = get_zmq_connection() - d = s.sendMsg(*data, timeout=20) + d = s.sendMsg(*data, timeout=60) d.addCallback(cb) d.addCallback(lambda x: reactor.stop()) d.addErrback(timeout_handler) diff --git a/src/leap/bitmask/core/api.py b/src/leap/bitmask/core/api.py new file mode 100644 index 00000000..9f3725dc --- /dev/null +++ b/src/leap/bitmask/core/api.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# api.py +# Copyright (C) 2016 LEAP Encryption Acess Project +# +# 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 . +""" +Registry for the public API for the Bitmask Backend. +""" +from collections import OrderedDict + +registry = OrderedDict() + + +class APICommand(type): + """ + A metaclass to keep a global registry of all the methods that compose the + public API for the Bitmask Backend. + """ + def __init__(cls, name, bases, attrs): + for key, val in attrs.iteritems(): + properties = getattr(val, 'register', None) + label = getattr(cls, 'label', None) + if label: + name = label + if properties is not None: + registry['%s.%s' % (name, key)] = properties + + +def register_method(*args): + """ + This method gathers info about all the methods that are supposed to + compose the public API to communicate with the backend. + + It sets up a register property for any method that uses it. + A type annotation is supposed to be in this property. + The APICommand metaclass collects these properties of the methods and + stores them in the global api_registry object, where they can be + introspected at runtime. + """ + def decorator(f): + f.register = tuple(args) + return f + return decorator diff --git a/src/leap/bitmask/core/api_contract.py b/src/leap/bitmask/core/api_contract.py new file mode 100644 index 00000000..86b600c1 --- /dev/null +++ b/src/leap/bitmask/core/api_contract.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# api_contract.py +# Copyright (C) 2016 LEAP Encryption Acess Project +# +# 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 . +""" +Display a human-readable representation of the methods that compound the public +api for Bitmask Core. + +The values are meant to be type annotations. +""" + +if __name__ == "__main__": + from leap.bitmask.core.service import BitmaskBackend + from leap.bitmask.core import api + backend = BitmaskBackend() + + print '========= Bitmask Core API ==================' + print + + for key in api.registry: + human_key = key.replace('do_', '').lower() + value = api.registry[key] + + print("{}:\t\t{}".format( + human_key, + ' '.join([x for x in value]))) + print + print '=============================================' diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py index 648cbe9b..e7c961fd 100644 --- a/src/leap/bitmask/core/dispatcher.py +++ b/src/leap/bitmask/core/dispatcher.py @@ -22,154 +22,214 @@ import json from twisted.internet import defer from twisted.python import failure, log +from .api import APICommand, register_method -# TODO implement sub-classes to dispatch subcommands (user, mail). +class SubCommand(object): -class CommandDispatcher(object): + __metaclass__ = APICommand - def __init__(self, core): + def dispatch(self, service, *parts, **kw): + subcmd = parts[1] - self.core = core + _method = getattr(self, 'do_' + subcmd.upper(), None) + if not _method: + raise RuntimeError('No such subcommand') + return _method(service, *parts, **kw) - def _get_service(self, name): - try: - return self.core.getServiceNamed(name) - except KeyError: - return None +class UserCmd(SubCommand): - def dispatch(self, msg): - cmd = msg[0] + label = 'user' - _method = getattr(self, 'do_' + cmd.upper(), None) + @register_method("{'srp_token': unicode, 'uuid': unicode}") + def do_AUTHENTICATE(self, bonafide, *parts): + user, password = parts[2], parts[3] + d = defer.maybeDeferred(bonafide.do_authenticate, user, password) + return d - if not _method: - return defer.fail(failure.Failure(RuntimeError('No such command'))) + @register_method("{'signup': 'ok', 'user': str}") + def do_SIGNUP(self, bonafide, *parts): + user, password = parts[2], parts[3] + d = defer.maybeDeferred(bonafide.do_signup, user, password) + return d - return defer.maybeDeferred(_method, *msg) + @register_method("{'logout': 'ok'}") + def do_LOGOUT(self, bonafide, *parts): + user, password = parts[2], parts[3] + d = defer.maybeDeferred(bonafide.do_logout, user, password) + return d - def do_STATS(self, *parts): - return _format_result(self.core.do_stats()) + @register_method('str') + def do_ACTIVE(self, bonafide, *parts): + d = defer.maybeDeferred(bonafide.do_get_active_user) + return d - def do_VERSION(self, *parts): - return _format_result(self.core.do_version()) - def do_STATUS(self, *parts): - return _format_result(self.core.do_status()) +class EIPCmd(SubCommand): - def do_SHUTDOWN(self, *parts): - return _format_result(self.core.do_shutdown()) + label = 'eip' - def do_USER(self, *parts): + @register_method('dict') + def do_ENABLE(self, service, *parts): + d = service.do_enable_service(self.label) + return d - subcmd = parts[1] - user, password = parts[2], parts[3] + @register_method('dict') + def do_DISABLE(self, service, *parts): + d = service.do_disable_service(self.label) + return d - bf = self._get_service('bonafide') + @register_method('dict') + def do_STATUS(self, eip, *parts): + d = eip.do_status() + return d - if subcmd == 'authenticate': - d = bf.do_authenticate(user, password) + @register_method('dict') + def do_START(self, eip, *parts): + # TODO --- attempt to get active provider + # TODO or catch the exception and send error + provider = parts[2] + d = eip.do_start(provider) + return d - elif subcmd == 'signup': - d = bf.do_signup(user, password) + @register_method('dict') + def do_STOP(self, eip, *parts): + d = eip.do_stop() + return d - elif subcmd == 'logout': - d = bf.do_logout(user, password) - elif subcmd == 'active': - d = bf.do_get_active_user() +class MailCmd(SubCommand): - d.addCallbacks(_format_result, _format_error) + label = 'mail' + + @register_method('dict') + def do_ENABLE(self, service, *parts): + d = service.do_enable_service(self.label) return d - def do_EIP(self, *parts): + @register_method('dict') + def do_DISABLE(self, service, *parts): + d = service.do_disable_service(self.label) + return d - subcmd = parts[1] - eip_label = 'eip' + @register_method('dict') + def do_STATUS(self, mail, *parts): + d = mail.do_status() + return d - if subcmd == 'enable': - return _format_result( - self.core.do_enable_service(eip_label)) + @register_method('dict') + def do_GET_IMAP_TOKEN(self, mail, *parts): + d = mail.get_imap_token() + return d - eip = self._get_service(eip_label) - if not eip: - return _format_result('eip: disabled') + @register_method('dict') + def do_GET_SMTP_TOKEN(self, mail, *parts): + d = mail.get_smtp_token() + return d - if subcmd == 'status': - return _format_result(eip.do_status()) + @register_method('dict') + def do_GET_SMTP_CERTIFICATE(self, mail, *parts, **kw): + # TODO move to mail service + # TODO should ask for confirmation? like --force or something, + # if we already have a valid one. or better just refuse if cert + # exists. + # TODO how should we pass the userid?? + # - Keep an 'active' user in bonafide (last authenticated) + # (doing it now) + # - Get active user from Mail Service (maybe preferred?) + # - Have a command/method to set 'active' user. + + @defer.inlineCallbacks + def save_cert(cert_data): + userid, cert_str = cert_data + cert_path = yield mail.do_get_smtp_cert_path(userid) + with open(cert_path, 'w') as outf: + outf.write(cert_str) + defer.returnValue('certificate saved to %s' % cert_path) + + bonafide = kw['bonafide'] + d = bonafide.do_get_smtp_cert() + d.addCallback(save_cert) + return d - elif subcmd == 'disable': - return _format_result( - self.core.do_disable_service(eip_label)) - elif subcmd == 'start': - # TODO --- attempt to get active provider - # TODO or catch the exception and send error - provider = parts[2] - d = eip.do_start(provider) - d.addCallbacks(_format_result, _format_error) - return d +class CommandDispatcher(object): - elif subcmd == 'stop': - d = eip.do_stop() - d.addCallbacks(_format_result, _format_error) - return d + __metaclass__ = APICommand - def do_MAIL(self, *parts): + label = 'core' + def __init__(self, core): + + self.core = core + self.subcommand_user = UserCmd() + self.subcommand_eip = EIPCmd() + self.subcommand_mail = MailCmd() + + # XXX -------------------------------------------- + # TODO move general services to another subclass + + @register_method("{'mem_usage': str}") + def do_STATS(self, *parts): + return _format_result(self.core.do_stats()) + + @register_method("{version_core': '0.0.0'}") + def do_VERSION(self, *parts): + return _format_result(self.core.do_version()) + + @register_method("{'mail': 'running'}") + def do_STATUS(self, *parts): + return _format_result(self.core.do_status()) + + @register_method("{'shutdown': 'ok'}") + def do_SHUTDOWN(self, *parts): + return _format_result(self.core.do_shutdown()) + + # ----------------------------------------------- + + def do_USER(self, *parts): + bonafide = self._get_service('bonafide') + d = self.subcommand_user.dispatch(bonafide, *parts) + d.addCallbacks(_format_result, _format_error) + return d + + def do_EIP(self, *parts): + eip = self._get_service(self.subcommand_eip.label) + if not eip: + return _format_result('eip: disabled') subcmd = parts[1] - mail_label = 'mail' - if subcmd == 'enable': - return _format_result( - self.core.do_enable_service(mail_label)) + dispatch = self._subcommand_eip.dispatch + if subcmd in ('enable', 'disable'): + d = dispatch(self.core, *parts) + else: + d = dispatch(eip, *parts) - m = self._get_service(mail_label) - bf = self._get_service('bonafide') + d.addCallbacks(_format_result, _format_error) + return d - if not m: - return _format_result('mail: disabled') + def do_MAIL(self, *parts): + subcmd = parts[1] + dispatch = self.subcommand_mail.dispatch - if subcmd == 'status': - return _format_result(m.do_status()) + if subcmd == 'enable': + d = dispatch(self.core, *parts) - elif subcmd == 'disable': - return _format_result(self.core.do_disable_service(mail_label)) + mail = self._get_service(self.subcommand_mail.label) + bonafide = self._get_service('bonafide') + kw = {'bonafide': bonafide} - elif subcmd == 'get_imap_token': - d = m.get_imap_token() - d.addCallbacks(_format_result, _format_error) - return d + if not mail: + return _format_result('mail: disabled') - elif subcmd == 'get_smtp_token': - d = m.get_smtp_token() - d.addCallbacks(_format_result, _format_error) - return d + if subcmd == 'disable': + d = dispatch(self.core) + else: + d = dispatch(mail, *parts, **kw) - elif subcmd == 'get_smtp_certificate': - # TODO move to mail service - # TODO should ask for confirmation? like --force or something, - # if we already have a valid one. or better just refuse if cert - # exists. - # TODO how should we pass the userid?? - # - Keep an 'active' user in bonafide (last authenticated) - # (doing it now) - # - Get active user from Mail Service (maybe preferred?) - # - Have a command/method to set 'active' user. - - @defer.inlineCallbacks - def save_cert(cert_data): - userid, cert_str = cert_data - cert_path = yield m.do_get_smtp_cert_path(userid) - with open(cert_path, 'w') as outf: - outf.write(cert_str) - defer.returnValue('certificate saved to %s' % cert_path) - - d = bf.do_get_smtp_cert() - d.addCallback(save_cert) - d.addCallbacks(_format_result, _format_error) - return d + d.addCallbacks(_format_result, _format_error) + return d def do_KEYS(self, *parts): subcmd = parts[1] @@ -187,6 +247,22 @@ class CommandDispatcher(object): d.addCallbacks(_format_result, _format_error) return d + def dispatch(self, msg): + cmd = msg[0] + + _method = getattr(self, 'do_' + cmd.upper(), None) + + if not _method: + return defer.fail(failure.Failure(RuntimeError('No such command'))) + + return defer.maybeDeferred(_method, *msg) + + def _get_service(self, name): + try: + return self.core.getServiceNamed(name) + except KeyError: + return None + def _format_result(result): return json.dumps({'error': None, 'result': result}) diff --git a/src/leap/bitmask/core/dummy.py b/src/leap/bitmask/core/dummy.py new file mode 100644 index 00000000..99dfafa5 --- /dev/null +++ b/src/leap/bitmask/core/dummy.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# dummy.py +# Copyright (C) 2016 LEAP Encryption Acess Project +# +# 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 . +""" +An authoritative dummy backend for tests. +""" +import json + +from leap.common.service_hooks import HookableService + + +class BackendCommands(object): + + """ + General commands for the BitmaskBackend Core Service. + """ + + def __init__(self, core): + self.core = core + + def do_status(self): + return json.dumps( + {'soledad': 'running', + 'keymanager': 'running', + 'mail': 'running', + 'eip': 'stopped', + 'backend': 'dummy'}) + + def do_version(self): + return {'version_core': '0.0.1'} + + def do_stats(self): + return {'mem_usage': '01 KB'} + + def do_shutdown(self): + return {'shutdown': 'ok'} + + +class mail_services(object): + + class SoledadService(HookableService): + pass + + class KeymanagerService(HookableService): + pass + + class StandardMailService(HookableService): + pass + + +class BonafideService(HookableService): + + def __init__(self, basedir): + pass + + def do_authenticate(self, user, password): + return {u'srp_token': u'deadbeef123456789012345678901234567890123', + u'uuid': u'01234567890abcde01234567890abcde'} + + def do_signup(self, user, password): + return {'signup': 'ok', 'user': 'dummyuser@provider.example.org'} + + def do_logout(self, user, password): + return {'logout': 'ok'} + + def do_get_active_user(self): + return 'dummyuser@provider.example.org' diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py index 3495aa39..fb9ee698 100644 --- a/src/leap/bitmask/core/mail_services.py +++ b/src/leap/bitmask/core/mail_services.py @@ -64,10 +64,10 @@ class ImproperlyConfigured(Exception): class SoledadContainer(Container): - def __init__(self, basedir=DEFAULT_BASEDIR): + def __init__(self, service=None, basedir=DEFAULT_BASEDIR): self._basedir = os.path.expanduser(basedir) self._usermap = UserMap() - super(SoledadContainer, self).__init__() + super(SoledadContainer, self).__init__(service=service) def add_instance(self, userid, passphrase, uuid=None, token=None): @@ -89,7 +89,7 @@ class SoledadContainer(Container): uuid, passphrase, soledad_path, soledad_url, cert_path, token) - self.add_instances(userid, soledad) + super(SoledadContainer, self).add_instance(userid, soledad) data = {'user': userid, 'uuid': uuid, 'token': token, 'soledad': soledad} @@ -202,9 +202,9 @@ class SoledadService(HookableService): class KeymanagerContainer(Container): - def __init__(self, basedir): + def __init__(self, service=None, basedir=DEFAULT_BASEDIR): self._basedir = os.path.expanduser(basedir) - super(KeymanagerContainer, self).__init__() + super(KeymanagerContainer, self).__init__(service=service) def add_instance(self, userid, token, uuid, soledad): diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index fca51048..99132c2d 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -25,19 +25,30 @@ from twisted.python import log from leap.bitmask import __version__ from leap.bitmask.core import configurable -from leap.bitmask.core import mail_services from leap.bitmask.core import _zmq from leap.bitmask.core import flags -from leap.bonafide.service import BonafideService from leap.common.events import server as event_server # from leap.vpn import EIPService +backend = flags.BACKEND + +if backend == 'default': + from leap.bitmask.core import mail_services + from leap.bonafide.service import BonafideService +elif backend == 'dummy': + from leap.bitmask.core.dummy import mail_services + from leap.bitmask.core.dummy import BonafideService +else: + raise RuntimeError('Backend not supported') + + class BitmaskBackend(configurable.ConfigurableService): def __init__(self, basedir=configurable.DEFAULT_BASEDIR): configurable.ConfigurableService.__init__(self, basedir) + self.core_commands = BackendCommands(self) def enabled(service): return self.get_config('services', service, False, boolean=True) @@ -117,38 +128,19 @@ class BitmaskBackend(configurable.ConfigurableService): service.setServiceParent(self) return service - # General commands for the BitmaskBackend Core Service - def do_stats(self): - log.msg('BitmaskCore Service STATS') - mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss - return 'BitmaskCore: [Mem usage: %s KB]' % (mem / 1024) + return self.core_commands.do_stats() def do_status(self): - # we may want to make this tuple a class member - services = ('soledad', 'keymanager', 'mail', 'eip') - - status = {} - for name in services: - _status = 'stopped' - try: - if self.getServiceNamed(name).running: - _status = 'running' - except KeyError: - pass - status[name] = _status - status['backend'] = flags.BACKEND - - return json.dumps(status) + return self.core_commands.do_status() def do_version(self): - version = __version__ - return 'BitmaskCore: %s' % version + return self.core_commands.do_version() def do_shutdown(self): - self.stopService() - reactor.callLater(1, reactor.stop) - return 'shutting down...' + return self.core_commands.do_shutdown() + + # Service Toggling def do_enable_service(self, service): assert service in self.service_names @@ -175,3 +167,43 @@ class BitmaskBackend(configurable.ConfigurableService): # TODO -- should stop also? self.set_config('services', service, 'False') return 'ok' + + +class BackendCommands(object): + + """ + General commands for the BitmaskBackend Core Service. + """ + + def __init__(self, core): + self.core = core + + def do_status(self): + # we may want to make this tuple a class member + services = ('soledad', 'keymanager', 'mail', 'eip') + + status = {} + for name in services: + _status = 'stopped' + try: + if self.core.getServiceNamed(name).running: + _status = 'running' + except KeyError: + pass + status[name] = _status + status['backend'] = flags.BACKEND + + return json.dumps(status) + + def do_version(self): + return {'version_core': __version__} + + def do_stats(self): + log.msg('BitmaskCore Service STATS') + mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + return {'mem_usage': '%s KB' % (mem / 1024)} + + def do_shutdown(self): + self.core.stopService() + reactor.callLater(1, reactor.stop) + return {'shutdown': 'ok'} -- cgit v1.2.3 From cfdd93937a7af65be73d5cc482686d1fe559d0a9 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 3 May 2016 10:18:06 -0400 Subject: [refactor] rename and move api_contract --- docs/core_api_contract | 41 +++++++++++++++++++++++++++++++++++ src/leap/bitmask/core/api_contract.py | 40 ---------------------------------- 2 files changed, 41 insertions(+), 40 deletions(-) create mode 100755 docs/core_api_contract delete mode 100644 src/leap/bitmask/core/api_contract.py diff --git a/docs/core_api_contract b/docs/core_api_contract new file mode 100755 index 00000000..b70fb8fa --- /dev/null +++ b/docs/core_api_contract @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# api_contract.py +# Copyright (C) 2016 LEAP Encryption Acess Project +# +# 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 . +""" +Display a human-readable representation of the methods that compound the public +api for Bitmask Core. + +The values are meant to be type annotations. +""" + +if __name__ == "__main__": + from leap.bitmask.core.service import BitmaskBackend + from leap.bitmask.core import api + backend = BitmaskBackend() + + print '========= Bitmask Core API ==================' + print + + for key in api.registry: + human_key = key.replace('do_', '').lower() + value = api.registry[key] + + print("{}:\t\t{}".format( + human_key, + ' '.join([x for x in value]))) + print + print '=============================================' diff --git a/src/leap/bitmask/core/api_contract.py b/src/leap/bitmask/core/api_contract.py deleted file mode 100644 index 86b600c1..00000000 --- a/src/leap/bitmask/core/api_contract.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# api_contract.py -# Copyright (C) 2016 LEAP Encryption Acess Project -# -# 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 . -""" -Display a human-readable representation of the methods that compound the public -api for Bitmask Core. - -The values are meant to be type annotations. -""" - -if __name__ == "__main__": - from leap.bitmask.core.service import BitmaskBackend - from leap.bitmask.core import api - backend = BitmaskBackend() - - print '========= Bitmask Core API ==================' - print - - for key in api.registry: - human_key = key.replace('do_', '').lower() - value = api.registry[key] - - print("{}:\t\t{}".format( - human_key, - ' '.join([x for x in value]))) - print - print '=============================================' -- cgit v1.2.3 From 2a802378a6ffd1c1c677e4fa035ee3b8f932dfbe Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 3 May 2016 10:40:30 -0400 Subject: [pkg] remove pixelated readme, can be enabled in prefswin --- pkg/PixelatedWebmail.README | 38 -------------------------------------- pkg/pyinst/pyinst-build.mk | 1 - 2 files changed, 39 deletions(-) delete mode 100644 pkg/PixelatedWebmail.README diff --git a/pkg/PixelatedWebmail.README b/pkg/PixelatedWebmail.README deleted file mode 100644 index 06e52964..00000000 --- a/pkg/PixelatedWebmail.README +++ /dev/null @@ -1,38 +0,0 @@ -How to enable Pixelated Webmail -------------------------------- - -WARNING! This is an experimental feature. -It can expose your mail to *any* user with access to your machine, since there -is no authentication in place at the moment. It could even eat your data. You -have been warned. - -Ok, how do I enable this wonderful feature? -------------------------------------------- - -First, run the bundle for a first time, and ensure that you can register a new -account with a mail-enabled provider (for instance, mail.bitmask.net). - -Then, you have to edit a config file living inside the bundle folders. You have -to add "Pixmail=true" under the [General] section, like this: - -lib/config/leap/leap.conf: - -[General] -SkipFirstRun=true -Provider=mail.bitmask.net -Pixmail=true - -[mail.bitmask.net] -Services=mx - -Then, run bitmask again: - -./bitmask --debug - -And a new "Bitmask Webmail" option should have appeared under the "Bitmask" -menu. - -If you want to disable the Webmail functionality, just set the Pixmail property -to 'false'. - -Enjoy your local and encrypted pixelated webmail! diff --git a/pkg/pyinst/pyinst-build.mk b/pkg/pyinst/pyinst-build.mk index 664f13ec..32239f8e 100644 --- a/pkg/pyinst/pyinst-build.mk +++ b/pkg/pyinst/pyinst-build.mk @@ -55,7 +55,6 @@ pyinst-cleanup: pyinst-distribution-data: cp release-notes.rst $(DIST_VERSION) - cp pkg/PixelatedWebmail.README $(DIST_VERSION) cp LICENSE $(DIST_VERSION) pyinst-helpers-linux: -- cgit v1.2.3 From 54560623ac5c325bebbe627582b3895c3354368a Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 3 May 2016 12:18:25 -0400 Subject: [feature] enable to download attachments from within webkit - Resolves: #8069 - Releases: 0.9.2 --- src/leap/bitmask/gui/qt_browser.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/leap/bitmask/gui/qt_browser.py b/src/leap/bitmask/gui/qt_browser.py index c62e7770..2f7e6086 100644 --- a/src/leap/bitmask/gui/qt_browser.py +++ b/src/leap/bitmask/gui/qt_browser.py @@ -17,8 +17,10 @@ """ QtWebKit-based browser to display Pixelated User Agent """ +import os +import urlparse -from PySide import QtCore, QtWebKit, QtGui +from PySide import QtCore, QtWebKit, QtGui, QtNetwork PIXELATED_URI = 'http://localhost:9090' @@ -37,3 +39,29 @@ class PixelatedWindow(QtGui.QDialog): def load_app(self): self.view.load(QtCore.QUrl(PIXELATED_URI)) + self.view.page().setForwardUnsupportedContent(True) + self.view.page().unsupportedContent.connect(self.download) + + self.manager = QtNetwork.QNetworkAccessManager() + self.manager.finished.connect(self.finished) + + def download(self, reply): + self.request = QtNetwork.QNetworkRequest(reply.url()) + self.reply = self.manager.get(self.request) + + def finished(self): + url = self.reply.url().toString() + + filename = urlparse.parse_qs(url).get('filename', None) + if filename: + filename = filename[0] + name = filename or url + + path = os.path.expanduser(os.path.join( + '~', unicode(name).split('/')[-1])) + destination = QtGui.QFileDialog.getSaveFileName(self, "Save", path) + if destination: + filename = destination[0] + with open(filename, 'wb') as f: + f.write(str(self.reply.readAll())) + f.close() -- cgit v1.2.3