Add support of TOTP and password hash (WeeChat >= 2.9)

This commit is contained in:
Sébastien Helleu 2021-11-14 18:40:26 +01:00
parent cca2a0fa68
commit 3b0947c9ef
7 changed files with 389 additions and 135 deletions

View File

@ -45,6 +45,7 @@ In QWeeChat, click on connect and enter fields:
- `server`: the IP address or hostname of your machine with WeeChat running
- `port`: the relay port (defined in WeeChat)
- `password`: the relay password (defined in WeeChat)
- `totp`: the Time-Based One-Time Password (optional, to set if required by WeeChat)
Options can be changed in file `~/.config/qweechat/qweechat.conf`.

View File

@ -32,35 +32,91 @@ class ConnectionDialog(QtWidgets.QDialog):
super().__init__(*args)
self.values = values
self.setModal(True)
self.setWindowTitle('Connect to WeeChat')
grid = QtWidgets.QGridLayout()
grid.setSpacing(10)
self.fields = {}
for line, field in enumerate(('server', 'port', 'password', 'lines')):
grid.addWidget(QtWidgets.QLabel(field.capitalize()), line, 0)
focus = None
# server
grid.addWidget(QtWidgets.QLabel('<b>Server</b>'), 0, 0)
line_edit = QtWidgets.QLineEdit()
line_edit.setFixedWidth(200)
value = self.values.get('server', '')
line_edit.insert(value)
grid.addWidget(line_edit, 0, 1)
self.fields['server'] = line_edit
if not focus and not value:
focus = 'server'
# port / SSL
grid.addWidget(QtWidgets.QLabel('<b>Port</b>'), 1, 0)
line_edit = QtWidgets.QLineEdit()
line_edit.setFixedWidth(200)
value = self.values.get('port', '')
line_edit.insert(value)
grid.addWidget(line_edit, 1, 1)
self.fields['port'] = line_edit
if not focus and not value:
focus = 'port'
ssl = QtWidgets.QCheckBox('SSL')
ssl.setChecked(self.values['ssl'] == 'on')
grid.addWidget(ssl, 1, 2)
self.fields['ssl'] = ssl
# password
grid.addWidget(QtWidgets.QLabel('<b>Password</b>'), 2, 0)
line_edit = QtWidgets.QLineEdit()
line_edit.setFixedWidth(200)
if field == 'password':
line_edit.setEchoMode(QtWidgets.QLineEdit.Password)
if field == 'lines':
value = self.values.get('password', '')
line_edit.insert(value)
grid.addWidget(line_edit, 2, 1)
self.fields['password'] = line_edit
if not focus and not value:
focus = 'password'
# TOTP (Time-Based One-Time Password)
label = QtWidgets.QLabel('TOTP')
label.setToolTip('Time-Based One-Time Password (6 digits)')
grid.addWidget(label, 3, 0)
line_edit = QtWidgets.QLineEdit()
line_edit.setPlaceholderText('6 digits')
validator = QtGui.QIntValidator(0, 999999, self)
line_edit.setValidator(validator)
line_edit.setFixedWidth(80)
value = self.values.get('totp', '')
line_edit.insert(value)
grid.addWidget(line_edit, 3, 1)
self.fields['totp'] = line_edit
if not focus and not value:
focus = 'totp'
# lines
grid.addWidget(QtWidgets.QLabel('Lines'), 4, 0)
line_edit = QtWidgets.QLineEdit()
line_edit.setFixedWidth(200)
validator = QtGui.QIntValidator(0, 2147483647, self)
line_edit.setValidator(validator)
line_edit.setFixedWidth(80)
line_edit.insert(self.values[field])
grid.addWidget(line_edit, line, 1)
self.fields[field] = line_edit
if field == 'port':
ssl = QtWidgets.QCheckBox('SSL')
ssl.setChecked(self.values['ssl'] == 'on')
grid.addWidget(ssl, line, 2)
self.fields['ssl'] = ssl
value = self.values.get('lines', '')
line_edit.insert(value)
grid.addWidget(line_edit, 4, 1)
self.fields['lines'] = line_edit
if not focus and not value:
focus = 'lines'
self.dialog_buttons = QtWidgets.QDialogButtonBox()
self.dialog_buttons.setStandardButtons(
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
self.dialog_buttons.rejected.connect(self.close)
grid.addWidget(self.dialog_buttons, 4, 0, 1, 2)
grid.addWidget(self.dialog_buttons, 5, 0, 1, 2)
self.setLayout(grid)
self.show()
if focus:
self.fields[focus].setFocus()

View File

@ -10,8 +10,9 @@ Files: weechat.png, bullet_green_8x8.png, bullet_yellow_8x8.png
Files: application-exit.png, dialog-close.png, dialog-ok-apply.png,
dialog-warning.png, document-save.png, edit-find.png, help-about.png,
network-connect.png, network-disconnect.png, preferences-other.png
dialog-password.png, dialog-warning.png, document-save.png,
edit-find.png, help-about.png, network-connect.png,
network-disconnect.png, preferences-other.png
Files come from Debian package "oxygen-icon-theme":

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

View File

@ -22,19 +22,38 @@
"""I/O with WeeChat/relay."""
import hashlib
import secrets
import struct
from PySide6 import QtCore, QtNetwork
from qweechat import config
from qweechat.debug import DebugDialog
_PROTO_INIT_CMD = [
# initialize with the password
'init password=%(password)s',
# list of supported hash algorithms on our side
# (the hash algorithm will be negotiated with the remote WeeChat)
_HASH_ALGOS_LIST = [
'plain',
'sha256',
'sha512',
'pbkdf2+sha256',
'pbkdf2+sha512',
]
_HASH_ALGOS = ':'.join(_HASH_ALGOS_LIST)
_PROTO_SYNC_CMDS = [
# handshake with remote WeeChat (before init)
_PROTO_HANDSHAKE = f'(handshake) handshake password_hash_algo={_HASH_ALGOS}\n'
# initialize with the password (plain text)
_PROTO_INIT_PWD = 'init password=%(password)s%(totp)s\n'
# initialize with the hashed password
_PROTO_INIT_HASH = ('init password_hash='
'%(algo)s:%(salt)s%(iter)s:%(hash)s%(totp)s\n')
_PROTO_SYNC = [
# get buffers
'(listbuffers) hdata buffer:gui_buffers(*) number,full_name,short_name,'
'type,nicklist,title,local_variables',
@ -49,26 +68,33 @@ _PROTO_SYNC_CMDS = [
STATUS_DISCONNECTED = 'disconnected'
STATUS_CONNECTING = 'connecting'
STATUS_AUTHENTICATING = 'authenticating'
STATUS_CONNECTED = 'connected'
NETWORK_STATUS = {
'disconnected': {
STATUS_DISCONNECTED: {
'label': 'Disconnected',
'color': '#aa0000',
'icon': 'dialog-close.png',
},
'connecting': {
STATUS_CONNECTING: {
'label': 'Connecting…',
'color': '#ff7f00',
'color': '#dd5f00',
'icon': 'dialog-warning.png',
},
'connected': {
STATUS_AUTHENTICATING: {
'label': 'Authenticating…',
'color': '#007fff',
'icon': 'dialog-password.png',
},
STATUS_CONNECTED: {
'label': 'Connected',
'color': 'green',
'icon': 'dialog-ok-apply.png',
},
}
class Network(QtCore.QObject):
"""I/O with WeeChat/relay."""
@ -77,10 +103,9 @@ class Network(QtCore.QObject):
def __init__(self, *args):
super().__init__(*args)
self._server = None
self._port = None
self._ssl = None
self._password = None
self._init_connection()
self.debug_lines = []
self.debug_dialog = None
self._lines = config.CONFIG_DEFAULT_RELAY_LINES
self._buffer = QtCore.QByteArray()
self._socket = QtNetwork.QSslSocket()
@ -88,22 +113,91 @@ class Network(QtCore.QObject):
self._socket.readyRead.connect(self._socket_read)
self._socket.disconnected.connect(self._socket_disconnected)
def _init_connection(self):
self.status = STATUS_DISCONNECTED
self._server = None
self._port = None
self._ssl = None
self._password = None
self._totp = None
self._handshake_received = False
self._handshake_timer = None
self._handshake_timer = False
self._pwd_hash_algo = None
self._pwd_hash_iter = 0
self._server_nonce = None
def set_status(self, status):
"""Set current status."""
self.status = status
self.statusChanged.emit(status, None)
def pbkdf2(self, hash_name, salt):
"""Return hashed password with PBKDF2-HMAC."""
return hashlib.pbkdf2_hmac(
hash_name,
password=self._password.encode('utf-8'),
salt=salt,
iterations=self._pwd_hash_iter,
).hex()
def _build_init_command(self):
"""Build the init command to send to WeeChat."""
cmd = '\n'.join(_PROTO_INIT_CMD) + '\n'
return cmd % {'password': self._password}
totp = f',totp={self._totp}' if self._totp else ''
if self._pwd_hash_algo == 'plain':
cmd = _PROTO_INIT_PWD % {
'password': self._password,
'totp': totp,
}
else:
client_nonce = secrets.token_bytes(16)
salt = self._server_nonce + client_nonce
pwd_hash = None
iterations = ''
if self._pwd_hash_algo == 'pbkdf2+sha512':
pwd_hash = self.pbkdf2('sha512', salt)
iterations = f':{self._pwd_hash_iter}'
elif self._pwd_hash_algo == 'pbkdf2+sha256':
pwd_hash = self.pbkdf2('sha256', salt)
iterations = f':{self._pwd_hash_iter}'
elif self._pwd_hash_algo == 'sha512':
pwd = salt + self._password.encode('utf-8')
pwd_hash = hashlib.sha512(pwd).hexdigest()
elif self._pwd_hash_algo == 'sha256':
pwd = salt + self._password.encode('utf-8')
pwd_hash = hashlib.sha256(pwd).hexdigest()
if not pwd_hash:
return None
cmd = _PROTO_INIT_HASH % {
'algo': self._pwd_hash_algo,
'salt': bytearray(salt).hex(),
'iter': iterations,
'hash': pwd_hash,
'totp': totp,
}
return cmd
def _build_sync_command(self):
"""Build the sync commands to send to WeeChat."""
cmd = '\n'.join(_PROTO_SYNC_CMDS) + '\n'
cmd = '\n'.join(_PROTO_SYNC) + '\n'
return cmd % {'lines': self._lines}
def handshake_timer_expired(self):
if self.status == STATUS_AUTHENTICATING:
self._pwd_hash_algo = 'plain'
self.send_to_weechat(self._build_init_command())
self.sync_weechat()
self.set_status(STATUS_CONNECTED)
def _socket_connected(self):
"""Slot: socket connected."""
self.statusChanged.emit(STATUS_CONNECTED, None)
if self._password:
cmd = self._build_init_command() + self._build_sync_command()
self.send_to_weechat(cmd)
self.set_status(STATUS_AUTHENTICATING)
self.send_to_weechat(_PROTO_HANDSHAKE)
self._handshake_timer = QtCore.QTimer()
self._handshake_timer.setSingleShot(True)
self._handshake_timer.setInterval(2000)
self._handshake_timer.timeout.connect(self.handshake_timer_expired)
self._handshake_timer.start()
def _socket_read(self):
"""Slot: data available on socket."""
@ -129,11 +223,10 @@ class Network(QtCore.QObject):
def _socket_disconnected(self):
"""Slot: socket disconnected."""
self._server = None
self._port = None
self._ssl = None
self._password = ""
self.statusChanged.emit(STATUS_DISCONNECTED, None)
if self._handshake_timer:
self._handshake_timer.stop()
self._init_connection()
self.set_status(STATUS_DISCONNECTED)
def is_connected(self):
"""Return True if the socket is connected, False otherwise."""
@ -143,7 +236,7 @@ class Network(QtCore.QObject):
"""Return True if SSL is used, False otherwise."""
return self._ssl
def connect_weechat(self, server, port, ssl, password, lines):
def connect_weechat(self, server, port, ssl, password, totp, lines):
"""Connect to WeeChat."""
self._server = server
try:
@ -152,6 +245,7 @@ class Network(QtCore.QObject):
self._port = 0
self._ssl = ssl
self._password = password
self._totp = totp
try:
self._lines = int(lines)
except ValueError:
@ -165,23 +259,40 @@ class Network(QtCore.QObject):
self._socket.connectToHostEncrypted(self._server, self._port)
else:
self._socket.connectToHost(self._server, self._port)
self.statusChanged.emit(STATUS_CONNECTING, "")
self.set_status(STATUS_CONNECTING)
def disconnect_weechat(self):
"""Disconnect from WeeChat."""
if self._socket.state() == QtNetwork.QAbstractSocket.UnconnectedState:
self.set_status(STATUS_DISCONNECTED)
return
if self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState:
self.send_to_weechat('quit\n')
self._socket.waitForBytesWritten(1000)
else:
self.statusChanged.emit(STATUS_DISCONNECTED, None)
self.set_status(STATUS_DISCONNECTED)
self._socket.abort()
def send_to_weechat(self, message):
"""Send a message to WeeChat."""
self.debug_print(0, '<==', message, forcecolor='#AA0000')
self._socket.write(message.encode('utf-8'))
def init_with_handshake(self, response):
"""Initialize with WeeChat using the handshake response."""
self._pwd_hash_algo = response['password_hash_algo']
self._pwd_hash_iter = int(response['password_hash_iterations'])
self._server_nonce = bytearray.fromhex(response['nonce'])
if self._pwd_hash_algo:
cmd = self._build_init_command()
if cmd:
self.send_to_weechat(cmd)
self.sync_weechat()
self.set_status(STATUS_CONNECTED)
return
# failed to initialize: disconnect
self.disconnect_weechat()
def desync_weechat(self):
"""Desynchronize from WeeChat."""
self.send_to_weechat('desync\n')
@ -211,3 +322,35 @@ class Network(QtCore.QObject):
'password': self._password,
'lines': str(self._lines),
}
def debug_print(self, *args, **kwargs):
"""Display a debug message."""
self.debug_lines.append((args, kwargs))
if self.debug_dialog:
self.debug_dialog.chat.display(*args, **kwargs)
def _debug_dialog_closed(self, result):
"""Called when debug dialog is closed."""
self.debug_dialog = None
def debug_input_text_sent(self, text):
"""Send debug buffer input to WeeChat."""
if self.network.is_connected():
text = str(text)
pos = text.find(')')
if text.startswith('(') and pos >= 0:
text = '(debug_%s)%s' % (text[1:pos], text[pos+1:])
else:
text = '(debug) %s' % text
self.network.debug_print(0, '<==', text, forcecolor='#AA0000')
self.network.send_to_weechat(text + '\n')
def open_debug_dialog(self):
"""Open a dialog with debug messages."""
if not self.debug_dialog:
self.debug_dialog = DebugDialog()
self.debug_dialog.input.textSent.connect(
self.debug_input_text_sent)
self.debug_dialog.finished.connect(self._debug_dialog_closed)
self.debug_dialog.display_lines(self.debug_lines)
self.debug_dialog.chat.scroll_bottom()

57
qweechat/preferences.py Normal file
View File

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
#
# preferences.py - preferences dialog box
#
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
#
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
#
# QWeeChat 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.
#
# QWeeChat 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 QWeeChat. If not, see <http://www.gnu.org/licenses/>.
#
"""Preferences dialog box."""
from PySide6 import QtCore, QtWidgets as QtGui
class PreferencesDialog(QtGui.QDialog):
"""Preferences dialog."""
def __init__(self, *args):
QtGui.QDialog.__init__(*(self,) + args)
self.setModal(True)
self.setWindowTitle('Preferences')
close_button = QtGui.QPushButton('Close')
close_button.pressed.connect(self.close)
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(close_button)
hbox.addStretch(1)
vbox = QtGui.QVBoxLayout()
label = QtGui.QLabel('Not yet implemented!')
label.setAlignment(QtCore.Qt.AlignHCenter)
vbox.addWidget(label)
label = QtGui.QLabel('')
label.setAlignment(QtCore.Qt.AlignHCenter)
vbox.addWidget(label)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.show()

View File

@ -40,21 +40,18 @@ from pkg_resources import resource_filename
from PySide6 import QtCore, QtGui, QtWidgets
from qweechat import config
from qweechat.weechat import protocol
from qweechat.network import Network, STATUS_DISCONNECTED, NETWORK_STATUS
from qweechat.connection import ConnectionDialog
from qweechat.buffer import BufferListWidget, Buffer
from qweechat.debug import DebugDialog
from qweechat.about import AboutDialog
from qweechat.buffer import BufferListWidget, Buffer
from qweechat.connection import ConnectionDialog
from qweechat.network import Network, STATUS_DISCONNECTED, NETWORK_STATUS
from qweechat.preferences import PreferencesDialog
from qweechat.weechat import protocol
APP_NAME = 'QWeeChat'
AUTHOR = 'Sébastien Helleu'
WEECHAT_SITE = 'https://weechat.org/'
# number of lines in buffer for debug window
DEBUG_NUM_LINES = 50
class MainWindow(QtWidgets.QMainWindow):
"""Main window."""
@ -67,9 +64,6 @@ class MainWindow(QtWidgets.QMainWindow):
self.resize(1000, 600)
self.setWindowTitle(APP_NAME)
self.debug_dialog = None
self.debug_lines = []
self.about_dialog = None
self.connection_dialog = None
self.preferences_dialog = None
@ -101,26 +95,47 @@ class MainWindow(QtWidgets.QMainWindow):
# actions for menu and toolbar
actions_def = {
'connect': [
'network-connect.png', 'Connect to WeeChat',
'Ctrl+O', self.open_connection_dialog],
'network-connect.png',
'Connect to WeeChat',
'Ctrl+O',
self.open_connection_dialog,
],
'disconnect': [
'network-disconnect.png', 'Disconnect from WeeChat',
'Ctrl+D', self.network.disconnect_weechat],
'network-disconnect.png',
'Disconnect from WeeChat',
'Ctrl+D',
self.network.disconnect_weechat,
],
'debug': [
'edit-find.png', 'Debug console window',
'Ctrl+B', self.open_debug_dialog],
'edit-find.png',
'Open debug console window',
'Ctrl+B',
self.network.open_debug_dialog,
],
'preferences': [
'preferences-other.png', 'Preferences',
'Ctrl+P', self.open_preferences_dialog],
'preferences-other.png',
'Change preferences',
'Ctrl+P',
self.open_preferences_dialog,
],
'about': [
'help-about.png', 'About',
'Ctrl+H', self.open_about_dialog],
'help-about.png',
'About QWeeChat',
'Ctrl+H',
self.open_about_dialog,
],
'save connection': [
'document-save.png', 'Save connection configuration',
'Ctrl+S', self.save_connection],
'document-save.png',
'Save connection configuration',
'Ctrl+S',
self.save_connection,
],
'quit': [
'application-exit.png', 'Quit application',
'Ctrl+Q', self.close],
'application-exit.png',
'Quit application',
'Ctrl+Q',
self.close,
],
}
self.actions = {}
for name, action in list(actions_def.items()):
@ -128,7 +143,7 @@ class MainWindow(QtWidgets.QMainWindow):
QtGui.QIcon(
resource_filename(__name__, 'data/icons/%s' % action[0])),
name.capitalize(), self)
self.actions[name].setStatusTip(action[1])
self.actions[name].setToolTip(f'{action[1]} ({action[2]})')
self.actions[name].setShortcut(action[2])
self.actions[name].triggered.connect(action[3])
@ -168,16 +183,18 @@ class MainWindow(QtWidgets.QMainWindow):
# open debug dialog
if self.config.getboolean('look', 'debug'):
self.open_debug_dialog()
self.network.open_debug_dialog()
# auto-connect to relay
if self.config.getboolean('relay', 'autoconnect'):
self.network.connect_weechat(self.config.get('relay', 'server'),
self.config.get('relay', 'port'),
self.config.getboolean('relay',
'ssl'),
self.config.get('relay', 'password'),
self.config.get('relay', 'lines'))
self.network.connect_weechat(
server=self.config.get('relay', 'server'),
port=self.config.get('relay', 'port'),
ssl=self.config.getboolean('relay', 'ssl'),
password=self.config.get('relay', 'password'),
totp=None,
lines=self.config.get('relay', 'lines'),
)
self.show()
@ -192,14 +209,12 @@ class MainWindow(QtWidgets.QMainWindow):
if self.network.is_connected():
message = 'input %s %s\n' % (full_name, text)
self.network.send_to_weechat(message)
self.debug_display(0, '<==', message, forcecolor='#AA0000')
self.network.debug_print(0, '<==', message, forcecolor='#AA0000')
def open_preferences_dialog(self):
"""Open a dialog with preferences."""
# TODO: implement the preferences dialog box
messages = ['Not yet implemented!',
'']
self.preferences_dialog = AboutDialog('Preferences', messages, self)
self.preferences_dialog = PreferencesDialog(self)
def save_connection(self):
"""Save connection configuration."""
@ -208,39 +223,6 @@ class MainWindow(QtWidgets.QMainWindow):
for option in options:
self.config.set('relay', option, options[option])
def debug_display(self, *args, **kwargs):
"""Display a debug message."""
self.debug_lines.append((args, kwargs))
self.debug_lines = self.debug_lines[-DEBUG_NUM_LINES:]
if self.debug_dialog:
self.debug_dialog.chat.display(*args, **kwargs)
def open_debug_dialog(self):
"""Open a dialog with debug messages."""
if not self.debug_dialog:
self.debug_dialog = DebugDialog(self)
self.debug_dialog.input.textSent.connect(
self.debug_input_text_sent)
self.debug_dialog.finished.connect(self._debug_dialog_closed)
self.debug_dialog.display_lines(self.debug_lines)
self.debug_dialog.chat.scroll_bottom()
def debug_input_text_sent(self, text):
"""Send debug buffer input to WeeChat."""
if self.network.is_connected():
text = str(text)
pos = text.find(')')
if text.startswith('(') and pos >= 0:
text = '(debug_%s)%s' % (text[1:pos], text[pos+1:])
else:
text = '(debug) %s' % text
self.debug_display(0, '<==', text, forcecolor='#AA0000')
self.network.send_to_weechat(text + '\n')
def _debug_dialog_closed(self, result):
"""Called when debug dialog is closed."""
self.debug_dialog = None
def open_about_dialog(self):
"""Open a dialog with info about QWeeChat."""
self.about_dialog = AboutDialog(APP_NAME, AUTHOR, WEECHAT_SITE, self)
@ -257,18 +239,20 @@ class MainWindow(QtWidgets.QMainWindow):
def connect_weechat(self):
"""Connect to WeeChat."""
self.network.connect_weechat(
self.connection_dialog.fields['server'].text(),
self.connection_dialog.fields['port'].text(),
self.connection_dialog.fields['ssl'].isChecked(),
self.connection_dialog.fields['password'].text(),
int(self.connection_dialog.fields['lines'].text()))
server=self.connection_dialog.fields['server'].text(),
port=self.connection_dialog.fields['port'].text(),
ssl=self.connection_dialog.fields['ssl'].isChecked(),
password=self.connection_dialog.fields['password'].text(),
totp=self.connection_dialog.fields['totp'].text(),
lines=int(self.connection_dialog.fields['lines'].text()),
)
self.connection_dialog.close()
def _network_status_changed(self, status, extra):
"""Called when the network status has changed."""
if self.config.getboolean('look', 'statusbar'):
self.statusBar().showMessage(status)
self.debug_display(0, '', status, forcecolor='#0000AA')
self.network.debug_print(0, '', status, forcecolor='#0000AA')
self.network_status_set(status)
def network_status_set(self, status):
@ -296,30 +280,40 @@ class MainWindow(QtWidgets.QMainWindow):
def _network_weechat_msg(self, message):
"""Called when a message is received from WeeChat."""
self.debug_display(0, '==>',
self.network.debug_print(
0, '==>',
'message (%d bytes):\n%s'
% (len(message),
protocol.hex_and_ascii(message.data(), 20)),
forcecolor='#008800')
forcecolor='#008800',
)
try:
proto = protocol.Protocol()
message = proto.decode(message.data())
if message.uncompressed:
self.debug_display(
self.network.debug_print(
0, '==>',
'message uncompressed (%d bytes):\n%s'
% (message.size_uncompressed,
protocol.hex_and_ascii(message.uncompressed, 20)),
forcecolor='#008800')
self.debug_display(0, '', 'Message: %s' % message)
self.network.debug_print(0, '', 'Message: %s' % message)
self.parse_message(message)
except Exception: # noqa: E722
print('Error while decoding message from WeeChat:\n%s'
% traceback.format_exc())
self.network.disconnect_weechat()
def _parse_handshake(self, message):
"""Parse a WeeChat message with handshake response."""
for obj in message.objects:
if obj.objtype != 'htb':
continue
self.network.init_with_handshake(obj.value)
break
def _parse_listbuffers(self, message):
"""Parse a WeeChat with list of buffers."""
"""Parse a WeeChat message with list of buffers."""
for obj in message.objects:
if obj.objtype != 'hda' or obj.value['path'][-1] != 'buffer':
continue
@ -462,7 +456,9 @@ class MainWindow(QtWidgets.QMainWindow):
def parse_message(self, message):
"""Parse a WeeChat message."""
if message.msgid.startswith('debug'):
self.debug_display(0, '', '(debug message, ignored)')
self.network.debug_print(0, '', '(debug message, ignored)')
elif message.msgid == 'handshake':
self._parse_handshake(message)
elif message.msgid == 'listbuffers':
self._parse_listbuffers(message)
elif message.msgid in ('listlines', '_buffer_line_added'):
@ -526,8 +522,8 @@ class MainWindow(QtWidgets.QMainWindow):
def closeEvent(self, event):
"""Called when QWeeChat window is closed."""
self.network.disconnect_weechat()
if self.debug_dialog:
self.debug_dialog.close()
if self.network.debug_dialog:
self.network.debug_dialog.close()
config.write(self.config)
QtWidgets.QMainWindow.closeEvent(self, event)