diff --git a/data/glade/account_creation_wizard_window.glade b/data/glade/account_creation_wizard_window.glade index 66b0d6fe6..38997769f 100644 --- a/data/glade/account_creation_wizard_window.glade +++ b/data/glade/account_creation_wizard_window.glade @@ -35,7 +35,6 @@ to the Jabber network. True - 0 GTK_SHADOW_NONE @@ -99,9 +98,6 @@ to the Jabber network. - - False - @@ -109,7 +105,6 @@ to the Jabber network. tab - False False @@ -144,61 +139,64 @@ to the Jabber network. - + True True - 0 - True - True - - - 1 - 3 - 3 - 4 - GTK_FILL - - - - - - True - 0 - Your JID: - - - 3 - 4 - GTK_FILL - - - - - - True - 0 - _Username: - True - username_entry - - - GTK_FILL - - - - - - True - True - False + True * - True + + 1 2 + + + + + + True + 0 + _Server: + True + + + 1 + 2 + GTK_FILL + + + + + + True + + + + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 0 + _Password: + True + password_entry + + 2 3 + GTK_FILL @@ -225,64 +223,61 @@ to the Jabber network. - - True - 0 - _Password: - True - password_entry - - - 2 - 3 - GTK_FILL - - - - - - True - - - - - - - - 1 - 2 - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - 0 - _Server: - True - - - 1 - 2 - GTK_FILL - - - - - + True True - True + False * - - + True 1 2 + 2 + 3 + + + + + + True + 0 + _Username: + True + username_entry + + + GTK_FILL + + + + + + True + 0 + Your JID: + + + 3 + 4 + GTK_FILL + + + + + + True + True + 0 + True + True + + + 1 + 3 + 3 + 4 + GTK_FILL @@ -294,7 +289,6 @@ to the Jabber network. 1 - False @@ -304,7 +298,6 @@ to the Jabber network. tab 1 - False False @@ -388,6 +381,62 @@ to the Jabber network. 3 5 5 + + + True + 0 + Prox_y: + True + + + GTK_FILL + + + + + + True + None + + + 1 + 2 + GTK_FILL + + + + + True + True + Manage... + True + 0 + + + + 2 + 3 + + + + + + + True + True + Use custom hostname/port + True + 0 + True + + + + 3 + 1 + 2 + + + True @@ -450,62 +499,6 @@ to the Jabber network. GTK_FILL - - - True - True - Use custom hostname/port - True - 0 - True - - - - 3 - 1 - 2 - - - - - - True - True - Manage... - True - 0 - - - - 2 - 3 - - - - - - - True - None - - - 1 - 2 - GTK_FILL - - - - - True - 0 - Prox_y: - True - - - GTK_FILL - - - @@ -526,7 +519,6 @@ to the Jabber network. 2 - False @@ -536,7 +528,69 @@ to the Jabber network. tab 2 - False + False + + + + + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-dialog-warning + 5 + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + + + + + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Add this certificate to the list of trusted certificates. +SHA1 fingerprint of the certificate: + + 0 + True + + + False + 1 + + + + + 3 + + + + + True + + + tab + 3 False @@ -548,8 +602,7 @@ to the Jabber network. - 3 - False + 4 @@ -558,8 +611,7 @@ to the Jabber network. tab - 3 - False + 4 False @@ -592,8 +644,7 @@ Please wait... - 4 - False + 5 @@ -602,8 +653,7 @@ Please wait... tab - 4 - False + 5 False @@ -671,8 +721,7 @@ Please wait... - 5 - False + 6 @@ -681,8 +730,7 @@ Please wait... tab - 5 - False + 6 False diff --git a/src/common/config.py b/src/common/config.py index 3c403f460..0327bb146 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -275,6 +275,7 @@ class Config: 'keyid': [ opt_str, '', '', True ], 'keyname': [ opt_str, '', '', True ], 'usessl': [ opt_bool, False, '', True ], + 'ssl_fingerprint_sha1': [ opt_str, '', '', True ], 'use_srv': [ opt_bool, True, '', True ], 'use_custom_host': [ opt_bool, False, '', True ], 'custom_port': [ opt_int, 5222, '', True ], diff --git a/src/common/configpaths.py b/src/common/configpaths.py index f71e6c9b2..516d849ef 100644 --- a/src/common/configpaths.py +++ b/src/common/configpaths.py @@ -79,9 +79,9 @@ class ConfigPaths: # LOG is deprecated k = ( 'LOG', 'LOG_DB', 'VCARD', 'AVATAR', 'MY_EMOTS', - 'MY_ICONSETS' ) + 'MY_ICONSETS', 'MY_CACERTS') v = (u'logs', u'logs.db', u'vcards', u'avatars', u'emoticons', - u'iconsets') + u'iconsets', u'cacerts.pem') if os.name == 'nt': v = map(lambda x: x.capitalize(), v) diff --git a/src/common/connection.py b/src/common/connection.py index 99c78654e..3b7bf061b 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -58,39 +58,39 @@ log = logging.getLogger('gajim.c.connection') import gtkgui_helpers -ssl_error = { -2: "Unable to get issuer certificate", -3: "Unable to get certificate CRL", -4: "Unable to decrypt certificate's signature", -5: "Unable to decrypt CRL's signature", -6: "Unable to decode issuer public key", -7: "Certificate signature failure", -8: "CRL signature failure", -9: "Certificate is not yet valid", -10: "Certificate has expired", -11: "CRL is not yet valid", -12: "CRL has expired", -13: "Format error in certificate's notBefore field", -14: "Format error in certificate's notAfter field", -15: "Format error in CRL's lastUpdate field", -16: "Format error in CRL's nextUpdate field", -17: "Out of memory", -18: "Self signed certificate in certificate chain", -19: "Unable to get local issuer certificate", -20: "Unable to verify the first certificate", -21: "Unable to verify the first certificate", -22: "Certificate chain too long", -23: "Certificate revoked", -24: "Invalid CA certificate", -25: "Path length constraint exceeded", -26: "Unsupported certificate purpose", -27: "Certificate not trusted", -28: "Certificate rejected", -29: "Subject issuer mismatch", -30: "Authority and subject key identifier mismatch", -31: "Authority and issuer serial number mismatch", -32: "Key usage does not include certificate signing", -50: "Application verification failure" +ssl_error = { +2: _("Unable to get issuer certificate"), +3: _("Unable to get certificate CRL"), +4: _("Unable to decrypt certificate's signature"), +5: _("Unable to decrypt CRL's signature"), +6: _("Unable to decode issuer public key"), +7: _("Certificate signature failure"), +8: _("CRL signature failure"), +9: _("Certificate is not yet valid"), +10: _("Certificate has expired"), +11: _("CRL is not yet valid"), +12: _("CRL has expired"), +13: _("Format error in certificate's notBefore field"), +14: _("Format error in certificate's notAfter field"), +15: _("Format error in CRL's lastUpdate field"), +16: _("Format error in CRL's nextUpdate field"), +17: _("Out of memory"), +18: _("Self signed certificate in certificate chain"), +19: _("Unable to get local issuer certificate"), +20: _("Unable to verify the first certificate"), +21: _("Unable to verify the first certificate"), +22: _("Certificate chain too long"), +23: _("Certificate revoked"), +24: _("Invalid CA certificate"), +25: _("Path length constraint exceeded"), +26: _("Unsupported certificate purpose"), +27: _("Certificate not trusted"), +28: _("Certificate rejected"), +29: _("Subject issuer mismatch"), +30: _("Authority and subject key identifier mismatch"), +31: _("Authority and issuer serial number mismatch"), +32: _("Key usage does not include certificate signing"), +50: _("Application verification failure") } class Connection(ConnectionHandlers): '''Connection class''' @@ -128,7 +128,7 @@ class Connection(ConnectionHandlers): self.last_history_line = {} self.password = passwords.get_password(name) self.server_resource = gajim.config.get_per('accounts', name, 'resource') - # All valid resource substitution strings should be added to this hash. + # All valid resource substitution strings should be added to this hash. if self.server_resource: self.server_resource = Template(self.server_resource).safe_substitute({ 'hostname': socket.gethostname() @@ -183,7 +183,7 @@ class Connection(ConnectionHandlers): # reconnect succeeded self.time_to_reconnect = None self.retrycount = 0 - + # We are doing disconnect at so many places, better use one function in all def disconnect(self, on_purpose=False): self.on_purpose = on_purpose @@ -196,7 +196,7 @@ class Connection(ConnectionHandlers): self.connection.disconnect() self.last_connection = None self.connection = None - + def _disconnectedReconnCB(self): '''Called when we are disconnected''' log.debug('disconnectedReconnCB') @@ -238,7 +238,7 @@ class Connection(ConnectionHandlers): self.disconnect() self.on_purpose = False # END disconenctedReconnCB - + def _connection_lost(self): self.disconnect(on_purpose = False) self.dispatch('STATUS', 'offline') @@ -295,12 +295,29 @@ class Connection(ConnectionHandlers): self._hostname, self.new_account_form, _on_register_result) return - else: - self.dispatch('NEW_ACC_CONNECTED', (conf, is_form)) - self.connection.UnregisterDisconnectHandler( - self._on_new_account) - self.disconnect(on_purpose=True) - return + try: + errnum = self.connection.Connection.ssl_errnum + except AttributeError: + errnum = -1 # we don't have an errnum + ssl_msg = '' + if errnum > 0: + if errnum in ssl_error: + ssl_msg = ssl_error[errnum] + else: + ssl_msg = _('Unknown SSL error: %d') % errnum + ssl_cert = '' + if hasattr(self.connection.Connection, 'ssl_cert_pem'): + ssl_cert = self.connection.Connection.ssl_cert_pem + ssl_fingerprint = '' + if hasattr(self.connection.Connection, 'ssl_fingerprint_sha1'): + ssl_fingerprint = \ + self.connection.Connection.ssl_fingerprint_sha1 + self.dispatch('NEW_ACC_CONNECTED', (conf, is_form, ssl_msg, + ssl_cert, ssl_fingerprint)) + self.connection.UnregisterDisconnectHandler( + self._on_new_account) + self.disconnect(on_purpose=True) + return if not data[1]: # wrong answer self.dispatch('ERROR', (_('Invalid answer'), _('Transport %s answered wrongly to register request: %s') % \ @@ -519,7 +536,6 @@ class Connection(ConnectionHandlers): con.RegisterDisconnectHandler(self._disconnectedReconnCB) log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'], self._current_host['port'], con_type)) - self._register_handlers(con, con_type) name = gajim.config.get_per('accounts', self.name, 'name') hostname = gajim.config.get_per('accounts', self.name, 'hostname') @@ -529,14 +545,31 @@ class Connection(ConnectionHandlers): except AttributeError: errnum = -1 # we don't have an errnum if errnum > 0: - # FIXME: tell the user that the certificat is untrusted, and ask him what to do - try: - log.warning("The authenticity of the "+hostname+" certificate could be invalid.\nSSL Error: "+ssl_error[errnum]) - except KeyError: - log.warning("Unknown SSL error: %d" % errnum) + text = _('The authenticity of the %s certificate could be invalid.') %\ + hostname + if errnum in ssl_error: + text += _('\nSSL Error: %s') % ssl_error[errnum] + else: + text += _('\nUnknown SSL error: %d') % errnum + self.dispatch('SSL_ERROR', (text, con.Connection.ssl_cert_pem, + con.Connection.ssl_fingerprint_sha1)) + return True + if hasattr(con.Connection, 'ssl_fingerprint_sha1'): + saved_fingerprint = gajim.config.get_per('accounts', self.name, 'ssl_fingerprint_sha1') + if saved_fingerprint: + # Check sha1 fingerprint + if con.Connection.ssl_fingerprint_sha1 != saved_fingerprint: + self.dispatch('FINGERPRINT_ERROR', + (con.Connection.ssl_fingerprint_sha1,)) + return True + self._register_handlers(con, con_type) con.auth(name, self.password, self.server_resource, 1, self.__on_auth) - return True + + def ssl_certificate_accepted(self): + name = gajim.config.get_per('accounts', self.name, 'name') + self._register_handlers(self.connection, 'ssl') + self.connection.auth(name, self.password, self.server_resource, 1, self.__on_auth) def _register_handlers(self, con, con_type): self.peerhost = con.get_peerhost() @@ -585,8 +618,8 @@ class Connection(ConnectionHandlers): def quit(self, kill_core): if kill_core and gajim.account_is_connected(self.name): - self.disconnect(on_purpose = True) - + self.disconnect(on_purpose=True) + def get_privacy_lists(self): if not self.connection: return @@ -612,7 +645,7 @@ class Connection(ConnectionHandlers): if not self.connection: return common.xmpp.features_nb.getActiveAndDefaultPrivacyLists(self.connection) - + def del_privacy_list(self, privacy_list): if not self.connection: return @@ -626,17 +659,17 @@ class Connection(ConnectionHandlers): 'again.') % privacy_list)) common.xmpp.features_nb.delPrivacyList(self.connection, privacy_list, _on_del_privacy_list_result) - + def get_privacy_list(self, title): if not self.connection: return common.xmpp.features_nb.getPrivacyList(self.connection, title) - + def set_privacy_list(self, listname, tags): if not self.connection: return common.xmpp.features_nb.setPrivacyList(self.connection, listname, tags) - + def set_active_list(self, listname): if not self.connection: return @@ -646,7 +679,7 @@ class Connection(ConnectionHandlers): if not self.connection: return common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname) - + def build_privacy_rule(self, name, action): '''Build a Privacy rule stanza for invisibility''' iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '') @@ -699,7 +732,7 @@ class Connection(ConnectionHandlers): #Get bookmarks from private namespace self.get_bookmarks() - + #Get annotations self.get_annotations() @@ -798,7 +831,7 @@ class Connection(ConnectionHandlers): # set old_show to requested 'show' in case we need to # recconect before we auth to server self.old_show = show - self.on_purpose = False + self.on_purpose = False self.server_resource = gajim.config.get_per('accounts', self.name, 'resource') # All valid resource substitution strings should be added to this hash. @@ -941,8 +974,8 @@ class Connection(ConnectionHandlers): msg_id = '' chatstate_node.setTagData('id', msg_id) # when msgtxt, requests JEP-0022 composing notification - if chatstate is 'composing' or msgtxt: - chatstate_node.addChild(name = 'composing') + if chatstate is 'composing' or msgtxt: + chatstate_node.addChild(name = 'composing') if forward_from: addresses = msg_iq.addChild('addresses', @@ -983,7 +1016,7 @@ class Connection(ConnectionHandlers): if not self.connection: return self.connection.send(stanza) - + def ack_subscribed(self, jid): if not self.connection: return @@ -1280,7 +1313,7 @@ class Connection(ConnectionHandlers): self.connection.send(p) # last date/time in history to avoid duplicate - if not self.last_history_line.has_key(room_jid): + if not self.last_history_line.has_key(room_jid): # Not in memory, get it from DB last_log = None no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')\ @@ -1343,7 +1376,7 @@ class Connection(ConnectionHandlers): # send instantly so when we go offline, status is sent to gc before we # disconnect from jabber server self.connection.send(p) - # Save the time we quit to avoid duplicate logs AND be faster than + # Save the time we quit to avoid duplicate logs AND be faster than # get that date from DB self.last_history_line[jid] = time_time() diff --git a/src/common/gajim.py b/src/common/gajim.py index 3ca639a53..dcb294034 100644 --- a/src/common/gajim.py +++ b/src/common/gajim.py @@ -77,6 +77,7 @@ VCARD_PATH = gajimpaths['VCARD'] AVATAR_PATH = gajimpaths['AVATAR'] MY_EMOTS_PATH = gajimpaths['MY_EMOTS'] MY_ICONSETS_PATH = gajimpaths['MY_ICONSETS'] +MY_CACERTS = gajimpaths['MY_CACERTS'] TMP = gajimpaths['TMP'] DATA_DIR = gajimpaths['DATA'] HOME_DIR = gajimpaths['HOME'] diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py index 35f908ea2..c4cda4636 100644 --- a/src/common/xmpp/transports_nb.py +++ b/src/common/xmpp/transports_nb.py @@ -745,10 +745,34 @@ class NonBlockingTLS(PlugIn): #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) tcpsock.ssl_errnum = 0 tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback) + cacerts = os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem') try: - tcpsock._sslContext.load_verify_locations(os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem')) + tcpsock._sslContext.load_verify_locations(cacerts) except: - log.warning(_("Unable to load SSL certificats from file %s" % os.path.abspath(os.path.join(gajim.DATA_DIR,'other','ca.crt')))) + log.warning('Unable to load SSL certificats from file %s' % \ + os.path.abspath(cacerts)) + # load users certs + if os.path.isfile(gajim.MY_CACERTS): + store = tcpsock._sslContext.get_cert_store() + f = open(gajim.MY_CACERTS) + lines = f.readlines() + i = 0 + begin = -1 + for line in lines: + if 'BEGIN CERTIFICATE' in line: + begin = i + continue + elif 'END CERTIFICATE' in line and begin > -1: + cert = ''.join(lines[begin:i+2]) + try: + X509cert = OpenSSL.crypto.load_certificate( + OpenSSL.crypto.FILETYPE_PEM, cert) + store.add_cert(X509cert) + except: + log.warning('Unable to load a certificate from file %s' % \ + gajim.MY_CACERTS) + begin = -1 + i += 1 tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) tcpsock._sslObj.set_connect_state() # set to client mode @@ -788,9 +812,12 @@ class NonBlockingTLS(PlugIn): def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok): # Exceptions can't propagate up through this callback, so print them here. try: + self._owner.Connection.ssl_fingerprint_sha1 = cert.digest('sha1') if errnum == 0: return True self._owner.Connection.ssl_errnum = errnum + self._owner.Connection.ssl_cert_pem = OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_PEM, cert) return True except: log.error("Exception caught in _ssl_info_callback:", exc_info=True) diff --git a/src/config.py b/src/config.py index 0b673337c..a47844400 100644 --- a/src/config.py +++ b/src/config.py @@ -2977,7 +2977,7 @@ class AccountCreationWizardWindow: def on_wizard_window_destroy(self, widget): page = self.notebook.get_current_page() - if page > 2 and page < 5 and self.account in gajim.connections: + if page in (4, 5) and self.account in gajim.connections: # connection instance is saved in gajim.connections and we canceled the # addition of the account del gajim.connections[self.account] @@ -2994,15 +2994,19 @@ class AccountCreationWizardWindow: self.window.destroy() def on_back_button_clicked(self, widget): - if self.notebook.get_current_page() in (1, 2): + cur_page = self.notebook.get_current_page() + if cur_page in (1, 2): self.notebook.set_current_page(0) self.back_button.set_sensitive(False) - elif self.notebook.get_current_page() == 3: + elif cur_page == 3: + self.xml.get_widget('form_vbox').remove(self.data_form_widget) + self.notebook.set_current_page(2) # show server page + elif cur_page == 4: if self.account in gajim.connections: del gajim.connections[self.account] self.notebook.set_current_page(2) self.xml.get_widget('form_vbox').remove(self.data_form_widget) - elif self.notebook.get_current_page() == 5: # finish page + elif cur_page == 6: # finish page self.forward_button.show() if self.modify: self.notebook.set_current_page(1) # Go to parameters page @@ -3085,7 +3089,7 @@ class AccountCreationWizardWindow: self.go_online_checkbutton.show() img = self.xml.get_widget('finish_image') img.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_DIALOG) - self.notebook.set_current_page(5) # show finish page + self.notebook.set_current_page(6) # show finish page self.show_vcard_checkbutton.set_active(False) elif cur_page == 2: # We are creating a new account @@ -3124,7 +3128,7 @@ class AccountCreationWizardWindow: config['custom_host'] = self.xml.get_widget( 'custom_host_entry').get_text().decode('utf-8') - self.notebook.set_current_page(4) # show creating page + self.notebook.set_current_page(5) # show creating page self.back_button.hide() self.forward_button.hide() self.update_progressbar_timeout_id = gobject.timeout_add(100, @@ -3134,6 +3138,18 @@ class AccountCreationWizardWindow: con.new_account(self.account, config) gajim.connections[self.account] = con elif cur_page == 3: + checked = self.xml.get_widget('ssl_checkbutton').get_active() + if checked: + hostname = gajim.connections[self.account].new_account_info[ + 'hostname'] + f = open(gajim.MY_CACERTS, 'a') + f.write(hostname + '\n') + f.write(self.ssl_cert + '\n\n') + f.close() + gajim.connections[self.account].new_account_info[ + 'ssl_fingerprint_sha1'] = self.ssl_fingerprint + self.notebook.set_current_page(4) # show fom page + elif cur_page == 4: if self.is_form: form = self.data_form_widget.data_form else: @@ -3142,7 +3158,7 @@ class AccountCreationWizardWindow: self.is_form) self.xml.get_widget('form_vbox').remove(self.data_form_widget) self.xml.get_widget('progressbar_label').set_markup('Account is being created\n\nPlease wait...') - self.notebook.set_current_page(4) # show creating page + self.notebook.set_current_page(5) # show creating page self.back_button.hide() self.forward_button.hide() self.update_progressbar_timeout_id = gobject.timeout_add(100, @@ -3172,13 +3188,13 @@ class AccountCreationWizardWindow: self.progressbar.pulse() return True # loop forever - def new_acc_connected(self, form, is_form): + def new_acc_connected(self, form, is_form, ssl_msg, ssl_cert, + ssl_fingerprint): '''connection to server succeded, present the form to the user.''' if self.update_progressbar_timeout_id is not None: gobject.source_remove(self.update_progressbar_timeout_id) self.back_button.show() self.forward_button.show() - self.notebook.set_current_page(3) # show form page self.is_form = is_form if is_form: dataform = dataforms.ExtendForm(node = form) @@ -3187,6 +3203,21 @@ class AccountCreationWizardWindow: self.data_form_widget = FakeDataForm(form) self.data_form_widget.show_all() self.xml.get_widget('form_vbox').pack_start(self.data_form_widget) + self.ssl_fingerprint = ssl_fingerprint + self.ssl_cert = ssl_cert + if ssl_msg: + # An SSL warning occured, show it + hostname = gajim.connections[self.account].new_account_info['hostname'] + self.xml.get_widget('ssl_label').set_markup(_('Security Warning' + '\n\nThe authenticity of the %s SSL certificate could be invalid.\n' + 'SSL Error: %s\n' + 'Do you still want to connect to this server?') % (hostname, + ssl_msg)) + text = _('Add this certificate to the list of trusted certificates.\nSHA1 fingerprint of the certificate:\n%s') % ssl_fingerprint + self.xml.get_widget('ssl_checkbutton').set_label(text) + self.notebook.set_current_page(3) # show SSL page + else: + self.notebook.set_current_page(4) # show form page def new_acc_not_connected(self, reason): '''Account creation failed: connection to server failed''' @@ -3202,7 +3233,7 @@ class AccountCreationWizardWindow: finish_text = '%s\n\n%s' % ( _('An error occurred during account creation') , reason) self.finish_label.set_markup(finish_text) - self.notebook.set_current_page(5) # show finish page + self.notebook.set_current_page(6) # show finish page def acc_is_ok(self, config): '''Account creation succeeded''' @@ -3225,7 +3256,7 @@ class AccountCreationWizardWindow: 'button, or later by choosing the Accounts menuitem under the Edit ' 'menu from the main window.')) self.finish_label.set_markup(finish_text) - self.notebook.set_current_page(5) # show finish page + self.notebook.set_current_page(6) # show finish page if self.update_progressbar_timeout_id is not None: gobject.source_remove(self.update_progressbar_timeout_id) @@ -3242,7 +3273,7 @@ class AccountCreationWizardWindow: finish_text = '%s\n\n%s' % (_('An error occurred during ' 'account creation') , reason) self.finish_label.set_markup(finish_text) - self.notebook.set_current_page(5) # show finish page + self.notebook.set_current_page(6) # show finish page if self.update_progressbar_timeout_id is not None: gobject.source_remove(self.update_progressbar_timeout_id) diff --git a/src/gajim.py b/src/gajim.py index fa8c4e88c..de50ce6f0 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -998,10 +998,11 @@ class Interface: return def handle_event_new_acc_connected(self, account, array): - #('NEW_ACC_CONNECTED', account, (infos, is_form)) + #('NEW_ACC_CONNECTED', account, (infos, is_form, ssl_msg, ssl_cert, + # ssl_fingerprint)) if self.instances.has_key('account_creation_wizard'): self.instances['account_creation_wizard'].new_acc_connected(array[0], - array[1]) + array[1], array[2], array[3], array[4]) def handle_event_new_acc_not_connected(self, account, array): #('NEW_ACC_NOT_CONNECTED', account, (reason)) @@ -2160,6 +2161,45 @@ class Interface: instance = data[1] instance.unique_room_id_error(data[0]) + def handle_event_ssl_error(self, account, data): + # ('SSL_ERROR', account, (text, cert, sha1_fingerprint)) + server = gajim.config.get_per('accounts', account, 'hostname') + def on_ok(is_checked): + if is_checked: + f = open(gajim.MY_CACERTS, 'a') + f.write(server + '\n') + f.write(data[1] + '\n\n') + f.close() + gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1', + data[2]) + gajim.connections[account].ssl_certificate_accepted() + def on_cancel(): + gajim.connections[account].disconnect(on_purpose=True) + self.handle_event_status(account, 'offline') + pritext = _('Error verifying SSL certificate') + sectext = _('There was an error verifying the SSL certificate of your jabber server: %(error)s\nDo you still want to connect to this server?') % {'error': data[0]} + checktext = _('Add this certificate to the list of trusted certificates.\nSHA1 fingerprint of the certificate:\n%s') % data[2] + dialogs.ConfirmationDialogCheck(pritext, sectext, checktext, + on_response_ok=on_ok, on_response_cancel=on_cancel) + + def handle_event_fingerprint_error(self, account, data): + # ('FINGERPRINT_ERROR', account, (fingerprint,)) + def on_yes(widget): + dialog.destroy() + gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1', + data[0]) + gajim.connections[account].ssl_certificate_accepted() + def on_no(widget): + dialog.destroy() + gajim.connections[account].disconnect(on_purpose=True) + self.handle_event_status(account, 'offline') + pritext = _('SSL certificate error') + sectext = _('It seems SSL certificate has changed or your connection is ' + 'being hacked. Do you still want to connect and update the fingerprint' + 'of the certificate?') + dialog = dialogs.YesNoDialog(pritext, sectext, on_response_yes=on_yes, + on_response_no=on_no) + def read_sleepy(self): '''Check idle status and change that status if needed''' if not self.sleeper.poll(): @@ -2497,6 +2537,8 @@ class Interface: 'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported, 'SESSION_NEG': self.handle_session_negotiation, 'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required, + 'SSL_ERROR': self.handle_event_ssl_error, + 'FINGERPRINT_ERROR': self.handle_event_fingerprint_error, } gajim.handlers = self.handlers