diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/leap/bitmask/cli/bitmask_cli.py | 375 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/command.py | 105 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/eip.py | 39 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/keys.py | 124 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/mail.py | 42 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/user.py | 75 | ||||
| -rw-r--r-- | src/leap/bitmask/core/dispatcher.py | 4 | ||||
| -rw-r--r-- | src/leap/bitmask/core/mail_services.py | 2 | 
8 files changed, 425 insertions, 341 deletions
diff --git a/src/leap/bitmask/cli/bitmask_cli.py b/src/leap/bitmask/cli/bitmask_cli.py index 79e15051..76b27c94 100755 --- a/src/leap/bitmask/cli/bitmask_cli.py +++ b/src/leap/bitmask/cli/bitmask_cli.py @@ -18,27 +18,20 @@  """  Bitmask Command Line interface: zmq client.  """ -import json  import sys -import getpass -import argparse -from colorama import init as color_init  from colorama import Fore -from twisted.internet import reactor -from txzmq import ZmqEndpoint, ZmqEndpointType -from txzmq import ZmqFactory, ZmqREQConnection -from txzmq import ZmqRequestTimeoutError +from twisted.internet import reactor, defer -from leap.bitmask.core import ENDPOINT -from leap.keymanager.validation import ValidationLevels +from leap.bitmask.cli.eip import Eip +from leap.bitmask.cli.keys import Keys +from leap.bitmask.cli.mail import Mail +from leap.bitmask.cli.command import Command +from leap.bitmask.cli.user import User -class BitmaskCLI(object): - -    def __init__(self): -        parser = argparse.ArgumentParser( -            usage='''bitmask_cli <command> [<args>] +class BitmaskCLI(Command): +    usage = '''bitmask_cli <command> [<args>]  Controls the Bitmask application. @@ -55,347 +48,53 @@ GENERAL COMMANDS:     launch     launch the Bitmask backend daemon     shutdown   shutdown Bitmask backend daemon     status     displays general status about the running Bitmask services -   debug      show some debug info about bitmask-core - - -''', epilog=("Use 'bitmask_cli <command> --help' to learn more " -             "about each command.")) -        parser.add_argument('command', help='Subcommand to run') - -        # parse_args defaults to [1:] for args, but you need to -        # exclude the rest of the args too, or validation will fail -        args = parser.parse_args(sys.argv[1:2]) -        self.args = args -        self.subargs = None +   stats      show some debug info about bitmask-core +   help       show this help message -        if not hasattr(self, args.command): -            print 'Unrecognized command' -            parser.print_help() -            exit(1) +''' +    epilog = ("Use 'bitmask_cli <command> help' to learn more " +              "about each command.") +    commands = ['shutdown', 'status', 'stats'] -        # use dispatch pattern to invoke method with same name -        getattr(self, args.command)() +    def user(self, raw_args): +        user = User() +        return user.execute(raw_args) -    def user(self): -        parser = argparse.ArgumentParser( -            description=('Handles Bitmask accounts: creation, authentication ' -                         'and modification'), -            prog='bitmask_cli user') -        parser.add_argument('username', nargs='?', -                            help='username ID, in the form <user@example.org>') -        parser.add_argument('--create', action='store_true', -                            help='register a new user, if possible') -        parser.add_argument('--authenticate', action='store_true', -                            help='logs in against the provider') -        parser.add_argument('--logout', action='store_true', -                            help='ends any active session with the provider') -        parser.add_argument('--active', action='store_true', -                            help='shows the active user, if any') -        # now that we're inside a subcommand, ignore the first -        # TWO argvs, ie the command (bitmask_cli) and the subcommand (user) -        args = parser.parse_args(sys.argv[2:]) -        self.subargs = args +    def mail(self, raw_args): +        mail = Mail() +        return mail.execute(raw_args) -    def mail(self): -        parser = argparse.ArgumentParser( -            description='Bitmask Encrypted Mail service', -            prog='bitmask_cli mail') -        parser.add_argument('--start', action='store_true', -                            help='tries to start the mail service') -        parser.add_argument('--stop', action='store_true', -                            help='stops the mail service if running') -        parser.add_argument('--status', action='store_true', -                            help='displays status about the mail service') -        parser.add_argument('--enable', action='store_true') -        parser.add_argument('--disable', action='store_true') -        parser.add_argument('--get-token', action='store_true', -                            help='returns token for the mail service') -        parser.add_argument('--get-smtp-certificate', action='store_true', -                            help='downloads a new smtp certificate') -        parser.add_argument('--check-smtp-certificate', action='store_true', -                            help='downloads a new smtp certificate ' -                            '(NOT IMPLEMENTED)') +    def eip(self, raw_args): +        eip = Eip() +        return eip.execute(raw_args) -        args = parser.parse_args(sys.argv[2:]) -        self.subargs = args - -    def eip(self): -        parser = argparse.ArgumentParser( -            description='Encrypted Internet Proxy service', -            prog='bitmask_cli eip') -        parser.add_argument('--start', action='store_true', -                            help='Start service') -        parser.add_argument('--stop', action='store_true', help='Stop service') -        parser.add_argument('--status', action='store_true', -                            help='Display status about service') -        parser.add_argument('--enable', action='store_true') -        parser.add_argument('--disable', action='store_true') -        args = parser.parse_args(sys.argv[2:]) -        self.subargs = args - -    def keys(self): -        parser = argparse.ArgumentParser( -            description='Bitmask Keymanager management service', -            prog='bitmask_cli keys') -        parser.add_argument('--list', action='store_true', -                            help='List all known keys') -        parser.add_argument('--export', action='store_true', -                            help='Export the given key') -        parser.add_argument('--import', action='store', metavar='file', -                            dest='imprt', -                            help='Import a key from the file') -        parser.add_argument('--delete', action='store_true', -                            help='Delete the given key') -        parser.add_argument('--private', action='store_true', -                            help='Use private keys (by default uses public)') -        parser.add_argument('--validation', choices=list(ValidationLevels), -                            default='Fingerprint', -                            help='Validation level for the key') -        parser.add_argument('address', nargs='?', -                            help='email address of the key') -        args = parser.parse_args(sys.argv[2:]) -        self.subargs = args +    def keys(self, raw_args): +        keys = Keys() +        return keys.execute(raw_args)      # Single commands -    def launch(self): -        pass - -    def shutdown(self): -        pass - -    def status(self): -        pass - -    def version(self): -        pass - -    def debug(self): -        pass - - -def get_zmq_connection(): -    zf = ZmqFactory() -    e = ZmqEndpoint(ZmqEndpointType.connect, ENDPOINT) -    return ZmqREQConnection(zf, e) - - -def error(msg, stop=False): -    print Fore.RED + "[!] %s" % msg + Fore.RESET -    if stop: -        reactor.stop() -    else: -        sys.exit(1) - - -def timeout_handler(failure, stop_reactor=True): -    # TODO ---- could try to launch the bitmask daemon here and retry - -    if failure.trap(ZmqRequestTimeoutError) == ZmqRequestTimeoutError: -        print (Fore.RED + "[ERROR] Timeout contacting the bitmask daemon. " -               "Is it running?" + Fore.RESET) -        reactor.stop() - - -def do_print_result(stuff): -    obj = json.loads(stuff[0]) -    if not obj['error']: -        print Fore.GREEN + '%s' % obj['result'] + Fore.RESET -    else: -        print Fore.RED + 'ERROR:' + '%s' % obj['error'] + Fore.RESET - - -def do_print_key_list(stuff): -    obj = json.loads(stuff[0]) -    if obj['error']: -        do_print_result(stuff) -        return - -    keys = obj['result'] -    print Fore.GREEN -    for key in keys: -        print key["fingerprint"] + " " + key['address'] -    print Fore.RESET - - -def do_print_key(stuff): -    obj = json.loads(stuff[0]) -    if obj['error']: -        do_print_result(stuff) -        return - -    key = obj['result'] -    print Fore.GREEN -    print "Uids:        " + ', '.join(key['uids']) -    print "Fingerprint: " + key['fingerprint'] -    print "Length:      " + str(key['length']) -    print "Expiration:  " + key['expiry_date'] -    print "Validation:  " + key['validation'] -    print("Used:        " + "sig:" + str(key['sign_used']) + -          ", encr:" + str(key['encr_used'])) -    print "Refresed:    " + key['refreshed_at'] -    print Fore.RESET -    print "" -    print key['key_data'] - - -def send_command(cli): - -    args = cli.args -    subargs = cli.subargs -    cb = do_print_result - -    cmd = args.command - -    if cmd == 'launch': +    def launch(self, raw_args):          # XXX careful! Should see if the process in PID is running,          # avoid launching again.          import commands          commands.getoutput('bitmaskd') -        reactor.stop() -        return - -    elif cmd == 'version': -        do_print_result([json.dumps( -            {'result': 'bitmask_cli: 0.0.1', -             'error': None})]) -        data = ('version',) - -    elif cmd == 'status': -        data = ('status',) +        return defer.succeed(None) -    elif cmd == 'shutdown': -        data = ('shutdown',) +    def version(self, raw_args): +        print Fore.GREEN + 'bitmask_cli: 0.0.1' + Fore.RESET +        self.data = ['version'] +        return self._send() -    elif cmd == 'debug': -        data = ('stats',) -    elif cmd == 'user': -        if 1 != (subargs.active + subargs.create + -                 subargs.authenticate + subargs.logout): -            error('Use bitmask_cli user --help to see available subcommands', -                  stop=True) -            return - -        data = ['user'] - -        if subargs.active: -            data += ['active', '', ''] - -        else: -            if subargs.create: -                data.append('signup') -            elif subargs.authenticate: -                data.append('authenticate') -            elif subargs.logout: -                data.append('logout') - -            username = subargs.username -            if username and '@' not in username: -                error("Username ID must be in the form <user@example.org>", -                      stop=True) -                return -            if not subargs.logout and not username: -                error("Missing username ID but needed for this command", -                      stop=True) -                return -            elif not username: -                username = '' -            data.append(username) - -            if not subargs.logout: -                passwd = getpass.getpass() -                data.append(passwd) - -    elif cmd == 'mail': -        data = ['mail'] - -        if subargs.status: -            data += ['status'] - -        elif subargs.enable: -            data += ['enable'] - -        elif subargs.disable: -            data += ['disable'] - -        elif subargs.get_token: -            data += ['get_token'] - -        elif subargs.get_smtp_certificate: -            data += ['get_smtp_certificate'] - -        else: -            error('Use bitmask_cli mail --help to see available subcommands', -                  stop=True) -            return - -    elif cmd == 'eip': -        data = ['eip'] - -        if subargs.status: -            data += ['status'] - -        elif subargs.enable: -            data += ['enable'] - -        elif subargs.disable: -            data += ['disable'] - -        elif subargs.start: -            data += ['start'] - -        elif subargs.stop: -            data += ['stop'] - -        else: -            error('Use bitmask_cli eip --help to see available subcommands', -                  stop=True) -            return - -    elif cmd == 'keys': -        data = ['keys'] - -        if subargs.list: -            data += ['list'] -            cb = do_print_key_list - -        elif subargs.export: -            data += ['export', subargs.address] -            cb = do_print_key - -        elif subargs.imprt: -            with open(subargs.imprt, 'r') as keyfile: -                rawkey = keyfile.read() - -            data += ['add', subargs.address, subargs.validation, rawkey] -            cb = do_print_key - -        elif subargs.delete: -            data += ['delete', subargs.address] - -        else: -            error('Use bitmask_cli keys --help to see available subcommands', -                  stop=True) -            return - -        if subargs.private: -            data += ['private'] -        else: -            data += ['public'] - -    s = get_zmq_connection() - -    d = s.sendMsg(*data, timeout=60) -    d.addCallback(cb) -    d.addCallback(lambda x: reactor.stop()) -    d.addErrback(timeout_handler) +def execute(): +    cli = BitmaskCLI() +    d = cli.execute(sys.argv[1:]) +    d.addCallback(lambda _: reactor.stop())  def main(): -    color_init() -    cli = BitmaskCLI() -    reactor.callWhenRunning(reactor.callLater, 0, send_command, cli) +    reactor.callWhenRunning(reactor.callLater, 0, execute)      reactor.run()  if __name__ == "__main__": diff --git a/src/leap/bitmask/cli/command.py b/src/leap/bitmask/cli/command.py new file mode 100644 index 00000000..a3fd6289 --- /dev/null +++ b/src/leap/bitmask/cli/command.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +# sender +# Copyright (C) 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/>. +""" +Bitmask Command Line interface: zmq sender. +""" +import argparse +import json +import sys + +from colorama import init as color_init +from colorama import Fore +from twisted.internet import defer +from txzmq import ZmqEndpoint, ZmqEndpointType +from txzmq import ZmqFactory, ZmqREQConnection +from txzmq import ZmqRequestTimeoutError + +from leap.bitmask.core import ENDPOINT + + +def _print_result(result): +    print Fore.GREEN + '%s' % result + Fore.RESET + + +class Command(object): +    service = '' +    usage = '''%s %s <subcommand>''' % tuple(sys.argv[:2]) +    epilog = ("Use '%s %s <subcommand> --help' to learn more " +              "about each command." % tuple(sys.argv[:2])) +    commands = [] + +    def __init__(self): +        color_init() +        zf = ZmqFactory() +        e = ZmqEndpoint(ZmqEndpointType.connect, ENDPOINT) +        self._conn = ZmqREQConnection(zf, e) + +        self.data = [] +        if self.service: +            self.data = [self.service] + +    def execute(self, raw_args): +        self.parser = argparse.ArgumentParser(usage=self.usage, +                                              epilog=self.epilog) +        self.parser.add_argument('command', help='Subcommand to run') +        try: +            args = self.parser.parse_args(raw_args[0:1]) +        except SystemExit: +            return defer.succeed(None) + +        if args.command in self.commands: +            self.data += [args.command] +            return self._send() + +        elif (args.command == 'execute' or +                args.command.startswith('_') or +                not hasattr(self, args.command)): +            print 'Unrecognized command' +            return self.help([]) + +        try: +            # use dispatch pattern to invoke method with same name +            return getattr(self, args.command)(raw_args[1:]) +        except SystemExit: +            return defer.succeed(None) + +    def help(self, raw_args): +        self.parser.print_help() +        return defer.succeed(None) + +    def _send(self, cb=_print_result): +        d = self._conn.sendMsg(*self.data, timeout=60) +        d.addCallback(self._check_err, cb) +        d.addErrback(self._timeout_handler) +        return d + +    def _error(self, msg): +        print Fore.RED + "[!] %s" % msg + Fore.RESET +        sys.exit(1) + +    def _check_err(self, stuff, cb): +        obj = json.loads(stuff[0]) +        if not obj['error']: +            return cb(obj['result']) +        else: +            print Fore.RED + 'ERROR:' + '%s' % obj['error'] + Fore.RESET + +    def _timeout_handler(self, failure): +        # TODO ---- could try to launch the bitmask daemon here and retry +        if failure.trap(ZmqRequestTimeoutError) == ZmqRequestTimeoutError: +            print (Fore.RED + "[ERROR] Timeout contacting the bitmask daemon. " +                   "Is it running?" + Fore.RESET) diff --git a/src/leap/bitmask/cli/eip.py b/src/leap/bitmask/cli/eip.py new file mode 100644 index 00000000..389b662e --- /dev/null +++ b/src/leap/bitmask/cli/eip.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# eip +# Copyright (C) 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/>. +""" +Bitmask Command Line interface: eip +""" +import sys + +from leap.bitmask.cli.command import Command + + +class Eip(Command): +    service = 'eip' +    usage = '''%s eip <subcommand> + +Bitmask encrypted internet service + +SUBCOMMANDS: + +   start      Start service +   stop       Stop service +   status     Display status about service + +''' % sys.argv[0] + +    commands = ['start', 'stop', 'status'] diff --git a/src/leap/bitmask/cli/keys.py b/src/leap/bitmask/cli/keys.py new file mode 100644 index 00000000..b07b4ab8 --- /dev/null +++ b/src/leap/bitmask/cli/keys.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# keys +# Copyright (C) 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/>. +""" +Bitmask Command Line interface: keys +""" +import argparse +import sys + +from colorama import Fore + +from leap.bitmask.cli.command import Command +from leap.keymanager.validation import ValidationLevels + + +class Keys(Command): +    service = 'keys' +    usage = '''%s keys <subcommand> + +Bitmask Keymanager management service + +SUBCOMMANDS: + +   list       List all known keys +   export     Export a given key +   insert     Insert a key to the key storage +   delete     Delete a key from the key storage + +''' % sys.argv[0] + +    def list(self, raw_args): +        parser = argparse.ArgumentParser( +            description='Bitmask list keys', +            prog='%s %s %s' % tuple(sys.argv[:3])) +        parser.add_argument('--private', action='store_true', +                            help='Use private keys (by default uses public)') +        subargs = parser.parse_args(raw_args) + +        self.data += ['list'] +        if subargs.private: +            self.data += ['private'] +        else: +            self.data += ['public'] + +        return self._send(self._print_key_list) + +    def export(self, raw_args): +        parser = argparse.ArgumentParser( +            description='Bitmask export key', +            prog='%s %s %s' % tuple(sys.argv[:3])) +        parser.add_argument('--private', action='store_true', +                            help='Use private keys (by default uses public)') +        parser.add_argument('address', nargs=1, +                            help='email address of the key') +        subargs = parser.parse_args(raw_args) +        self.data += ['export', subargs.address[0]] + +        return self._send(self._print_key) + +    def insert(self, raw_args): +        parser = argparse.ArgumentParser( +            description='Bitmask import key', +            prog='%s %s %s' % tuple(sys.argv[:3])) +        parser.add_argument('--validation', choices=list(ValidationLevels), +                            default='Fingerprint', +                            help='Validation level for the key') +        parser.add_argument('file', nargs=1, +                            help='file where the key is stored') +        parser.add_argument('address', nargs=1, +                            help='email address of the key') +        subargs = parser.parse_args(raw_args) + +        with open(subargs.file[0], 'r') as keyfile: +            rawkey = keyfile.read() +        self.data += ['insert', subargs.address[0], subargs.validation, +                      rawkey] + +        return self._send(self._print_key) + +    def delete(self, raw_args): +        parser = argparse.ArgumentParser( +            description='Bitmask delete key', +            prog='%s %s %s' % tuple(sys.argv[:3])) +        parser.add_argument('--private', action='store_true', +                            help='Use private keys (by default uses public)') +        parser.add_argument('address', nargs=1, +                            help='email address of the key') +        subargs = parser.parse_args(raw_args) +        self.data += ['delete', subargs.address[0]] + +        return self._send() + +    def _print_key_list(self, keys): +        print Fore.GREEN +        for key in keys: +            print key["fingerprint"] + " " + key['address'] +        print Fore.RESET + +    def _print_key(self, key): +        print Fore.GREEN +        print "Uids:        " + ', '.join(key['uids']) +        print "Fingerprint: " + key['fingerprint'] +        print "Length:      " + str(key['length']) +        print "Expiration:  " + key['expiry_date'] +        print "Validation:  " + key['validation'] +        print("Used:        " + "sig:" + str(key['sign_used']) + +              ", encr:" + str(key['encr_used'])) +        print "Refresed:    " + key['refreshed_at'] +        print Fore.RESET +        print "" +        print key['key_data'] diff --git a/src/leap/bitmask/cli/mail.py b/src/leap/bitmask/cli/mail.py new file mode 100644 index 00000000..a02797cc --- /dev/null +++ b/src/leap/bitmask/cli/mail.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# mail +# Copyright (C) 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/>. +""" +Bitmask Command Line interface: mail +""" +import sys + +from leap.bitmask.cli.command import Command + + +class Mail(Command): +    service = 'mail' +    usage = '''%s mail <subcommand> + +Bitmask encrypted internet service + +SUBCOMMANDS: + +   enable               Start service +   disable              Stop service +   status               Display status about service +   get-token            Returns token for the mail service +   get-smtp-certificate Downloads a new smtp certificate + +''' % sys.argv[0] + +    commands = ['enable', 'disable', 'status', 'get-token', +                'get-smtp-certificate'] diff --git a/src/leap/bitmask/cli/user.py b/src/leap/bitmask/cli/user.py new file mode 100644 index 00000000..f2bfc0c0 --- /dev/null +++ b/src/leap/bitmask/cli/user.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# user +# Copyright (C) 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/>. +""" +Bitmask Command Line interface: user +""" +import argparse +import getpass +import sys + +from leap.bitmask.cli.command import Command + + +class User(Command): +    service = 'user' +    usage = '''%s user <subcommand> + +Bitmask account service + +SUBCOMMANDS: + +   create     Register a new user, if possible +   auth       Logs in gainst the provider +   logout     Ends any active session with the provider +   active     Shows the active user, if any + +''' % sys.argv[0] + +    commands = ['active'] + +    def create(self, raw_args): +        username = self.username(raw_args) +        passwd = getpass.getpass() +        self.data += ['signup', username, passwd] +        return self._send() + +    def auth(self, raw_args): +        username = self.username(raw_args) +        passwd = getpass.getpass() +        self.data += ['authenticate', username, passwd] +        return self._send() + +    def logout(self, raw_args): +        username = self.username(raw_args) +        self.data += ['logout', username] +        return self._send() + +    def username(self, raw_args): +        parser = argparse.ArgumentParser( +            description='Bitmask user', +            prog='%s %s %s' % tuple(sys.argv[:3])) +        parser.add_argument('username', nargs=1, +                            help='username ID, in the form <user@example.org>') +        subargs = parser.parse_args(raw_args) + +        username = subargs.username[0] +        if not username: +            self._error("Missing username ID but needed for this command") +        if '@' not in username: +            self._error("Username ID must be in the form <user@example.org>") + +        return username diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py index 9e2b96de..f57790e2 100644 --- a/src/leap/bitmask/core/dispatcher.py +++ b/src/leap/bitmask/core/dispatcher.py @@ -180,7 +180,7 @@ class KeysCmd(SubCommand):          return d      @register_method('dict') -    def do_ADD(self, service, *parts, **kw): +    def do_INSERT(self, service, *parts, **kw):          if len(parts) < 5:              return defer.fail("An email address is needed")          address = parts[2] @@ -189,7 +189,7 @@ class KeysCmd(SubCommand):          bonafide = kw['bonafide']          d = bonafide.do_get_active_user() -        d.addCallback(service.do_add, address, rawkey, validation) +        d.addCallback(service.do_insert, address, rawkey, validation)          return d      @register_method('str') diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py index adf5a92a..799ff3c6 100644 --- a/src/leap/bitmask/core/mail_services.py +++ b/src/leap/bitmask/core/mail_services.py @@ -359,7 +359,7 @@ class KeymanagerService(HookableService):          d.addCallback(lambda key: dict(key))          return d -    def do_add(self, userid, address, rawkey, validation='Fingerprint'): +    def do_insert(self, userid, address, rawkey, validation='Fingerprint'):          km = self._container.get_instance(userid)          validation = ValidationLevels.get(validation)          d = km.put_raw_key(rawkey, address, validation=validation)  | 
