]> jfr.im git - irc/weechat/qweechat.git/blobdiff - src/qweechat/qweechat.py
Add SSL support
[irc/weechat/qweechat.git] / src / qweechat / qweechat.py
index 91512afffa87ed6c5783a8bec60eea385b7c7398..265a60453e9a4318212c9f1e894fad9b59d734b9 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 #
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2011 Sebastien Helleu <flashcode@flashtux.org>
+# Copyright (C) 2011-2012 Sebastien Helleu <flashcode@flashtux.org>
 #
 # This file is part of QWeeChat, a Qt remote GUI for WeeChat.
 #
 #
 # This file is part of QWeeChat, a Qt remote GUI for WeeChat.
 #
@@ -35,35 +35,36 @@ QtCore = qt_compat.import_module('QtCore')
 QtGui = qt_compat.import_module('QtGui')
 import config
 import weechat.protocol as protocol
 QtGui = qt_compat.import_module('QtGui')
 import config
 import weechat.protocol as protocol
-import weechat.color as color
 from network import Network
 from connection import ConnectionDialog
 from buffer import BufferListWidget, Buffer
 from debug import DebugDialog
 from about import AboutDialog
 
 from network import Network
 from connection import ConnectionDialog
 from buffer import BufferListWidget, Buffer
 from debug import DebugDialog
 from about import AboutDialog
 
-NAME = 'qweechat'
-VERSION = '0.1-dev'
+NAME = 'QWeeChat'
+VERSION = '0.0.1-dev'
 AUTHOR = 'Sébastien Helleu'
 AUTHOR_MAIL= 'flashcode@flashtux.org'
 WEECHAT_SITE = 'http://www.weechat.org/'
 
 AUTHOR = 'Sébastien Helleu'
 AUTHOR_MAIL= 'flashcode@flashtux.org'
 WEECHAT_SITE = 'http://www.weechat.org/'
 
+# number of lines in buffer for debug window
+DEBUG_NUM_LINES = 50
+
 
 class MainWindow(QtGui.QMainWindow):
     """Main window."""
 
     def __init__(self, *args):
 
 class MainWindow(QtGui.QMainWindow):
     """Main window."""
 
     def __init__(self, *args):
-        apply(QtGui.QMainWindow.__init__, (self,) + args)
+        QtGui.QMainWindow.__init__(*(self,) + args)
 
         self.config = config.read()
 
 
         self.config = config.read()
 
-        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.resize(1000, 600)
         self.setWindowTitle(NAME)
 
         self.resize(1000, 600)
         self.setWindowTitle(NAME)
 
+        self.debug_dialog = None
+        self.debug_lines = []
+
         # network
         self.network = Network()
         self.network.statusChanged.connect(self.network_status_changed)
         # network
         self.network = Network()
         self.network.statusChanged.connect(self.network_status_changed)
@@ -97,7 +98,7 @@ class MainWindow(QtGui.QMainWindow):
                        'quit'       : ['application-exit.png', 'Quit application', 'Ctrl+Q', self.close],
                        }
         self.actions = {}
                        'quit'       : ['application-exit.png', 'Quit application', 'Ctrl+Q', self.close],
                        }
         self.actions = {}
-        for name, action in actions_def.iteritems():
+        for name, action in list(actions_def.items()):
             self.actions[name] = QtGui.QAction(QtGui.QIcon('data/icons/%s' % action[0]), name.capitalize(), self)
             self.actions[name].setStatusTip(action[1])
             self.actions[name].setShortcut(action[2])
             self.actions[name] = QtGui.QAction(QtGui.QIcon('data/icons/%s' % action[0]), name.capitalize(), self)
             self.actions[name].setStatusTip(action[1])
             self.actions[name].setShortcut(action[2])
@@ -138,6 +139,7 @@ class MainWindow(QtGui.QMainWindow):
         if self.config.getboolean('relay', 'autoconnect'):
             self.network.connect_weechat(self.config.get('relay', 'server'),
                                          self.config.get('relay', 'port'),
         if self.config.getboolean('relay', 'autoconnect'):
             self.network.connect_weechat(self.config.get('relay', 'server'),
                                          self.config.get('relay', 'port'),
+                                         self.config.get('relay', 'ssl') == 'on',
                                          self.config.get('relay', 'password'))
 
         self.show()
                                          self.config.get('relay', 'password'))
 
         self.show()
@@ -151,14 +153,24 @@ class MainWindow(QtGui.QMainWindow):
         if self.network.is_connected():
             message = 'input %s %s\n' % (full_name, text)
             self.network.send_to_weechat(message)
         if self.network.is_connected():
             message = 'input %s %s\n' % (full_name, text)
             self.network.send_to_weechat(message)
