Add olm account loading/creation and a initial olm command.
This commit is contained in:
parent
45e6f742ec
commit
c52b7866c8
5 changed files with 210 additions and 0 deletions
1
main.py
1
main.py
|
@ -36,6 +36,7 @@ from matrix.colors import Formatted
|
|||
from matrix.utf import utf8_decode
|
||||
from matrix.http import HttpResponse
|
||||
from matrix.api import MatrixSendMessage
|
||||
from matrix.encryption import matrix_olm_command_cb
|
||||
|
||||
# Weechat searches for the registered callbacks in the scope of the main script
|
||||
# file, import the callbacks here so weechat can find them.
|
||||
|
|
|
@ -32,6 +32,7 @@ from matrix.utils import key_from_value, tags_from_line_data
|
|||
from matrix.plugin_options import DebugType
|
||||
from matrix.server import MatrixServer
|
||||
from matrix.colors import Formatted
|
||||
from matrix.encryption import matrix_hook_olm_command
|
||||
|
||||
|
||||
def hook_commands():
|
||||
|
@ -98,6 +99,8 @@ def hook_commands():
|
|||
"matrix_me_command_cb",
|
||||
"")
|
||||
|
||||
matrix_hook_olm_command()
|
||||
|
||||
W.hook_command_run('/topic', 'matrix_command_topic_cb', '')
|
||||
W.hook_command_run('/buffer clear', 'matrix_command_buf_clear_cb', '')
|
||||
W.hook_command_run('/join', 'matrix_command_join_cb', '')
|
||||
|
|
176
matrix/encryption.py
Normal file
176
matrix/encryption.py
Normal file
|
@ -0,0 +1,176 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Weechat Matrix Protocol Script
|
||||
# Copyright © 2018 Damir Jelić <poljar@termina.org.uk>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
# above copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
from builtins import str
|
||||
|
||||
from functools import wraps
|
||||
from future.moves.itertools import zip_longest
|
||||
|
||||
import matrix.globals
|
||||
|
||||
try:
|
||||
from olm.account import Account, OlmAccountError
|
||||
except ImportError:
|
||||
matrix.globals.ENCRYPTION = False
|
||||
|
||||
from matrix.globals import W, SERVERS
|
||||
from matrix.utf import utf8_decode
|
||||
|
||||
|
||||
def own_buffer(f):
|
||||
|
||||
@wraps(f)
|
||||
def wrapper(data, buffer, *args, **kwargs):
|
||||
|
||||
for server in SERVERS.values():
|
||||
if buffer in server.buffers.values():
|
||||
return f(server.name, buffer, *args, **kwargs)
|
||||
elif buffer == server.server_buffer:
|
||||
return f(server.name, buffer, *args, **kwargs)
|
||||
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def encrypt_enabled(f):
|
||||
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwds):
|
||||
if matrix.globals.ENCRYPTION:
|
||||
return f(*args, **kwds)
|
||||
return None
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@encrypt_enabled
|
||||
def matrix_hook_olm_command():
|
||||
W.hook_command(
|
||||
# Command name and short description
|
||||
"olm",
|
||||
"Matrix olm encryption command",
|
||||
# Synopsis
|
||||
("info all|blacklisted|private|unverified|verified <filter>||"
|
||||
"blacklist <device-id> ||"
|
||||
"unverify <device-id> ||"
|
||||
"verify <device-id>"),
|
||||
# Description
|
||||
(" info: show info about known devices and their keys\n"
|
||||
"blacklist: blacklist a device\n"
|
||||
" unverify: unverify a device\n"
|
||||
" verify: verify a device\n\n"
|
||||
"Examples:\n"),
|
||||
# Completions
|
||||
('info all|blacklisted|private|unverified|verified ||'
|
||||
'blacklist %(device_ids) ||'
|
||||
'unverify %(device_ids) ||'
|
||||
'verify %(device_ids)'),
|
||||
# Function name
|
||||
'matrix_olm_command_cb',
|
||||
'')
|
||||
|
||||
|
||||
def olm_cmd_parse_args(args):
|
||||
split_args = args.split()
|
||||
|
||||
command = split_args.pop(0) if split_args else "info"
|
||||
|
||||
rest_args = split_args if split_args else []
|
||||
|
||||
return command, rest_args
|
||||
|
||||
|
||||
def grouper(iterable, n, fillvalue=None):
|
||||
"Collect data into fixed-length chunks or blocks"
|
||||
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
|
||||
args = [iter(iterable)] * n
|
||||
return zip_longest(*args, fillvalue=fillvalue)
|
||||
|
||||
|
||||
def partition_key(key):
|
||||
groups = grouper(key, 4, " ")
|
||||
return ' '.join(''.join(g) for g in groups)
|
||||
|
||||
|
||||
@own_buffer
|
||||
@utf8_decode
|
||||
def matrix_olm_command_cb(server_name, buffer, args):
|
||||
server = SERVERS[server_name]
|
||||
command, args = olm_cmd_parse_args(args)
|
||||
|
||||
if not command or command == "info":
|
||||
olm = server.olm
|
||||
device_msg = (" - Device ID: {}\n".format(server.device_id)
|
||||
if server.device_id else "")
|
||||
id_key = partition_key(olm.account.identity_keys()["curve25519"])
|
||||
fp_key = partition_key(olm.account.identity_keys()["ed25519"])
|
||||
message = ("{prefix}matrix: Identity keys:\n"
|
||||
" - User: {user}\n"
|
||||
"{device_msg}"
|
||||
" - Identity key: {id_key}\n"
|
||||
" - Fingerprint key: {fp_key}\n").format(
|
||||
prefix=W.prefix("network"),
|
||||
user=server.user,
|
||||
device_msg=device_msg,
|
||||
id_key=id_key,
|
||||
fp_key=fp_key)
|
||||
W.prnt(server.server_buffer, message)
|
||||
else:
|
||||
message = ("{prefix}matrix: Command not implemented.".format(
|
||||
prefix=W.prefix("error")))
|
||||
W.prnt(server.server_buffer, message)
|
||||
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
|
||||
class EncryptionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Olm():
|
||||
|
||||
@encrypt_enabled
|
||||
def __init__(self, server, account=None):
|
||||
# type: (Server, Account) -> None
|
||||
self.server = server
|
||||
if account:
|
||||
self.account = account
|
||||
else:
|
||||
self.account = Account()
|
||||
|
||||
@classmethod
|
||||
@encrypt_enabled
|
||||
def from_session_dir(cls, server):
|
||||
# type: (Server) -> Olm
|
||||
account_file_name = "{}_{}.account".format(server.user,
|
||||
server.device_id)
|
||||
session_path = server.get_session_path()
|
||||
path = os.path.join(session_path, account_file_name)
|
||||
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
pickle = f.read()
|
||||
account = Account.from_pickle(pickle)
|
||||
return cls(server, account)
|
||||
except OlmAccountError as error:
|
||||
raise EncryptionError(error)
|
|
@ -31,3 +31,4 @@ except ImportError:
|
|||
OPTIONS = PluginOptions() # type: PluginOptions
|
||||
SERVERS = dict() # type: Dict[str, MatrixServer]
|
||||
CONFIG = None # type: weechat.config
|
||||
ENCRYPTION = True # type: bool
|
||||
|
|
|
@ -35,6 +35,13 @@ from matrix.globals import W, SERVERS, OPTIONS
|
|||
import matrix.api as API
|
||||
from matrix.api import MatrixClient, MatrixSyncMessage, MatrixLoginMessage
|
||||
|
||||
from matrix.encryption import Olm, EncryptionError
|
||||
|
||||
try:
|
||||
FileNotFoundError
|
||||
except NameError:
|
||||
FileNotFoundError = IOError
|
||||
|
||||
|
||||
class MatrixServer:
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
@ -48,6 +55,7 @@ class MatrixServer:
|
|||
self.options = dict() # type: Dict[str, weechat.config]
|
||||
self.device_name = "Weechat Matrix" # type: str
|
||||
self.device_id = "" # type: str
|
||||
self.olm = None # type: Olm
|
||||
|
||||
self.user = "" # type: str
|
||||
self.password = "" # type: str
|
||||
|
@ -126,6 +134,24 @@ class MatrixServer:
|
|||
with open(path, 'w') as f:
|
||||
f.write(self.device_id)
|
||||
|
||||
def _load_olm(self):
|
||||
try:
|
||||
self.olm = Olm.from_session_dir(self)
|
||||
except FileNotFoundError:
|
||||
message = ("{prefix}matrix: Creating new Olm identity for {user}"
|
||||
" on {server} for device {device}.").format(
|
||||
prefix=W.prefix("network"),
|
||||
user=self.user,
|
||||
server=self.name,
|
||||
device=self.device_id)
|
||||
W.prnt("", message)
|
||||
self.olm = Olm(self)
|
||||
except EncryptionError as error:
|
||||
message = ("{prefix}matrix: Error loading Olm"
|
||||
"account: {error}.").format(
|
||||
prefix=W.prefix("error"), error=error)
|
||||
W.prnt("", message)
|
||||
|
||||
def _create_options(self, config_file):
|
||||
options = [
|
||||
Option('autoconnect', 'boolean', '', 0, 0, 'off',
|
||||
|
@ -200,6 +226,9 @@ class MatrixServer:
|
|||
|
||||
self._load_device_id()
|
||||
|
||||
if self.device_id:
|
||||
self._load_olm()
|
||||
|
||||
elif option_name == "password":
|
||||
value = W.config_string(option)
|
||||
self.password = W.string_eval_expression(value, {}, {}, {})
|
||||
|
|
Loading…
Reference in a new issue