[pan2/testing: 226/279] * added CertStore for ssl certificates * added callbacks for certificate verification * added remove



commit eb2516e8c5b565d8c10fbb48a76d9cfe083b4d5b
Author: Heinrich MÃller <sphemuel stud informatik uni-erlangen de>
Date:   Mon Nov 7 09:18:02 2011 +0100

    * added CertStore for ssl certificates
    * added callbacks for certificate verification
    * added remove/add/inspect cert functionality in GUI
    * added 'urgent' warnings for suspicious behaviour of the server when using SSL connections
    * added GUI info dialog for adding new certs to Store

 pan.cbp                          |    3 +
 pan/data-impl/Makefile.am        |   43 +++---
 pan/data-impl/add-server.cc      |   12 +-
 pan/data-impl/data-impl.h        |   10 +
 pan/general/file-util.cc         |    2 +-
 pan/gui/actions.cc               |    6 +
 pan/gui/gui.cc                   |  134 ++++++++++++----
 pan/gui/gui.h                    |   13 ++-
 pan/gui/pan-ui.h                 |    1 +
 pan/gui/pan.cc                   |   18 ++-
 pan/gui/pan.ui.h                 |    1 +
 pan/gui/post-ui.cc               |   81 +++++-----
 pan/gui/server-ui.cc             |  336 +++++++++++++++++++++++++++++++++++++-
 pan/gui/server-ui.h              |   18 ++
 pan/gui/task-pane.cc             |    3 +-
 pan/icons/Makefile.am            |    4 +-
 pan/icons/icon_cert.png          |  Bin 0 -> 689 bytes
 pan/icons/icon_plain.png         |  Bin 0 -> 687 bytes
 pan/icons/icon_tabbar.png        |  Bin 0 -> 594 bytes
 pan/tasks/Makefile.am            |    6 +-
 pan/tasks/cert-store.cc          |  266 ++++++++++++++++++++++++++++++
 pan/tasks/cert-store.h           |  121 ++++++++++++++
 pan/tasks/nntp-pool.cc           |   30 +++-
 pan/tasks/nntp-pool.h            |   18 ++-
 pan/tasks/queue.cc               |    6 +-
 pan/tasks/queue.h                |   10 +-
 pan/tasks/socket-impl-gio.h      |    3 +-
 pan/tasks/socket-impl-main.cc    |   35 +++-
 pan/tasks/socket-impl-main.h     |   23 ++-
 pan/tasks/socket-impl-openssl.cc |  146 +++++++++--------
 pan/tasks/socket-impl-openssl.h  |   29 ++--
 pan/tasks/socket.h               |   10 +
 pan/usenet-utils/Makefile.am     |    3 +-
 33 files changed, 1164 insertions(+), 227 deletions(-)
---
diff --git a/pan.cbp b/pan.cbp
index 3d72e28..300777c 100644
--- a/pan.cbp
+++ b/pan.cbp
@@ -203,6 +203,8 @@
 		<Unit filename="pan/tasks/adaptable-set-test.cc" />
 		<Unit filename="pan/tasks/adaptable-set.cc" />
 		<Unit filename="pan/tasks/adaptable-set.h" />
+		<Unit filename="pan/tasks/cert-store.cc" />
+		<Unit filename="pan/tasks/cert-store.h" />
 		<Unit filename="pan/tasks/decoder.cc" />
 		<Unit filename="pan/tasks/decoder.h" />
 		<Unit filename="pan/tasks/defgroup.h" />
@@ -264,6 +266,7 @@
 		<Unit filename="pan/usenet-utils/scorefile-test.cc" />
 		<Unit filename="pan/usenet-utils/scorefile.cc" />
 		<Unit filename="pan/usenet-utils/scorefile.h" />
+		<Unit filename="pan/usenet-utils/ssl-utils.h" />
 		<Unit filename="pan/usenet-utils/text-massager-test.cc" />
 		<Unit filename="pan/usenet-utils/text-massager.cc" />
 		<Unit filename="pan/usenet-utils/text-massager.h" />
diff --git a/pan/data-impl/Makefile.am b/pan/data-impl/Makefile.am
index 19142bf..c20532d 100644
--- a/pan/data-impl/Makefile.am
+++ b/pan/data-impl/Makefile.am
@@ -1,4 +1,4 @@
-AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@
+AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@
 
 noinst_LIBRARIES = libpandata.a
 
@@ -24,26 +24,25 @@ noinst_HEADERS = \
  profiles.h \
  memchunk.h
 
-#noinst_PROGRAMS = \
-# add-server \
-# headers-test \
-# speed-test-load-group
+noinst_PROGRAMS = \
+ add-server \
+ headers-test \
+ speed-test-load-group
 
-#TESTS = \
-# add-server \
-# headers-test
+TESTS = \
+ add-server \
+ headers-test
 
-#TEST_LDADD = \
-# ./libpandata.a \
-# ../tasks/libtasks.a \
-# ../data/libdata.a \
-# ../usenet-utils/libusenetutils.a \
-# ../general/libgeneralutils.a \
-# ../../uulib/libuu.a \
-# @GMIME_LIBS@ @GLIB_LIBS@
-#add_server_SOURCES = add-server.cc
-#add_server_LDADD = $(TEST_LDADD)
-#headers_test_SOURCES = headers-test.cc
-#headers_test_LDADD = $(TEST_LDADD)
-#speed_test_load_group_SOURCES = speed-test-load-group.cc
-#speed_test_load_group_LDADD = $(TEST_LDADD)
+TEST_LDADD = \
+ ./libpandata.a \
+ ../tasks/libtasks.a \
+ ../data/libdata.a \
+ ../usenet-utils/libusenetutils.a \
+ ../general/libgeneralutils.a \
+ ../../uulib/libuu.a @GMIME_LIBS@ @GLIB_LIBS@ @OPENSSL_LIBS@
+add_server_SOURCES = add-server.cc
+add_server_LDADD = $(TEST_LDADD)
+headers_test_SOURCES = headers-test.cc
+headers_test_LDADD = $(TEST_LDADD)
+speed_test_load_group_SOURCES = speed-test-load-group.cc
+speed_test_load_group_LDADD = $(TEST_LDADD)
diff --git a/pan/data-impl/add-server.cc b/pan/data-impl/add-server.cc
index 3ae41b0..d5e82ac 100644
--- a/pan/data-impl/add-server.cc
+++ b/pan/data-impl/add-server.cc
@@ -5,9 +5,12 @@
 #include <pan/tasks/queue.h>
 #include <pan/tasks/socket-impl-gio.h>
 #include <pan/tasks/task-groups.h>
-#include <pan/tasks/socket-impl-main.h>
+#include <pan/tasks/socket.h>
 #include "data-impl.h"
 
+
+//FIXME adapt to SocketCreator & SSL-Support!
+
 using namespace pan;
 
 namespace
@@ -58,10 +61,11 @@ int main (int argc, char *argv[])
   // initialize the queue
   TaskArchive null_task_archive;
   WorkerPool pool;
+  CertStore cs(0);
 
-    // FIXME : DBG!
-//  SocketCreator _socket_creator;
-//  Queue queue (data, null_task_archive, &_socket_creator, pool, true, 10);
+  // FIXME : DBG!
+//  SocketCreator _socket_creator(cs);
+//  Queue queue (data, null_task_archive, &_socket_creator, cs, pool, true, 10);
 //  queue.add_task (new TaskGroups (data, servername));
 //
 //  // start the event loop...
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index fb27ba0..09549a2 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -44,6 +44,16 @@
 #include <pan/data-impl/profiles.h>
 #include <pan/data-impl/memchunk.h>
 
+#ifdef HAVE_OPENSSL
+  #include <pan/tasks/cert-store.h>
+  #include <openssl/crypto.h>
+  #include <openssl/x509.h>
+  #include <openssl/x509v3.h>
+  #include <openssl/pem.h>
+  #include <openssl/ssl.h>
+  #include <openssl/err.h>
+#endif
+
 namespace pan
 {
   typedef std::vector<const Article*> articles_t;
diff --git a/pan/general/file-util.cc b/pan/general/file-util.cc
index 2e08bc2..6b3ea29 100644
--- a/pan/general/file-util.cc
+++ b/pan/general/file-util.cc
@@ -66,7 +66,7 @@ file :: get_pan_home ()
     }
   }
 
-  file :: ensure_dir_exists (pan_home);
+  ensure_dir_exists (pan_home);
   return pan_home;
 }
 
diff --git a/pan/gui/actions.cc b/pan/gui/actions.cc
index 187eb7f..72db18f 100644
--- a/pan/gui/actions.cc
+++ b/pan/gui/actions.cc
@@ -141,6 +141,7 @@ namespace
   void do_read_previous_thread         (GtkAction*) { pan_ui->do_read_previous_thread(); }
   void do_read_parent_article          (GtkAction*) { pan_ui->do_read_parent_article(); }
   void do_show_servers_dialog          (GtkAction*) { pan_ui->do_show_servers_dialog(); }
+  void do_show_sec_dialog              (GtkAction*) { pan_ui->do_show_sec_dialog(); }
   void do_plonk                        (GtkAction*) { pan_ui->do_plonk(); }
   void do_ignore                       (GtkAction*) { pan_ui->do_ignore(); }
   void do_watch                        (GtkAction*) { pan_ui->do_watch(); }
@@ -415,6 +416,11 @@ namespace
       NULL,
       G_CALLBACK(do_show_servers_dialog) },
 
+    { "show-sec-dialog", GTK_STOCK_DIALOG_AUTHENTICATION,
+      N_("Edit _SSL Certificates"), NULL,
+      NULL,
+      G_CALLBACK(do_show_sec_dialog) },
+
     { "jump-to-group-tab", GTK_STOCK_JUMP_TO,
       N_("Jump to _Group Tab"), "1",
       NULL,
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index f510038..ff0a185 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -75,6 +75,13 @@ namespace pan
   {
     gtk_box_pack_start( box, child, TRUE, TRUE, 0 );
   }
+
+  void remove_from_parent (GtkWidget * w)
+  {
+    GtkWidget *parent = gtk_widget_get_parent(w);
+    if (parent != 0)
+      gtk_container_remove (GTK_CONTAINER(parent), w);
+  }
 }
 
 using namespace pan;
@@ -124,8 +131,6 @@ GUI :: add_widget (GtkUIManager *,
   const char * name (gtk_widget_get_name (widget));
   GUI * self (static_cast<GUI*>(gui_g));
 
-  std::cerr<<"add widget "<<name<<std::endl;
-
   if (name && strstr(name,"main-window-")==name)
   {
     if (!GTK_IS_TOOLBAR (widget))
@@ -178,9 +183,7 @@ namespace
 //  };
 //}
 
-
-
-GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_cache, Prefs& prefs, GroupPrefs& group_prefs):
+GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_cache, CertStore& cs, Prefs& prefs, GroupPrefs& group_prefs):
   _data (data),
   _queue (queue),
   _cache (cache),
