From 46aac301c8cb797590c321f96dffc54c4aa713d6 Mon Sep 17 00:00:00 2001
From: Yann Leboulanger <asterix@lagaule.org>
Date: Tue, 4 Dec 2007 09:38:39 +0000
Subject: [PATCH] SSL certificate verification, certificate fingerprint
 verification. fixes #720, #2499

---
 .../account_creation_wizard_window.glade      | 308 ++++++++++--------
 src/common/config.py                          |   1 +
 src/common/configpaths.py                     |   4 +-
 src/common/connection.py                      | 161 +++++----
 src/common/gajim.py                           |   1 +
 src/common/xmpp/transports_nb.py              |  31 +-
 src/config.py                                 |  55 +++-
 src/gajim.py                                  |  46 ++-
 8 files changed, 395 insertions(+), 212 deletions(-)

diff --git a/data/glade/account_creation_wizard_window.glade b/data/glade/account_creation_wizard_window.glade
index 66b0d6fe67..38997769fa 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.</property>
                 <child>
                   <widget class="GtkFrame" id="frame33">
                     <property name="visible">True</property>
-                    <property name="label_xalign">0</property>
                     <property name="shadow_type">GTK_SHADOW_NONE</property>
                     <child>
                       <widget class="GtkAlignment" id="alignment68">
@@ -99,9 +98,6 @@ to the Jabber network.</property>
                   </packing>
                 </child>
               </widget>
-              <packing>
-                <property name="tab_expand">False</property>
-              </packing>
             </child>
             <child>
               <widget class="GtkLabel" id="label259">
@@ -109,7 +105,6 @@ to the Jabber network.</property>
               </widget>
               <packing>
                 <property name="type">tab</property>
-                <property name="tab_expand">False</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -144,61 +139,64 @@ to the Jabber network.</property>
                       <placeholder/>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="jid_label">
+                      <widget class="GtkEntry" id="username_entry">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
-                        <property name="xalign">0</property>
-                        <property name="use_markup">True</property>
-                        <property name="selectable">True</property>
+                        <property name="has_focus">True</property>
+                        <property name="invisible_char">*</property>
+                        <signal name="changed" handler="on_username_entry_changed"/>
+                        <signal name="key_press_event" handler="on_username_entry_key_press_event"/>
                       </widget>
                       <packing>
                         <property name="left_attach">1</property>
-                        <property name="right_attach">3</property>
-                        <property name="top_attach">3</property>
-                        <property name="bottom_attach">4</property>
-                        <property name="x_options">GTK_FILL</property>
+                        <property name="right_attach">2</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label258">
+                      <widget class="GtkLabel" id="label267">
                         <property name="visible">True</property>
                         <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Your JID:</property>
+                        <property name="label" translatable="yes">_Server:</property>
+                        <property name="use_underline">True</property>
                       </widget>
                       <packing>
-                        <property name="top_attach">3</property>
-                        <property name="bottom_attach">4</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label262">
+                      <widget class="GtkComboBoxEntry" id="server_comboboxentry">
                         <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Username:</property>
-                        <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">username_entry</property>
+                        <signal name="changed" handler="on_server_comboboxentry_changed"/>
+                        <child internal-child="entry">
+                          <widget class="GtkEntry" id="comboboxentry-entry1">
+                          </widget>
+                        </child>
                       </widget>
                       <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
                         <property name="x_options">GTK_FILL</property>
-                        <property name="y_options"></property>
+                        <property name="y_options">GTK_FILL</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkEntry" id="password_entry">
+                      <widget class="GtkLabel" id="label263">
                         <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="visibility">False</property>
-                        <property name="invisible_char">*</property>
-                        <property name="activates_default">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Password:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">password_entry</property>
                       </widget>
                       <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
                         <property name="top_attach">2</property>
                         <property name="bottom_attach">3</property>
+                        <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
@@ -225,64 +223,61 @@ to the Jabber network.</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label263">
+                      <widget class="GtkEntry" id="password_entry">
                         <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Password:</property>
-                        <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">password_entry</property>
+                        <property name="can_focus">True</property>
+                        <property name="visibility">False</property>
+                        <property name="invisible_char">*</property>
+                        <property name="activates_default">True</property>
                       </widget>
                       <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
                         <property name="top_attach">2</property>
                         <property name="bottom_attach">3</property>
-                        <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkComboBoxEntry" id="server_comboboxentry">
+                      <widget class="GtkLabel" id="label262">
                         <property name="visible">True</property>
-                        <signal name="changed" handler="on_server_comboboxentry_changed"/>
-                        <child internal-child="entry">
-                          <widget class="GtkEntry" id="comboboxentry-entry1">
-                          </widget>
-                        </child>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Username:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">username_entry</property>
                       </widget>
                       <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
                         <property name="x_options">GTK_FILL</property>
-                        <property name="y_options">GTK_FILL</property>
+                        <property name="y_options"></property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label267">
+                      <widget class="GtkLabel" id="label258">
                         <property name="visible">True</property>
                         <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Server:</property>
-                        <property name="use_underline">True</property>
+                        <property name="label" translatable="yes">Your JID:</property>
                       </widget>
                       <packing>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkEntry" id="username_entry">
+                      <widget class="GtkLabel" id="jid_label">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
-                        <property name="has_focus">True</property>
-                        <property name="invisible_char">*</property>
-                        <signal name="changed" handler="on_username_entry_changed"/>
-                        <signal name="key_press_event" handler="on_username_entry_key_press_event"/>
+                        <property name="xalign">0</property>
+                        <property name="use_markup">True</property>
+                        <property name="selectable">True</property>
                       </widget>
                       <packing>
                         <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
