jp (message/send, encryption): encryption handling:
- encryption algorithm can now be requested when sending a message (using --encrypt option) - new encryption commands to (de)activate encryption session, check available algorithms, or manage trust.
This commit is contained in:
parent
bd9cec8b47
commit
c059645abf
3 changed files with 317 additions and 64 deletions
|
|
@ -38,7 +38,6 @@ v 0.7.0 (NOT RELEASED YET):
|
|||
- new debug commands, to monitor stream, call bridge method or send fake signals
|
||||
- new info/session command, to get data on current session
|
||||
- new blog/get command, to retrieve locally XMPP blog
|
||||
- new message/encryption command, to handle encryption sessions
|
||||
- new message/mam command, to check MAM archives (this command may be renamed or merged in an other one in the future)
|
||||
- new pubsub commands, for low level pubsub manipulations
|
||||
- include pubusb/search command for "grepping" pubsub nodes
|
||||
|
|
@ -52,6 +51,8 @@ v 0.7.0 (NOT RELEASED YET):
|
|||
- new forums commands
|
||||
- new avatar commands
|
||||
- new ping command
|
||||
- new encryption commands, to handle encryption sessions
|
||||
- e2e encryption algorithm can now be requested in jp message/send
|
||||
- better handling of arguments for pubsub related commands
|
||||
- improved outputs
|
||||
- new template output, jp can now act as a static site generator
|
||||
|
|
|
|||
253
sat_frontends/jp/cmd_encryption.py
Normal file
253
sat_frontends/jp/cmd_encryption.py
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# jp: a SAT command line tool
|
||||
# Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sat_frontends.jp import base
|
||||
from sat_frontends.jp.constants import Const as C
|
||||
from sat.core.i18n import _
|
||||
from functools import partial
|
||||
from sat.tools.common import data_format
|
||||
from sat_frontends.jp import xmlui_manager
|
||||
|
||||
__commands__ = ["Encryption"]
|
||||
|
||||
|
||||
class EncryptionAlgorithms(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
extra_outputs = {"default": self.default_output}
|
||||
super(EncryptionAlgorithms, self).__init__(
|
||||
host, "algorithms",
|
||||
use_output=C.OUTPUT_LIST_DICT,
|
||||
extra_outputs=extra_outputs,
|
||||
use_profile=False,
|
||||
help=_("show available encryption algorithms"))
|
||||
self.need_loop = True
|
||||
|
||||
def add_parser_options(self):
|
||||
pass
|
||||
|
||||
def encryptionPluginsGetCb(self, plugins):
|
||||
self.output(plugins)
|
||||
self.host.quit()
|
||||
|
||||
def default_output(self, plugins):
|
||||
if not plugins:
|
||||
self.disp(_(u"No encryption plugin registered!"))
|
||||
self.host.quit(C.EXIT_NOT_FOUND)
|
||||
else:
|
||||
self.disp(_(u"Following encryption algorithms are available: {algos}").format(
|
||||
algos=', '.join([p['name'] for p in plugins])))
|
||||
self.host.quit()
|
||||
|
||||
def start(self):
|
||||
self.host.bridge.encryptionPluginsGet(
|
||||
callback=self.encryptionPluginsGetCb,
|
||||
errback=partial(
|
||||
self.errback,
|
||||
msg=_(u"can't retrieve plugins: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class EncryptionGet(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
super(EncryptionGet, self).__init__(
|
||||
host, "get",
|
||||
use_output=C.OUTPUT_DICT,
|
||||
help=_(u"get encryption session data"))
|
||||
self.need_loop = True
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument(
|
||||
"jid", type=base.unicode_decoder,
|
||||
help=_(u"jid of the entity to check")
|
||||
)
|
||||
|
||||
def messageEncryptionGetCb(self, serialised):
|
||||
session_data = data_format.deserialise(serialised)
|
||||
if session_data is None:
|
||||
self.disp(
|
||||
u"No encryption session found, the messages are sent in plain text.")
|
||||
self.host.quit(C.EXIT_NOT_FOUND)
|
||||
self.output(session_data)
|
||||
self.host.quit()
|
||||
|
||||
def start(self):
|
||||
jids = self.host.check_jids([self.args.jid])
|
||||
jid = jids[0]
|
||||
self.host.bridge.messageEncryptionGet(
|
||||
jid, self.profile,
|
||||
callback=self.messageEncryptionGetCb,
|
||||
errback=partial(
|
||||
self.errback,
|
||||
msg=_(u"can't get session: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class EncryptionStart(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
super(EncryptionStart, self).__init__(
|
||||
host, "start",
|
||||
help=_(u"start encrypted session with an entity"))
|
||||
self.need_loop = True
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument(
|
||||
"--encrypt-noreplace",
|
||||
action="store_true",
|
||||
help=_(u"don't replace encryption algorithm if an other one is already used"))
|
||||
algorithm = self.parser.add_mutually_exclusive_group()
|
||||
algorithm.add_argument(
|
||||
"-n", "--name", help=_(u"algorithm name (DEFAULT: choose automatically)"))
|
||||
algorithm.add_argument(
|
||||
"-N", "--namespace",
|
||||
help=_(u"algorithm namespace (DEFAULT: choose automatically)"))
|
||||
self.parser.add_argument(
|
||||
"jid", type=base.unicode_decoder,
|
||||
help=_(u"jid of the entity to stop encrypted session with")
|
||||
)
|
||||
|
||||
def encryptionNamespaceGetCb(self, namespace):
|
||||
jids = self.host.check_jids([self.args.jid])
|
||||
jid = jids[0]
|
||||
self.host.bridge.messageEncryptionStart(
|
||||
jid, namespace, not self.args.encrypt_noreplace,
|
||||
self.profile,
|
||||
callback=self.host.quit,
|
||||
errback=partial(self.errback,
|
||||
msg=_(u"Can't start encryption session: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
))
|
||||
|
||||
def start(self):
|
||||
if self.args.name is not None:
|
||||
self.host.bridge.encryptionNamespaceGet(self.args.name,
|
||||
callback=self.encryptionNamespaceGetCb,
|
||||
errback=partial(self.errback,
|
||||
msg=_(u"Can't get encryption namespace: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
))
|
||||
elif self.args.namespace is not None:
|
||||
self.encryptionNamespaceGetCb(self.args.namespace)
|
||||
else:
|
||||
self.encryptionNamespaceGetCb(u"")
|
||||
|
||||
|
||||
class EncryptionStop(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
super(EncryptionStop, self).__init__(
|
||||
host, "stop",
|
||||
help=_(u"stop encrypted session with an entity"))
|
||||
self.need_loop = True
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument(
|
||||
"jid", type=base.unicode_decoder,
|
||||
help=_(u"jid of the entity to stop encrypted session with")
|
||||
)
|
||||
|
||||
def start(self):
|
||||
jids = self.host.check_jids([self.args.jid])
|
||||
jid = jids[0]
|
||||
self.host.bridge.messageEncryptionStop(
|
||||
jid, self.profile,
|
||||
callback=self.host.quit,
|
||||
errback=partial(
|
||||
self.errback,
|
||||
msg=_(u"can't end encrypted session: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TrustUI(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
super(TrustUI, self).__init__(
|
||||
host, "ui",
|
||||
help=_(u"get UI to manage trust"))
|
||||
self.need_loop = True
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument(
|
||||
"jid", type=base.unicode_decoder,
|
||||
help=_(u"jid of the entity to stop encrypted session with")
|
||||
)
|
||||
algorithm = self.parser.add_mutually_exclusive_group()
|
||||
algorithm.add_argument(
|
||||
"-n", "--name", help=_(u"algorithm name (DEFAULT: current algorithm)"))
|
||||
algorithm.add_argument(
|
||||
"-N", "--namespace",
|
||||
help=_(u"algorithm namespace (DEFAULT: current algorithm)"))
|
||||
|
||||
def encryptionTrustUIGetCb(self, xmlui_raw):
|
||||
xmlui = xmlui_manager.create(self.host, xmlui_raw)
|
||||
xmlui.show()
|
||||
xmlui.submitForm()
|
||||
|
||||
def encryptionNamespaceGetCb(self, namespace):
|
||||
jids = self.host.check_jids([self.args.jid])
|
||||
jid = jids[0]
|
||||
self.host.bridge.encryptionTrustUIGet(
|
||||
jid, namespace, self.profile,
|
||||
callback=self.encryptionTrustUIGetCb,
|
||||
errback=partial(
|
||||
self.errback,
|
||||
msg=_(u"can't end encrypted session: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
),
|
||||
)
|
||||
|
||||
def start(self):
|
||||
if self.args.name is not None:
|
||||
self.host.bridge.encryptionNamespaceGet(self.args.name,
|
||||
callback=self.encryptionNamespaceGetCb,
|
||||
errback=partial(self.errback,
|
||||
msg=_(u"Can't get encryption namespace: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
))
|
||||
elif self.args.namespace is not None:
|
||||
self.encryptionNamespaceGetCb(self.args.namespace)
|
||||
else:
|
||||
self.encryptionNamespaceGetCb(u"")
|
||||
|
||||
|
||||
class EncryptionTrust(base.CommandBase):
|
||||
subcommands = (TrustUI,)
|
||||
|
||||
def __init__(self, host):
|
||||
super(EncryptionTrust, self).__init__(
|
||||
host, "trust", use_profile=False, help=_(u"trust manangement")
|
||||
)
|
||||
|
||||
|
||||
class Encryption(base.CommandBase):
|
||||
subcommands = (EncryptionAlgorithms, EncryptionGet, EncryptionStart, EncryptionStop,
|
||||
EncryptionTrust)
|
||||
|
||||
def __init__(self, host):
|
||||
super(Encryption, self).__init__(
|
||||
host, "encryption", use_profile=False, help=_(u"encryption sessions handling")
|
||||
)
|
||||
|
|
@ -33,6 +33,7 @@ __commands__ = ["Message"]
|
|||
class Send(base.CommandBase):
|
||||
def __init__(self, host):
|
||||
super(Send, self).__init__(host, "send", help=_("send a message to a contact"))
|
||||
self.need_loop=True
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument(
|
||||
|
|
@ -71,6 +72,12 @@ class Send(base.CommandBase):
|
|||
default=C.MESS_TYPE_AUTO,
|
||||
help=_("type of the message"),
|
||||
)
|
||||
self.parser.add_argument("-e", "--encrypt", metavar="ALGORITHM",
|
||||
help=_(u"encrypt message using given algorithm"))
|
||||
self.parser.add_argument(
|
||||
"--encrypt-noreplace",
|
||||
action="store_true",
|
||||
help=_(u"don't replace encryption algorithm if an other one is already used"))
|
||||
syntax = self.parser.add_mutually_exclusive_group()
|
||||
syntax.add_argument("-x", "--xhtml", action="store_true", help=_(u"XHTML body"))
|
||||
syntax.add_argument("-r", "--rich", action="store_true", help=_(u"rich body"))
|
||||
|
|
@ -78,17 +85,16 @@ class Send(base.CommandBase):
|
|||
"jid", type=base.unicode_decoder, help=_(u"the destination jid")
|
||||
)
|
||||
|
||||
def start(self):
|
||||
if self.args.xhtml and self.args.separate:
|
||||
self.disp(
|
||||
u"argument -s/--separate is not compatible yet with argument -x/--xhtml",
|
||||
error=True,
|
||||
)
|
||||
self.host.quit(2)
|
||||
def multi_send_cb(self):
|
||||
self.sent += 1
|
||||
if self.sent == self.to_send:
|
||||
self.host.quit(self.errcode)
|
||||
|
||||
jids = self.host.check_jids([self.args.jid])
|
||||
jid = jids[0]
|
||||
self.sendStdin(jid)
|
||||
def multi_send_eb(self, failure_, msg):
|
||||
self.disp(_(u"Can't send message [{msg}]: {reason}").format(
|
||||
msg=msg, reason=failure_))
|
||||
self.errcode = C.EXIT_BRIDGE_ERRBACK
|
||||
self.multi_send_cb()
|
||||
|
||||
def sendStdin(self, dest_jid):
|
||||
"""Send incomming data on stdin to jabber contact
|
||||
|
|
@ -113,7 +119,12 @@ class Send(base.CommandBase):
|
|||
stdin_lines = []
|
||||
|
||||
if self.args.separate: # we send stdin in several messages
|
||||
self.to_send = 0
|
||||
self.sent = 0
|
||||
self.errcode = 0
|
||||
|
||||
if header:
|
||||
self.to_send += 1
|
||||
self.host.bridge.messageSend(
|
||||
dest_jid,
|
||||
{self.args.lang: header},
|
||||
|
|
@ -124,6 +135,7 @@ class Send(base.CommandBase):
|
|||
errback=lambda ignore: ignore,
|
||||
)
|
||||
|
||||
self.to_send += len(stdin_lines)
|
||||
for line in stdin_lines:
|
||||
self.host.bridge.messageSend(
|
||||
dest_jid,
|
||||
|
|
@ -132,8 +144,8 @@ class Send(base.CommandBase):
|
|||
self.args.type,
|
||||
extra,
|
||||
profile_key=self.host.profile,
|
||||
callback=lambda: None,
|
||||
errback=lambda ignore: ignore,
|
||||
callback=self.multi_send_cb,
|
||||
errback=partial(self.multi_send_eb, msg=line),
|
||||
)
|
||||
|
||||
else:
|
||||
|
|
@ -149,9 +161,44 @@ class Send(base.CommandBase):
|
|||
self.args.type,
|
||||
extra,
|
||||
profile_key=self.host.profile,
|
||||
callback=lambda: None,
|
||||
errback=lambda ignore: ignore,
|
||||
callback=self.host.quit,
|
||||
errback=partial(self.errback,
|
||||
msg=_(u"Can't send message: {}")))
|
||||
|
||||
def encryptionNamespaceGetCb(self, namespace, jid_):
|
||||
self.host.bridge.messageEncryptionStart(
|
||||
jid_, namespace, not self.args.encrypt_noreplace,
|
||||
self.profile,
|
||||
callback=lambda: self.sendStdin(jid_),
|
||||
errback=partial(self.errback,
|
||||
msg=_(u"Can't start encryption session: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
))
|
||||
|
||||
|
||||
def start(self):
|
||||
if self.args.xhtml and self.args.separate:
|
||||
self.disp(
|
||||
u"argument -s/--separate is not compatible yet with argument -x/--xhtml",
|
||||
error=True,
|
||||
)
|
||||
self.host.quit(2)
|
||||
|
||||
jids = self.host.check_jids([self.args.jid])
|
||||
jid_ = jids[0]
|
||||
|
||||
if self.args.encrypt_noreplace and self.args.encrypt is None:
|
||||
self.parser.error("You need to use --encrypt if you use --encrypt-noreplace")
|
||||
|
||||
if self.args.encrypt is not None:
|
||||
self.host.bridge.encryptionNamespaceGet(self.args.encrypt,
|
||||
callback=partial(self.encryptionNamespaceGetCb, jid_=jid_),
|
||||
errback=partial(self.errback,
|
||||
msg=_(u"Can't get encryption namespace: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
))
|
||||
else:
|
||||
self.sendStdin(jid_)
|
||||
|
||||
|
||||
class MAM(base.CommandBase):
|
||||
|
|
@ -216,56 +263,8 @@ class MAM(base.CommandBase):
|
|||
callback=self._MAMGetCb, errback=self.errback)
|
||||
|
||||
|
||||
class EncryptionAlgorithms(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
extra_outputs = {"default": self.default_output}
|
||||
super(EncryptionAlgorithms, self).__init__(
|
||||
host, "algorithms",
|
||||
use_output=C.OUTPUT_LIST_DICT,
|
||||
extra_outputs=extra_outputs,
|
||||
use_profile=False,
|
||||
help=_("show available encryption algorithms"))
|
||||
self.need_loop = True
|
||||
|
||||
def add_parser_options(self):
|
||||
pass
|
||||
|
||||
def encryptionPluginsGetCb(self, plugins):
|
||||
self.output(plugins)
|
||||
self.host.quit()
|
||||
|
||||
def default_output(self, plugins):
|
||||
if not plugins:
|
||||
self.disp(_(u"No encryption plugin registered!"))
|
||||
self.host.quit(C.EXIT_NOT_FOUND)
|
||||
else:
|
||||
self.disp(_(u"Following encryption algorithms are available: {algos}").format(
|
||||
algos=', '.join([p['name'] for p in plugins])))
|
||||
self.host.quit()
|
||||
|
||||
def start(self):
|
||||
self.host.bridge.encryptionPluginsGet(
|
||||
callback=self.encryptionPluginsGetCb,
|
||||
errback=partial(
|
||||
self.errback,
|
||||
msg=_(u"can't retrieve plugins: {}"),
|
||||
exit_code=C.EXIT_BRIDGE_ERRBACK,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class Encryption(base.CommandBase):
|
||||
subcommands = (EncryptionAlgorithms,)
|
||||
|
||||
def __init__(self, host):
|
||||
super(Encryption, self).__init__(
|
||||
host, "encryption", use_profile=False, help=_("encryption sessions handling")
|
||||
)
|
||||
|
||||
|
||||
class Message(base.CommandBase):
|
||||
subcommands = (Send, MAM, Encryption)
|
||||
subcommands = (Send, MAM)
|
||||
|
||||
def __init__(self, host):
|
||||
super(Message, self).__init__(
|
||||
|
|
|
|||
Loading…
Reference in a new issue