@@ -199,7 +202,8 @@ GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_c
   _connection_size_label (0),
   _queue_size_label (0),
   _queue_size_button (0),
-  _taskbar (0)
+  _taskbar (0),
+  _certstore(cs)
 {
 
   char * filename = g_build_filename (file::get_pan_home().c_str(), "pan.ui", NULL);
@@ -348,6 +352,8 @@ GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_c
         on_queue_task_active_changed (queue, *(*it), true);
     }
   }
+
+  _certstore.add_listener(this);
 }
 
 namespace
@@ -358,6 +364,9 @@ namespace
 
 GUI :: ~GUI ()
 {
+
+  _certstore.remove_listener(this);
+
   const std::string accel_filename (get_accel_filename());
   gtk_accel_map_save (accel_filename.c_str());
   chmod (accel_filename.c_str(), 0600);
@@ -997,6 +1006,12 @@ void GUI ::  server_list_dialog_destroyed_cb (GtkWidget * w, gpointer self)
   static_cast<GUI*>(self)->server_list_dialog_destroyed (w);
 }
 
+void GUI ::  sec_dialog_destroyed_cb (GtkWidget * w, gpointer self)
+{
+  static_cast<GUI*>(self)->sec_dialog_destroyed (w);
+}
+
+
 // this queues up a grouplist task for any servers that
 // were added while the server list dialog was up.
 void GUI :: server_list_dialog_destroyed (GtkWidget *)
@@ -1010,6 +1025,18 @@ void GUI :: server_list_dialog_destroyed (GtkWidget *)
   }
 }
 
+
+void GUI :: sec_dialog_destroyed (GtkWidget * w)
+{
+//  quarks_t empty_servers, all_servers (_data.get_servers());
+//  foreach_const (quarks_t, all_servers, it) {
+//    quarks_t tmp;
+//    _data.server_get_groups (*it, tmp);
+//    if (tmp.empty() && _data.get_server_limits(*it))
+//      _queue.add_task (new TaskGroups (_data, *it));
+//  }
+}
+
 void GUI ::  prefs_dialog_destroyed_cb (GtkWidget * w, gpointer self)
 {
   static_cast<GUI*>(self)->prefs_dialog_destroyed (w);
@@ -1036,6 +1063,15 @@ void GUI :: do_show_servers_dialog ()
   g_signal_connect (w, "destroy", G_CALLBACK(server_list_dialog_destroyed_cb), this);
 }
 
+
+void GUI :: do_show_sec_dialog ()
+{
+  GtkWidget * w = sec_dialog_new (_data, _queue, get_window(_root));
+  g_signal_connect (w, "destroy", G_CALLBACK(sec_dialog_destroyed_cb), this);
+  gtk_widget_show_all (w);
+}
+
+
 void GUI :: do_show_score_dialog ()
 {
   const Quark& group (_header_pane->get_group());
@@ -1247,25 +1283,48 @@ void GUI :: do_cancel_article ()
 }
 
 bool GUI::deletion_confirmation_dialog()