-            self.debug_dialog.chat.display(0, '<==', message, color='red')
+            self.debug_display(0, '<==', message, forcecolor='#AA0000')
 
     def open_preferences_dialog(self):
         pass # TODO
 
 
     def open_preferences_dialog(self):
         pass # TODO
 
+    def debug_display(self, *args, **kwargs):
+        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):
     def open_debug_dialog(self):
-        self.debug_dialog.chat.scroll_bottom()
-        self.debug_dialog.show()
+        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):
         if self.network.is_connected():
 
     def debug_input_text_sent(self, text):
         if self.network.is_connected():
@@ -168,15 +180,15 @@ class MainWindow(QtGui.QMainWindow):
                 text = '(debug_%s)%s' % (text[1:pos], text[pos+1:])
             else:
                 text = '(debug) %s' % text
                 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')
             self.network.send_to_weechat(text + '\n')
-            self.debug_dialog.chat.display(0, '<==', text, color='red')
 
     def debug_dialog_closed(self, result):
 
     def debug_dialog_closed(self, result):
-        self.debug_dialog.hide()
+        self.debug_dialog = None
 
     def open_about_dialog(self):
         messages = ['<b>%s</b> %s' % (NAME, VERSION),
 
     def open_about_dialog(self):
         messages = ['<b>%s</b> %s' % (NAME, VERSION),
-                    '&copy; 2011 %s &lt;<a href="mailto:%s">%s</a>&gt;' % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL),
+                    '&copy; 2011-2012 %s &lt;<a href="mailto:%s">%s</a>&gt;' % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL),
                     '',
                     'WeeChat site: <a href="%s">%s</a>' % (WEECHAT_SITE, WEECHAT_SITE),
                     '']
                     '',
                     'WeeChat site: <a href="%s">%s</a>' % (WEECHAT_SITE, WEECHAT_SITE),
                     '']
@@ -184,7 +196,7 @@ class MainWindow(QtGui.QMainWindow):
 
     def open_connection_dialog(self):
         values = {}
 
     def open_connection_dialog(self):
         values = {}
-        for option in ('server', 'port', 'password'):
+        for option in ('server', 'port', 'ssl', 'password'):
             values[option] = self.config.get('relay', option)
         self.connection_dialog = ConnectionDialog(values, self)
         self.connection_dialog.dialog_buttons.accepted.connect(self.connect_weechat)
             values[option] = self.config.get('relay', option)
         self.connection_dialog = ConnectionDialog(values, self)
         self.connection_dialog.dialog_buttons.accepted.connect(self.connect_weechat)
@@ -192,25 +204,27 @@ class MainWindow(QtGui.QMainWindow):
     def connect_weechat(self):
         self.network.connect_weechat(self.connection_dialog.fields['server'].text(),
                                      self.connection_dialog.fields['port'].text(),
     def connect_weechat(self):
         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())
         self.connection_dialog.close()
 
     def network_status_changed(self, status, extra):
         if self.config.getboolean('look', 'statusbar'):
             self.statusBar().showMessage(status)
                                      self.connection_dialog.fields['password'].text())
         self.connection_dialog.close()
 
     def network_status_changed(self, status, extra):
         if self.config.getboolean('look', 'statusbar'):
             self.statusBar().showMessage(status)
-        self.debug_dialog.chat.display(0, '', status, color='blue')
+        self.debug_display(0, '', status, forcecolor='#0000AA')
         self.network_status_set(status, extra)
 
     def network_status_set(self, status, extra):
         pal = self.network_status.palette()
         self.network_status_set(status, extra)
 
     def network_status_set(self, status, extra):
         pal = self.network_status.palette()
-        if self.network.is_connected():
+        if status == self.network.status_connected:
             pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('green'))
         else:
             pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('#aa0000'))
             pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('green'))
         else:
             pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('#aa0000'))
+        ssl = ' (SSL)' if status != self.network.status_disconnected and self.network.is_ssl() else ''
         self.network_status.setPalette(pal)
         icon = self.network.status_icon(status)
         if icon:
         self.network_status.setPalette(pal)
         icon = self.network.status_icon(status)
         if icon:
-            self.network_status.setText('<img src="data/icons/%s"> %s' % (icon, status.capitalize()))
+            self.network_status.setText('<img src="data/icons/%s"> %s' % (icon, status.capitalize() + ssl))
         else:
             self.network_status.setText(status.capitalize())
         if status == self.network.status_disconnected:
         else:
             self.network_status.setText(status.capitalize())
         if status == self.network.status_disconnected:
