From 5b1c15150e0c6499cffb8171eae28fc2ce01fd27 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Fri, 1 Jul 2016 18:28:49 +0200 Subject: [feat] use subcommands in the cli Reorganize all the commands to don't use '--' but parse each subcommand separately. --- src/leap/bitmask/cli/bitmask_cli.py | 375 ++++----------------------------- src/leap/bitmask/cli/command.py | 105 +++++++++ src/leap/bitmask/cli/eip.py | 39 ++++ src/leap/bitmask/cli/keys.py | 124 +++++++++++ src/leap/bitmask/cli/mail.py | 42 ++++ src/leap/bitmask/cli/user.py | 75 +++++++ src/leap/bitmask/core/dispatcher.py | 4 +- src/leap/bitmask/core/mail_services.py | 2 +- 8 files changed, 425 insertions(+), 341 deletions(-) create mode 100644 src/leap/bitmask/cli/command.py create mode 100644 src/leap/bitmask/cli/eip.py create mode 100644 src/leap/bitmask/cli/keys.py create mode 100644 src/leap/bitmask/cli/mail.py create mode 100644 src/leap/bitmask/cli/user.py 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 [] +class BitmaskCLI(Command): + usage = '''bitmask_cli [] 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 --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 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 ') - 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 ", - 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 . +""" +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 ''' % tuple(sys.argv[:2]) + epilog = ("Use '%s %s --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 . +""" +Bitmask Command Line interface: eip +""" +import sys + +from leap.bitmask.cli.command import Command + + +class Eip(Command): + service = 'eip' + usage = '''%s eip + +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 . +""" +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 + +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 . +""" +Bitmask Command Line interface: mail +""" +import sys + +from leap.bitmask.cli.command import Command + + +class Mail(Command): + service = 'mail' + usage = '''%s mail + +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 . +""" +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 + +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 ') + 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 ") + + 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) -- cgit v1.2.3