Add Encryption Plugin API
- Add extension points when receiving/sending a message - Add extension point for setting the lock image - Add extension point after clicking the send button - Add extension point when typing - Add a new menu button to choose the desired encryption - Extend the PluginManager to hold a list of encryption plugins
This commit is contained in:
		
							parent
							
								
									3e6ecccc26
								
							
						
					
					
						commit
						5116af037b
					
				
					 11 changed files with 160 additions and 8 deletions
				
			
		|  | @ -939,6 +939,26 @@ | |||
|                     <property name="position">11</property> | ||||
|                   </packing> | ||||
|                 </child> | ||||
|                 <child> | ||||
|                   <object class="GtkMenuButton" id="encryption_menu"> | ||||
|                     <property name="visible">True</property> | ||||
|                     <property name="can_focus">True</property> | ||||
|                     <property name="receives_default">True</property> | ||||
|                     <property name="relief">none</property> | ||||
|                     <child> | ||||
|                       <object class="GtkImage"> | ||||
|                         <property name="visible">True</property> | ||||
|                         <property name="can_focus">False</property> | ||||
|                         <property name="icon_name">channel-secure-symbolic.symbolic</property> | ||||
|                       </object> | ||||
|                     </child> | ||||
|                   </object> | ||||
|                   <packing> | ||||
|                     <property name="expand">False</property> | ||||
|                     <property name="fill">True</property> | ||||
|                     <property name="position">12</property> | ||||
|                   </packing> | ||||
|                 </child> | ||||
|                 <child> | ||||
|                   <object class="GtkBox" id="audio_buttons_hbox"> | ||||
|                     <property name="can_focus">False</property> | ||||
|  | @ -1039,7 +1059,7 @@ audio-mic-volume-low</property> | |||
|                   <packing> | ||||
|                     <property name="expand">False</property> | ||||
|                     <property name="fill">True</property> | ||||
|                     <property name="position">12</property> | ||||
|                     <property name="position">13</property> | ||||
|                   </packing> | ||||
|                 </child> | ||||
|                 <child> | ||||
|  | @ -1050,12 +1070,9 @@ audio-mic-volume-low</property> | |||
|                   <packing> | ||||
|                     <property name="expand">True</property> | ||||
|                     <property name="fill">True</property> | ||||
|                     <property name="position">13</property> | ||||
|                     <property name="position">14</property> | ||||
|                   </packing> | ||||
|                 </child> | ||||
|                 <child> | ||||
|                   <placeholder/> | ||||
|                 </child> | ||||
|                 <child> | ||||
|                   <object class="GtkButton" id="send_button"> | ||||
|                     <property name="label" translatable="yes">_Send</property> | ||||
|  | @ -1077,6 +1094,9 @@ audio-mic-volume-low</property> | |||
|                     <property name="position">15</property> | ||||
|                   </packing> | ||||
|                 </child> | ||||
|                 <child> | ||||
|                   <placeholder/> | ||||
|                 </child> | ||||
|               </object> | ||||
|               <packing> | ||||
|                 <property name="expand">False</property> | ||||
|  |  | |||
|  | @ -64,7 +64,7 @@ | |||
|       </object> | ||||
|     </child> | ||||
|   </object> | ||||
|   <object class="GtkWindow" id="message_window"> | ||||
|   <object class="GtkApplicationWindow" id="message_window"> | ||||
|     <property name="can_focus">False</property> | ||||
|     <property name="default_width">480</property> | ||||
|     <property name="default_height">440</property> | ||||
|  |  | |||
|  | @ -94,6 +94,7 @@ class ChatControl(ChatControlBase): | |||
|         self.last_recv_message_id = None | ||||
|         self.last_recv_message_marks = None | ||||
|         self.last_message_timestamp = None | ||||
| 
 | ||||
|         # for muc use: | ||||
|         # widget = self.xml.get_object('muc_window_actions_button') | ||||
|         self.actions_button = self.xml.get_object('message_window_actions_button') | ||||
|  | @ -302,6 +303,11 @@ class ChatControl(ChatControlBase): | |||
|                 True) | ||||
| 
 | ||||
|         self.update_ui() | ||||
|         self.set_lock_image() | ||||
| 
 | ||||
|         self.encryption_menu = self.xml.get_object('encryption_menu') | ||||
|         self.encryption_menu.set_menu_model( | ||||
|             gui_menu_builder.get_encryption_menu(self.contact)) | ||||
|         # restore previous conversation | ||||
|         self.restore_conversation() | ||||
|         self.msg_textview.grab_focus() | ||||
|  | @ -907,6 +913,21 @@ class ChatControl(ChatControlBase): | |||
|         self._show_lock_image(self.gpg_is_active, 'OpenPGP', | ||||
|                 self.gpg_is_active, loggable, True) | ||||
| 
 | ||||