@@ -221,24 +235,28 @@ class MainWindow(QtGui.QMainWindow):
             self.actions['disconnect'].setEnabled(True)
 
     def network_message_from_weechat(self, message):
             self.actions['disconnect'].setEnabled(True)
 
     def network_message_from_weechat(self, message):
-        self.debug_dialog.chat.display(0, '==>',
-                                       'message (%d bytes):\n%s'
-                                       % (len(message), protocol.hex_and_ascii(message, 20)),
-                                       color='green')
-        proto = protocol.Protocol()
-        message = proto.decode(str(message))
-        if message.uncompressed:
-            self.debug_dialog.chat.display(0, '==>',
-                                           'message uncompressed (%d bytes):\n%s'
-                                           % (message.size_uncompressed,
-                                              protocol.hex_and_ascii(message.uncompressed, 20)),
-                                           color='green')
-        self.debug_dialog.chat.display(0, '', 'Message: %s' % message)
-        self.parse_message(message)
+        self.debug_display(0, '==>',
+                           'message (%d bytes):\n%s'
+                           % (len(message), protocol.hex_and_ascii(message, 20)),
+                           forcecolor='#008800')
+        try:
+            proto = protocol.Protocol()
+            message = proto.decode(str(message))
+            if message.uncompressed:
+                self.debug_display(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.parse_message(message)
+        except:
+            print("Error while decoding message from WeeChat")
+            self.network.disconnect_weechat()
 
     def parse_message(self, message):
         if message.msgid.startswith('debug'):
 
     def parse_message(self, message):
         if message.msgid.startswith('debug'):
-            self.debug_dialog.chat.display(0, '', '(debug message, ignored)')
+            self.debug_display(0, '', '(debug message, ignored)')
             return
         if message.msgid == 'listbuffers':
             for obj in message.objects:
             return
         if message.msgid == 'listbuffers':
             for obj in message.objects:
@@ -249,42 +267,115 @@ class MainWindow(QtGui.QMainWindow):
                         self.stacked_buffers.removeWidget(buf)
                     self.buffers = []
                     for item in obj.value['items']:
                         self.stacked_buffers.removeWidget(buf)
                     self.buffers = []
                     for item in obj.value['items']:
-                        self.list_buffers.addItem('%d. %s' % (item['number'], item['full_name']))
-                        self.buffers.append(Buffer(item))
-                        self.stacked_buffers.addWidget(self.buffers[-1].widget)
-                        self.buffers[-1].bufferInput.connect(self.buffer_input)
-                        self.buffers[-1].widget.input.bufferSwitchPrev.connect(self.list_buffers.switch_prev_buffer)
-                        self.buffers[-1].widget.input.bufferSwitchNext.connect(self.list_buffers.switch_next_buffer)
+                        buf = self.create_buffer(item)
+                        self.insert_buffer(len(self.buffers), buf)
                     self.list_buffers.setCurrentRow(0)
                     self.buffers[0].widget.input.setFocus()
                     self.list_buffers.setCurrentRow(0)
                     self.buffers[0].widget.input.setFocus()
-        elif message.msgid == 'listlines':
+        elif message.msgid in ('listlines', '_buffer_line_added'):
             for obj in message.objects:
                 if obj.objtype == 'hda' and obj.value['path'][-1] == 'line_data':
                     for item in obj.value['items']:
             for obj in message.objects:
                 if obj.objtype == 'hda' and obj.value['path'][-1] == 'line_data':
                     for item in obj.value['items']:
-                        pointer = item['__path'][0]
-                        buf = filter(lambda b: b.pointer() == pointer, self.buffers)
-                        if buf:
-                            buf[0].widget.chat.display(item['date'],
-                                                       color.remove(item['prefix']),
-                                                       color.remove(item['message']))
-        elif message.msgid == 'nicklist':
+                        if message.msgid == 'listlines':
+                            ptrbuf = item['__path'][0]
+                        else:
+                            ptrbuf = item['buffer']
+                        index = [i for i, b in enumerate(self.buffers) if b.pointer() == ptrbuf]
+                        if index:
+                            self.buffers[index[0]].widget.chat.display(item['date'],
+                                                                       item['prefix'],
+                                                                       item['message'])
+        elif message.msgid in ('_nicklist', 'nicklist'):
+            buffer_nicklist = {}
+            for obj in message.objects:
+                if obj.objtype == 'hda' and obj.value['path'][-1] == 'nicklist_item':
+                    for item in obj.value['items']:
+                        index = [i for i, b in enumerate(self.buffers) if b.pointer() == item['__path'][0]]
+                        if index:
+                            if not item['__path'][0] in buffer_nicklist:
+                                self.buffers[index[0]].remove_all_nicks()
+                            buffer_nicklist[item['__path'][0]] = True
+                            if not item['group'] and item['visible']:
+                                self.buffers[index[0]].add_nick(item['prefix'], item['name'])
+        elif message.msgid == '_buffer_opened':
+            for obj in message.objects:
+                if obj.objtype == 'hda' and obj.value['path'][-1] == 'buffer':
+                    for item in obj.value['items']:
+                        buf = self.create_buffer(item)
+                        index = self.find_buffer_index_for_insert(item['next_buffer'])
+                        self.insert_buffer(index, buf)
+        elif message.msgid.startswith('_buffer_'):
             for obj in message.objects:
             for obj in message.objects:
-                if obj.objtype == 'hda' and obj.value['path'][-1] == 'nick_group':
+                if obj.objtype == 'hda' and obj.value['path'][-1] == 'buffer':
                     for item in obj.value['items']:
                     for item in obj.value['items']:
-                        if not item['group'] and item['visible']:
-                            pointer = item['__path'][0]
-                            buf = filter(lambda b: b.pointer() == pointer, self.buffers)
-                            if buf:
-                                buf[0].add_nick(item['prefix'], item['name'])
+                        index = [i for i, b in enumerate(self.buffers) if b.pointer() == item['__path'][0]]
+                        if index:
+                            index = index[0]
+                            if message.msgid == '_buffer_type_changed':
+                                self.buffers[index].data['type'] = item['type']
+                            elif message.msgid in ('_buffer_moved', '_buffer_merged', '_buffer_unmerged'):
+                                buf = self.buffers[index]
+                                buf.data['number'] = item['number']
+                                self.remove_buffer(index)
+                                index2 = self.find_buffer_index_for_insert(item['next_buffer'])
+                                self.insert_buffer(index2, buf)
+                            elif message.msgid == '_buffer_renamed':
+                                self.buffers[index].data['full_name'] = item['full_name']
+                                self.buffers[index].data['short_name'] = item['short_name']
+                            elif message.msgid == '_buffer_title_changed':
+                                self.buffers[index].data['title'] = item['title']
+                                self.buffers[index].update_title()
+                            elif message.msgid.startswith('_buffer_localvar_'):
+                                self.buffers[index].data['local_variables'] = item['local_variables']
+                                self.buffers[index].update_prompt()
+                            elif message.msgid == '_buffer_closing':
+                                self.remove_buffer(index)
+        elif message.msgid == '_upgrade':
+            self.network.desync_weechat()
+        elif message.msgid == '_upgrade_ended':
+            self.network.sync_weechat()
+
+    def create_buffer(self, item):
+        buf = Buffer(item)
+        buf.bufferInput.connect(self.buffer_input)
+        buf.widget.input.bufferSwitchPrev.connect(self.list_buffers.switch_prev_buffer)
+        buf.widget.input.bufferSwitchNext.connect(self.list_buffers.switch_next_buffer)
+        return buf
+
+    def insert_buffer(self, index, buf):
+        self.buffers.insert(index, buf)
+        self.list_buffers.insertItem(index, '%d. %s' % (buf.data['number'], buf.data['full_name']))
+        self.stacked_buffers.insertWidget(index, buf.widget)
+
+    def remove_buffer(self, index):
+        if self.list_buffers.currentRow == index and index > 0:
+            self.list_buffers.setCurrentRow(index - 1)
+        self.list_buffers.takeItem(index)
+        self.stacked_buffers.removeWidget(self.stacked_buffers.widget(index))
+        self.buffers.pop(index)
+
+    def find_buffer_index_for_insert(self, next_buffer):
+        index = -1
+        if next_buffer == '0x0':
+            index = len(self.buffers)
+        else:
+            index = [i for i, b in enumerate(self.buffers) if b.pointer() == next_buffer]
+            if index:
+                index = index[0]
+        if index < 0:
+            print('Warning: unable to find position for buffer, using end of list by default')
+            index = len(self.buffers)
+        return index
 
     def closeEvent(self, event):
         self.network.disconnect_weechat()
 
     def closeEvent(self, event):
         self.network.disconnect_weechat()
-        self.debug_dialog.close()
+        if self.debug_dialog:
+            self.debug_dialog.close()
         config.write(self.config)
         QtGui.QMainWindow.closeEvent(self, event)
 
 
 app = QtGui.QApplication(sys.argv)
 app.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
         config.write(self.config)
         QtGui.QMainWindow.closeEvent(self, event)
 
 
 app = QtGui.QApplication(sys.argv)
 app.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
+app.setWindowIcon(QtGui.QIcon('data/icons/weechat_icon_32.png'))
 main = MainWindow()
 sys.exit(app.exec_())
 main = MainWindow()
 sys.exit(app.exec_())