+                        <property name="right_attach">3</property>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                        <property name="x_options">GTK_FILL</property>
                         <property name="y_options"></property>
                       </packing>
                     </child>
@@ -294,7 +289,6 @@ to the Jabber network.</property>
               </widget>
               <packing>
                 <property name="position">1</property>
-                <property name="tab_expand">False</property>
               </packing>
             </child>
             <child>
@@ -304,7 +298,6 @@ to the Jabber network.</property>
               <packing>
                 <property name="type">tab</property>
                 <property name="position">1</property>
-                <property name="tab_expand">False</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -388,6 +381,62 @@ to the Jabber network.</property>
                         <property name="n_columns">3</property>
                         <property name="column_spacing">5</property>
                         <property name="row_spacing">5</property>
+                        <child>
+                          <widget class="GtkLabel" id="label381">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Prox_y:</property>
+                            <property name="use_underline">True</property>
+                          </widget>
+                          <packing>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkComboBox" id="proxies_combobox">
+                            <property name="visible">True</property>
+                            <property name="items">None</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkButton" id="manage_proxies_button">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label" translatable="yes">Manage...</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <signal name="clicked" handler="on_manage_proxies_button_clicked"/>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="right_attach">3</property>
+                            <property name="x_options"></property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkCheckButton" id="custom_host_port_checkbutton">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label" translatable="yes">Use custom hostname/port</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="on_custom_host_port_checkbutton_toggled"/>
+                          </widget>
+                          <packing>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
                         <child>
                           <widget class="GtkHBox" id="custom_host_hbox">
                             <property name="visible">True</property>
@@ -450,62 +499,6 @@ to the Jabber network.</property>
                             <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>
-                        <child>
-                          <widget class="GtkCheckButton" id="custom_host_port_checkbutton">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label" translatable="yes">Use custom hostname/port</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="on_custom_host_port_checkbutton_toggled"/>
-                          </widget>
-                          <packing>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" id="manage_proxies_button">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="label" translatable="yes">Manage...</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <signal name="clicked" handler="on_manage_proxies_button_clicked"/>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">2</property>
-                            <property name="right_attach">3</property>
-                            <property name="x_options"></property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkComboBox" id="proxies_combobox">
-                            <property name="visible">True</property>
-                            <property name="items">None</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label381">
-                            <property name="visible">True</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">Prox_y:</property>
-                            <property name="use_underline">True</property>
-                          </widget>
-                          <packing>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
                       </widget>
                     </child>
                     <child>
@@ -526,7 +519,6 @@ to the Jabber network.</property>
               </widget>
               <packing>
                 <property name="position">2</property>
-                <property name="tab_expand">False</property>
               </packing>
             </child>
             <child>
@@ -536,7 +528,69 @@ to the Jabber network.</property>
               <packing>
                 <property name="type">tab</property>
                 <property name="position">2</property>
-                <property name="tab_expand">False</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="ssl_vbox">
+                <property name="visible">True</property>
+                <child>
+                  <widget class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <child>
+                      <widget class="GtkImage" id="image1">
+                        <property name="visible">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="stock">gtk-dialog-warning</property>
+                        <property name="icon_size">5</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="ssl_label">
+                        <property name="visible">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                      </widget>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkCheckButton" id="ssl_checkbutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">Add this certificate to the list of trusted certificates.
+SHA1 fingerprint of the certificate:
+</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                  <packing>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label482">
+                <property name="visible">True</property>
+              </widget>
+              <packing>
+                <property name="type">tab</property>
+                <property name="position">3</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -548,8 +602,7 @@ to the Jabber network.</property>
                 </child>
               </widget>
               <packing>
-                <property name="position">3</property>
-                <property name="tab_expand">False</property>
+                <property name="position">4</property>
               </packing>
             </child>
             <child>
@@ -558,8 +611,7 @@ to the Jabber network.</property>
               </widget>
               <packing>
                 <property name="type">tab</property>
-                <property name="position">3</property>
-                <property name="tab_expand">False</property>
+                <property name="position">4</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -592,8 +644,7 @@ Please wait...</property>
                 </child>
               </widget>
               <packing>
-                <property name="position">4</property>
-                <property name="tab_expand">False</property>
+                <property name="position">5</property>
               </packing>
             </child>
             <child>
@@ -602,8 +653,7 @@ Please wait...</property>
               </widget>
               <packing>
                 <property name="type">tab</property>
-                <property name="position">4</property>
-                <property name="tab_expand">False</property>
+                <property name="position">5</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -671,8 +721,7 @@ Please wait...</property>
                 </child>
               </widget>
               <packing>
-                <property name="position">5</property>
-                <property name="tab_expand">False</property>
+                <property name="position">6</property>
               </packing>
             </child>
             <child>
@@ -681,8 +730,7 @@ Please wait...</property>
               </widget>
               <packing>
                 <property name="type">tab</property>
-                <property name="position">5</property>
-                <property name="tab_expand">False</property>
+                <property name="position">6</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
diff --git a/src/common/config.py b/src/common/config.py
index 3c403f460f..0327bb146e 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 f71e6c9b24..516d849ef3 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 99c78654ee..3b7bf061bd 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 3ca639a53b..dcb294034b 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 35f908ea24..c4cda46366 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 0b673337c8..a47844400a 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('<b>Account is being created</b>\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(_('<b>Security Warning</b>'
+				'\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 = '<big><b>%s</b></big>\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 = '<big><b>%s</b></big>\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 fa8c4e88c6..de50ce6f02 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
 
-- 
GitLab