-  {
-    bool ret(false);
-    GtkWidget * d = gtk_message_dialog_new (
-      get_window(_root),
-      GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
-      GTK_MESSAGE_WARNING,
-      GTK_BUTTONS_NONE, NULL);
-    HIG :: message_dialog_set_text (GTK_MESSAGE_DIALOG(d),
-      _("You marked some articles for deletion"),
-      _("Are you sure you want to delete them?"));
-    gtk_dialog_add_buttons (GTK_DIALOG(d),
-                            GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
-                            GTK_STOCK_APPLY, GTK_RESPONSE_YES,
-                            NULL);
-    gtk_dialog_set_default_response (GTK_DIALOG(d), GTK_RESPONSE_NO);
-    ret = gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_YES;
-    gtk_widget_destroy(d);
-    return ret;
-  }
+{
+  bool ret(false);
+  GtkWidget * d = gtk_message_dialog_new (
+    get_window(_root),
+    GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
+    GTK_MESSAGE_WARNING,
+    GTK_BUTTONS_NONE, NULL);
+  HIG :: message_dialog_set_text (GTK_MESSAGE_DIALOG(d),
+    _("You marked some articles for deletion"),
+    _("Are you sure you want to delete them?"));
+  gtk_dialog_add_buttons (GTK_DIALOG(d),
+                          GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
+                          GTK_STOCK_APPLY, GTK_RESPONSE_YES,
+                          NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG(d), GTK_RESPONSE_NO);
+  ret = gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_YES;
+  gtk_widget_destroy(d);
+  return ret;
+}
+
+
+bool GUI::confirm_accept_new_cert_dialog(X509* cert, const Quark& server)
+{
+  bool ret(false);
+  GtkWidget * d = gtk_message_dialog_new (
+    get_window(_root),
+    GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
+    GTK_MESSAGE_WARNING,
+    GTK_BUTTONS_NONE, NULL);
+  char buf[4096];
+  CertStore::pretty_print_x509(buf,sizeof(buf), server, cert);
+  HIG :: message_dialog_set_text (GTK_MESSAGE_DIALOG(d), buf,
+    _("Do you want to accept it permanently (deletable afterwards) ?"));
+  gtk_dialog_add_buttons (GTK_DIALOG(d),
+                          GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
+                          GTK_STOCK_APPLY, GTK_RESPONSE_YES,
+                          NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG(d), GTK_RESPONSE_NO);
+  ret = gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_YES;
+  gtk_widget_destroy(d);
+  return ret;
+}
 
 void GUI :: do_delete_article ()
 {
@@ -1423,13 +1482,6 @@ void GUI :: do_work_online (bool b)
 
 namespace
 {
-  void remove_from_parent (GtkWidget * w)
-  {
-    GtkWidget *parent = gtk_widget_get_parent(w);
-    if (parent != 0)
-      gtk_container_remove (GTK_CONTAINER(parent), w);
-  }
-
   enum { HORIZONTAL, VERTICAL };
 
   void hpane_destroy_cb (GtkWidget *, gpointer)
@@ -2032,3 +2084,21 @@ GUI :: on_prefs_string_changed (const StringView& key, const StringView& value)
   if (key == "default-save-attachments-path")
     prev_path.assign (value.str, value.len);
 }
+
+
+void
+GUI :: on_verify_cert_failed(X509* cert, std::string server, int nr)
+{
+  std::cerr<<"gui cert failed : "<<cert<<"\n";
+
+  if (confirm_accept_new_cert_dialog(cert,server))
+    if (!_certstore.add(cert, server))
+      std::cerr<<"error adding cert to "<<server<<std::endl;
+
+}
+
+void
+GUI :: on_valid_cert_added (X509* cert, std::string server)
+{
+
+}
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index 06f668c..0a81773 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -51,11 +51,12 @@ namespace pan
     private Log::Listener,
     private Progress::Listener,
     private Queue::Listener,
-    private Prefs::Listener
+    private Prefs::Listener,
+    private CertStore::Listener
   {
 
     public:
-      GUI (Data& data, Queue&, ArticleCache&, EncodeCache&, Prefs&, GroupPrefs&);
+      GUI (Data& data, Queue&, ArticleCache&, EncodeCache&, CertStore&, Prefs&, GroupPrefs&);
       virtual ~GUI ();
       GtkWidget* root () { return _root; }
       typedef std::vector<std::string> strings_t;
@@ -115,6 +116,7 @@ namespace pan
       virtual void do_read_previous_thread ();
       virtual void do_read_parent_article ();
       virtual void do_show_servers_dialog ();
+      virtual void do_show_sec_dialog ();
       virtual void do_show_selected_article_info ();
       virtual void do_plonk ();
       virtual void do_watch ();
@@ -125,6 +127,7 @@ namespace pan
       virtual void do_supersede_article ();
       virtual void do_delete_article ();
       virtual bool deletion_confirmation_dialog();
+      virtual bool confirm_accept_new_cert_dialog(X509*, const Quark&);
       virtual void do_clear_article_cache ();
       virtual void do_mark_article_read ();
       virtual void do_mark_article_unread ();
@@ -176,6 +179,9 @@ namespace pan
       virtual void on_queue_online_changed (Queue&, bool online);
       virtual void on_queue_error (Queue&, const StringView& message);
 
+    private:  // CertStore::Listener
+      virtual void on_verify_cert_failed(X509*, std::string, int);
+      virtual void on_valid_cert_added (X509*, std::string);
 
     private: // Log::Listener
       virtual void on_log_entry_added (const Log::Entry& e);
@@ -198,6 +204,7 @@ namespace pan
       EncodeCache& _encode_cache;
       Prefs& _prefs;
       GroupPrefs& _group_prefs;
+      CertStore& _certstore;
 
     private:
       GtkWidget * _root;
@@ -241,6 +248,8 @@ namespace pan
       static void add_widget (GtkUIManager*, GtkWidget*, gpointer);
       static void server_list_dialog_destroyed_cb (GtkWidget*, gpointer);
       void server_list_dialog_destroyed (GtkWidget*);
+      static void sec_dialog_destroyed_cb (GtkWidget*, gpointer);
+      void sec_dialog_destroyed (GtkWidget*);
       static void prefs_dialog_destroyed_cb (GtkWidget * w, gpointer self);
       void prefs_dialog_destroyed (GtkWidget* w);
       int score_int_from_string(std::string val, const char* rules[]);
diff --git a/pan/gui/pan-ui.h b/pan/gui/pan-ui.h
index 4f35574..2cac8b5 100644
--- a/pan/gui/pan-ui.h
+++ b/pan/gui/pan-ui.h
@@ -89,6 +89,7 @@ namespace pan
     virtual void do_show_group_preferences_dialog () = 0;
     virtual void do_show_profiles_dialog () = 0;
     virtual void do_show_servers_dialog () = 0;
+    virtual void do_show_sec_dialog () = 0;
     virtual void do_show_score_dialog () = 0;
     virtual void do_show_new_score_dialog () = 0;
     virtual void do_show_selected_article_info () = 0;
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index 62a2ca2..a17a999 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -36,6 +36,12 @@ extern "C" {
 #include <pan/general/log.h>
 #include <pan/general/file-util.h>
 #include <pan/general/worker-pool.h>
+
+#ifdef HAVE_OPENSSL
+  #include <pan/tasks/socket-impl-openssl.h>
+  #include <pan/tasks/cert-store.h>
+#endif
+
 #include <pan/tasks/socket-impl-gio.h>
 #include <pan/tasks/socket-impl-main.h>
 #include <pan/tasks/task-groups.h>
@@ -130,6 +136,7 @@ namespace
 
   void run_pan_in_window (ArticleCache  & cache,
                           EncodeCache   & encode_cache,
+                          CertStore     & certstore,
                           Data          & data,
                           Queue         & queue,
                           Prefs         & prefs,
@@ -139,7 +146,7 @@ namespace
     {
       const gulong delete_cb_id =  g_signal_connect (window, "delete-event", G_CALLBACK(delete_event_cb), 0);
 
-      GUI gui (data, queue, cache, encode_cache, prefs, group_prefs);
+      GUI gui (data, queue, cache, encode_cache, certstore, prefs, group_prefs);
       gtk_container_add (GTK_CONTAINER(window), gui.root());
       gtk_widget_show (GTK_WIDGET(window));
 
@@ -326,6 +333,9 @@ main (int argc, char *argv[])
     ArticleCache& cache (data.get_cache ());
     EncodeCache& encode_cache (data.get_encode_cache());
 
+    /* init certificate store for SSL */
+    CertStore certstore(0);
+
     if (nzb && data.get_servers().empty()) {
       std::cerr << _("Please configure Pan's news servers before using it as an nzb client.") << std::endl;
        return EXIT_FAILURE;
@@ -335,9 +345,9 @@ main (int argc, char *argv[])
     // instantiate the queue...
     WorkerPool worker_pool (4, true);
 
-    SocketCreator socket_creator;
+    SocketCreator socket_creator(certstore);
 
-    Queue queue (data, data, &socket_creator, worker_pool,
+    Queue queue (data, data, &socket_creator, certstore, worker_pool,
                  prefs.get_flag ("work-online", true),
                  prefs.get_int ("task-save-delay-secs", 10));
     g_timeout_add (5000, queue_upkeep_timer_cb, &queue);
@@ -399,7 +409,7 @@ main (int argc, char *argv[])
       gtk_window_set_resizable (GTK_WINDOW(window), true);
       gtk_window_set_default_icon (pixbuf);
       g_object_unref (pixbuf);
-      run_pan_in_window (cache, encode_cache, data, queue, prefs, group_prefs, GTK_WINDOW(window));
+      run_pan_in_window (cache, encode_cache, certstore, data, queue, prefs, group_prefs, GTK_WINDOW(window));
     }
 
     worker_pool.cancel_all_silently ();
diff --git a/pan/gui/pan.ui.h b/pan/gui/pan.ui.h
index 06891fa..c09a505 100644
--- a/pan/gui/pan.ui.h
+++ b/pan/gui/pan.ui.h
@@ -20,6 +20,7 @@ const char * fallback_ui_file =
 "      <menuitem action='select-article-body' />\n"
 "      <separator />\n"
 "      <menuitem action='show-servers-dialog' />\n"
+"      <menuitem action='show-sec-dialog' />\n"
 "      <menuitem action='show-profiles-dialog' />\n"
 "      <menuitem action='show-preferences-dialog' />\n"
 "      <menuitem action='show-group-preferences-dialog' />\n"
diff --git a/pan/gui/post-ui.cc b/pan/gui/post-ui.cc
index c97cd28..77ec248 100644
--- a/pan/gui/post-ui.cc
+++ b/pan/gui/post-ui.cc
@@ -2659,7 +2659,6 @@ PostUI :: create_parts_tab ()
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(w), t);
-//  gtk_container_add (GTK_CONTAINER(w), t);
 
   return w;
 }
@@ -2897,7 +2896,8 @@ PostUI :: select_parts ()
   gtk_window_set_role (GTK_WINDOW(w), "pan-parts-window");
   gtk_window_set_title (GTK_WINDOW(w), _("Select Parts"));
   int x,y;
-  x = _prefs.get_int("post-ui-width", 450); // FIXME BUG: Native Windows wider or taller than 65535 pixels are not supported
+  // FIXME (sometimes....) BUG: Native Windows wider or taller than 65535 pixels are not supported
+  x = _prefs.get_int("post-ui-width", 450);
   y = _prefs.get_int("post-ui-height", 450);
   gtk_window_set_default_size (GTK_WINDOW(w), x, y);
   // populate the window
@@ -3116,51 +3116,50 @@ PostUI :: prompt_user_for_queueable_files (GtkWindow * parent, const Prefs& pref
 
 	const int response (gtk_dialog_run (GTK_DIALOG(w)));
 	if (response == GTK_RESPONSE_ACCEPT) {
+    GSList * tmp_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER (w));
+    gtk_widget_destroy (w);
 
-        GSList * tmp_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER (w));
-        gtk_widget_destroy (w);
-
-        TaskUpload::UploadInfo ui;
-        // query lines per file value
-        ui.bpf = _prefs.get_int("upload-option-bpf",512*1024);
-
-        std::string author;
-        profile.get_from_header(author);
-        std::string subject(utf8ize (g_mime_message_get_subject (tmp)));
-
-        // insert groups info from msg
-        quarks_t groups;
-        StringView line (g_mime_object_get_header ((GMimeObject *) tmp, "Newsgroups"));
-        StringView groupname;
-        while (line.pop_token (groupname, ',')) {
-          groupname.trim ();
-          if (!groupname.empty())
-              groups.insert(Quark(groupname));
-        }
+    TaskUpload::UploadInfo ui;
+    // query lines per file value
+    ui.bpf = _prefs.get_int("upload-option-bpf",512*1024);
 
-        GSList * cur = g_slist_nth (tmp_list,0);
-        for (; cur; cur = cur->next)
-        {
-          GMimeMessage * msg (new_message_from_ui (UPLOADING));
+    std::string author;
+    profile.get_from_header(author);
+    std::string subject(utf8ize (g_mime_message_get_subject (tmp)));
+
+    // insert groups info from msg
+    quarks_t groups;
+    StringView line (g_mime_object_get_header ((GMimeObject *) tmp, "Newsgroups"));
+    StringView groupname;
+    while (line.pop_token (groupname, ',')) {
+      groupname.trim ();
+      if (!groupname.empty())
+          groups.insert(Quark(groupname));
+    }
+
+    GSList * cur = g_slist_nth (tmp_list,0);
+    for (; cur; cur = cur->next)
+    {
+      GMimeMessage * msg (new_message_from_ui (UPLOADING));
 
-          //for nzb handling
-          Article a;
-          a.subject = subject;
-          a.author = author;
-          foreach_const (quarks_t, groups, git)
-             a.xref.insert (profile.posting_server, *git,0);
+      //for nzb handling
+      Article a;
+      a.subject = subject;
+      a.author = author;
+      foreach_const (quarks_t, groups, git)
+         a.xref.insert (profile.posting_server, *git,0);
 
-          struct stat sb;
-          ui.total = get_total_parts((const char*)cur->data);
-          TaskUpload* tmp = new TaskUpload(std::string((const char*)cur->data),
-                            profile.posting_server, _cache, a, ui, msg);
+      struct stat sb;
+      ui.total = get_total_parts((const char*)cur->data);
+      TaskUpload* tmp = new TaskUpload(std::string((const char*)cur->data),
+                        profile.posting_server, _cache, a, ui, msg);
 
-          // insert wanted parts to upload
-          for (int i=1;i<=ui.total; ++i)
-            tmp->_wanted.insert(i);
+      // insert wanted parts to upload
+      for (int i=1;i<=ui.total; ++i)
+        tmp->_wanted.insert(i);
 
-          _upload_queue.add_task(tmp);
-        }
+      _upload_queue.add_task(tmp);
+    }
 
     if (_file_queue_empty) _file_queue_empty= false;
     g_slist_free (tmp_list);
diff --git a/pan/gui/server-ui.cc b/pan/gui/server-ui.cc
index 8507a9c..8c269d4 100644
--- a/pan/gui/server-ui.cc
+++ b/pan/gui/server-ui.cc
@@ -26,6 +26,9 @@ extern "C" {
   #include <glib/gi18n.h>
   #include <gtk/gtk.h>
 }
+
+#include <pan/usenet-utils/ssl-utils.h>
+#include <pan/icons/pan-pixbufs-internal.h>
 #include <pan/general/macros.h>
 #include <pan/general/quark.h>
 #include <pan/data/data.h>
@@ -34,6 +37,16 @@ extern "C" {
 #include "hig.h"
 #include "gtk_compat.h"
 
+#ifdef HAVE_OPENSSL
+  #include <pan/tasks/cert-store.h>
+  #include <openssl/crypto.h>
+  #include <openssl/x509.h>
+  #include <openssl/x509v3.h>
+  #include <openssl/pem.h>
+  #include <openssl/ssl.h>
+  #include <openssl/err.h>
+#endif
+
 using namespace pan;
 
 /************
@@ -220,6 +233,32 @@ namespace
   }
 }
 
+std::string
+pan :: import_sec_from_disk_dialog_new (Data& data, Queue& queue, GtkWindow * window)
+{
+  std::string prev_path = g_get_home_dir ();
+  std::string res;
+
+  GtkWidget * w = gtk_file_chooser_dialog_new (_("Import SSL certificate (PEM format) from File"),
+				      window,
+				      GTK_FILE_CHOOSER_ACTION_OPEN,
+				      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+				      GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+				      NULL);
+	gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w), prev_path.c_str());
+	gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (w), false);
+	gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (w), false);
+
+	const int response (gtk_dialog_run (GTK_DIALOG(w)));
+	if (response == GTK_RESPONSE_ACCEPT) {
+    res = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
+    gtk_widget_destroy (w);
+  } else
+    gtk_widget_destroy (w);
+
+  return res;
+}
+
 GtkWidget*
 pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, const Quark& server)
 {
@@ -367,6 +406,24 @@ pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, con
   return d->dialog;
 }
 
+namespace
+{
+  enum
+  {
+    ICON_PLAIN,
+    ICON_CERT,
+    ICON_QTY
+   };
+
+  struct Icon {
+    const guint8 * pixbuf_txt;
+    GdkPixbuf * pixbuf;
+  } _icons[ICON_QTY] = {
+    { icon_plain,  0 },
+    { icon_cert,   0 }
+  };
+}
+
 
 /************
 *************  LIST DIALOG
@@ -376,6 +433,7 @@ namespace
 {
   enum
   {
+    COL_FLAG,
     COL_HOST,
     COL_DATA,
     N_COLUMNS
@@ -393,6 +451,7 @@ namespace
     ServerListDialog (Data& d, Queue& q): data(d), queue(q) {}
   };
 
+
   Quark
   get_selected_server (ServerListDialog * d)
   {
@@ -416,6 +475,29 @@ namespace
     return server;
   }
 
+  Quark
+  get_selected_server_name (ServerListDialog * d)
+  {
+    g_assert (d != 0);
+
+    Quark server;
+
+    GtkTreeSelection * selection (gtk_tree_view_get_selection(GTK_TREE_VIEW (d->server_tree_view)));
+    GtkTreeModel * model;
+    GtkTreeIter iter;
+    if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+      char * host (0);
+      gtk_tree_model_get (model, &iter, COL_HOST, &host, -1);
+      if (host) {
+        server = host;
+        g_free (host);
+      }
+    }
+
+    //std::cerr << LINE_ID << " selected server is " << server << std::endl;
+    return server;
+  }
+
   void
   button_refresh (ServerListDialog * d)
   {
@@ -458,12 +540,20 @@ namespace
     delete (ServerListDialog*)p;
   }
 
+  void delete_sec_dialog (gpointer p)
+  {
+   for (guint i=0; i<ICON_QTY; ++i)
+      g_object_unref (_icons[i].pixbuf);
+   delete (ServerListDialog*)p;
+  }
+
   void
   server_list_dialog_response_cb (GtkDialog * dialog, int, gpointer)
   {
     gtk_widget_destroy (GTK_WIDGET(dialog));
   }
 
+
   void
   remove_button_clicked_cb (GtkButton * button, gpointer data)
   {
@@ -543,6 +633,147 @@ namespace
   }
 }
 
+/* security dialog */
+namespace
+{
+
+  /* Show the current certificate of the selected server, if any */
+  void
+  cert_edit_button_clicked_cb (GtkButton *, gpointer user_data)
+  {
+    GtkWidget * list_dialog = GTK_WIDGET (user_data);
+    ServerListDialog * d = (ServerListDialog*) g_object_get_data (G_OBJECT(list_dialog), "dialog");
+    Quark selected_server (get_selected_server (d));
+
+    int port;
+    std::string addr;
+    d->data.get_server_addr (selected_server, addr, port);
+
+    char buf[4096] ;
+
+    if (!selected_server.empty()) {
+      X509* cert = (X509*)d->queue.store().get_cert_to_server(addr);
+      if (cert)
+      {
+        CertStore::pretty_print_x509(buf,sizeof(buf),addr, cert);
+        if (!buf) g_snprintf(buf,sizeof(buf), "%s", _("No information available.")) ;
+        GtkWidget * w = gtk_message_dialog_new_with_markup (
+        0,
+        GTK_DIALOG_MODAL,
+        GTK_MESSAGE_INFO,
+        GTK_BUTTONS_CLOSE, buf);
+        gtk_widget_show_all (w);
+        g_signal_connect_swapped (w, "response", G_CALLBACK (gtk_widget_destroy), w);
+      }
+    }
+  }
+
+  void
+  sec_tree_view_refresh (ServerListDialog * d)
+  {
+    GtkTreeSelection * selection (gtk_tree_view_get_selection(GTK_TREE_VIEW (d->server_tree_view)));
+    const quarks_t servers (d->data.get_servers ());
+    const Quark selected_server (get_selected_server (d));
+
+    bool found_selected (false);
+    GtkTreeIter selected_iter;
+    gtk_list_store_clear (d->servers_store);
+    foreach_const (quarks_t, servers, it)
+    {
+      const Quark& server (*it);
+      std::string addr; int port;
+      d->data.get_server_addr (server, addr, port);
+
+      if(d->data.get_server_ssl_support(server))
+      {
+        GtkTreeIter iter;
+        gtk_list_store_append (d->servers_store, &iter);
+        gtk_list_store_set (d->servers_store, &iter,
+                            COL_FLAG, d->queue.store().exist(addr),
+                            COL_HOST, addr.c_str(),
+                            COL_DATA, server.c_str(),
+                            -1);
+        if ((found_selected = (server == selected_server)))
+          selected_iter = iter;
+      }
+    }
+
+    if (found_selected)
+      gtk_tree_selection_select_iter (selection, &selected_iter);
+  }
+
+  void
+  sec_dialog_destroy_cb (GtkWidget *, gpointer user_data)
+  {
+    if (GTK_IS_WIDGET (user_data))
+    {
+      ServerListDialog * d = (ServerListDialog*) g_object_get_data (G_OBJECT(user_data), "dialog");
+      sec_tree_view_refresh (d);
+    }
+  }
+
+
+  /* add a cert from disk, overwriting the current certificate for the selected server */
+  void
+  cert_add_button_clicked_cb (GtkButton *, gpointer user_data)
+  {
+    const Quark empty_quark;
+    GtkWidget * list_dialog = GTK_WIDGET (user_data);
+    ServerListDialog * d = (ServerListDialog*) g_object_get_data (G_OBJECT(list_dialog), "dialog");
+    std::string ret = import_sec_from_disk_dialog_new (d->data, d->queue, GTK_WINDOW(list_dialog));
+    const Quark selected_server (get_selected_server (d));
+
+    if (!ret.empty() )
+    {
+      std::string addr; int port;
+      FILE *fp = fopen(ret.c_str(),"r");
+      X509 *x = X509_new();
+      PEM_read_X509(fp,&x, 0, 0);
+      fclose(fp);
+      d->data.get_server_addr(selected_server, addr, port);
+      d->queue.store().add(x,addr);
+      sec_tree_view_refresh (d);
+    }
+  }
+
+
+  /* remove cert from certstore */
+  void
+  cert_remove_button_clicked_cb (GtkButton * button, gpointer data)
+  {
+    ServerListDialog * d (static_cast<ServerListDialog*>(data));
+    Quark selected_server (get_selected_server (d));
+    if (!selected_server.empty())
+    {
+      int port;
+      std::string addr;
+      d->data.get_server_addr (selected_server, addr, port);
+
+      GtkWidget * w = gtk_message_dialog_new (GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
+                                              GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
+                                              GTK_MESSAGE_QUESTION,
+                                              GTK_BUTTONS_NONE,
+                                              _("Really delete certificate for \"%s\"?"),
+                                              addr.c_str());
+      gtk_dialog_add_buttons (GTK_DIALOG(w),
+                              GTK_STOCK_NO, GTK_RESPONSE_NO,
+                              GTK_STOCK_DELETE, GTK_RESPONSE_YES,
+                              NULL);
+      gtk_dialog_set_default_response (GTK_DIALOG(w), GTK_RESPONSE_NO);
+      const int response (gtk_dialog_run (GTK_DIALOG (w)));
+      gtk_widget_destroy (w);
+
+      d->data.get_server_addr (selected_server, addr, port);
+      d->queue.store().remove(addr);
+
+      if (response == GTK_RESPONSE_YES)
+        sec_tree_view_refresh (d);
+
+      button_refresh (d);
+    }
+  }
+
+}
 
 GtkWidget*
 pan :: server_list_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
@@ -568,7 +799,7 @@ pan :: server_list_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
 
 
   // create the list
-  d->servers_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+  d->servers_store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
   w = d->server_tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (d->servers_store));
   GtkCellRenderer * renderer = gtk_cell_renderer_text_new ();
   GtkTreeViewColumn * column = gtk_tree_view_column_new_with_attributes (_("Servers"), renderer, "text", COL_HOST, NULL);
@@ -618,3 +849,106 @@ pan :: server_list_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
   button_refresh (d);
   return d->dialog;
 }
+
+
+void
+pan :: render_cert_flag (GtkTreeViewColumn * ,
+                         GtkCellRenderer   * renderer,
+                         GtkTreeModel      * model,
+                         GtkTreeIter       * iter,
+                         gpointer            )
+{
+  bool index (false);
+  gtk_tree_model_get (model, iter, COL_FLAG, &index, -1);
+  g_object_set (renderer, "pixbuf", _icons[index].pixbuf, NULL);
+}
+
+
+GtkWidget*
+pan :: sec_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
+{
+  ServerListDialog * d = new ServerListDialog (data, queue);
+
+  for (guint i=0; i<ICON_QTY; ++i)
+    _icons[i].pixbuf = gdk_pixbuf_new_from_inline (-1, _icons[i].pixbuf_txt, FALSE, 0);
+
+  // dialog
+  char * title = g_strdup_printf ("Pan: %s", _("SSL Certificates"));
+  GtkWidget * w = d->dialog = gtk_dialog_new_with_buttons (title, parent,
+                                                           GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                           GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
+                                                           NULL);
+  g_free (title);
+  gtk_window_set_role (GTK_WINDOW(w), "pan-sec-dialog");
+  gtk_window_set_resizable (GTK_WINDOW(w), TRUE);
+  g_signal_connect (GTK_OBJECT(w), "response", G_CALLBACK(server_list_dialog_response_cb), d);
+  g_object_set_data_full (G_OBJECT(w), "dialog", d, delete_sec_dialog);
+
+  // workarea
+  GtkWidget * hbox = gtk_hbox_new (FALSE, PAD);
+  gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);
+  gtk_box_pack_start (GTK_BOX( gtk_dialog_get_content_area( GTK_DIALOG(w))), hbox, TRUE, TRUE, 0);
+
+  // create the list
+  d->servers_store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
+  w = d->server_tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (d->servers_store));
+
+  GtkCellRenderer * r = GTK_CELL_RENDERER (g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF, "xpad", 2,"ypad", 0,NULL));
+//  GtkTreeViewColumn * col = gtk_tree_view_column_new ();
+//  gtk_tree_view_column_set_resizable (col, false);
+//  gtk_tree_view_column_set_title (col, _("Certificate"));
+//  gtk_tree_view_column_pack_start (col, r, false);
+//  gtk_tree_view_column_set_cell_data_func (col, r, render_cert_flag, 0, 0);
+//  gtk_tree_view_append_column (GTK_TREE_VIEW(w), col);
+  GtkTreeViewColumn * column = gtk_tree_view_column_new_with_attributes (_("Certificates"), r, NULL);
+  gtk_tree_view_column_set_cell_data_func (column, r, render_cert_flag, 0, 0);
+  gtk_tree_view_append_column (GTK_TREE_VIEW(w), column);
+
+  r = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes (_("Servers"), r, "text", COL_HOST, NULL);
+  gtk_tree_view_column_set_sort_column_id (column, COL_HOST);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (w), column);
+  GtkTreeSelection * selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (w));
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+  // add callbacks
+  g_signal_connect (GTK_TREE_VIEW (w), "row-activated",
+                    G_CALLBACK (server_tree_view_row_activated_cb), d->dialog);
+  g_signal_connect (G_OBJECT (selection), "changed",
+                    G_CALLBACK (server_tree_view_selection_changed_cb), d);
+
+  w = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
+  gtk_container_add (GTK_CONTAINER(w), d->server_tree_view);
+  gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
+  gtk_widget_set_size_request (w, 300, 300);
+
+  // button box
+  GtkWidget * bbox = gtk_vbox_new (FALSE, PAD_SMALL);
+  gtk_box_pack_start (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
+
+  // add button
+  w = gtk_button_new_from_stock (GTK_STOCK_ADD);
+  gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
+  gtk_widget_set_tooltip_text(w, _("Import Certificate"));
+  g_signal_connect (w, "clicked", G_CALLBACK(cert_add_button_clicked_cb), d->dialog);
+
+  // inspect button
+  w = gtk_button_new_from_stock (GTK_STOCK_FIND);
+  gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
+  gtk_widget_set_tooltip_text(w, _("Inspect Certificate"));
+  g_signal_connect (w, "clicked", G_CALLBACK(cert_edit_button_clicked_cb), d->dialog);
+  d->edit_button = w;
+
+  // remove button
+  w = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
+  gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
+  gtk_widget_set_tooltip_text(w, _("Remove Certificate"));
+  g_signal_connect (w, "clicked", G_CALLBACK(cert_remove_button_clicked_cb), d);
+  d->remove_button = w;
+
+  sec_tree_view_refresh (d);
+  button_refresh (d);
+  return d->dialog;
+}
diff --git a/pan/gui/server-ui.h b/pan/gui/server-ui.h
index 2201976..971d387 100644
--- a/pan/gui/server-ui.h
+++ b/pan/gui/server-ui.h
@@ -20,17 +20,35 @@
 #ifndef SERVER_UI_H
 #define SERVER_UI_H
 