|     def set_lock_image(self): | ||||
|         visible = self.encryption != 'disabled' | ||||
|         loggable = self.session and self.session.is_loggable() | ||||
| 
 | ||||
|         encryption_state = {'visible': visible, | ||||
|                             'enc_type': self.encryption, | ||||
|                             'enc_enabled': False, | ||||
|                             'chat_logged': loggable, | ||||
|                             'authenticated': False} | ||||
| 
 | ||||
|         gajim.plugin_manager.gui_extension_point( | ||||
|             'encryption_state' + self.encryption, self, encryption_state) | ||||
| 
 | ||||
|         self._show_lock_image(**encryption_state) | ||||
|   | ||||
|     def _show_lock_image(self, visible, enc_type='', enc_enabled=False, | ||||
|                     chat_logged=False, authenticated=False): | ||||
|         """ | ||||
|  | @ -940,6 +961,8 @@ class ChatControl(ChatControlBase): | |||
|         self.lock_image.set_sensitive(enc_enabled) | ||||
| 
 | ||||
|     def _on_authentication_button_clicked(self, widget): | ||||
|         gajim.plugin_manager.gui_extension_point( | ||||
|             'encryption_dialog' + self.encryption, self) | ||||
|         if self.gpg_is_active: | ||||
|             dialogs.GPGInfoWindow(self, self.parent_win.window) | ||||
|         elif self.session and self.session.enable_encryption: | ||||
|  | @ -950,6 +973,14 @@ class ChatControl(ChatControlBase): | |||
|         """ | ||||
|         Send a message to contact | ||||
|         """ | ||||
| 
 | ||||
|         if self.encryption: | ||||
|             self.sendmessage = True | ||||
|             gajim.plugin_manager.gui_extension_point( | ||||
|                     'send_message' + self.encryption, self) | ||||
|             if not self.sendmessage: | ||||
|                 return | ||||
| 
 | ||||
|         message = helpers.remove_invalid_xml_chars(message) | ||||
|         if message in ('', None, '\n'): | ||||
|             return None | ||||
|  | @ -1481,6 +1512,8 @@ class ChatControl(ChatControlBase): | |||
|     def _on_message_tv_buffer_changed(self, textbuffer): | ||||
|         super()._on_message_tv_buffer_changed(textbuffer) | ||||
|         if textbuffer.get_char_count(): | ||||
|             gajim.plugin_manager.gui_extension_point( | ||||
|                 'typing' + self.encryption, self) | ||||
|             e2e_is_active = self.session and \ | ||||
|                     self.session.enable_encryption | ||||
|             e2e_pref = gajim.config.get_per('accounts', self.account, | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ from gi.repository import Gdk | |||
| from gi.repository import Pango | ||||
| from gi.repository import GObject | ||||
| from gi.repository import GLib | ||||
| from gi.repository import Gio | ||||
| import gtkgui_helpers | ||||
| import message_control | ||||
| import dialogs | ||||
|  | @ -395,6 +396,10 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | |||
|                 self._on_window_motion_notify) | ||||
|             self.handlers[id_] = parent_win.window | ||||
| 
 | ||||
|         self.encryption = 'disabled' | ||||
|         self.set_encryption_state() | ||||
|         self.add_window_actions() | ||||
| 
 | ||||
|         # PluginSystem: adding GUI extension point for ChatControlBase | ||||
|         # instance object (also subclasses, eg. ChatControl or GroupchatControl) | ||||
|         gajim.plugin_manager.gui_extension_point('chat_control_base', self) | ||||
|  | @ -412,6 +417,38 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | |||
|         # to properly use the super, because of the old code. | ||||
|         CommandTools.__init__(self) | ||||
| 
 | ||||
|     def add_window_actions(self): | ||||
|         action = Gio.SimpleAction.new_stateful( | ||||
|             "%s-encryptiongroup" % self.contact.jid, | ||||
|             GLib.VariantType.new("s"), | ||||
|             GLib.Variant("s", self.encryption)) | ||||
|         action.connect("change-state", self.activate_encryption) | ||||
|         self.parent_win.window.add_action(action) | ||||
| 
 | ||||
|     def activate_encryption(self, action, param): | ||||
|         encryption = param.get_string() | ||||
|         if self.encryption == encryption: | ||||
|             return | ||||
| 
 | ||||
|         if encryption != 'disabled': | ||||
|             plugin = gajim.plugin_manager.encryption_plugins[encryption] | ||||
|             if not plugin.activate_encryption(self): | ||||
|                 return | ||||
|         action.set_state(param) | ||||
|         gajim.config.set_per( | ||||
|             'contacts', self.contact.jid, 'encryption', encryption) | ||||
|         self.encryption = encryption | ||||
|         self.set_lock_image() | ||||
| 
 | ||||
|     def set_encryption_state(self): | ||||
|         enc = gajim.config.get_per('contacts', self.contact.jid, 'encryption') | ||||
|         if enc not in gajim.plugin_manager.encryption_plugins: | ||||
|             self.encryption = 'disabled' | ||||
|             gajim.config.set_per( | ||||
|                 'contacts', self.contact.jid, 'encryption', 'disabled') | ||||
|         else: | ||||
|             self.encryption = enc | ||||
| 
 | ||||
|     def set_speller(self): | ||||
|         # now set the one the user selected | ||||
|         per_type = 'contacts' | ||||
|  | @ -723,7 +760,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | |||
|             keyID=keyID, type_=type_, chatstate=chatstate, msg_id=msg_id, | ||||
|             resource=resource, user_nick=self.user_nick, xhtml=xhtml, | ||||
|             label=label, callback=_cb, callback_args=[callback] + callback_args, | ||||
|             control=self, attention=attention, correction_msg=correction_msg, automatic_message=False)) | ||||
|             control=self, attention=attention, correction_msg=correction_msg, | ||||
|             automatic_message=False, encryption=self.encryption)) | ||||
| 
 | ||||
|         # Record the history of sent messages | ||||
|         self.save_message(message, 'sent') | ||||
|  |  | |||
|  | @ -481,6 +481,7 @@ class Config: | |||
|             }, {}), | ||||
|             'contacts': ({ | ||||
|                     'gpg_enabled': [ opt_bool, False, _('Is OpenPGP enabled for this contact?')], | ||||
|                     'encryption': [ opt_str, '', _('Encryption used for this contact.')], | ||||
|                     'autonegotiate_esessions': [opt_bool, False, _('Should Gajim automatically start an encrypted session with this contact when possible?')], | ||||
|                     'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')], | ||||
|             }, {}), | ||||
|  |  | |||
|  | @ -2136,6 +2136,14 @@ class Connection(CommonConnection, ConnectionHandlers): | |||
|     def _nec_stanza_message_outgoing(self, obj): | ||||
|         if obj.conn.name != self.name: | ||||
|             return | ||||
|         encryption = gajim.config.get_per('contacts', obj.jid, 'encryption') | ||||
|         if encryption != 'disabled': | ||||
|             gajim.plugin_manager.gui_extension_point( | ||||
|                 'encrypt' + encryption, self, obj, self.send_message) | ||||
|         else: | ||||
|             self.send_message(obj) | ||||
| 
 | ||||
|     def send_message(self, obj): | ||||
|         obj.msg_id = self.connection.send(obj.msg_iq, now=obj.now) | ||||
| 
 | ||||
|         gajim.nec.push_incoming_event(MessageSentEvent( | ||||
|  |  | |||
|  | @ -1091,6 +1091,12 @@ class ConnectionHandlersBase: | |||
|     def _nec_message_received(self, obj): | ||||
|         if obj.conn.name != self.name: | ||||
|             return | ||||
| 
 | ||||
|         gajim.plugin_manager.gui_extension_point( | ||||
|             'decrypt', self, obj, self._on_message_received) | ||||
|         if not obj.encrypted: | ||||
|             self._on_message_received(obj) | ||||
| 
 | ||||
|         if obj.encrypted == 'xep200': | ||||
|             try: | ||||
|                 obj.stanza = obj.session.decrypt_stanza(obj.stanza) | ||||
|  | @ -1153,6 +1159,14 @@ class ConnectionHandlersBase: | |||
|         gajim.nec.push_incoming_event(MamDecryptedMessageReceivedEvent(None, | ||||
|             conn=self, msg_obj=obj)) | ||||
| 
 | ||||
|     def _on_message_received(self, obj): | ||||
|         if isinstance(obj, MessageReceivedEvent): | ||||
|             gajim.nec.push_incoming_event( | ||||
|                 DecryptedMessageReceivedEvent(None, conn=self, msg_obj=obj)) | ||||
|         else: | ||||
|             gajim.nec.push_incoming_event( | ||||
|                 MamDecryptedMessageReceivedEvent(None, conn=self, msg_obj=obj)) | ||||
| 
 | ||||
|     def _nec_decrypted_message_received(self, obj): | ||||
|         if obj.conn.name != self.name: | ||||
|             return | ||||
|  |  | |||
|  | @ -2796,6 +2796,7 @@ class MessageOutgoingEvent(nec.NetworkOutgoingEvent): | |||
|         self.attention = False | ||||
|         self.correction_msg = None | ||||
|         self.automatic_message = True | ||||
|         self.encryption = '' | ||||
| 
 | ||||
|     def get_full_jid(self): | ||||
|         if self.resource: | ||||
|  |  | |||
|  | @ -781,3 +781,14 @@ def build_bookmark_menu(account): | |||
|     label = menu.get_item_attribute_value(1, 'label').get_string() | ||||
|     menu.remove(1) | ||||
|     menu.insert_submenu(1, label, bookmark_menu) | ||||
| 
 | ||||
| 
 | ||||
| def get_encryption_menu(contact): | ||||
|     menu = Gio.Menu() | ||||
|     menu.append( | ||||
|         'Disabled', 'win.{}-encryptiongroup::{}'.format(contact.jid, 'disabled')) | ||||
|     for encryption in gajim.plugin_manager.encryption_plugins: | ||||
|         menu_action = 'win.{}-encryptiongroup::{}'.format( | ||||
|             contact.jid, encryption) | ||||
|         menu.append(encryption, menu_action) | ||||
|     return menu | ||||
|  |  | |||
|  | @ -58,6 +58,16 @@ class GajimPlugin(object): | |||
| 
 | ||||
|     :todo: decide whether we really need this one, because class name (with | ||||
|             module name) can act as such short name | ||||
|     ''' | ||||
|     encryption_name = '' | ||||
|     ''' | ||||
|     Name of the encryption scheme. | ||||
| 
 | ||||
|     The name that Gajim displays in the encryption menu. | ||||
|     Leave empty if the plugin is not an encryption plugin. | ||||
| 
 | ||||
|     :type: str | ||||
| 
 | ||||
|     ''' | ||||
|     version = '' | ||||
|     ''' | ||||
|  |  | |||
|  | @ -101,6 +101,12 @@ class PluginManager(metaclass=Singleton): | |||
|         ''' | ||||
|         Registered handlers of GUI extension points. | ||||
|         ''' | ||||
| 
 | ||||
|         self.encryption_plugins = {} | ||||
|         ''' | ||||
|         Registered names with instances of encryption Plugins. | ||||
|         ''' | ||||
| 
 | ||||
|         for path in [gajim.PLUGINS_DIRS[1], gajim.PLUGINS_DIRS[0]]: | ||||
|             pc = PluginManager.scan_dir_for_plugins(path) | ||||
|             self.add_plugins(pc) | ||||
|  | @ -291,6 +297,10 @@ class PluginManager(metaclass=Singleton): | |||
|             elif issubclass(event_class, nec.NetworkOutgoingEvent): | ||||
|                 gajim.nec.unregister_outgoing_event(event_class) | ||||
| 
 | ||||
|     def _remove_name_from_encryption_plugins(self, plugin): | ||||
|         if plugin.encryption_name: | ||||
|             del self.encryption_plugins[plugin.encryption_name] | ||||
| 
 | ||||
|     @log_calls('PluginManager') | ||||
|     def activate_plugin(self, plugin): | ||||
|         ''' | ||||
|  | @ -300,6 +310,7 @@ class PluginManager(metaclass=Singleton): | |||
|         if not plugin.active and plugin.activatable: | ||||
| 
 | ||||
|             self._add_gui_extension_points_handlers_from_plugin(plugin) | ||||
|             self._add_encryption_name_from_plugin(plugin) | ||||
|             self._handle_all_gui_extension_points_with_plugin(plugin) | ||||
|             self._register_events_handlers_in_ged(plugin) | ||||
|             self._register_network_events_in_nec(plugin) | ||||
|  | @ -339,6 +350,7 @@ class PluginManager(metaclass=Singleton): | |||
| 
 | ||||
|         self._remove_events_handler_from_ged(plugin) | ||||
|         self._remove_network_events_from_nec(plugin) | ||||
|         self._remove_name_from_encryption_plugins(plugin) | ||||
| 
 | ||||
|         # removing plug-in from active plug-ins list | ||||
|         plugin.deactivate() | ||||
|  | @ -357,6 +369,10 @@ class PluginManager(metaclass=Singleton): | |||
|             self.gui_extension_points_handlers.setdefault(gui_extpoint_name, | ||||
|                 []).append(gui_extpoint_handlers) | ||||
| 
 | ||||
|     def _add_encryption_name_from_plugin(self, plugin): | ||||
|         if plugin.encryption_name: | ||||
|             self.encryption_plugins[plugin.encryption_name] = plugin | ||||
| 
 | ||||
|     @log_calls('PluginManager') | ||||
|     def _handle_all_gui_extension_points_with_plugin(self, plugin): | ||||
|         for gui_extpoint_name, gui_extpoint_handlers in \ | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue