1 # -*- coding: utf-8 -*-
3 # network.py - I/O with WeeChat/relay
5 # Copyright (C) 2011-2021 SĂ©bastien Helleu <flashcode@flashtux.org>
7 # This file is part of QWeeChat, a Qt remote GUI for WeeChat.
9 # QWeeChat is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # QWeeChat is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
23 """I/O with WeeChat/relay."""
27 from PySide6
import QtCore
, QtNetwork
29 from qweechat
import config
32 _PROTO_INIT_CMD
= ['init password=%(password)s']
35 '(listbuffers) hdata buffer:gui_buffers(*) number,full_name,short_name,'
36 'type,nicklist,title,local_variables',
38 '(listlines) hdata buffer:gui_buffers(*)/own_lines/last_line(-%(lines)d)/'
39 'data date,displayed,prefix,message',
41 '(nicklist) nicklist',
48 STATUS_DISCONNECTED
= 'disconnected'
49 STATUS_CONNECTING
= 'connecting'
50 STATUS_CONNECTED
= 'connected'
54 'label': 'Disconnected',
56 'icon': 'dialog-close.png',
59 'label': 'Connecting…',
61 'icon': 'dialog-warning.png',
66 'icon': 'dialog-ok-apply.png',
70 class Network(QtCore
.QObject
):
71 """I/O with WeeChat/relay."""
73 statusChanged
= QtCore
.Signal(str, str)
74 messageFromWeechat
= QtCore
.Signal(QtCore
.QByteArray
)
76 def __init__(self
, *args
):
77 super().__init
__(*args
)
82 self
._lines
= config
.CONFIG_DEFAULT_RELAY_LINES
83 self
._buffer
= QtCore
.QByteArray()
84 self
._socket
= QtNetwork
.QSslSocket()
85 self
._socket
.connected
.connect(self
._socket
_connected
)
86 # self._socket.error.connect(self._socket_error)
87 self
._socket
.readyRead
.connect(self
._socket
_read
)
88 self
._socket
.disconnected
.connect(self
._socket
_disconnected
)
90 def _socket_connected(self
):
91 """Slot: socket connected."""
92 self
.statusChanged
.emit(STATUS_CONNECTED
, None)
94 self
.send_to_weechat('\n'.join(_PROTO_INIT_CMD
+ _PROTO_SYNC_CMDS
)
95 % {'password': str(self
._password
),
96 'lines': self
._lines
})
98 def _socket_error(self
, error
):
99 """Slot: socket error."""
100 self
.statusChanged
.emit(
102 'Failed, error: %s' % self
._socket
.errorString())
104 def _socket_read(self
):
105 """Slot: data available on socket."""
106 data
= self
._socket
.readAll()
107 self
._buffer
.append(data
)
108 while len(self
._buffer
) >= 4:
110 length
= struct
.unpack('>i', self
._buffer
[0:4].data())[0]
111 if len(self
._buffer
) < length
:
112 # partial message, just wait for end of message
114 # more than one message?
115 if length
< len(self
._buffer
):
116 # save beginning of another message
117 remainder
= self
._buffer
[length
:]
118 self
._buffer
= self
._buffer
[0:length
]
119 self
.messageFromWeechat
.emit(self
._buffer
)
120 if not self
.is_connected():
124 self
._buffer
.append(remainder
)
126 def _socket_disconnected(self
):
127 """Slot: socket disconnected."""
132 self
.statusChanged
.emit(STATUS_DISCONNECTED
, None)
134 def is_connected(self
):
135 """Return True if the socket is connected, False otherwise."""
136 return self
._socket
.state() == QtNetwork
.QAbstractSocket
.ConnectedState
139 """Return True if SSL is used, False otherwise."""
142 def connect_weechat(self
, server
, port
, ssl
, password
, lines
):
143 """Connect to WeeChat."""
144 self
._server
= server
146 self
._port
= int(port
)
150 self
._password
= password
152 self
._lines
= int(lines
)
154 self
._lines
= config
.CONFIG_DEFAULT_RELAY_LINES
155 if self
._socket
.state() == QtNetwork
.QAbstractSocket
.ConnectedState
:
157 if self
._socket
.state() != QtNetwork
.QAbstractSocket
.UnconnectedState
:
160 self
._socket
.ignoreSslErrors()
161 self
._socket
.connectToHostEncrypted(self
._server
, self
._port
)
163 self
._socket
.connectToHost(self
._server
, self
._port
)
164 self
.statusChanged
.emit(STATUS_CONNECTING
, "")
166 def disconnect_weechat(self
):
167 """Disconnect from WeeChat."""
168 if self
._socket
.state() == QtNetwork
.QAbstractSocket
.UnconnectedState
:
170 if self
._socket
.state() == QtNetwork
.QAbstractSocket
.ConnectedState
:
171 self
.send_to_weechat('quit\n')
172 self
._socket
.waitForBytesWritten(1000)
174 self
.statusChanged
.emit(STATUS_DISCONNECTED
, None)
177 def send_to_weechat(self
, message
):
178 """Send a message to WeeChat."""
179 self
._socket
.write(message
.encode('utf-8'))
181 def desync_weechat(self
):
182 """Desynchronize from WeeChat."""
183 self
.send_to_weechat('desync\n')
185 def sync_weechat(self
):
186 """Synchronize with WeeChat."""
187 self
.send_to_weechat('\n'.join(_PROTO_SYNC_CMDS
))
189 def status_label(self
, status
):
190 """Return the label for a given status."""
191 return NETWORK_STATUS
.get(status
, {}).get('label', '')
193 def status_color(self
, status
):
194 """Return the color for a given status."""
195 return NETWORK_STATUS
.get(status
, {}).get('color', 'black')
197 def status_icon(self
, status
):
198 """Return the name of icon for a given status."""
199 return NETWORK_STATUS
.get(status
, {}).get('icon', '')
201 def get_options(self
):
202 """Get connection options."""
204 'server': self
._server
,
206 'ssl': 'on' if self
._ssl
else 'off',
207 'password': self
._password
,
208 'lines': str(self
._lines
),