+#include <set>
 #include <gtk/gtk.h>
 #include <pan/data/data.h>
 #include <pan/tasks/queue.h>
 
 namespace pan
 {
+
+  typedef std::set<std::string> strings_t;
+
   /** @ingroup GUI */
   GtkWidget* server_edit_dialog_new (Data&, Queue&, GtkWindow*, const Quark& server);
 
   /** @ingroup GUI */
   GtkWidget* server_list_dialog_new (Data&, Queue&, GtkWindow*);
+
+  /** @ingroup GUI */
+  GtkWidget* sec_dialog_new (Data& data, Queue& queue, GtkWindow* parent);
+
+  /** @ingroup GUI */
+  std::string
+  import_sec_from_disk_dialog_new (Data& data, Queue& queue, GtkWindow * window);
+
+  /** @ingroup GUI */
+  void render_cert_flag (GtkTreeViewColumn * ,
+                         GtkCellRenderer   * ,
+                         GtkTreeModel      * ,
+                         GtkTreeIter       * ,
+                         gpointer            );
 }
 
 #endif
diff --git a/pan/gui/task-pane.cc b/pan/gui/task-pane.cc
index cb49bb9..b25e57d 100644
--- a/pan/gui/task-pane.cc
+++ b/pan/gui/task-pane.cc
@@ -127,7 +127,6 @@ TaskPane:: on_tooltip_query(GtkWidget  *widget,
     return false;
 
   gtk_tree_model_get (model, &iter, COL_TASK_POINTER, &task, -1);
-//  if (!task) return false;
 
   g_snprintf(buffer,sizeof(buffer),"...");
 
@@ -611,7 +610,7 @@ namespace
   void do_stop           (GtkAction*, gpointer p)  { static_cast<TaskPane*>(p)->stop_clicked_cb(0, static_cast<TaskPane*>(p)); }
   void do_delete         (GtkAction*, gpointer p)  { static_cast<TaskPane*>(p)->delete_clicked_cb(0, static_cast<TaskPane*>(p)); }
   void do_restart        (GtkAction*, gpointer p)  { static_cast<TaskPane*>(p)->restart_clicked_cb(0, static_cast<TaskPane*>(p)); }
-  void do_change_dest    (GtkAction* a, gpointer p){ static_cast<TaskPane*>(p)->change_dest_clicked_cb(0, static_cast<TaskPane*>(p)); }
+  void do_change_dest    (GtkAction*, gpointer p)  { static_cast<TaskPane*>(p)->change_dest_clicked_cb(0, static_cast<TaskPane*>(p)); }
 
   GtkActionEntry taskpane_popup_entries[] =
   {
diff --git a/pan/icons/Makefile.am b/pan/icons/Makefile.am
index 040a872..6490d38 100644
--- a/pan/icons/Makefile.am
+++ b/pan/icons/Makefile.am
@@ -44,7 +44,9 @@ stock_images = \
 	icon_mozilla_smile.png \
 	icon_mozilla_surprised.png \
 	icon_mozilla_tongueout.png \
-	icon_mozilla_wink.png
+	icon_mozilla_wink.png \
+	icon_plain.png \
+	icon_cert.png
     
 
 EXTRA_DIST = \
diff --git a/pan/icons/icon_cert.png b/pan/icons/icon_cert.png
new file mode 100644
index 0000000..ddf83d9
Binary files /dev/null and b/pan/icons/icon_cert.png differ
diff --git a/pan/icons/icon_plain.png b/pan/icons/icon_plain.png
new file mode 100644
index 0000000..b09e418
Binary files /dev/null and b/pan/icons/icon_plain.png differ
diff --git a/pan/icons/icon_tabbar.png b/pan/icons/icon_tabbar.png
new file mode 100644
index 0000000..d013c93
Binary files /dev/null and b/pan/icons/icon_tabbar.png differ
diff --git a/pan/tasks/Makefile.am b/pan/tasks/Makefile.am
index 2a05e51..9264efb 100644
--- a/pan/tasks/Makefile.am
+++ b/pan/tasks/Makefile.am
@@ -1,6 +1,6 @@
-AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@ 
+AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@
 
-AM_LDFLAGS = ../../uulib/libuu.a -lz @OPENSSL_LIBS@ 
+AM_LDFLAGS = ../../uulib/libuu.a -lz @OPENSSL_LIBS@
 
 noinst_LIBRARIES = libtasks.a
 
@@ -18,6 +18,7 @@ libtasks_a_SOURCES = \
   queue.cc \
   upload-queue.cc \
   socket.cc \
+  cert-store.cc \
   socket-impl-main.cc \
   socket-impl-openssl.cc \
   socket-impl-gio.cc \
@@ -43,6 +44,7 @@ noinst_HEADERS = \
   queue.h  \
   upload-queue.h \
   socket.h \
+  cert-store.h \
   socket-impl-main.h \
   socket-impl-openssl.cc \
   socket-impl-gio.h \
diff --git a/pan/tasks/cert-store.cc b/pan/tasks/cert-store.cc
new file mode 100644
index 0000000..4cc0b84
--- /dev/null
+++ b/pan/tasks/cert-store.cc
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <string>
+#include <glib/giochannel.h>
+#include <glib/gstring.h>
+#include <pan/tasks/socket.h>
+#include <config.h>
+#include <map>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <cerrno>
+#include <cstring>
+
+extern "C" {
+  #include <glib/gi18n.h>
+  #include <dirent.h>
+}
+
+#include <pan/general/debug.h>
+#include <pan/general/e-util.h>
+#include <pan/general/macros.h>
+#include <pan/usenet-utils/mime-utils.h>
+
+#include <pan/general/debug.h>
+#include <pan/general/file-util.h>
+#include <pan/general/macros.h>
+#include <pan/general/messages.h>
+#include <pan/general/log.h>
+#include <pan/general/string-view.h>
+#include <pan/usenet-utils/mime-utils.h>
+
+#include "cert-store.h"
+
+using namespace pan;
+
+namespace pan
+{
+
+  int
+  verify_callback(int ok, X509_STORE_CTX *store)
+//  verify_callback(X509_STORE_CTX *store, void* args)
+  {
+
+    SSL * ssl = (SSL*)X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());
+    mydata_t* mydata = (mydata_t*)SSL_get_ex_data(ssl, SSL_get_fd(ssl));
+
+//    CertStore * me = (CertStore*)args;
+
+    if (!ok)
+    {
+      if (mydata->ignore_all==1) return 1;
+
+      X509 *cert = X509_STORE_CTX_get_current_cert(store);
+      int depth = X509_STORE_CTX_get_error_depth(store);
+      int err = X509_STORE_CTX_get_error(store);
+
+      if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)
+        mydata->cs->verify_failed(cert, mydata->server, err);
+    }
+    return ok;
+
+//      SSL_CTX_set_verify(me->get_ctx(), SSL_VERIFY_PEER, store->verify_cb);
+//
+//      int ok =
+//
+//  }
+
+}
+
+void
+CertStore :: get_all_certs_from_disk(std::set<X509*>& setme)
+{
+  char filename[PATH_MAX];
+  const char * fname;
+  GError * err = NULL;
+  GDir * dir = g_dir_open (_path.c_str(), 0, &err);
+
+  int cnt(0);
+  while ((fname = g_dir_read_name (dir)))
+  {
+    if (strlen(fname)<=1) continue;
+
+    g_snprintf (filename, sizeof(filename), "%s%c%s", _path.c_str(), G_DIR_SEPARATOR, fname);
+    FILE *fp = fopen(filename,"r");
+    X509 *x = X509_new();
+    PEM_read_X509(fp,&x, 0, 0);
+    fclose(fp);
+    setme.insert(x);
+
+    std::string fn(fname);
+    std::string::size_type idx = fn.rfind(".pem");
+
+    if(idx != std::string::npos)
+    {
+      std::string server = fn.substr(0,idx);
+      _certs.insert(server);
+      _cert_to_server[server] = x;
+    }
+    ++cnt;
+  }
+  g_dir_close (dir);
+
+  if (cnt != 0) Log::add_info_va(_("Succesfully added %d SSL PEM certificate(s) to Certificate Store."), cnt);
+}
+
+void
+CertStore :: init_me()
+{
+  assert (_ctx);
+
+  _store = SSL_CTX_get_cert_store(_ctx);
+
+  std::set<X509*> certs;
+  get_all_certs_from_disk (certs);
+  foreach_const (std::set<X509*>, certs, it)
+    X509_STORE_add_cert(_store, *it);
+//  SSL_CTX_set_cert_verify_callback(_ctx, verify_callback, (void*)this);
+  SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, verify_callback);
+
+}
+
+void
+CertStore :: remove_hard(const Quark& server)
+{
+  char buf[2048];
+  g_snprintf (buf, sizeof(buf), "%s%c%s.pem", _path.c_str(), G_DIR_SEPARATOR, server.c_str());
+  unlink(buf);
+}
+
+void
+CertStore :: remove (const Quark& server)
+{
+  if (_cert_to_server.count(server) > 0)
+  {
+    _cert_to_server.erase(server);
+    _certs.erase(server);
+    remove_hard(server);
+  }
+}
+
+CertStore :: CertStore (SSL_CTX * ctx) : _ctx(ctx)
+{
+  if (ctx) init_me();
+  char buf[2048];
+  g_snprintf(buf,sizeof(buf),"%s%cssl_certs",file::get_pan_home().c_str(), G_DIR_SEPARATOR);
+  file::ensure_dir_exists (buf);
+  _path = buf;
+}
+
+CertStore :: ~CertStore ()
+{}
+
+
+
+bool
+CertStore :: add(X509* cert, const Quark& server)
+{
+  if (_certs.count(server) > 0 || !cert || server.empty()) return false;
+
+  X509_STORE_add_cert(get_store(),cert);
+  _certs.insert(server);
+  _cert_to_server[server] = cert;
+
+  char buf[2048];
+  g_snprintf(buf,sizeof(buf),"%s%c%s.pem",_path.c_str(),G_DIR_SEPARATOR,server.c_str());
+  FILE * fp = fopen(buf, "wb");
+  PEM_write_X509(fp, cert);
+  fclose(fp);
+
+  valid_cert_added(cert, server.c_str());
+  return true;
+}
+
+const X509*
+CertStore :: get_cert_to_server(const Quark& server) const
+{
+  const X509* ret(0);
+  Quark serv;
+
+  /* strip port from server if existing */
+  std::string s(server);
+  std::string::size_type idx = s.rfind(":");
+  if(idx != std::string::npos)
+    serv = s.substr(0,idx);
+  else
+    serv = server;
+
+  /* dbg dump all */
+//  std::cerr<<"asking for server cert "<<serv<<std::endl;
+//  std::cerr<<"existing certs : \n";
+//  foreach_const(certs_m, _cert_to_server, it)
+//  {
+//    std::cerr<<it->first<<" "<<it->second<<std::endl;
+//  }
+
+  if (_cert_to_server.count(serv) > 0)
+    ret = _cert_to_server.find(serv)->second;
+  return ret;
+}
+
+namespace
+{
+  std::string
+  get_x509_fingerpint_md5(X509* cert)
+  {
+    std::string res;
+    unsigned char md[EVP_MAX_MD_SIZE];
+    unsigned int n;
+
+    if (! X509_digest(cert, EVP_md5(), md, &n))
+			res += _("Not available.");
+		else {
+			char hex[] = "0123456789ABCDEF";
+			char fp[EVP_MAX_MD_SIZE*3];
+			if (n < sizeof(fp)) {
+				unsigned int i;
+				for (i = 0; i < n; i++) {
+					fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
+					fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
+					fp[i*3+2] = i == n - 1 ? '\0' : ':';
+				}
+				res += fp;
+			}
+		}
+    return res;
+  }
+
+}
+
+void
+CertStore :: pretty_print_x509 (char* buf, size_t size, const Quark& server, X509* cert)
+{
+  if (!cert) return;
+  g_snprintf(buf,size, _("The current server <b>'%s'</b> sent this security certificate :\n\n"
+                              "<b>Issuer :</b> \n%s\n\n"
+                              "<b>Subject : </b>\n%s\n\n"
+                              "<b>Fingerprint : </b>\n%s\n\n"),
+                              server.c_str(),
+                              X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0),
+                              X509_NAME_oneline(X509_get_subject_name(cert), 0, 0),
+                              get_x509_fingerpint_md5(cert).c_str());
+}
+
+
+
+}  // namespace pan
diff --git a/pan/tasks/cert-store.h b/pan/tasks/cert-store.h
new file mode 100644
index 0000000..d5be2c8
--- /dev/null
+++ b/pan/tasks/cert-store.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __CertStore_h__
+#define __CertStore_h__
+
+#ifdef HAVE_OPENSSL
+  #include <openssl/pem.h>
+  #include <openssl/err.h>
+  #include <openssl/pkcs12.h>
+  #include <openssl/bio.h>
+  #include <openssl/rand.h>
+  #include <openssl/x509.h>
+#endif
+
+#include <pan/tasks/socket.h>
+#include <pan/general/quark.h>
+#include <pan/general/macros.h>
+#include <pan/general/worker-pool.h>
+#include <pan/general/string-view.h>
+
+#include <map>
+
+namespace pan
+{
+#ifdef HAVE_OPENSSL
+  class CertStore
+  {
+    public:
+      CertStore (SSL_CTX*) ;
+      virtual ~CertStore () ;
+
+    private:
+      SSL_CTX* _ctx;
+      typedef std::set<Quark> certs_t;
+      certs_t _certs;
+      typedef std::map<Quark,X509*> certs_m;
+      typedef std::pair<Quark,X509*> certs_p;
+      certs_m _cert_to_server;
+      X509_STORE* _store;
+      std::string _path;
+
+    public:
+      SSL_CTX* get_ctx() { return _ctx; }
+      X509_STORE* get_store() const { return _store; }
+      void get_all_certs_from_disk(std::set<X509*>& setme);
+      const X509* get_cert_to_server(const Quark& server) const;
+      static void pretty_print_x509 (char* buf, size_t size, const Quark& server, X509* cert);
+
+    private:
+      void remove_hard(const Quark&);
+
+    public:
+      bool add(X509*, const Quark&) ;
+      void remove (const Quark&);
+      bool exist (const Quark& q) { return (_certs.count(q) > 0); }
+
+      struct Listener
+      {
+        virtual ~Listener() {}
+        /* functions that other listeners listen on */
+        virtual void on_verify_cert_failed (X509* cert UNUSED, std::string server UNUSED, int nr UNUSED) = 0;
+        virtual void on_valid_cert_added (X509* cert UNUSED, std::string server UNUSED) = 0;
+      };
+
+      typedef std::set<Listener*> listeners_t;
+      listeners_t _listeners;
+
+      void add_listener (Listener * l) { _listeners.insert(l); }
+      void remove_listener (Listener * l) { _listeners.erase(l); }
+
+      /* notify functions for listener list */
+      void verify_failed (X509* c, std::string server, int nr)
+      {
+        for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; ++it)
+          (*it)->on_verify_cert_failed (c, server, nr);
+      }
+
+      void valid_cert_added (X509* c, std::string server)
+      {
+        for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; ++it)
+          (*it)->on_valid_cert_added (c, server);
+      }
+
+    private:
+      void init_me();
+
+    protected:
+      friend class SocketCreator;
+      void set_ctx(SSL_CTX* c) { _ctx = c; init_me(); }
+
+  };
+
+  struct mydata_t {
+   SSL_CTX* ctx;
+   int depth;
+   int ignore_all;
+   CertStore* cs;
+   std::string server;
+   CertStore::Listener* l;
+  };
+#endif
+}
+
+ #endif
diff --git a/pan/tasks/nntp-pool.cc b/pan/tasks/nntp-pool.cc
index 12e4fc0..ebcddf3 100644
--- a/pan/tasks/nntp-pool.cc
+++ b/pan/tasks/nntp-pool.cc
@@ -39,14 +39,17 @@ namespace
 
 NNTP_Pool :: NNTP_Pool (const Quark        & server,
                         ServerInfo         & server_info,
-                        SocketCreator      * creator):
+                        SocketCreator      * creator,
+                        CertStore          & certstore):
   _server_info (server_info),
   _server (server),
   _socket_creator (creator),
