summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2016-03-04 11:44:41 -0400
committerKali Kaneko <kali@leap.se>2016-04-19 09:59:18 -0400
commit6fd1c73db49c5e1e08cf7963017470511fef0059 (patch)
tree04a73adebb69176653f44880debb65980dae4eaa
parentcea88f895f3fe2fadd2a59f9301e84cb13020dd4 (diff)
[feature] add bitmask_cli entrypoint
-rwxr-xr-xsetup.py9
-rw-r--r--src/leap/bitmask/cli/__init__.py0
-rwxr-xr-xsrc/leap/bitmask/cli/bitmask_cli.py341
3 files changed, 346 insertions, 4 deletions
diff --git a/setup.py b/setup.py
index 44e55709..3e720971 100755
--- a/setup.py
+++ b/setup.py
@@ -170,9 +170,6 @@ else:
reqfiles=["pkg/requirements-leap.pip"])
-leap_launcher = 'bitmask=leap.bitmask.app:start_app'
-
-
def copy_reqs(path, withsrc=False):
# add a copy of the processed requirements to the package
_reqpath = ('leap', 'bitmask', 'util', 'reqs.txt')
@@ -480,6 +477,10 @@ if IS_LINUX:
extra_options = {}
+gui_launcher = 'bitmask=leap.bitmask.app:start_app'
+bitmask_cli = 'bitmask_cli=leap.bitmask.cli.bitmask_cli:main'
+
+
setup(
name="leap.bitmask",
package_dir={"": "src"},
@@ -515,7 +516,7 @@ setup(
zip_safe=False,
platforms="all",
entry_points={
- 'console_scripts': [leap_launcher]
+ 'console_scripts': [gui_launcher, bitmask_cli]
},
**extra_options
)
diff --git a/src/leap/bitmask/cli/__init__.py b/src/leap/bitmask/cli/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/leap/bitmask/cli/__init__.py
diff --git a/src/leap/bitmask/cli/bitmask_cli.py b/src/leap/bitmask/cli/bitmask_cli.py
new file mode 100755
index 00000000..c5bb1b15
--- /dev/null
+++ b/src/leap/bitmask/cli/bitmask_cli.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# bitmask_cli
+# Copyright (C) 2015, 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 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 leap.bitmask.core import ENDPOINT
+
+
+class BitmaskCLI(object):
+
+ def __init__(self):
+ parser = argparse.ArgumentParser(
+ usage='''bitmask_cli <command> [<args>]
+
+Controls the Bitmask application.
+
+SERVICE COMMANDS:
+
+ user Handles Bitmask accounts
+ mail Bitmask Encrypted Mail
+ eip Encrypted Internet Proxy
+ keys Bitmask Keymanager
+
+GENERAL COMMANDS:
+
+ version prints version number and exit
+ 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
+
+ if not hasattr(self, args.command):
+ print 'Unrecognized command'
+ parser.print_help()
+ exit(1)
+
+ # use dispatch pattern to invoke method with same name
+ getattr(self, args.command)()
+
+ 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):
+ 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-imap-token', action='store_true',
+ help='returns token for the IMAP service')
+ parser.add_argument('--get-smtp-token', action='store_true',
+ help='returns token for the SMTP 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)')
+
+ 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('--status', action='store_true',
+ help='Display status about service')
+ parser.add_argument('--list-keys', action='store_true',
+ help='List all known keys')
+ parser.add_argument('--export-key', action='store_true',
+ help='Export the given key')
+ args = parser.parse_args(sys.argv[2:])
+ self.subargs = 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 send_command(cli):
+
+ args = cli.args
+ subargs = cli.subargs
+ cb = do_print_result
+
+ cmd = args.command
+
+ if cmd == 'launch':
+ # 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',)
+
+ elif cmd == 'shutdown':
+ data = ('shutdown',)
+
+ elif cmd == 'debug':
+ data = ('stats',)
+
+ elif cmd == 'user':
+ 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 username:
+ username = ''
+
+ # TODO check that ONLY ONE FLAG is True
+ # TODO check that AT LEAST ONE FLAG is True
+
+ passwd = getpass.getpass()
+ data = ['user']
+
+ if subargs.active:
+ data += ['active', '', '']
+
+ elif subargs.create:
+ data += ['signup', username, passwd]
+
+ elif subargs.authenticate:
+ data += ['authenticate', username, passwd]
+
+ elif subargs.logout:
+ data += ['logout', username, passwd]
+
+ else:
+ error('Use bitmask_cli user --help to see available subcommands')
+ return
+
+ elif cmd == 'mail':
+ data = ['mail']
+
+ if subargs.status:
+ data += ['status']
+
+ elif subargs.enable:
+ data += ['enable']
+
+ elif subargs.disable:
+ data += ['disable']
+
+ elif subargs.get_imap_token:
+ data += ['get_imap_token']
+
+ elif subargs.get_smtp_token:
+ data += ['get_smtp_token']
+
+ elif subargs.get_smtp_certificate:
+ data += ['get_smtp_certificate']
+
+ else:
+ error('Use bitmask_cli mail --help to see available subcommands')
+ 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.status:
+ data += ['status']
+
+ elif subargs.list_keys:
+ data += ['list_keys']
+
+ elif subargs.export_key:
+ data += ['export_keys']
+
+ else:
+ error('Use bitmask_cli keys --help to see available subcommands',
+ stop=True)
+ return
+
+ s = get_zmq_connection()
+
+ d = s.sendMsg(*data, timeout=20)
+ d.addCallback(cb)
+ d.addCallback(lambda x: reactor.stop())
+ d.addErrback(timeout_handler)
+
+
+def main():
+ color_init()
+ cli = BitmaskCLI()
+ reactor.callWhenRunning(reactor.callLater, 0, send_command, cli)
+ reactor.run()
+
+if __name__ == "__main__":
+ main()