+  _certstore(certstore),
   _pending_connections (0),
   _active_count (0),
   _time_to_allow_new_connections (0)
 {
+  certstore.add_listener(this);
 }
 
 NNTP_Pool :: ~NNTP_Pool ()
@@ -55,6 +58,7 @@ NNTP_Pool :: ~NNTP_Pool ()
     delete it->nntp->_socket;
     delete it->nntp;
   }
+  _certstore.remove_listener(this);
 }
 
 /***
@@ -174,6 +178,7 @@ NNTP_Pool :: on_socket_created (const StringView  & host UNUSED,
   }
 }
 
+
 void
 NNTP_Pool :: on_nntp_done (NNTP* nntp, Health health, const StringView& response)
 {
@@ -270,9 +275,12 @@ NNTP_Pool :: request_nntp (WorkerPool& threadpool)
     int port;
     if (_server_info.get_server_addr (_server, address, port))
     {
-      ++_pending_connections;
-      const bool ssl(_server_info.get_server_ssl_support(_server));
-      _socket_creator->create_socket (address, port, threadpool, this, ssl);
+      if (_blacklist.count(address) == 0)
+      {
+        ++_pending_connections;
+        const bool ssl(_server_info.get_server_ssl_support(_server));
+        _socket_creator->create_socket (address, port, threadpool, this, ssl);
+      }
     }
   }
 }
@@ -333,3 +341,17 @@ NNTP_Pool :: idle_upkeep ()
     item = 0;
   }
 }
+
+
+void
+NNTP_Pool :: on_verify_cert_failed (X509* cert, std::string server, int nr)
+{
+  _blacklist.insert(server);
+  abort_tasks();
+}
+
+void
+NNTP_Pool :: on_valid_cert_added (X509* cert, std::string server)
+{
+  _blacklist.erase(server);
+}
diff --git a/pan/tasks/nntp-pool.h b/pan/tasks/nntp-pool.h
index 4815e1b..81b00ac 100644
--- a/pan/tasks/nntp-pool.h
+++ b/pan/tasks/nntp-pool.h
@@ -27,6 +27,7 @@
 #include <pan/tasks/socket.h>
 #include <pan/tasks/nntp.h>
 #include <pan/tasks/socket-impl-main.h>
+#include <pan/tasks/cert-store.h>
 
 namespace pan
 {
@@ -40,13 +41,15 @@ namespace pan
   class NNTP_Pool:
     public NNTP::Source,
     private NNTP::Listener,
-    private Socket::Creator::Listener
+    private Socket::Creator::Listener,
+    private CertStore::Listener
   {
     public:
 
       NNTP_Pool (const Quark       & server,
                  ServerInfo        & server_info,
-                 SocketCreator     *);
+                 SocketCreator     *,
+                 CertStore         & certstore);
 
       virtual ~NNTP_Pool ();
 
@@ -58,7 +61,7 @@ namespace pan
       void get_counts (int& setme_active,
                        int& setme_idle,
                        int& setme_connecting,
-                          int& setme_max) const;
+                       int& setme_max) const;
 
     public:
 
@@ -76,7 +79,11 @@ namespace pan
 
     private: //  NNTP::Listener
       virtual void on_nntp_done (NNTP*, Health, const StringView&);
-
+ #ifdef HAVE_OPENSSL
+    private: //  CertStore::Listener
+      virtual void on_verify_cert_failed (X509*, std::string, int);
+      virtual void on_valid_cert_added (X509* cert, std::string server);
+#endif
     private: // Socket::Creator::Listener
       virtual void on_socket_created (const StringView& host, int port, bool ok, Socket*);
 
@@ -95,6 +102,7 @@ namespace pan
       const Quark _server;
       SocketCreator * _socket_creator;
       int _pending_connections;
+      std::set<std::string> _blacklist;
 
       struct PoolItem {
         NNTP * nntp;
@@ -105,6 +113,8 @@ namespace pan
       pool_items_t _pool_items;
       int _active_count;
 
+      CertStore& _certstore;
+
     private:
 
       time_t _time_to_allow_new_connections;
diff --git a/pan/tasks/queue.cc b/pan/tasks/queue.cc
index 3fbc306..4992042 100644
--- a/pan/tasks/queue.cc
+++ b/pan/tasks/queue.cc
@@ -34,6 +34,7 @@ using namespace pan;
 Queue :: Queue (ServerInfo         & server_info,
                 TaskArchive        & archive,
                 SocketCreator      * socket_creator,
+                CertStore          & store,
                 WorkerPool         & pool,
                 bool                 online,
                 int                  save_delay_secs):
@@ -49,7 +50,8 @@ Queue :: Queue (ServerInfo         & server_info,
   _needs_saving (false),
   _last_time_saved (0),
   _archive (archive),
-  _uploads_total(0)
+  _uploads_total(0),
+  _certstore (store)
 {
   tasks_t tasks;
   _archive.load_tasks (tasks);
@@ -98,7 +100,7 @@ Queue :: get_pool (const Quark& servername)
   }
   else // have to build one
   {
-    pool = new NNTP_Pool (servername, _server_info, _socket_creator);
+    pool = new NNTP_Pool (servername, _server_info, _socket_creator, _certstore);
     pool->add_listener (this);
     _pools[servername] = pool;
   }
diff --git a/pan/tasks/queue.h b/pan/tasks/queue.h
index 1257641..ffb19cc 100644
--- a/pan/tasks/queue.h
+++ b/pan/tasks/queue.h
@@ -35,6 +35,7 @@
 #include <pan/tasks/encoder.h>
 #include <pan/tasks/task-weak-ordering.h>
 #include <pan/tasks/socket-impl-main.h>
+#include <pan/tasks/cert-store.h>
 
 namespace pan
 {
@@ -70,7 +71,7 @@ namespace pan
     private AdaptableSet<Task*, TaskWeakOrdering>::Listener
   {
     public:
-      Queue (ServerInfo&, TaskArchive&, SocketCreator*, WorkerPool&,
+      Queue (ServerInfo&, TaskArchive&, SocketCreator*, CertStore&, WorkerPool&,
              bool online, int save_delay_secs);
       virtual ~Queue ();
 
@@ -241,8 +242,13 @@ namespace pan
     private:
       TaskArchive& _archive;
       void clean_n_save ();
-
       int _uploads_total;
+      CertStore& _certstore;
+
+    /* FIXME: move all this certstore crap to data */
+    public:
+      const CertStore& store() const { return _certstore; }
+      CertStore& store()  { return _certstore; }
 
     private:
       typedef AdaptableSet<Task*, TaskWeakOrdering> TaskSet;
diff --git a/pan/tasks/socket-impl-gio.h b/pan/tasks/socket-impl-gio.h
index 736eb76..22070a8 100644
--- a/pan/tasks/socket-impl-gio.h
+++ b/pan/tasks/socket-impl-gio.h
@@ -25,8 +25,6 @@
 #include <glib/gstring.h>
 #include <pan/tasks/socket.h>
 
-
-
 namespace pan
 {
   /**
@@ -55,6 +53,7 @@ namespace pan
       bool _io_performed;
 
     private:
+      friend class GIOChannelSocketSSL;
       enum WatchMode { READ_NOW, WRITE_NOW, IGNORE_NOW };
       void set_watch_mode (WatchMode mode);
       static gboolean gio_func (GIOChannel*, GIOCondition, gpointer);
diff --git a/pan/tasks/socket-impl-main.cc b/pan/tasks/socket-impl-main.cc
index f5a09d7..9b0b87b 100644
--- a/pan/tasks/socket-impl-main.cc
+++ b/pan/tasks/socket-impl-main.cc
@@ -60,15 +60,16 @@ namespace pan
     std::string err;
     bool use_ssl;
     SSL_CTX * context;
+    CertStore& store;
 
-    ThreadWorker (const StringView& h, int p, Socket::Creator::Listener *l, bool ssl, SSL_CTX* ctx):
-      host(h), port(p), listener(l), ok(false), socket(0), use_ssl(ssl), context(ctx) {}
+    ThreadWorker (const StringView& h, int p, Socket::Creator::Listener *l, bool ssl, SSL_CTX* ctx, CertStore& cs):
+      host(h), port(p), listener(l), ok(false), socket(0), use_ssl(ssl), context(ctx), store(cs) {}
 
     void do_work ()
     {
       #ifdef HAVE_OPENSSL
         if (use_ssl)
-          socket = new GIOChannelSocketSSL (context);
+          socket = new GIOChannelSocketSSL (context, store);
         else
       #endif
           socket = new GIOChannelSocket ();
@@ -112,28 +113,33 @@ namespace
     for (int i=0; i<CRYPTO_num_locks(); i++)
       pthread_mutex_destroy(&lock_cs[i]);
     CRYPTO_set_locking_callback(0);
-//    CRYPTO_set_id_callback(0);
     OPENSSL_free(lock_cs);
   }
+
 }
 #endif
 
-SocketCreator :: SocketCreator()
+SocketCreator :: SocketCreator(CertStore& cs) : store(cs)
 {
 #ifdef HAVE_OPENSSL
   SSL_library_init();
   SSL_load_error_strings();
+  OpenSSL_add_all_algorithms();
+  ERR_load_crypto_strings();
+
   /* init static locks for threads */
   ssl_thread_setup();
   ssl_ctx = SSL_CTX_new(SSLv3_client_method());
+  cs.set_ctx(ssl_ctx);
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_AUTO_RETRY);
 #endif
-
 }
+
 SocketCreator :: ~SocketCreator()
 {
 #ifdef HAVE_OPENSSL
   ssl_thread_cleanup();
-  SSL_CTX_free(ssl_ctx);
+  if (ssl_ctx) SSL_CTX_free(ssl_ctx);
 #endif
 }
 
@@ -144,8 +150,17 @@ SocketCreator :: create_socket (const StringView & host,
                                 Socket::Creator::Listener * listener,
                                 bool               use_ssl)
 {
-  ensure_module_init ();
+    ensure_module_init ();
+
+    ThreadWorker * w = new ThreadWorker (host, port, listener, use_ssl, ssl_ctx, store);
+    threadpool.push_work (w, w, true);
+}
 
-  ThreadWorker * w = new ThreadWorker (host, port, listener, use_ssl, ssl_ctx);
-  threadpool.push_work (w, w, true);
+void
+SocketCreator :: on_verify_cert_failed(X509* cert, std::string server, int nr)
+{
 }
+
+void
+SocketCreator :: on_valid_cert_added (X509* cert, std::string server)
+{}
diff --git a/pan/tasks/socket-impl-main.h b/pan/tasks/socket-impl-main.h
index 8942357..8776803 100644
--- a/pan/tasks/socket-impl-main.h
+++ b/pan/tasks/socket-impl-main.h
@@ -35,11 +35,14 @@
 #include <pan/general/string-view.h>
 #include <pan/general/worker-pool.h>
 #include "socket.h"
+
 #ifdef HAVE_OPENSSL
   #include <openssl/crypto.h>
   #include <openssl/ssl.h>
   #include "socket-impl-openssl.h"
+  #include "cert-store.h"
 #endif
+
 #include "socket-impl-gio.h"
 
 namespace
@@ -112,20 +115,30 @@ namespace
 
 namespace pan
 {
-#ifdef HAVE_OPENSSL
-  static SSL_CTX* ssl_ctx;
-#endif
-  class SocketCreator
+
+  class SocketCreator:
+    private CertStore::Listener
   {
     public:
-      SocketCreator ();
+      SocketCreator (CertStore&);
       virtual ~SocketCreator ();
 
+#ifdef HAVE_OPENSSL
+    private:
+      SSL_CTX* ssl_ctx;
+      CertStore & store;
+#endif
+
+    public:
       virtual void create_socket (const StringView & host,
                                   int                port,
                                   WorkerPool       & threadpool,
                                   Socket::Creator::Listener * listener,
                                   bool               use_ssl);
+
+      // CertStore::Listener
+      virtual void on_verify_cert_failed(X509*, std::string, int);
+      virtual void on_valid_cert_added (X509*, std::string );
   };
 
 }
diff --git a/pan/tasks/socket-impl-openssl.cc b/pan/tasks/socket-impl-openssl.cc
index 83f17da..290a007 100644
--- a/pan/tasks/socket-impl-openssl.cc
+++ b/pan/tasks/socket-impl-openssl.cc
@@ -19,6 +19,10 @@
 
 /* #define DEBUG_SOCKET_IO */
 
+/** Copyright notice: Some code taken from here :
+  * http://dslinux.gits.kiev.ua/trunk/user/irssi/src/src/core/network-openssl.c
+  * Copyright (C) 2002 vjt (irssi project) */
+
 /******
 *******
 ******/
@@ -34,6 +38,7 @@ extern "C" {
   #include <glib/gi18n.h>
 }
 
+#include <pan/usenet-utils/ssl-utils.h>
 #include <pan/general/file-util.h>
 #include <pan/general/log.h>
 #include <pan/general/macros.h>
@@ -90,6 +95,7 @@ extern "C" {
 #include <pan/usenet-utils/gnksa.h>
 #include "socket-impl-openssl.h"
 #include "socket-impl-main.h"
+#include "cert-store.h"
 
 using namespace pan;
 
@@ -106,7 +112,7 @@ extern t_freeaddrinfo p_freeaddrinfo;
 
 #ifdef HAVE_OPENSSL // without libssl this class is just a stub....
 
-GIOChannelSocketSSL :: GIOChannelSocketSSL (SSL_CTX* ctx):
+GIOChannelSocketSSL :: GIOChannelSocketSSL (SSL_CTX* ctx, CertStore& cs):
    _channel (0),
    _tag_watch (0),
    _tag_timeout (0),
@@ -114,9 +120,11 @@ GIOChannelSocketSSL :: GIOChannelSocketSSL (SSL_CTX* ctx):
    _out_buf (g_string_new (0)),
    _in_buf (g_string_new (0)),
    _io_performed (false),
-   _ctx(ctx)
+   _ctx(ctx),
+   _certstore(cs)
 {
-   debug ("GIOChannelSocketSSL ctor " << (void*)this);
+//   std::cerr<<"GIOChannelSocketSSL ctor " << (void*)this<<std::endl;
+   cs.add_listener(this);
 }
 
 
@@ -201,7 +209,7 @@ GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std:
     // try to open a socket on any ipv4 or ipv6 addresses we found
     errno = 0;
     sockfd = -1;
-    for (struct addrinfo * walk(ans); walk && sockfd<0; walk=walk->ai_next)
+    for (struct addrinfo * walk(ans); walk && sockfd<=0; walk=walk->ai_next)
     {
       // only use ipv4 or ipv6 addresses
       if ((walk->ai_family!=PF_INET) && (walk->ai_family!=PF_INET6))
@@ -224,7 +232,7 @@ GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std:
   }
 
   // create the giochannel...
-  if (sockfd < 0) {
+  if (sockfd <= 0) {
     char buf[512];
     snprintf (buf, sizeof(buf), _("Error connecting to \"%s\""), hpbuf);
     setme_err = buf;
@@ -239,7 +247,6 @@ GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std:
   GIOChannel * channel (0);
 #ifndef G_OS_WIN32
   channel = g_io_channel_unix_new (sockfd);
-  g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, 0);
 #else
   channel = g_io_channel_win32_new_socket (sockfd);
 #endif
@@ -270,51 +277,11 @@ namespace
     GIOChannel *giochan;
     SSL *ssl;
     SSL_CTX *ctx;
+    char* host;
     unsigned int verify;
   } GIOSSLChannel;
 
 
-  /* FIXME todo: real verify ! */
-  gboolean ssl_verify(SSL *ssl, SSL_CTX *ctx, X509 *cert)
-  {
-    if (SSL_get_verify_result(ssl) != X509_V_OK) {
-      unsigned char md[EVP_MAX_MD_SIZE];
-      unsigned int n;
-      char *str;
-
-      if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
-        g_warning("  Could not get subject-name from peer certificate");
-      else {
-        g_warning("  Subject : %s", str);
-        free(str);
-      }
-      if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL)
-        g_warning("  Could not get issuer-name from peer certificate");
-      else {
-        g_warning("  Issuer  : %s", str);
-        free(str);
-      }
-      if (! X509_digest(cert, EVP_md5(), md, &n))
-        g_warning("  Could not get fingerprint from peer certificate");
-      else {
-        char hex[] = "0123456789ABCDEF";
-        char fp[EVP_MAX_MD_SIZE*3];
-        if (n < sizeof(fp)) {
-          unsigned int i;
-          for (i = 0; i < n; i++) {
-            fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
-            fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
-            fp[i*3+2] = i == n - 1 ? '\0' : ':';
-          }
-          g_warning("  MD5 Fingerprint : %s", fp);
-        }
-      }
-      return FALSE;
-    }
-    return TRUE;
-  }
-
-
   void ssl_free(GIOChannel *handle)
   {
     GIOSSLChannel *chan = (GIOSSLChannel *)handle;
@@ -327,7 +294,10 @@ namespace
 
 GIOChannelSocketSSL :: ~GIOChannelSocketSSL ()
 {
-//std::cerr << LINE_ID << " destroying socket " << this << std::endl;
+
+//  std::cerr << LINE_ID << " destroying socket " << this << std::endl;
+
+  _certstore.remove_listener(this);
 
   remove_source (_tag_watch);
   remove_source (_tag_timeout);
@@ -362,7 +332,7 @@ GIOChannelSocketSSL :: get_host (std::string& setme) const
 }
 
 void
-GIOChannelSocketSSL :: write_command (const StringView& command, Listener * l)
+GIOChannelSocketSSL :: write_command (const StringView& command, Socket::Listener * l)
 {
   _partial_read.clear ();
   _listener = l;
@@ -381,11 +351,6 @@ GIOChannelSocketSSL :: write_command (const StringView& command, Listener * l)
 namespace
 {
 
-  SSL_CTX* ssl_init(void)
-  {
-    return 0;
-  }
-
   static GIOStatus ssl_errno(gint e)
   {
     switch(e)
@@ -401,31 +366,60 @@ namespace
     return G_IO_STATUS_ERROR;
   }
 
-  int ssl_handshake(GIOChannel *handle)
+
+  int ssl_handshake(GIOChannel *handle, CertStore::Listener* listener, CertStore* cs, std::string host)
   {
+
     GIOSSLChannel *chan = (GIOSSLChannel *)handle;
     int ret;
     int err;
     X509 *cert;
     const char *errstr;
 
-    if (!handle || !chan->ssl || !chan->ctx) return -1;
+    /* init custom data for callback */
+    mydata_t mydata;// = new mydata_t();
+    mydata.ctx = chan->ctx;
+    mydata.cs = cs;
+    mydata.ignore_all = 0;
+    mydata.l = listener;
+    mydata.server = host;
+    SSL_set_ex_data(chan->ssl, SSL_get_fd(chan->ssl), &mydata);
 
     ret = SSL_connect(chan->ssl);
     if (ret <= 0) {
       err = SSL_get_error(chan->ssl, ret);
-      if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
-        return -1;
-      return err == SSL_ERROR_WANT_READ ? 1 : 3;
+      switch (err) {
+        case SSL_ERROR_WANT_READ:
+          return 1;
+        case SSL_ERROR_WANT_WRITE:
+          return 3;
+        case SSL_ERROR_ZERO_RETURN:
+          g_warning("SSL handshake failed: %s", "server closed connection");
+          return -1;
+        case SSL_ERROR_SYSCALL:
+          errstr = ERR_reason_error_string(ERR_get_error());
+          if (errstr == NULL && ret == -1)
+            errstr = strerror(errno);
+          g_warning("SSL handshake failed: %s", errstr != NULL ? errstr : "server closed connection unexpectedly");
+          return -1;
+        default:
+          errstr = ERR_reason_error_string(ERR_get_error());
+          g_warning("SSL handshake failed: %s", errstr != NULL ? errstr : "unknown SSL error");
+          return -1;
+      }
     }
 
     cert = SSL_get_peer_certificate(chan->ssl);
-    if (!cert && chan->ssl)
+    if (!cert) {
+      g_warning("SSL server supplied no certificate");
       return -1;
+    }
 
-    ret = chan->verify ?  ssl_verify(chan->ssl, chan->ctx, cert) : 0;
+    ret = !chan->verify || ssl_verify(chan->ssl, chan->ctx, host.c_str(), cert);
     X509_free(cert);
-    return ret;
+
+    return ret ? 0 : -1;
+
   }
 
   GIOStatus ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
@@ -742,26 +736,23 @@ GIOChannelSocketSSL :: set_watch_mode (WatchMode mode)
 GIOChannel *
 GIOChannelSocketSSL :: ssl_get_iochannel(GIOChannel *handle, gboolean verify)
 {
+
 	GIOSSLChannel *chan(0);
 	GIOChannel *gchan(0);
 	int err(0), fd(0);
 	SSL *ssl(0);
-	SSL_CTX *ctx(0);
+	SSL_CTX *ctx(_ctx);
 
 	g_return_val_if_fail(handle != 0, 0);
-
-	ctx = _ctx;
-	if (!ctx) return 0;
+	g_return_val_if_fail(ctx != 0, 0);
 
 	if(!(fd = g_io_channel_unix_get_fd(handle)))
 	{
-	  if (ctx) SSL_CTX_free(ctx);
     return 0;
 	}
 
 	if(!(ssl = SSL_new(ctx)))
 	{
-	  SSL_CTX_free(ctx);
 		g_warning("Failed to allocate SSL structure");
 		return 0;
 	}
@@ -770,7 +761,6 @@ GIOChannelSocketSSL :: ssl_get_iochannel(GIOChannel *handle, gboolean verify)
 	{
 		g_warning("Failed to associate socket to SSL stream");
 		SSL_free(ssl);
-  	SSL_CTX_free(ctx);
 		return 0;
 	}
 
@@ -781,18 +771,32 @@ GIOChannelSocketSSL :: ssl_get_iochannel(GIOChannel *handle, gboolean verify)
 	chan->giochan = handle;
 	chan->ssl = ssl;
 	chan->ctx = ctx;
-	chan->verify = verify ? 0 : -1;
+	chan->verify = verify ? 1 : 0;
 
 	gchan = (GIOChannel *)chan;
 	gchan->funcs = &ssl_channel_funcs;
 	g_io_channel_init(gchan);
   gchan->read_buf = g_string_sized_new(4096*128);
 
-  if (ssl_handshake(gchan))
+  if (ssl_handshake(gchan, this, &_certstore, _host) == 0)
   {
+    std::cerr<<"handshake success\n";
+    g_io_channel_set_flags (handle, G_IO_FLAG_NONBLOCK, 0);
     return gchan;
   }
+  std::cerr<<"handshake fail\n";
   return 0;
+}
+
+void
+GIOChannelSocketSSL :: on_verify_cert_failed (X509* cert, std::string server, int nr)
+{
+
+}
+
+void
+GIOChannelSocketSSL :: on_valid_cert_added (X509* cert, std::string server)
+{
 
 }
 #endif  //HAVE_OPENSSL
diff --git a/pan/tasks/socket-impl-openssl.h b/pan/tasks/socket-impl-openssl.h
index 9529522..4bb9a11 100644
--- a/pan/tasks/socket-impl-openssl.h
+++ b/pan/tasks/socket-impl-openssl.h
@@ -23,9 +23,12 @@
 #include <string>
 #include <glib/giochannel.h>
 #include <glib/gstring.h>
+#include <pan/general/quark.h>
 #include <pan/tasks/socket.h>
+#include <pan/tasks/socket-impl-gio.h>
 
 #ifdef HAVE_OPENSSL
+  #include <pan/tasks/cert-store.h>
   #include <openssl/crypto.h>
   #include <openssl/x509.h>
   #include <openssl/x509v3.h>
@@ -34,10 +37,6 @@
   #include <openssl/err.h>
 #endif
 
-#include "socket-impl-gio.h"
-#include "socket-impl-main.h"
-
-
 namespace pan
 {
   /**
@@ -45,32 +44,30 @@ namespace pan
    *
    * @ingroup tasks
    */
-  class GIOChannelSocketSSL: public GIOChannelSocket
+  class GIOChannelSocketSSL:
+    public GIOChannelSocket,
+    private CertStore::Listener
   {
     public:
       virtual ~GIOChannelSocketSSL ();
-#ifndef HAVE_OPENSSL
-      GIOChannelSocketSSL ();
-#else
-      GIOChannelSocketSSL (SSL_CTX* ctx=0);
-#endif
+      GIOChannelSocketSSL (SSL_CTX* ctx, CertStore& cs);
+
       virtual bool open (const StringView& address, int port, std::string& setme_err);
-      virtual void write_command (const StringView& chars, Listener *);
+      virtual void write_command (const StringView& chars, Socket::Listener *);
       virtual void get_host (std::string& setme) const;
 
     private:
       GIOChannel * _channel;
       unsigned int _tag_watch;
       unsigned int _tag_timeout;
-      Listener * _listener;
+      Socket::Listener * _listener;
       GString * _out_buf;
       GString * _in_buf;
       std::string _partial_read;
       std::string _host;
       bool _io_performed;
-#ifdef HAVE_OPENSSL
       SSL_CTX * _ctx;
-#endif
+      CertStore& _certstore;
 
     private:
       enum WatchMode { READ_NOW, WRITE_NOW, IGNORE_NOW };
@@ -82,6 +79,10 @@ namespace pan
       DoResult do_read ();
       DoResult do_write ();
 
+      // CertStore::Listener
+      virtual void on_verify_cert_failed (X509*, std::string, int) ;
+      virtual void on_valid_cert_added (X509*, std::string );
+
       GIOChannel * create_channel (const StringView& host_in, int port, std::string& setme_err);
       void gio_lock(int mode, int type, const char *file, int line);
 
diff --git a/pan/tasks/socket.h b/pan/tasks/socket.h
index a4cfa7e..bc6fd2e 100644
--- a/pan/tasks/socket.h
+++ b/pan/tasks/socket.h
@@ -21,6 +21,16 @@
 #define __Socket_h__
 
 #include <string>
+#include <config.h>
+
+#ifdef HAVE_OPENSSL
+  #include <openssl/crypto.h>
+  #include <openssl/x509.h>
+  #include <openssl/x509v3.h>
+  #include <openssl/pem.h>
+  #include <openssl/ssl.h>
+  #include <openssl/err.h>
+#endif
 
 namespace pan
 {
diff --git a/pan/usenet-utils/Makefile.am b/pan/usenet-utils/Makefile.am
index e51556d..df16332 100644
--- a/pan/usenet-utils/Makefile.am
+++ b/pan/usenet-utils/Makefile.am
@@ -23,7 +23,8 @@ noinst_HEADERS = \
  numbers.h \
  scorefile.h \
  text-massager.h \
- url-find.h
+ url-find.h \
+ ssl-utils.h
 
 #noinst_PROGRAMS = \
 # gnksa-test \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]