[pan2: 248/268] Dumped OpenSSL, implemented GnuTLS



commit 6c0845861d0d60a29c4c9cf653242edbc5aa35cb
Author: Heinrich MÃller <henmull src gnome org>
Date:   Mon Dec 19 15:56:06 2011 +0100

    Dumped OpenSSL, implemented GnuTLS

 configure.in                     |   32 ++-
 pan/data-impl/Makefile.am        |    2 +-
 pan/data-impl/data-impl.h        |   17 +-
 pan/data-impl/server.cc          |   34 +++-
 pan/data/article-cache.cc        |    2 +
 pan/data/article.cc              |    9 +
 pan/data/cert-store.cc           |  282 +++++++++++++++-------
 pan/data/cert-store.h            |  107 +++------
 pan/data/data.h                  |   10 +-
 pan/data/server-info.h           |    5 +
 pan/gui/Makefile.am              |    6 +-
 pan/gui/body-pane.cc             |  195 ++++++++++++++-
 pan/gui/body-pane.h              |   42 +++-
 pan/gui/e-cte-dialog.c           |   13 +-
 pan/gui/e-cte-dialog.h           |    1 +
 pan/gui/gui.cc                   |   40 ++--
 pan/gui/gui.h                    |   17 +-
 pan/gui/pan.cc                   |    4 +-
 pan/gui/post-ui.cc               |    3 +-
 pan/gui/save-attach-ui.cc        |  320 ++++++++++++++++++++++++
 pan/gui/save-attach-ui.h         |   82 +++++++
 pan/gui/server-ui.cc             |   76 +++---
 pan/gui/task-pane.cc             |    2 +
 pan/tasks/Makefile.am            |    4 +-
 pan/tasks/decoder.cc             |   18 +-
 pan/tasks/decoder.h              |    6 +-
 pan/tasks/encoder.cc             |    3 -
 pan/tasks/nntp-pool.cc           |    8 +-
 pan/tasks/nntp-pool.h            |    8 +-
 pan/tasks/queue.h                |    2 +-
 pan/tasks/socket-impl-main.cc    |   95 ++------
 pan/tasks/socket-impl-main.h     |   21 +--
 pan/tasks/socket-impl-openssl.cc |  490 ++++++++++++++++++-------------------
 pan/tasks/socket-impl-openssl.h  |   44 ++--
 pan/tasks/socket.h               |    9 +-
 pan/tasks/task-article.cc        |   13 +-
 pan/tasks/task-article.h         |   26 +--
 pan/usenet-utils/mime-utils.cc   |   20 +-
 pan/usenet-utils/ssl-utils.h     |  499 ++++++--------------------------------
 39 files changed, 1450 insertions(+), 1117 deletions(-)
---
diff --git a/configure.in b/configure.in
index f40f73c..247ef74 100644
--- a/configure.in
+++ b/configure.in
@@ -50,7 +50,7 @@ GMIME_REQUIRED=2.6.0
 GTK_REQUIRED=2.16.0
 GTK3_REQUIRED=3.0.0
 GTKSPELL_REQUIRED=2.0.7
-OPENSSL_REQUIRED=1.0.0
+GNUTLS_REQUIRED=3.0.9
 LIBNOTIFY_REQUIRED=0.4.1
 LIBGSASL_REQUIRED=1.6.1
 LIBGKR_REQUIRED=3.2.2
@@ -58,7 +58,7 @@ AC_SUBST(GLIB_REQUIRED)
 AC_SUBST(GMIME_REQUIRED)
 AC_SUBST(GTK_REQUIRED)
 AC_SUBST(GTKSPELL_REQUIRED)
-AC_SUBST(OPENSSL_REQUIRED)
+AC_SUBST(GNUTLS_REQUIRED)
 AC_SUBST(LIBNOTIFY_REQUIRED)
 AC_SUBST(LIBGSASL_REQUIRED)
 AC_SUBST(LIBGKR_REQUIRED)
@@ -109,16 +109,20 @@ if test "x$want_gtkspell" = "xyes" ; then
 	                  AC_MSG_RESULT(no)])
 fi
 
-openssl_msg=no
-AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl], [Enable OpenSSL support]), [want_openssl=$withval], [want_openssl=no])
-if test "x$want_openssl" = "xyes" ; then
-	PKG_CHECK_MODULES([OPENSSL], [openssl >= $OPENSSL_REQUIRED],
-	                  [openssl_msg=yes
-	                  AC_DEFINE(HAVE_OPENSSL,[1],[OpenSSL 3.0])],
-	                  [openssl_msg=no
-	                  AC_MSG_RESULT(no)])
+
+gnutls_msg=no
+AC_ARG_WITH(gnutls, AC_HELP_STRING([--with-gnutls], [Enable GnuTLS support]), [want_gnutls=$withval], [want_gnutls=no])
+if test "x$want_gnutls" = "xyes" ; then
+  PKG_CHECK_MODULES([GNUTLS], [gnutls >= $GNUTLS_REQUIRED],
+  [gnutls_msg=yes,
+  AC_DEFINE(HAVE_GNUTLS,[1],[GnuTLS])],
+  [gnutls_msg=no
+  AC_MSG_RESULT(no)])
+  AC_SUBST([GNUTLS_CFLAGS])
+  AC_SUBST([GNUTLS_LIBS])
 fi
 
+
 dnl Check for libnotify if user-enabled for popup notifications
 AC_ARG_ENABLE([libnotify],
 AC_HELP_STRING([--enable-libnotify],[enable libnotify support]),[enable_libnotify=$enableval],[enable_libnotify=yes])
@@ -205,9 +209,9 @@ case $host_os in
 esac
 AM_CONDITIONAL([HAVE_WIN32],[test "$win32" = "yes"])
 
-CXXFLAGS="$CXXFLAGS -O0 -g -I/usr/lib/"
-CPPFLAGS="$CPPFLAGS -O0 -g -I/usr/lib/"
-CFLAGS="$CFLAGS -g -I/usr/lib/"
+CXXFLAGS="-O0 -g -I/usr/lib/"
+CPPFLAGS="-O0 -g -I/usr/lib/"
+CFLAGS="-g -O0 -I/usr/lib/"
 
 dnl build the output files
 AC_SUBST(panlocaledir)
@@ -237,6 +241,6 @@ Configuration:
         Compiler:		${CXX}
         With GtkSpell:          ${gtkspell_msg}
         With GTK3:              ${gtk_msg}
-        With OpenSSL 3.0:       ${openssl_msg}
+        With GnuTLS:            ${gnutls_msg}
 
 "
diff --git a/pan/data-impl/Makefile.am b/pan/data-impl/Makefile.am
index e080653..dea396c 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@ @OPENSSL_CFLAGS@ @LIBGNOME_KEYRING_1_CFLAGS@
+AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @GNUTLS_CFLAGS@ @LIBGNOME_KEYRING_1_CFLAGS@
 
 AM_LDFLAGS = @LIBGNOME_KEYRING_1_LIBS@
 
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index 2929477..10f271c 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -45,14 +45,9 @@
 #include <pan/data-impl/profiles.h>
 #include <pan/data-impl/memchunk.h>
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
   #include <pan/data/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>
+  #include <gnutls/gnutls.h>
 #endif
 
 namespace pan
@@ -134,10 +129,11 @@ namespace pan
 
       servers_t _servers;
 
-      Server* find_server (const Quark& server);
+//      Server* find_server (const Quark& server);
 
     public:
       virtual const Server* find_server (const Quark& server) const;
+      virtual Server* find_server (const Quark& server);
       virtual bool find_server_by_hn (const Quark& server, Quark& setme) const;
 
     public: // mutators
@@ -151,6 +147,9 @@ namespace pan
                                     const StringView  & username,
                                     const StringView  & password);
 
+      virtual void set_server_trust (const Quark      & servername,
+                                     int                setme);
+
       virtual void set_server_addr (const Quark       & server,
                                     const StringView  & host,
                                     const int           port);
@@ -182,6 +181,8 @@ namespace pan
                                     std::string   & setme_username,
                                     std::string   & setme_password) const;
 
+      virtual bool get_server_trust (const Quark  & servername, int&) const;
+
       virtual bool get_server_addr (const Quark   & server,
                                     std::string   & setme_host,
                                     int           & setme_port) const;
diff --git a/pan/data-impl/server.cc b/pan/data-impl/server.cc
index 898f606..1082202 100644
--- a/pan/data-impl/server.cc
+++ b/pan/data-impl/server.cc
@@ -140,6 +140,15 @@ DataImpl :: set_server_auth (const Quark       & server,
 }
 
 void
+DataImpl :: set_server_trust  (const Quark   & server,
+                               const int       setme)
+{
+  Server * s (find_server (server));
+  assert (s);
+  s->trust = setme;
+}
+
+void
 DataImpl :: set_server_addr (const Quark       & server,
                              const StringView  & host,
                              int                 port)
@@ -186,10 +195,10 @@ void
 DataImpl :: set_server_cert  (const Quark   & server,
                               const StringView & cert)
 {
+
   Server * s (find_server (server));
   assert (s);
   s->cert = cert;
-  debug(s->cert<<" "<<cert);
 
 }
 
@@ -238,11 +247,24 @@ DataImpl :: get_server_auth (const Quark   & server,
     }
 #endif
   }
+
   return found;
 
 }
 
 bool
+DataImpl :: get_server_trust (const Quark   & server, int& setme) const
+{
+  const Server * s (find_server (server));
+  const bool found (s);
+  if (found) {
+    setme = s->trust;
+  }
+
+  return found;
+}
+
+bool
 DataImpl :: get_server_addr (const Quark   & server,
                              std::string   & setme_host,
                              int           & setme_port) const
@@ -253,6 +275,7 @@ DataImpl :: get_server_addr (const Quark   & server,
     setme_host = s->host;
     setme_port = s->port;
   }
+
   return found;
 
 }
@@ -267,6 +290,7 @@ DataImpl :: get_server_address (const Quark& server) const
     x << ":" << s->port;
     str = x.str();
   }
+
   return str;
 
 }
@@ -278,6 +302,7 @@ DataImpl :: get_server_ssl_support (const Quark & server) const
   const Server * s (find_server (server));
   if (s)
     retval = (s->ssl_support != 0);
+
   return retval;
 
 }
@@ -289,6 +314,7 @@ DataImpl :: get_server_cert (const Quark & server) const
   const Server * s (find_server (server));
   if (s)
     str = s->cert;
+
   return str;
 
 }
@@ -300,6 +326,7 @@ DataImpl :: get_server_limits (const Quark & server) const
   const Server * s (find_server (server));
   if (s)
     retval = s->max_connections;
+
   return retval;
 
 }
@@ -311,6 +338,7 @@ DataImpl :: get_server_rank (const Quark & server) const
   const Server * s (find_server (server));
   if (s)
     retval = s->rank;
+
   return retval;
 
 }
@@ -322,6 +350,7 @@ DataImpl :: get_server_article_expiration_age  (const Quark  & server) const
   const Server * s (find_server (server));
   if (s)
     retval = s->article_expiration_age;
+
   return retval;
 
 }
@@ -426,6 +455,8 @@ DataImpl :: load_server_properties (const DataIO& source)
     int ssl(to_int(kv["use-ssl"], 0));
     s.ssl_support = ssl;
     s.cert = kv["cert"];
+    int trust(to_int(kv["trust"], 0));
+    s.trust = trust;
     s.newsrc_filename = kv["newsrc"];
     if (s.newsrc_filename.empty()) { // set a default filename
       std::ostringstream o;
@@ -481,6 +512,7 @@ DataImpl :: save_server_properties (DataIO& data_io) const
          << indent(depth) << "<newsrc>" << s->newsrc_filename << "</newsrc>\n"
          << indent(depth) << "<rank>" << s->rank << "</rank>\n"
          << indent(depth) << "<use-ssl>" << s->ssl_support << "</use-ssl>\n"
+         << indent(depth) << "<trust>" << s->trust << "</trust>\n"
          << indent(depth) << "<cert>"    << s->cert << "</cert>\n";
 
     *out << indent(--depth) << "</server>\n";
diff --git a/pan/data/article-cache.cc b/pan/data/article-cache.cc
index 2d82a31..035407b 100644
--- a/pan/data/article-cache.cc
+++ b/pan/data/article-cache.cc
@@ -438,6 +438,8 @@ ArticleCache :: get_filenames (const mid_sequence_t& mids)
   char filename[PATH_MAX];
   foreach_const (mid_sequence_t, mids, it)
     if (get_filename (filename, sizeof(filename), *it))
+    {
       ret.push_back (filename);
+    }
   return ret;
 }
diff --git a/pan/data/article.cc b/pan/data/article.cc
index 218957b..fa07591 100644
--- a/pan/data/article.cc
+++ b/pan/data/article.cc
@@ -97,10 +97,19 @@ Article :: get_part_mids () const
 {
   mid_sequence_t mids;
   for (part_iterator it(pbegin()), end(pend()); it!=end; ++it)
+  {
     mids.push_back (it.mid());
+  }
   return mids;
 }
 
+//const Quark&
+//Article :: get_attachment () const
+//{
+//  for (part_iterator it(pbegin()), end(pend()); it!=end; ++it)
+//    if (it.mid() == search)
+//}
+
 void
 Article :: clear ()
 {
diff --git a/pan/data/cert-store.cc b/pan/data/cert-store.cc
index a5817fa..1d9da03 100644
--- a/pan/data/cert-store.cc
+++ b/pan/data/cert-store.cc
@@ -50,83 +50,207 @@ extern "C" {
 
 using namespace pan;
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
+
 namespace pan
 {
 
   int
-  verify_callback(int ok, X509_STORE_CTX *store)
+  verify_callback(gnutls_session_t session)
   {
 
-    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));
+    mydata_t* mydata = (mydata_t*)gnutls_session_get_ptr (session);
+
+    unsigned int status;
+    const gnutls_datum_t *cert_list;
+    unsigned int cert_list_size;
+    int ret;
+    gnutls_x509_crt_t cert;
+    bool fail(false);
+
+
+
+    ret = gnutls_certificate_verify_peers2 (session, &status);
+
+    if (ret < 0)
+      return GNUTLS_E_CERTIFICATE_ERROR;
+
+    if (status & GNUTLS_CERT_INVALID)
+    {
+      g_warning ("The certificate is not trusted.\n");
+      fail = !mydata->always_trust;
+    }
+
+    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+    {
+      fail = !mydata->always_trust;
+      g_warning ("The certificate hasn't got a known issuer.\n");
+    }
+
+    if (status & GNUTLS_CERT_REVOKED)
+    {
+      g_warning ("The certificate has been revoked.\n");
+      fail = true;
+    }
 
-    if (!ok)
+    if (status & GNUTLS_CERT_EXPIRED)
     {
-      int err = X509_STORE_CTX_get_error(store);
-
-      X509 *cert = X509_STORE_CTX_get_current_cert(store);
-      CRYPTO_add (&(cert->references), 1, CRYPTO_LOCK_X509); // refcount +1
-      if (err == X509_V_ERR_CERT_HAS_EXPIRED)
-        if (!mydata->cs->is_ignored(cert))
-            mydata->cs->ignore(cert);
-        else return 1;
-
-      /* accept user-override on self-signed certificates */
-      if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN ||
-          err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
-          err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY )
-        mydata->cs->verify_failed(cert, mydata->server, mydata->cert_name, err);
-      else
-        g_warning("[[DEBUG:]] unknown error condition, please report me: %s", ssl_err_to_string(err).c_str());
+      g_warning ("The certificate has expired\n");
+      fail = true;
     }
 
-    return ok;
+    if (status & GNUTLS_CERT_NOT_ACTIVATED)
+    {
+      g_warning ("The certificate is not yet activated\n");
+      fail = true;
+    }
+
+    /* Up to here the process is the same for X.509 certificates and
+     * OpenPGP keys. From now on X.509 certificates are assumed. This can
+     * be easily extended to work with openpgp keys as well.
+     */
+    if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
+    {
+      g_warning ("The certificate is not a X509 certificate!\n");
+      goto _fail;
+    }
+
+    if (gnutls_x509_crt_init (&cert) < 0)
+    {
+      g_error ("Error in initialization\n");
+      goto _fail;
+    }
+
+    cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+    if (cert_list == NULL)
+    {
+      g_error ("No certificate found!\n");
+      goto _fail;
+    }
+
+    /* TODO verify whole chain perhaps?
+     */
+    if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
+    {
+      g_error ("Error parsing certificate!\n");
+      goto _fail;
+    }
+
+    if (!gnutls_x509_crt_check_hostname (cert, mydata->hostname_full.c_str()))
+    {
+      g_error ("The certificate's owner does not match hostname '%s' !\n", mydata->hostname_full.c_str());
+      goto _fail;
+    }
+
+    if (fail) goto _fail;
+
+    /* auto-add new cert if we always trust this server */
+    if (mydata->always_trust)
+      mydata->cs->add(cert, mydata->host);
+    else
+      gnutls_x509_crt_deinit(cert);
+
+    /* notify gnutls to continue handshake normally */
+    return 0;
+
+    _fail:
+
+    if (cert)
+      mydata->cs->verify_failed (cert, mydata->host.c_str(), status);
+
+    return GNUTLS_E_CERTIFICATE_ERROR;
+
   }
 
   int
-  CertStore :: get_all_certs_from_disk(std::set<X509*>& setme)
+  CertStore :: get_all_certs_from_disk()
   {
 
     int cnt(0);
     quarks_t servers(_data.get_servers());
+    int ret(0);
+    size_t filelen;
+    char * buf;
+
     foreach_const(quarks_t, servers, it)
     {
-      const Data::Server* s(_data.find_server(*it));
+      Data::Server* s(_data.find_server(*it));
       if (!s) continue;
       const char* filename(s->cert.c_str());
       if (!filename) continue;
-      FILE *fp = fopen(filename,"r");
+
+      FILE * fp = fopen(filename, "rb");
       if (!fp) continue;
-      X509 *x = X509_new();
-      if (!x) { fclose(fp); continue; }
-      PEM_read_X509(fp,&x, 0, 0);
-      fclose(fp);
-      setme.insert(x);
+
+      fseek (fp, 0, SEEK_END);
+      filelen = ftell (fp);
+      fseek (fp, 0, SEEK_SET);
+      buf = new char[filelen];
+      fread (buf, sizeof(char), filelen, fp);
+
+      gnutls_datum_t in;
+      in.data = (unsigned char*)buf;
+      in.size = filelen;
+      gnutls_x509_crt_t cert;
+      gnutls_x509_crt_init(&cert);
+      gnutls_x509_crt_import(cert, &in, GNUTLS_X509_FMT_PEM);
+
+      delete buf;
+
+      ret = gnutls_certificate_set_x509_trust(_creds, &cert, 1);
+      if (ret > 0) cnt += ret; else goto fail;
+
       _certs.insert(*it);
-      _cert_to_server[*it] = x;
-      ++cnt;
+      _cert_to_server[*it] = cert;
+
+      continue;
+
+      fail:
+        s->cert.clear();
+        gnutls_x509_crt_deinit (cert);
+        _data.save_server_info(*it);
     }
 
+    // get certs from ssl certs directory
+#ifdef SSL_DIR
+    GError* err(NULL);
+    const char * ssldir = SSL_DIR;
+    GDir * dir = g_dir_open (ssldir, 0, &err);
+    if (err != NULL)
+    {
+      Log::add_err_va (_("Error opening SSL certificate directory: \"%s\": %s"), _path.c_str(), err->message);
+      g_error_free (err);
+    }
+    else
+    {
+      char filename[PATH_MAX];
+      const char * fname;
+      struct stat stat_p;
+      while ((fname = g_dir_read_name (dir)))
+      {
+        struct stat stat_p;
+        g_snprintf (filename, sizeof(filename), "%s%c%s", ssldir, G_DIR_SEPARATOR, fname);
+        if (!stat (filename, &stat_p))
+        {
+          if (!S_ISREG(stat_p.st_mode)) continue;
+          ret = gnutls_certificate_set_x509_trust_file(_creds, filename, GNUTLS_X509_FMT_PEM);
+          if (ret > 0) cnt += ret;
+        }
+      }
+      g_dir_close (dir);
+    }
+#endif
     return cnt;
   }
 
 
   void
-  CertStore :: init_me()
+  CertStore :: init()
   {
-    assert (_ctx);
-
-    _store = SSL_CTX_get_cert_store(_ctx);
-
-    std::set<X509*> certs;
     int r(0);
-    get_all_certs_from_disk (certs);
-    foreach_const (std::set<X509*>, certs, it)
-      if (X509_STORE_add_cert(_store, *it) != 0) ++r;
+    r = get_all_certs_from_disk ();
 
     if (r != 0) Log::add_info_va(_("Succesfully added %d SSL PEM certificate(s) to Certificate Store."), r);
-    SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
 
   }
 
@@ -141,17 +265,8 @@ namespace pan
   void
   CertStore :: remove (const Quark& server)
   {
-    debug("remove cert "<<server);
-    if (_cert_to_server.count(server))
-    {
-      _cert_to_server.erase(server);
-      _certs.erase(server);
-      remove_hard(server);
-      Quark setme;
-      _data.set_server_cert(server, "");
-      _data.save_server_info(server);
-    }
-
+    _cert_to_server.erase(server);
+    remove_hard (server);
   }
 
   CertStore :: CertStore (Data& data): _data(data)
@@ -161,70 +276,69 @@ namespace pan
     _path = buf;
     if (!file::ensure_dir_exists (buf))
     {
-      std::cerr<<"Error initializing certstore. Check your permissions for the directory \"ssl-certs\" and main subfolder in your home directory! Fatal, exiting.";
+      std::cerr<<"Error initializing certstore. Check your permissions for the pan2 subfolder \"ssl-certs\" and "
+                 "the pan2 folder in your Home directory! Fatal, exiting.";
       file::print_file_info(std::cerr, buf);
       exit(EXIT_FAILURE);
     }
+
+    gnutls_certificate_allocate_credentials (&_creds);
+    gnutls_certificate_set_verify_function(_creds, verify_callback);
+
   }
 
   CertStore :: ~CertStore ()
-  {}
+  {
+    gnutls_certificate_free_credentials (_creds);
+    foreach (certs_m, _cert_to_server, it)
+      gnutls_x509_crt_deinit(it->second);
+  }
 
 
 
   bool
-  CertStore :: add(X509* cert, const Quark& server)
+  CertStore :: add (gnutls_x509_crt_t cert, const Quark& server)
   {
-    debug(server<<" "<<cert);
+    debug("adding server cert "<<server<<" "<<cert);
     if (!cert || server.empty()) return false;
-    debug(X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0));
-    X509_STORE_add_cert(get_store(),cert);
 
     std::string addr; int port;
     _data.get_server_addr(server, addr, port);
     _certs.insert(server);
     _cert_to_server[server] = cert;
 
-    debug(server<<" "<<addr);
+    const char* buf(build_cert_name(addr).c_str());
 
-    const char* buf(build_cert_name(addr.c_str()).c_str());
     _data.set_server_cert(server, buf);
+
     _data.save_server_info(server);
 
     FILE * fp = fopen(buf, "wb");
     if (!fp) return false;
-    if (!PEM_write_X509(fp, cert)) { fclose(fp); return false; }
+    size_t outsize;
+
+    /* make up for dumbness of this function */
+    gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, NULL, &outsize);
+    char* out = new char[outsize];
+    gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, out, &outsize);
+
+    fputs ((const char*)out, fp);
+
     fclose(fp);
-    chmod (buf, 0600);
 
-    debug(server<<" "<<buf);
+    delete out;
+
+    chmod (buf, 0600);
 
+    gnutls_certificate_set_x509_trust(_creds, &cert, 1); // for now, only 1 is saved
     valid_cert_added(cert, server.c_str());
+
     return true;
   }
 
 
-  X509*
-  CertStore :: get_cert_to_server(const Quark& server) 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;
-
-    if (_cert_to_server.count(serv))
-      ret = _cert_to_server.find(serv)->second;
-    return ret;
-  }
-
   std::string
-  CertStore :: build_cert_name(std::string host)
+  CertStore :: build_cert_name(std::string& host)
   {
     char buf[2048];
     g_snprintf(buf,sizeof(buf),"%s%cssl_certs%c%s.pem",file::get_pan_home().c_str(),
diff --git a/pan/data/cert-store.h b/pan/data/cert-store.h
index b60d351..9554ecc 100644
--- a/pan/data/cert-store.h
+++ b/pan/data/cert-store.h
@@ -23,13 +23,9 @@
 #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>
+#ifdef HAVE_GNUTLS
+  #include <gnutls/gnutls.h>
+  #include <gnutls/x509.h>
 #endif
 
 #include <pan/data/data.h>
@@ -49,70 +45,50 @@ namespace pan
 
   class CertStore
   {
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
     public:
       CertStore (Data& data) ;
       virtual ~CertStore () ;
 
     private:
-      SSL_CTX* _ctx;
       typedef std::set<Quark> certs_t;
-      typedef std::set<X509*> certs_s;
       certs_t _certs;
-      certs_s _ignores;
-      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;
-      std::vector<SSL_SESSION*> _sessions;
       certs_t _blacklist;
+      typedef std::map<Quark,gnutls_x509_crt_t> certs_m;
+      typedef std::pair<Quark,gnutls_x509_crt_t> certs_p;
+      std::string _path;
+      certs_m _cert_to_server;
       Data& _data;
 
+      gnutls_certificate_credentials_t _creds;
+
     public:
-      SSL_CTX* get_ctx() { return _ctx; }
-      X509_STORE* get_store() const { return _store; }
-      int get_all_certs_from_disk(std::set<X509*>& setme);
-      X509* get_cert_to_server(const Quark& server) const;
-      SSL_SESSION* get_session()
-      {
-        SSL_SESSION* ret(0);
-        if (!_sessions.empty())
-        {
-          ret = _sessions.back();
-          _sessions.pop_back();
-        }
-        return ret;
-      }
-      void add_session (SSL_SESSION* s)
-      {
-        if (!s) return;
-        _sessions.push_back(s);
-      }
+
+      int get_all_certs_from_disk();
 
       bool in_blacklist (const Quark& s)
       {
         return _blacklist.count(s);
       }
+
       void blacklist (const Quark& s)
       {
         _blacklist.insert(s);
       }
+
       void whitelist (const Quark& s)
       {
         _blacklist.erase(s);
       }
 
-      void ignore (X509* cert)
+      gnutls_x509_crt_t get_cert_to_server (const Quark& s)
       {
-        _ignores.insert(cert);
-      }
-
-      bool is_ignored(X509* c)
-      {
-        foreach (certs_s, _ignores, it)
-          if (X509_cmp(c, *it)==0) return true;
-        return false;
+        if (_cert_to_server.count(s) > 0)
+          return _cert_to_server[s];
+        std::cerr<<"server "<<s<<" cert to server "<<_cert_to_server.count(s)<<"\n";
+        foreach (certs_m, _cert_to_server, it)
+          std::cerr<<it->first<<" "<<it->second<<"\n";
+        return 0;
       }
 
     private:
@@ -120,18 +96,20 @@ namespace pan
 
     public:
 
-      bool add(X509*, const Quark&) ;
+      bool add (gnutls_x509_crt_t, const Quark&) ;
       void remove (const Quark&);
       bool exist (const Quark& q) { return (_certs.count(q) > 0); }
 
-      static std::string build_cert_name(std::string host);
+      static std::string build_cert_name(std::string& host);
+
+      gnutls_certificate_credentials_t get_creds() { return _creds; }
 
       struct Listener
       {
         virtual ~Listener() {}
         /* functions that other listeners listen on */
-        virtual void on_verify_cert_failed (X509* cert UNUSED, std::string server UNUSED, std::string cert_name UNUSED, int nr UNUSED) = 0;
-        virtual void on_valid_cert_added (X509* cert UNUSED, std::string server UNUSED) = 0;
+        virtual void on_verify_cert_failed (gnutls_x509_crt_t cert UNUSED, std::string server UNUSED, int nr UNUSED) = 0;
+        virtual void on_valid_cert_added   (gnutls_x509_crt_t cert UNUSED, std::string server UNUSED) = 0;
       };
 
       typedef std::set<Listener*> listeners_t;
@@ -141,36 +119,29 @@ namespace pan
       void remove_listener (Listener * l) { _listeners.erase(l);  }
 
       /* notify functions for listener list */
-      void verify_failed (X509* c, std::string server, std::string cn, int nr)
+      void verify_failed (gnutls_x509_crt_t c, std::string server, int nr)
       {
-        debug("verify failed listeners");
         for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; ++it)
-          (*it)->on_verify_cert_failed (c, server, cn, nr);
+          (*it)->on_verify_cert_failed (c, server, nr);
       }
 
-      void valid_cert_added (X509* c, std::string server)
+      void valid_cert_added (gnutls_x509_crt_t 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(); }
+    public:
+      void init();
   };
 
   struct mydata_t {
-   SSL_CTX* ctx;
-   int depth;
-   int ignore_all;
+   gnutls_session_t session;
+   Quark host;
+   Quark hostname_full;
    CertStore* cs;
-   std::string server;
-   std::string cert_name;
-   CertStore::Listener* l;
-
+   int always_trust;
+  };
 #else
 
   public:
@@ -185,10 +156,8 @@ namespace pan
     {
       virtual ~Listener() {}
     };
-
-#endif   // HAVE_OPENSSL
   };
-
+#endif   // HAVE_GNUTLS
 }
 
 
diff --git a/pan/data/data.h b/pan/data/data.h
index 02d9667..3ba0db5 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -194,10 +194,11 @@ namespace pan
          int max_connections;
          int rank;
          int ssl_support;
+         int trust;
          typedef sorted_vector<Quark,true,AlphabeticalQuarkOrdering> groups_t;
          groups_t groups;
 
-         Server(): port(STD_NNTP_PORT), article_expiration_age(31), max_connections(2), rank(1), ssl_support(0) {}
+         Server(): port(STD_NNTP_PORT), article_expiration_age(31), max_connections(2), rank(1), ssl_support(0), trust(0) {}
       };
 
     protected:
@@ -230,6 +231,8 @@ namespace pan
 
       virtual const Server* find_server (const Quark& server) const = 0;
 
+      virtual Server* find_server (const Quark& server) = 0;
+
       virtual quarks_t get_servers () const = 0;
 
       virtual void delete_server (const Quark& server) = 0;
@@ -240,6 +243,9 @@ namespace pan
                                     const StringView  & username,
                                     const StringView  & password) = 0;
 
+      virtual void set_server_trust (const Quark      & servername,
+                                     const int          setme) = 0;
+
       virtual void set_server_addr (const Quark       & server,
                                     const StringView  & address,
                                     const int           port) = 0;
@@ -255,6 +261,8 @@ namespace pan
                                     std::string   & setme_username,
                                     std::string   & setme_password) const = 0;
 
+      virtual bool get_server_trust (const Quark  & servername, int&) const = 0;
+
       virtual std::string get_server_cert (const Quark & server) const = 0;
 
       virtual int get_server_limits (const Quark & server) const = 0;
diff --git a/pan/data/server-info.h b/pan/data/server-info.h
index 81796e8..6ac87e5 100644
--- a/pan/data/server-info.h
+++ b/pan/data/server-info.h
@@ -48,6 +48,9 @@ namespace pan
                                     const StringView  & username,
                                     const StringView  & password) = 0;
 
+      virtual void set_server_trust (const Quark       & servername,
+                                     const int           setme) = 0;
+
       virtual void set_server_addr (const Quark       & servername,
                                     const StringView  & address,
                                     const int           port) = 0;
@@ -74,6 +77,8 @@ namespace pan
                                     std::string   & setme_username,
                                     std::string   & setme_password) const=0;
 
+      virtual bool get_server_trust (const Quark  & servername, int&) const = 0;
+
       virtual bool get_server_addr (const Quark   & servername,
                                     std::string   & setme_address,
                                     int           & setme_port) const = 0;
diff --git a/pan/gui/Makefile.am b/pan/gui/Makefile.am
index 53ae102..0314f05 100644
--- a/pan/gui/Makefile.am
+++ b/pan/gui/Makefile.am
@@ -1,5 +1,5 @@
 AM_CPPFLAGS = -I top_srcdir@ @GTKSPELL_CFLAGS@ @GTK_CFLAGS@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ \
-							@OPENSSL_CFLAGS@ @LIBNOTIFY_CFLAGS@ @LIBGNOME_KEYRING_1_CFLAGS@ -DPANLOCALEDIR=\""$(panlocaledir)"\"
+							@GNUTLS_CFLAGS@ @LIBNOTIFY_CFLAGS@ @LIBGNOME_KEYRING_1_CFLAGS@ -DPANLOCALEDIR=\""$(panlocaledir)"\"
 
 noinst_LIBRARIES = libpangui.a
 
@@ -29,6 +29,7 @@ libpangui_a_SOURCES = \
  profiles-dialog.cc \
  render-bytes.cc \
  save-ui.cc \
+ save-attach-ui.cc \
  score-add-ui.cc \
  score-view-ui.cc \
  server-ui.cc \
@@ -70,6 +71,7 @@ noinst_HEADERS = \
  progress-view.h \
  render-bytes.h \
  save-ui.h \
+ save-attach-ui.h \
  score-add-ui.h \
  score-view-ui.h \
  server-ui.h \
@@ -97,7 +99,7 @@ endif
 
 pan_SOURCES = gui.cc pan.cc $(WINRC)
 pan_LDADD = ./libpangui.a $(WINRCOBJ) ../data-impl/libpandata.a ../tasks/libtasks.a ../data/libdata.a ../usenet-utils/libusenetutils.a ../general/libgeneralutils.a \
-            ../../uulib/libuu.a @GTKSPELL_LIBS@ @GTK_LIBS@ @GMIME_LIBS@ @GLIB_LIBS@ @OPENSSL_LIBS@ @LIBNOTIFY_LIBS@ @LIBGNOME_KEYRING_1_LIBS@
+            ../../uulib/libuu.a @GTKSPELL_LIBS@ @GTK_LIBS@ @GMIME_LIBS@ @GLIB_LIBS@ @GNUTLS_LIBS@ @LIBNOTIFY_LIBS@ @LIBGNOME_KEYRING_1_LIBS@
 if HAVE_WIN32
 pan_LDFLAGS = -mwindows
 endif
diff --git a/pan/gui/body-pane.cc b/pan/gui/body-pane.cc
index ea8697f..94b144e 100644
--- a/pan/gui/body-pane.cc
+++ b/pan/gui/body-pane.cc
@@ -45,6 +45,7 @@ extern "C" {
 #include "xface.h"
 #include "url.h"
 #include "gtk_compat.h"
+#include "save-attach-ui.h"
 
 #define FIRST_PICTURE "first-picture"
 
@@ -57,6 +58,11 @@ using namespace pan;
 namespace
 {
 
+  GtkWindow* get_window (GtkWidget* w)
+  {
+    return GTK_WINDOW (gtk_widget_get_toplevel (w));
+  }
+
   enum Icons
   {
     ICON_SIG_OK,
@@ -366,6 +372,7 @@ namespace
                                const GtkAllocation  * size,
                                GtkTextTag           * apply_tag)
   {
+
     const int begin_offset (gtk_text_iter_get_offset (iter));
 
     GdkPixbuf * original (0);
@@ -528,8 +535,6 @@ namespace
 
     g_return_val_if_fail (GTK_IS_TEXT_VIEW(w), false);
 
-//    if (event->keyval==GDK_KEY_Enter) return false;
-
     const bool up = event->keyval==GDK_KEY_Up || event->keyval==GDK_KEY_KP_Up;
     const bool down = event->keyval==GDK_KEY_Down || event->keyval==GDK_KEY_KP_Down;
 
@@ -713,6 +718,7 @@ namespace
                              bool                  do_markup,
                              bool                  do_urls)
   {
+
     g_return_if_fail (buffer!=0);
     g_return_if_fail (GTK_IS_TEXT_BUFFER(buffer));
 
@@ -999,16 +1005,14 @@ BodyPane :: append_part (GMimeObject * parent, GMimeObject * obj, GtkAllocation
     is_done = true;
   }
 
-  // otherwise, bitch and moan.
+  // otherwise, add to list of attachments
   if (!is_done) {
     const char * filename = g_mime_part_get_filename (part);
     char * pch = (filename && *filename)
-      ? g_strdup_printf (_("\nAttachment not shown: MIME type %s/%s; filename %s\n"), type->type, type->subtype, filename)
-      : g_strdup_printf (_("\nAttachment not shown: MIME type %s/%s\n"), type->type, type->subtype);
-    GtkTextIter iter;
-    gtk_text_buffer_get_end_iter (_buffer, &iter);
-    gtk_text_buffer_insert (_buffer, &iter, pch, -1);
-    g_free (pch);
+      ? g_strdup_printf ("%s", filename)
+      : g_strdup_printf (_("Unnamed File"));
+    add_attachment_to_toolbar (pch);
+    _freeme.insert(pch);
   }
 }
 
@@ -1184,11 +1188,12 @@ BodyPane :: set_text_from_message (GMimeMessage * message)
     g_free (headers);
   }
 
+  clear_attachments();
+
   // FIXME: need to set a mark here so that when user hits follow-up,
   // the all-headers don't get included in the followup
 
   // set the text buffer...
-
   if (message)
     g_mime_message_foreach (message, foreach_part_cb, this);
 
@@ -1319,9 +1324,10 @@ BodyPane :: clear ()
   if (_message)
     g_object_unref (_message);
   _message = 0;
+
   refresh ();
-  update_sig_valid(-1);
 
+  update_sig_valid(-1);
 }
 
 void
@@ -1415,6 +1421,7 @@ BodyPane :: text_size_allocated_idle_cb (gpointer pane)
 void
 BodyPane :: text_size_allocated_idle ()
 {
+
   // prevent oscillation
   const bool old_h (_hscroll_visible);
   const bool old_v (_vscroll_visible);
@@ -1437,6 +1444,7 @@ BodyPane :: text_size_allocated_idle ()
     if (gtk_text_iter_begins_tag (&iter, tag))
     {
       gtk_widget_get_allocation(_text, &aloc);
+      // BUG : resize
       resize_picture_at_iter (buf, &iter, fullsize, &aloc, tag);
     }
     if (!gtk_text_iter_forward_char (&iter))
@@ -1496,25 +1504,178 @@ BodyPane :: populate_popup (GtkTextView *v G_GNUC_UNUSED, GtkMenu *m)
   gtk_menu_shell_prepend (GTK_MENU_SHELL(m), mi);
 }
 
+namespace
+{
+  static gboolean
+  attachment_clicked_cb (GtkWidget *w, GdkEventButton *event, gpointer p)
+  {
+    BodyPane * bp = static_cast<BodyPane*>(p);
+    const gchar * fn = (char*)g_object_get_data (G_OBJECT(w), "filename");
+    if (!fn) return TRUE;
+
+    bp->_current_attachment = fn;
+
+    if (event->type == GDK_BUTTON_PRESS && event->button == 3)
+    {
+      gtk_menu_popup
+      (GTK_MENU(bp->_menu), NULL, NULL, NULL, NULL,
+      (event ? event->button : 0),
+      (event ? event->time : 0));
+    }
+
+    return TRUE;
+  }
+}
+
+void
+BodyPane :: menu_clicked (const MenuSelection& ms)
+{
+  std::vector<Article> copies;
+  copies.push_back(_article);
+  GroupPrefs _group_prefs;
+  SaveAttachmentsDialog * dialog = new SaveAttachmentsDialog (
+    _prefs, _group_prefs, _data, _data, _cache,
+    _data, _queue, get_window(_root),
+    _header_pane->get_group(), copies,
+    ms == MENU_SAVE_ALL ? TaskArticle::SAVE_ALL : TaskArticle::SAVE_AS,
+    _current_attachment);
+  gtk_widget_show (dialog->root());
+}
+
+void
+BodyPane :: menu_clicked_as_cb (GtkWidget* w, gpointer ptr)
+{
+  BodyPane* p = static_cast<BodyPane*>(ptr);
+  if (!p) return;
+  p->menu_clicked(MENU_SAVE_AS);
+}
+
+void
+BodyPane :: menu_clicked_all_cb (GtkWidget* w, gpointer ptr)
+{
+  BodyPane* p = static_cast<BodyPane*>(ptr);
+  if (!p) return;
+  p->menu_clicked(MENU_SAVE_ALL);
+}
+
+
+GtkWidget*
+BodyPane :: new_attachment (const char* filename)
+{
+  if (!filename) return NULL;
+
+  GtkWidget* w = gtk_hbox_new(false, 0);
+  GtkWidget* attachment = gtk_label_new(filename);
+  GtkWidget * image = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
+
+  gtk_label_set_selectable (GTK_LABEL(attachment), true);
+  gtk_label_set_ellipsize (GTK_LABEL(attachment), PANGO_ELLIPSIZE_MIDDLE);
+
+  GtkWidget *event_box = gtk_event_box_new ();
+  gtk_container_add (GTK_CONTAINER (event_box), image);
+  g_object_set_data (G_OBJECT(event_box), "filename", (gpointer)filename);
+
+  gtk_box_pack_start (GTK_BOX(w), event_box, false, false, 0);
+  gtk_box_pack_start (GTK_BOX(w), attachment, false, false, 0);
+
+  g_signal_connect(event_box, "button_press_event", G_CALLBACK(attachment_clicked_cb), this);
+
+  return w;
+}
+
+
+void
+BodyPane :: clear_attachments()
+{
+  _cur_col = 0;
+  _cur_row = 0;
+  _attachments = 0;
+  _current_attachment = 0;
+
+  {
+    gtk_container_remove (GTK_CONTAINER (_att_frame), _att_toolbar);
+    if (G_IS_OBJECT(_att_toolbar)) g_object_unref(_att_toolbar);
+    (void)create_attachments_toolbar(_att_frame);
+  }
+
+}
+
+void
+BodyPane :: add_attachment_to_toolbar (const char* fn)
+{
+  if (!fn) return;
+
+  GtkWidget* w = new_attachment(fn);
+  guint cols(0), rows(0);
+  gtk_table_get_size (GTK_TABLE(_att_toolbar), &rows, &cols);
+
+  if (_attachments % 4 == 0 && _attachments != 0)
+  {
+    gtk_table_resize (GTK_TABLE(_att_toolbar), rows+1, cols);
+    ++_cur_row;
+    _cur_col = 0;
+  }
+
+  gtk_table_attach_defaults (GTK_TABLE(_att_toolbar), w, _cur_col, _cur_col+1, _cur_row,_cur_row+1);
+
+  ++_attachments;
+  ++_cur_col;
+
+  gtk_widget_show_all(_att_toolbar);
+}
+
+GtkWidget*
+BodyPane :: create_attachments_toolbar (GtkWidget* frame)
+{
+
+  _cur_col = 0;
+  _cur_row = 0;
+
+  GtkWidget * w = _att_toolbar = gtk_table_new(4,1,TRUE);
+  gtk_widget_set_size_request (w, -1, 20);
+  gtk_table_set_col_spacings (GTK_TABLE(w), PAD);
+  gtk_container_add (GTK_CONTAINER (frame), w);
+  gtk_widget_show_all (frame);
+
+  return frame;
+}
+
 /***
 ****
 ***/
 
-BodyPane :: BodyPane (Data& data, ArticleCache& cache, Prefs& prefs):
+BodyPane :: BodyPane (Data& data, ArticleCache& cache, Prefs& prefs, GroupPrefs & gp, Queue& q, HeaderPane* hp):
   _prefs (prefs),
+  _group_prefs(gp),
+  _queue(q),
+  _header_pane(hp),
   _data (data),
   _cache (cache),
   _hscroll_visible (false),
   _vscroll_visible (false),
   _message (0),
-  _gpgerr(GPG_DECODE)
+  _gpgerr(GPG_DECODE),
+  _attachments(0),
+  _current_attachment(0)
 {
 
   for (guint i=0; i<NUM_ICONS; ++i)
     icons[i].pixbuf = gdk_pixbuf_new_from_inline (-1, icons[i].pixbuf_txt, FALSE, 0);
 
+  // signature pgp valid/invalid icon
   _sig_icon = gtk_image_new();
 
+  // menu for popup menu for attachments
+  _menu = gtk_menu_new ();
+  GtkWidget* l = gtk_menu_item_new_with_label(_("Save attachment as ...."));
+  g_signal_connect (l, "activate", G_CALLBACK(menu_clicked_as_cb), this);
+  gtk_menu_shell_append (GTK_MENU_SHELL(_menu), l);
+  l =  gtk_menu_item_new_with_label(_("Save all attachments"));
+  _selection = MENU_SAVE_ALL;
+  g_signal_connect (l, "activate", G_CALLBACK(menu_clicked_all_cb), this);
+  gtk_menu_shell_append (GTK_MENU_SHELL(_menu),l);
+  gtk_widget_show_all(_menu);
+
   GtkWidget * vbox = gtk_vbox_new (false, PAD);
   gtk_container_set_resize_mode (GTK_CONTAINER(vbox), GTK_RESIZE_QUEUE);
 
@@ -1575,6 +1736,11 @@ BodyPane :: BodyPane (Data& data, ArticleCache& cache, Prefs& prefs):
   gtk_widget_show_all (vbox);
   gtk_box_pack_start (GTK_BOX(vbox), _scroll, true, true, 0);
 
+  // add a toolbar for attachments
+  GtkWidget * frame = _att_frame = gtk_frame_new (_("Attachments"));
+  gtk_widget_set_size_request (frame, -1, 20);
+  gtk_box_pack_start (GTK_BOX(vbox), create_attachments_toolbar(frame), false, false, 0);
+
   // set up the buffer tags
   _buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(_text));
   set_text_buffer_tags (_buffer, _prefs);
@@ -1610,6 +1776,9 @@ BodyPane :: ~BodyPane ()
 
   for (int i=0; i<NUM_ICONS; ++i)
     g_object_unref (icons[i].pixbuf);
+
+  foreach (std::set<char*>, _freeme, it)
+    g_free(*it);
 }
 
 
diff --git a/pan/gui/body-pane.h b/pan/gui/body-pane.h
index 8ced5d0..0bdda2f 100644
--- a/pan/gui/body-pane.h
+++ b/pan/gui/body-pane.h
@@ -27,7 +27,9 @@
 #include <pan/data/article.h>
 #include <pan/data/article-cache.h>
 #include <pan/data/data.h>
+#include <pan/gui/header-pane.h>
 #include "prefs.h"
+#include "group-prefs.h"
 
 namespace pan
 {
@@ -39,6 +41,9 @@ namespace pan
   {
     private:
       Prefs& _prefs;
+      GroupPrefs& _group_prefs;
+      Queue& _queue;
+      HeaderPane* _header_pane;
       Data& _data;
       ArticleCache& _cache;
       GtkWidget* _sig_icon;
@@ -53,7 +58,7 @@ namespace pan
                                        gpointer    data);
 
     public:
-      BodyPane (Data&, ArticleCache&, Prefs&);
+      BodyPane (Data&, ArticleCache&, Prefs&, GroupPrefs&, Queue&, HeaderPane*);
       ~BodyPane ();
       GtkWidget* root () { return _root; }
       GtkWidget* get_default_focus_widget() { return _text; }
@@ -83,6 +88,14 @@ namespace pan
         return _message;
       }
 
+
+    public:
+      enum MenuSelection
+      {
+        MENU_SAVE_AS,
+        MENU_SAVE_ALL
+      };
+
     public:
       void set_character_encoding (const char * character_encoding);
 
@@ -107,11 +120,17 @@ namespace pan
       void populate_popup (GtkTextView*, GtkMenu*);
       static void copy_url_cb (GtkMenuItem*, gpointer);
       void copy_url ();
+
+      GtkWidget* create_attachments_toolbar(GtkWidget*);
+      void add_attachment_to_toolbar (const char* fn);
+      void clear_attachments();
+      GtkWidget* new_attachment (const char* filename);
+
       static gboolean mouse_button_pressed_cb (GtkWidget*, GdkEventButton*, gpointer);
       gboolean mouse_button_pressed (GtkWidget*, GdkEventButton*);
-
-      /* updated with values from gmimemessage */
-   public:
+      void menu_clicked (const MenuSelection& ms);
+      static void menu_clicked_as_cb (GtkWidget* w, gpointer p);
+      static void menu_clicked_all_cb (GtkWidget* w, gpointer p);
 
     private:
       std::string _hover_url;
@@ -125,13 +144,28 @@ namespace pan
       GtkWidget * _root;
       GtkWidget * _text;
       GtkWidget * _scroll;
+      GtkWidget * _att_toolbar;
+      GtkWidget * _att_frame;
       bool _hscroll_visible;
       bool _vscroll_visible;
       Article _article;
       GMimeMessage * _message;
       TextMassager _tm;
       std::string _charset;
+
       GPGDecErr _gpgerr;
+
+      int _attachments;
+      int _cur_col, _cur_row;
+      std::set<char*> _freeme;
+      MenuSelection _selection;
+
+    public:
+      const char* _current_attachment;
+
+
+    public:
+      GtkWidget* _menu;
   };
 }
 
diff --git a/pan/gui/e-cte-dialog.c b/pan/gui/e-cte-dialog.c
index c452d2d..efa36e6 100644
--- a/pan/gui/e-cte-dialog.c
+++ b/pan/gui/e-cte-dialog.c
@@ -20,9 +20,6 @@
 
 #include "e-cte-dialog.h"
 
-#include <gmime/gmime.h>
-#include <glib/gi18n.h>
-
 /**
  * e_cte_dialog:
  * @title: title for the dialog box
@@ -44,11 +41,11 @@ e_cte_dialog (const char *title, const char *prompt, GMimeContentEncoding now, G
   GMimeContentEncoding ret = GMIME_CONTENT_ENCODING_8BIT;
 
 	dialog = GTK_DIALOG (gtk_dialog_new_with_buttons (title,
-							  parent,
-							  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-							  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-							  GTK_STOCK_OK, GTK_RESPONSE_OK,
-							  NULL));
+                       parent,
+                       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                       GTK_STOCK_OK, GTK_RESPONSE_OK,
+                       NULL));
 
 	//gtk_dialog_set_has_separator (dialog, FALSE);
 	gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
diff --git a/pan/gui/e-cte-dialog.h b/pan/gui/e-cte-dialog.h
index fa610d5..5bd5100 100644
--- a/pan/gui/e-cte-dialog.h
+++ b/pan/gui/e-cte-dialog.h
@@ -4,6 +4,7 @@
 #include <config.h>
 #include <gmime/gmime.h>
 #include <gtk/gtk.h>
+#include <glib/gi18n.h>
 
 G_BEGIN_DECLS
 
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index 74462e4..4c8f0d7 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -207,7 +207,7 @@ GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs):
 
   _group_pane = new GroupPane (*this, data, _prefs);
   _header_pane = new HeaderPane (*this, data, _queue, _cache, _prefs, _group_prefs, *this, *this);
-  _body_pane = new BodyPane (data, _cache, _prefs);
+  _body_pane = new BodyPane (data, _cache, _prefs, _group_prefs, _queue, _header_pane);
 
   std::string path = "/ui/main-window-toolbar";
   GtkWidget * toolbar = gtk_ui_manager_get_widget (_ui_manager, path.c_str());
@@ -1073,7 +1073,7 @@ void GUI :: do_show_servers_dialog ()
 
 void GUI :: do_show_sec_dialog ()
 {
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
   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);
@@ -1186,7 +1186,6 @@ GUI :: do_last_flag ()
 void
 GUI :: do_invert_selection ()
 {
-//  std::cerr<<__LINE__<< " "<<__FILE__<<" : implement me.\n";
   _header_pane->invert_selection();
 }
 
@@ -1372,21 +1371,22 @@ bool GUI::deletion_confirmation_dialog()
   return ret;
 }
 
-#ifdef HAVE_OPENSSL
-bool GUI :: confirm_accept_new_cert_dialog(GtkWindow * parent, X509* cert, const Quark& server)
+#ifdef HAVE_GNUTLS
+bool GUI :: confirm_accept_new_cert_dialog(GtkWindow * parent, gnutls_x509_crt_t cert, const Quark& server)
 {
-  bool ret(false);
 
-  char buf[4096];
+  char buf[4096*256];
   std::string host; int port;
   _data.get_server_addr(server,host,port);
-  pretty_print_x509(buf,sizeof(buf), host, cert,true);
+  pretty_print_x509(buf,sizeof(buf), host, cert, true);
   GtkWidget * d = gtk_message_dialog_new (
     parent,
     GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
     GTK_MESSAGE_WARNING,
     GTK_BUTTONS_NONE, NULL);
 
+  gtk_dialog_add_button (GTK_DIALOG(d), _("Always trust"), -66);
+
   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),
@@ -1395,10 +1395,17 @@ bool GUI :: confirm_accept_new_cert_dialog(GtkWindow * parent, X509* cert, const
                           NULL);
   gtk_dialog_set_default_response (GTK_DIALOG(d), GTK_RESPONSE_NO);
 
-  debug("confirm cert gui");
-  ret = gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_YES;
+  gint ret_code = gtk_dialog_run (GTK_DIALOG(d));
+
+  if (ret_code == -66)
+  {
+    _data.set_server_trust (server, 1);
+    _data.save_server_info(server);
+  }
+
   gtk_widget_destroy(d);
-  return ret;
+
+  return ret_code == GTK_RESPONSE_YES || ret_code == -66;
 }
 #endif
 
@@ -2170,7 +2177,7 @@ GUI :: on_prefs_string_changed (const StringView& key, const StringView& value)
     prev_path.assign (value.str, value.len);
 }
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
 
 void
 GUI :: do_show_cert_failed_dialog(VerifyData* data)
@@ -2194,16 +2201,14 @@ GUI :: show_cert_failed_cb(gpointer gp)
 }
 
 void
-GUI :: on_verify_cert_failed(X509* cert, std::string server, std::string cert_name, int nr)
+GUI :: on_verify_cert_failed(gnutls_x509_crt_t cert, std::string server, int nr)
 {
-  debug("on verify failed GUI ("<<cert<<") ("<<cert_name<<") ("<<server<<")");
+  debug("on verify failed GUI ("<<cert<<") ("<<server<<")");
   if (!cert || server.empty()) return;
 
-  debug(X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0));
   VerifyData* data = new VerifyData();
   data->cert = cert;
   data->server = server;
-  data->cert_name = cert_name;
   data->nr = nr;
   data->gui = this;
   g_idle_add(show_cert_failed_cb, data);
@@ -2211,10 +2216,9 @@ GUI :: on_verify_cert_failed(X509* cert, std::string server, std::string cert_na
 }
 
 void
-GUI :: on_valid_cert_added (X509* cert, std::string server)
+GUI :: on_valid_cert_added (gnutls_x509_crt_t cert, std::string server)
 {
   /* whitelist to make avaible for nntp-pool */
-  X509_free(cert); // refcount -1
   _certstore.whitelist(server);
 }
 
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index 9bb36ad..4ceb442 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -66,8 +66,8 @@ namespace pan
 
       struct VerifyData
       {
-#ifdef HAVE_OPENSSL
-        X509* cert;
+#ifdef HAVE_GNUTLS
+        gnutls_x509_crt_t cert;
 #endif
         std::string server;
         std::string cert_name;
@@ -182,9 +182,9 @@ namespace pan
       virtual void do_refresh_groups ();
       virtual void do_subscribe_selected_groups ();
       virtual void do_unsubscribe_selected_groups ();
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
       void do_show_cert_failed_dialog(VerifyData* data);
-      bool confirm_accept_new_cert_dialog(GtkWindow*, X509*, const Quark&);
+      bool confirm_accept_new_cert_dialog(GtkWindow*, gnutls_x509_crt_t, const Quark&);
 #endif
 
       void step_bookmarks(int step);
@@ -205,10 +205,10 @@ namespace pan
       virtual void on_queue_size_changed (Queue&, int active, int total);
       virtual void on_queue_online_changed (Queue&, bool online);
       virtual void on_queue_error (Queue&, const StringView& message);
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
     private:  // CertStore::Listener
-      virtual void on_verify_cert_failed(X509*, std::string, std::string, int);
-      virtual void on_valid_cert_added (X509*, std::string);
+      virtual void on_verify_cert_failed(gnutls_x509_crt_t, std::string, int);
+      virtual void on_valid_cert_added (gnutls_x509_crt_t, std::string);
 #endif
     private: // Log::Listener
       virtual void on_log_entry_added (const Log::Entry& e);
@@ -280,9 +280,8 @@ namespace pan
       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[]);
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
       static gboolean show_cert_failed_cb(gpointer gp);
-//      static void cert_failed_dty(gpointer gp);
 #endif
     public:
       GtkUIManager* get_ui_manager() { return _ui_manager; }
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index f7b20c6..013d63c 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -47,7 +47,7 @@ extern "C" {
 #include <pan/general/file-util.h>
 #include <pan/general/worker-pool.h>
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
   #include <pan/tasks/socket-impl-openssl.h>
 #endif
 
@@ -841,7 +841,7 @@ main (int argc, char *argv[])
                            &error);
     if (!error)
     {
-      std::cerr<<"Added "<<nzb_files.size()<<" files to the queue. Exiting.\n";
+      std::cout<<"Added "<<nzb_files.size()<<" files to the queue. Exiting.\n";
       exit(EXIT_SUCCESS);
     }
   #endif
diff --git a/pan/gui/post-ui.cc b/pan/gui/post-ui.cc
index 4db69a5..cea6721 100644
--- a/pan/gui/post-ui.cc
+++ b/pan/gui/post-ui.cc
@@ -1509,9 +1509,10 @@ PostUI :: new_message_from_ui (Mode mode, bool copy_body)
       ? GNKSA::generate_message_id (profile.fqdn)
       : GNKSA::generate_message_id_from_email_address (profile.address);
     pan_g_mime_message_set_message_id (msg, message_id.c_str());
+
   }
 
-    // body & charset
+  // body & charset
   {
     std::string body;
     if (copy_body) body = get_body();
diff --git a/pan/gui/save-attach-ui.cc b/pan/gui/save-attach-ui.cc
new file mode 100644
index 0000000..5145bb7
--- /dev/null
+++ b/pan/gui/save-attach-ui.cc
@@ -0,0 +1,320 @@
+/* -*- 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 <config.h>
+extern "C" {
+  #include <glib/gi18n.h>
+  #include <gtk/gtk.h>
+}
+#include <pan/general/debug.h>
+#include <pan/general/macros.h>
+#include <pan/icons/pan-pixbufs.h>
+#include <pan/tasks/task-article.h>
+#include <pan/tasks/queue.h>
+#include <pan/usenet-utils/text-massager.h>
+#include "hig.h"
+#include "pad.h"
+#include "pan-file-entry.h"
+#include "save-attach-ui.h"
+#include "gtk_compat.h"
+
+using namespace pan;
+
+namespace
+{
+  std::string expand_download_dir (const char * dir, const StringView& group)
+  {
+    std::string val (dir);
+    std::string::size_type pos;
+
+    while (((pos = val.find ("%g"))) != val.npos)
+      val.replace (pos, 2, group.str, group.len);
+
+    std::string tmp (group.str, group.len);
+    std::replace (tmp.begin(), tmp.end(), '.', G_DIR_SEPARATOR);
+    while (((pos = val.find ("%G"))) != val.npos)
+      val.replace (pos, 2, tmp);
+
+    return val;
+  }
+
+  std::string expand_download_dir_subject (const char * dir, const char * subjectline, const std::string &sep)
+  {
+    std::string val (dir);
+    std::string sub (subject_to_path(subjectline, false, sep));
+    std::string::size_type pos;
+
+    while (((pos = val.find ("%s"))) != val.npos)
+      val.replace (pos, 2, sub);
+
+    sub = subject_to_path(subjectline, true, sep);
+    while (((pos = val.find ("%S"))) != val.npos)
+      val.replace (pos, 2, sub);
+
+    return val;
+  }
+
+
+  void
+  show_group_substitution_help_dialog (gpointer window)
+  {
+    const char * str = _("%g - group as one directory (alt.binaries.pictures.trains)\n"
+                         "%G - group as nested directory (/alt/binaries/pictures/trains)\n"
+                         "%s - subject line excerpt\n"
+                         "%S - subject line\n"
+                         " \n"
+                         "\"/home/user/News/Pan/%g\" becomes\n"
+                         "\"/home/user/News/Pan/alt.binaries.pictures.trains\", and\n"
+                         "\"/home/user/News/Pan/%G\" becomes\n"
+                         "\"/home/user/News/Pan/alt/binaries/pictures/trains\",");
+    GtkWidget * w = gtk_message_dialog_new (GTK_WINDOW(window),
+                                            GTK_DIALOG_DESTROY_WITH_PARENT,
+                                            GTK_MESSAGE_INFO,
+                                            GTK_BUTTONS_CLOSE, "%s", str);
+    g_signal_connect_swapped (GTK_OBJECT(w), "response",
+                              G_CALLBACK(gtk_widget_destroy), GTK_OBJECT (w));
+    gtk_widget_show_all (w);
+  }
+
+  void delete_save_dialog (gpointer castme)
+  {
+    delete static_cast<SaveAttachmentsDialog*>(castme);
+  }
+
+  enum { PATH_GROUP, PATH_ENTRY };
+
+  int path_mode (PATH_GROUP);
+}
+
+void
+SaveAttachmentsDialog :: response_cb (GtkDialog * dialog,
+                           int response,
+                           gpointer user_data)
+{
+
+  if (response == GTK_RESPONSE_OK)
+  {
+    SaveAttachmentsDialog * self (static_cast<SaveAttachmentsDialog*>(user_data));
+
+    bool subject_in_path = false;
+
+    // set the path mode based on what widgets exist & are set
+    GtkWidget * gr (self->_save_group_path_radio);
+    GtkWidget * er (self->_save_custom_path_radio);
+    if (gr && er)
+      path_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gr)) ? PATH_GROUP : PATH_ENTRY;
+    else
+      path_mode = PATH_ENTRY;
+
+    // get the save path
+    std::string path, opath;
+    if (path_mode == PATH_GROUP)
+      path = (const char*) g_object_get_data (G_OBJECT(gr), "default-group-save-path");
+    else if (path_mode == PATH_ENTRY)  {
+      path = pan :: file_entry_get (self->_save_path_entry);
+      self->_prefs.set_string ("default-save-attachments-path", path);
+    }
+    path = opath = expand_download_dir (path.c_str(), self->_group.to_view());
+    if ((path.find("%s") != path.npos) || (path.find("%S") != path.npos))
+      subject_in_path = true;
+
+    std::string sep( self->_prefs.get_string("save-subj-seperator", "-") );
+
+    // make the tasks...
+    Queue::tasks_t tasks;
+    foreach_const (std::vector<Article>, self->_articles, it)
+    {
+      if (subject_in_path)
+        path = expand_download_dir_subject(opath.c_str(), it->subject, sep);
+      tasks.push_back (new TaskArticle (self->_server_rank,
+                                        self->_group_server,
+                                        *it,
+                                        self->_cache,
+                                        self->_read,
+                                        0,
+                                        TaskArticle::DECODE,
+                                        path,
+                                        self->_filename,
+                                        self->_options));
+    }
+
+    // get the queue mode...
+    Queue::AddMode queue_mode;
+    std::string s = self->_prefs.get_string ("save-article-priority", "age");
+    if      (s == "top")    queue_mode = Queue::TOP;
+    else if (s == "bottom") queue_mode = Queue::BOTTOM;
+    else                    queue_mode = Queue::AGE;
+
+    // queue up the tasks...
+    if (!tasks.empty())
+      self->_queue.add_tasks (tasks, queue_mode);
+  }
+
+  gtk_widget_destroy (GTK_WIDGET(dialog));
+}
+
+namespace
+{
+  void entry_changed_cb (GtkEditable*, gpointer radio_or_null)
+  {
+    if (radio_or_null)
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(radio_or_null), true);
+  }
+
+  void combo_box_selection_changed (GtkComboBox * combo, gpointer prefs, const char * key)
+  {
+    GtkTreeIter iter;
+    gtk_combo_box_get_active_iter (combo, &iter);
+    GtkTreeModel * model (gtk_combo_box_get_model (combo));
+    char * s (0);
+    gtk_tree_model_get (model, &iter, 0, &s, -1);
+    static_cast<Prefs*>(prefs)->set_string (key, s);
+    g_free (s);
+  }
+
+  void priority_combo_box_selection_changed (GtkComboBox * combo, gpointer prefs)
+  {
+    combo_box_selection_changed (combo, prefs, "save-article-priority");
+  }
+
+  GtkWidget* create_combo_box (Prefs& prefs, const char * key, const char * fallback, ...)
+  {
+    const std::string active_str (prefs.get_string (key, fallback));
+    int active (-1);
+    GtkListStore * store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+    va_list args;
+    va_start (args, fallback);
+    for (int i=0;; ++i) {
+      const char * key_str = va_arg (args, const char*);
+      if (!key_str) break;
+      const char * gui_str = va_arg (args, const char*);
+      GtkTreeIter iter;
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter, 0, key_str, 1, gui_str, -1);
+      if (active_str == key_str) active = i;
+    }
+    GtkWidget * w = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
+    GtkCellRenderer * renderer (gtk_cell_renderer_text_new ());
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer, true);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), renderer, "text", 1, NULL);
+    gtk_combo_box_set_active (GTK_COMBO_BOX(w), active);
+    return w;
+  }
+
+  GtkWidget* create_priority_combo_box (Prefs& prefs)
+  {
+    return create_combo_box (prefs, "save-article-priority", "age",
+                             "age", _("Add to the queue sorted by date posted"),
+                             "top", _("Add to the front of the queue"),
+                             "bottom", _("Add to the back of the queue"),
+                             NULL);
+  }
+}
+
+SaveAttachmentsDialog :: SaveAttachmentsDialog
+                         (Prefs                       & prefs,
+                          const GroupPrefs            & group_prefs,
+                          const ServerRank            & server_rank,
+                          const GroupServer           & group_server,
+                          ArticleCache                & cache,
+                          ArticleRead                 & read,
+                          Queue                       & queue,
+                          GtkWindow                   * parent_window,
+                          const Quark                 & group,
+                          const std::vector<Article>  & articles,
+                          const TaskArticle::SaveOptions & options,
+                          const char                  * filename):
+  _prefs(prefs),
+  _server_rank (server_rank),
+  _group_server (group_server),
+  _cache (cache),
+  _read (read),
+  _queue (queue),
+  _group (group),
+  _root (0),
+  _save_custom_path_radio (0),
+  _save_group_path_radio (0),
+  _articles (articles),
+  _options (options),
+  _filename (filename)
+{
+  GtkWidget * dialog = gtk_dialog_new_with_buttons (_("Pan: Save Attachments"),
+                                                    parent_window,
+                                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                    NULL);
+  gtk_dialog_add_button (GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+  GtkWidget * focus = gtk_dialog_add_button (GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_OK);
+  gtk_window_set_role (GTK_WINDOW(dialog), "pan-save-attachments-dialog");
+  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+  g_signal_connect (dialog, "response", G_CALLBACK(response_cb), this);
+  g_signal_connect_swapped (dialog, "destroy", G_CALLBACK(delete_save_dialog), this);
+
+  const std::string group_path (group_prefs.get_string (group, "default-group-save-path", ""));
+  const bool have_group_default (!group_path.empty());
+
+  int row (0);
+  GtkWidget *t, *w, *h;
+  t = HIG :: workarea_create ();
+
+  HIG :: workarea_add_section_spacer (t, row, have_group_default ? 4 : 3);
+
+  if (path_mode==PATH_GROUP && !have_group_default)
+      path_mode = PATH_ENTRY;
+
+  h = gtk_hbox_new (FALSE, 0);
+  if (have_group_default) {
+    w = _save_custom_path_radio = gtk_radio_button_new_with_mnemonic (NULL, _("_Location:"));
+    gtk_box_pack_start (GTK_BOX(h), w, false, false, 0);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), path_mode==PATH_ENTRY);
+  }
+  w = _save_path_entry = file_entry_new (_("Save Articles"));
+  gtk_box_pack_start (GTK_BOX(h), w, true, true, 0);
+  std::string path (_prefs.get_string ("default-save-attachments-path", ""));
+  if (path.empty())
+    path = g_get_home_dir ();
+  file_entry_set (w, path.c_str());
+  g_signal_connect (file_entry_gtk_entry(w), "changed", G_CALLBACK(entry_changed_cb), _save_custom_path_radio);
+  gtk_widget_set_size_request (GTK_WIDGET(w), 400, 0);
+  w = gtk_button_new_from_stock (GTK_STOCK_HELP);
+  gtk_box_pack_start (GTK_BOX(h), w, false, false, 0);
+  g_signal_connect_swapped (w, "clicked", G_CALLBACK (show_group_substitution_help_dialog), dialog);
+  if (have_group_default)
+    HIG :: workarea_add_wide_control (t, &row, h);
+  else
+    HIG :: workarea_add_row (t, &row, _("_Location:"), h, file_entry_gtk_entry(_save_path_entry));
+
+  if (have_group_default) {
+    char * pch = g_strdup_printf (_("_Group's path: %s"), group_path.c_str());
+    w = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON(_save_custom_path_radio), pch);
+    _save_group_path_radio = w;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), path_mode==PATH_GROUP);
+    g_object_set_data_full (G_OBJECT(w), "default-group-save-path", g_strdup(group_path.c_str()), g_free);
+    g_free (pch);
+    HIG :: workarea_add_wide_control (t, &row, w);
+  }
+
+  w = create_priority_combo_box (prefs);
+  g_signal_connect (w,  "changed", G_CALLBACK(priority_combo_box_selection_changed), &prefs);
+  w = HIG :: workarea_add_row (t, &row, _("_Priority:"), w);
+
+  gtk_widget_show_all (t);
+  gtk_box_pack_start (GTK_BOX( gtk_dialog_get_content_area( GTK_DIALOG(dialog))), t, true, true, 0);
+  gtk_widget_grab_focus (focus);
+  _root = dialog;
+}
diff --git a/pan/gui/save-attach-ui.h b/pan/gui/save-attach-ui.h
new file mode 100644
index 0000000..8a455e7
--- /dev/null
+++ b/pan/gui/save-attach-ui.h
@@ -0,0 +1,82 @@
+/* -*- 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 __SaveAttachUI_h__
+#define __SaveattachUI_h__
+
+#include <string>
+#include <vector>
+#include <pan/general/quark.h>
+#include <pan/data/article.h>
+#include <pan/data/article-cache.h>
+#include <pan/data/data.h>
+#include <pan/tasks/task-article.h>
+#include <gtk/gtk.h>
+#include "group-prefs.h"
+#include "prefs.h"
+
+namespace pan
+{
+  class SaveAttachmentsDialog
+  {
+    public:
+
+      SaveAttachmentsDialog (Prefs            & prefs,
+                  const GroupPrefs            & group_prefs,
+                  const ServerRank            & server_rank,
+                  const GroupServer           & group_server,
+                  ArticleCache                & cache,
+                  ArticleRead                 & read,
+                  Queue                       & queue,
+                  GtkWindow                   * parent_window,
+                  const Quark                 & group,
+                  const std::vector<Article>  & articles,
+                  const TaskArticle::SaveOptions & options,
+                  const char                  * filename=0
+                  );
+
+
+      ~SaveAttachmentsDialog () {}
+      GtkWidget * root() { return _root; }
+
+    private:
+      Prefs& _prefs;
+      const ServerRank& _server_rank;
+      const GroupServer& _group_server;
+      ArticleCache& _cache;
+      ArticleRead& _read;
+      Queue& _queue;
+      const Quark _group;
+      GtkWidget * _root;
+      GtkWidget * _save_path_entry;
+      GtkWidget * _save_custom_path_radio;
+      GtkWidget * _save_group_path_radio;
+      std::vector<Article> _articles;
+      TaskArticle::SaveOptions _options;
+      const char* _filename;
+
+    private:
+      static void response_cb (GtkDialog*, int, gpointer);
+
+    private:
+      static bool _save_text;
+      static bool _save_attachments;
+  };
+}
+#endif
diff --git a/pan/gui/server-ui.cc b/pan/gui/server-ui.cc
index 826872e..8b58427 100644
--- a/pan/gui/server-ui.cc
+++ b/pan/gui/server-ui.cc
@@ -38,15 +38,9 @@ extern "C" {
 #include "hig.h"
 #include "gtk_compat.h"
 
-#ifdef HAVE_OPENSSL
-
+#ifdef HAVE_GNUTLS
   #include <pan/data/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>
+  #include <gnutls/gnutls.h>
 #endif
 
 using namespace pan;
@@ -73,6 +67,7 @@ namespace
     GtkWidget * expiration_age_combo;
     GtkWidget * rank_combo;
     GtkWidget * ssl_combo;
+    GtkWidget * always_trust_checkbox;
     ServerEditDialog (Data& d, Queue& q): data(d), queue(q) {}
   };
 
@@ -103,7 +98,7 @@ namespace
   void ssl_changed_cb(GtkComboBox* w, ServerEditDialog* d)
   {
     int ssl(0);
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
     GtkTreeIter iter;
     if (gtk_combo_box_get_active_iter (w, &iter))
       gtk_tree_model_get (gtk_combo_box_get_model(w), &iter, 1, &ssl, -1);
@@ -120,7 +115,7 @@ namespace
 
     d->server = server;
 
-    int port(STD_NNTP_PORT), max_conn(4), age(31*3), rank(1), ssl(0);
+    int port(STD_NNTP_PORT), max_conn(4), age(31*3), rank(1), ssl(0), trust(0);
     std::string addr, user, pass, cert;
     if (!server.empty()) {
       d->data.get_server_addr (server, addr, port);
@@ -130,6 +125,7 @@ namespace
       max_conn = d->data.get_server_limits (server);
       ssl = d->data.get_server_ssl_support(server);
       cert = d->data.get_server_cert(server);
+      d->data.get_server_trust (server, trust);
     }
 
     pan_entry_set_text (d->address_entry, addr);
@@ -164,7 +160,7 @@ namespace
       }
     } while (gtk_tree_model_iter_next(model, &iter));
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
     // set ssl combo
     combo = GTK_COMBO_BOX (d->ssl_combo);
     model = gtk_combo_box_get_model (combo);
@@ -176,8 +172,9 @@ namespace
         break;
       }
     } while (gtk_tree_model_iter_next(model, &iter));
-#endif
 
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(d->always_trust_checkbox), trust);
+#endif
   }
 
   void
@@ -208,13 +205,15 @@ namespace
       if (gtk_combo_box_get_active_iter (combo, &iter))
         gtk_tree_model_get (gtk_combo_box_get_model(combo), &iter, 1, &rank, -1);
       int ssl(0);
+      int trust(0);
 
       StringView cert(d->cert);
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
       combo = GTK_COMBO_BOX (d->ssl_combo);
       if (gtk_combo_box_get_active_iter (combo, &iter))
         gtk_tree_model_get (gtk_combo_box_get_model(combo), &iter, 1, &ssl, -1);
+      trust = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->always_trust_checkbox)) ? 1 : 0;
 #endif
 
       const char * err_msg (0);
@@ -240,6 +239,7 @@ namespace
         d->data.set_server_rank (d->server, rank);
         d->data.set_server_ssl_support(d->server, ssl);
         d->data.set_server_cert(d->server,cert);
+        d->data.set_server_trust(d->server,trust);
         d->data.save_server_info(d->server);
         d->queue.upkeep ();
       }
@@ -390,7 +390,7 @@ pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, con
     HIG::workarea_add_row (t, &row, e, w);
 
     // ssl 3.0 option
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
     // select ssl/plaintext
     HIG::workarea_add_section_divider (t, &row);
     HIG::workarea_add_section_title (t, &row, _("Security"));
@@ -424,6 +424,10 @@ pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, con
             "If you enable SSL/TLS, your data is encrypted and secure. "
             "It is encouraged to use this option for privacy reasons."));
     HIG::workarea_add_row (t, &row, e, w);
+
+    d->always_trust_checkbox = w = gtk_check_button_new_with_label (_("Always trust this server's certificate"));
+    HIG::workarea_add_row (t, &row, NULL, w, NULL);
+
 #endif
 
   d->server = server;
@@ -659,7 +663,7 @@ namespace
   }
 }
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
 /* security dialog */
 namespace
 {
@@ -680,7 +684,7 @@ namespace
     char buf[4096] ;
 
     if (!selected_server.empty()) {
-      X509* cert (store.get_cert_to_server(selected_server));
+      const gnutls_x509_crt_t cert (store.get_cert_to_server(selected_server));
       if (cert)
       {
         pretty_print_x509(buf,sizeof(buf),addr, cert,false);
@@ -755,25 +759,25 @@ namespace
     const Quark selected_server (get_selected_server (d));
     CertStore& store (d->data.get_certstore());
 
-    if (!ret.empty() )
-    {
-      std::string addr; int port;
-      FILE *fp = fopen(ret.c_str(),"rb");
-      X509 *x;
-      if (!fp) goto _err;
-      x = X509_new();
-      if (!x) { fclose(fp); goto _err; }
-      PEM_read_X509(fp,&x, 0, 0);
-      fclose(fp);
-      d->data.get_server_addr(selected_server, addr, port);
-      if (!store.add(x,selected_server))
-      {
-      _err:
-        Log::add_err_va("Error adding certificate of server '%s' to CertStore. Check the console output!", addr.c_str());
-        file::print_file_info(std::cerr,ret.c_str());
-      }
-      sec_tree_view_refresh (d);
-    }
+//    if (!ret.empty() )
+//    {
+//      std::string addr; int port;
+//      FILE *fp = fopen(ret.c_str(),"rb");
+//      X509 *x;
+//      if (!fp) goto _err;
+//      x = X509_new();
+//      if (!x) { fclose(fp); goto _err; }
+//      PEM_read_X509(fp,&x, 0, 0);
+//      fclose(fp);
+//      d->data.get_server_addr(selected_server, addr, port);
+//      if (!store.add(x,selected_server))
+//      {
+//      _err:
+//        Log::add_err_va("Error adding certificate of server '%s' to CertStore. Check the console output!", addr.c_str());
+//        file::print_file_info(std::cerr,ret.c_str());
+//      }
+//      sec_tree_view_refresh (d);
+//    }
   }
 
 
@@ -908,7 +912,7 @@ pan :: render_cert_flag (GtkTreeViewColumn * ,
 GtkWidget*
 pan :: sec_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
 {
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
   ServerListDialog * d = new ServerListDialog (data, queue);
 
   for (guint i=0; i<ICON_QTY; ++i)
diff --git a/pan/gui/task-pane.cc b/pan/gui/task-pane.cc
index c1cd93c..89f9a50 100644
--- a/pan/gui/task-pane.cc
+++ b/pan/gui/task-pane.cc
@@ -166,6 +166,8 @@ TaskPane:: on_tooltip_query(GtkWidget  *widget,
   }
   gtk_tree_path_free (path);
 
+  g_free (date);
+
   return true;
 }
 
diff --git a/pan/tasks/Makefile.am b/pan/tasks/Makefile.am
index 54e5145..cca353d 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@ @GNUTLS_CFLAGS@ @GTK_CFLAGS@
 
-AM_LDFLAGS = ../../uulib/libuu.a @OPENSSL_LIBS@
+AM_LDFLAGS = ../../uulib/libuu.a @GNUTLS_LIBS@
 
 noinst_LIBRARIES = libtasks.a
 
diff --git a/pan/tasks/decoder.cc b/pan/tasks/decoder.cc
index 57be091..bc09d16 100644
--- a/pan/tasks/decoder.cc
+++ b/pan/tasks/decoder.cc
@@ -55,10 +55,12 @@ Decoder :: ~Decoder()
 ***/
 
 void
-Decoder :: enqueue (TaskArticle                 * task,
-                    const Quark                 & save_path,
-                    const strings_t             & input_files,
-                    const TaskArticle::SaveMode & save_mode)
+Decoder :: enqueue (TaskArticle                     * task,
+                    const Quark                     & save_path,
+                    const strings_t                 & input_files,
+                    const TaskArticle::SaveMode     & save_mode,
+                    const TaskArticle::SaveOptions  & options,
+                    const StringView                & filename)
 {
   disable_progress_update ();
 
@@ -66,6 +68,8 @@ Decoder :: enqueue (TaskArticle                 * task,
   this->save_path = save_path;
   this->input_files = input_files;
   this->save_mode = save_mode;
+  this->options = options;
+  this->attachment_filename = filename;
 
   mark_read = false;
   percent = 0;
@@ -141,6 +145,7 @@ Decoder :: do_work()
       int i (0);
       foreach_const (strings_t, input_files, it)
       {
+
         if (was_cancelled()) break;
         if ((res = UULoadFileWithPartNo (const_cast<char*>(it->c_str()), 0, 0, ++i)) != UURET_OK) {
           g_snprintf(buf, bufsz,
@@ -162,6 +167,11 @@ Decoder :: do_work()
       i = 0;
       while ((item = UUGetFileListItem (i++)))
       {
+        // skip all other attachments in SAVE_AS mode (single attachment download)
+        /// DBG why is this failing if article isn't cached????
+        if (!attachment_filename.empty())
+          if(strcmp(item->filename, attachment_filename.str) != 0 && options == TaskArticle::SAVE_AS) continue;
+
         file_errors.clear ();
 
         if (was_cancelled()) break; // poll WorkerPool::Worker stop flag
diff --git a/pan/tasks/decoder.h b/pan/tasks/decoder.h
index beaabc6..b34b790 100644
--- a/pan/tasks/decoder.h
+++ b/pan/tasks/decoder.h
@@ -59,7 +59,9 @@ namespace pan
       void enqueue (TaskArticle                    * task,
                     const Quark                    & save_path,
                     const strings_t                & input_files,
-                    const TaskArticle::SaveMode    & save_mode);
+                    const TaskArticle::SaveMode    & save_mode,
+                    const TaskArticle::SaveOptions & options,
+                    const StringView               & filename);
 
     public:
 
@@ -77,6 +79,8 @@ namespace pan
       std::string save_path;
       strings_t input_files;
       TaskArticle::SaveMode save_mode;
+      TaskArticle::SaveOptions options;
+      StringView attachment_filename;
 
       // These are set in the worker thread and polled in the main thread.
       Mutex mut;
diff --git a/pan/tasks/encoder.cc b/pan/tasks/encoder.cc
index c9028e2..25c1c69 100644
--- a/pan/tasks/encoder.cc
+++ b/pan/tasks/encoder.cc
@@ -141,9 +141,6 @@ Encoder :: do_work()
     /* build real subject line for article*/
     tmp->subject = subject;
 
-    // DBG
-    // fp = fopen("/home/imhotep/test.pdf", "wb+");
-
     for (TaskUpload::needed_t::iterator it = needed->begin(); it != needed->end(); ++it, ++cnt)
     {
       g_snprintf(buf,sizeof(buf),"%s.%d",basename.c_str(), cnt);
diff --git a/pan/tasks/nntp-pool.cc b/pan/tasks/nntp-pool.cc
index a82b694..e4ca0ec 100644
--- a/pan/tasks/nntp-pool.cc
+++ b/pan/tasks/nntp-pool.cc
@@ -289,7 +289,7 @@ NNTP_Pool :: request_nntp (WorkerPool& threadpool)
     {
       ++_pending_connections;
       const bool ssl(_server_info.get_server_ssl_support(_server));
-      _socket_creator->create_socket (_server_info,address, port, threadpool, this, ssl);
+      _socket_creator->create_socket (_server_info, address, port, threadpool, this, ssl);
     }
   }
 }
@@ -351,14 +351,14 @@ NNTP_Pool :: idle_upkeep ()
   }
 }
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
 void
-NNTP_Pool:: on_verify_cert_failed(X509* cert, std::string server, std::string cert_name, int nr)
+NNTP_Pool:: on_verify_cert_failed(gnutls_x509_crt_t cert, std::string server, int nr)
 {
 }
 
 void
-NNTP_Pool :: on_valid_cert_added (X509* cert, std::string server)
+NNTP_Pool :: on_valid_cert_added (gnutls_x509_crt_t cert, std::string server)
 {
 }
 #endif
diff --git a/pan/tasks/nntp-pool.h b/pan/tasks/nntp-pool.h
index f173863..4a976cb 100644
--- a/pan/tasks/nntp-pool.h
+++ b/pan/tasks/nntp-pool.h
@@ -28,7 +28,7 @@
 #include <pan/tasks/nntp.h>
 #include <pan/tasks/socket-impl-main.h>
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
   #include <pan/data/cert-store.h>
 #endif
 
@@ -86,11 +86,11 @@ namespace pan
     private: // Socket::Creator::Listener
       virtual void on_socket_created (const StringView& host, int port, bool ok, Socket*);
       virtual void on_socket_shutdown (const StringView& host, int port, Socket*) {}
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
     private:
       // CertStore::Listener
-      virtual void on_verify_cert_failed (X509*, std::string, std::string, int) ;
-      virtual void on_valid_cert_added (X509*, std::string );
+      virtual void on_verify_cert_failed (gnutls_x509_crt_t, std::string, int) ;
+      virtual void on_valid_cert_added (gnutls_x509_crt_t, std::string );
 #endif
     private:
 
diff --git a/pan/tasks/queue.h b/pan/tasks/queue.h
index 6b1c9c5..6a1f32d 100644
--- a/pan/tasks/queue.h
+++ b/pan/tasks/queue.h
@@ -36,7 +36,7 @@
 #include <pan/tasks/task-weak-ordering.h>
 #include <pan/tasks/socket-impl-main.h>
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
   #include <pan/data/cert-store.h>
 #endif
 
diff --git a/pan/tasks/socket-impl-main.cc b/pan/tasks/socket-impl-main.cc
index 24ddff7..589dacb 100644
--- a/pan/tasks/socket-impl-main.cc
+++ b/pan/tasks/socket-impl-main.cc
@@ -37,7 +37,8 @@
 #include <cerrno>
 #include <cstring>
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
+  #include <gcrypt.h>
   #include <pan/usenet-utils/ssl-utils.h>
 #endif
 #include <pan/general/debug.h>
@@ -74,13 +75,11 @@ namespace
     std::string err;
     bool use_ssl;
     const Quark server;
-#ifdef HAVE_OPENSSL
-    std::multimap<std::string, Socket*>& socket_map;
-    SSL_CTX * context;
+#ifdef HAVE_GNUTLS
     CertStore& store;
     ThreadWorker (ServerInfo& d, const Quark& s, const StringView& h, int p, Socket::Creator::Listener *l,
-                  bool ssl, SSL_CTX* ctx, CertStore& cs, std::multimap<std::string, Socket*>& m):
-      data(d), server(s), host(h), port(p), listener(l), ok(false), socket(0), use_ssl(ssl), context(ctx), store(cs), socket_map(m) {}
+                  bool ssl, CertStore& cs):
+      data(d), server(s), host(h), port(p), listener(l), ok(false), socket(0), use_ssl(ssl), store(cs) {}
 #else
     ThreadWorker (ServerInfo& d, const Quark& s, const StringView& h, int p, Socket::Creator::Listener *l):
       data(d), server(s), host(h), port(p), listener(l), ok(false), socket(0), use_ssl(false) {}
@@ -88,11 +87,10 @@ namespace
 
     void do_work ()
     {
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
         if (use_ssl)
         {
-          socket = new GIOChannelSocketSSL (data, server, context, store);
-          socket_map.insert(std::pair<std::string, Socket*>(host, socket));
+          socket = new GIOChannelSocketGnuTLS (data, server, store);
         }
         else
 #endif
@@ -110,81 +108,22 @@ namespace
   };
 }
 
-#ifdef HAVE_OPENSSL
-
-// TODO remove this later if it works with GMutex
-#ifdef G_OS_WIN32
-  #define MUTEX_TYPE HANDLE
-  #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE)
-  #define MUTEX_UNLOCK(x) ReleaseMutex(x)
-  #define THREAD_ID GetCurrentThreadId( )
-#else
-  #define MUTEX_TYPE Mutex
-  #define MUTEX_LOCK(x) x.lock()
-  #define MUTEX_UNLOCK(x) x.unlock()
-  #define THREAD_ID pthread_self( )
-#endif
-  #define MUTEX_SETUP(x) (x) = new MUTEX_TYPE[CRYPTO_num_locks()];
-  #define MUTEX_CLEANUP(x) delete [] x
-namespace
-{
-  static MUTEX_TYPE* mutex;
-
-  void gio_lock(int mode, int type, const char *file, int line)
-  {
-    if (mode & CRYPTO_LOCK)
-      MUTEX_LOCK(mutex[type]);//.lock();
-    else
-      MUTEX_UNLOCK(mutex[type]);//.unlock();
-  }
-
-  void ssl_thread_setup() {
-    MUTEX_SETUP(mutex);
-#ifdef G_OS_WIN32
-    for (int i = 0; i < CRYPTO_num_locks(); ++i) mutex[i] = CreateMutex(NULL, FALSE, NULL);
-#endif
-    CRYPTO_set_locking_callback(gio_lock);
-  }
-
-  void ssl_thread_cleanup() {
-#ifdef G_OS_WIN32
-    for (int i = 0; i < CRYPTO_num_locks(); ++i) CloseHandle(mutex[i]);
-#endif
-    MUTEX_CLEANUP(mutex);
-    CRYPTO_set_locking_callback(0);
-  }
-
-}
-#endif
-
 SocketCreator :: SocketCreator(Data& d, CertStore& cs) : data(d), 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);
-  SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT);
-
+#ifdef HAVE_GNUTLS
+  gnutls_global_init();
   cs.add_listener(this);
+  cs.init();
 #endif
 }
 
 
 SocketCreator :: ~SocketCreator()
 {
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
+  gnutls_global_deinit();
   store.remove_listener(this);
-
-  ssl_thread_cleanup();
-  if (ssl_ctx) SSL_CTX_free(ssl_ctx);
 #endif
 }
 
@@ -200,20 +139,20 @@ SocketCreator :: create_socket (ServerInfo& info,
     data.find_server_by_hn(host, server);
     ensure_module_init ();
     if (store.in_blacklist(server)) return;
-#ifdef HAVE_OPENSSL
-    ThreadWorker * w = new ThreadWorker (info, server, host, port, listener, use_ssl, ssl_ctx, store, socket_map);
+#ifdef HAVE_GNUTLS
+    ThreadWorker * w = new ThreadWorker (info, server, host, port, listener, use_ssl, store);
 #else
     ThreadWorker * w = new ThreadWorker (info, server, host, port, listener);
 #endif
     threadpool.push_work (w, w, true);
 }
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
 void
-SocketCreator :: on_verify_cert_failed(X509* cert, std::string server, std::string cert_name, int nr)
+SocketCreator :: on_verify_cert_failed(gnutls_x509_crt_t cert, std::string server, int nr)
 {}
 
 void
-SocketCreator :: on_valid_cert_added (X509* cert, std::string server)
+SocketCreator :: on_valid_cert_added (gnutls_x509_crt_t cert, std::string server)
 {}
 #endif
diff --git a/pan/tasks/socket-impl-main.h b/pan/tasks/socket-impl-main.h
index 0699a12..e0f0643 100644
--- a/pan/tasks/socket-impl-main.h
+++ b/pan/tasks/socket-impl-main.h
@@ -39,9 +39,8 @@
 #include <pan/general/worker-pool.h>
 #include "socket.h"
 
-#ifdef HAVE_OPENSSL
-  #include <openssl/crypto.h>
-  #include <openssl/ssl.h>
+#ifdef HAVE_GNUTLS
+  #include <gnutls/gnutls.h>
   #include "socket-impl-openssl.h"
 #endif
 
@@ -131,20 +130,12 @@ namespace pan
     private:
       //socket::creator::Listener
       virtual void on_socket_created (const StringView& host, int port, bool ok, Socket*) {}
-      virtual void on_socket_shutdown (const StringView& host, int port, Socket*)
-      {
-#ifdef HAVE_OPENSSL
-
-#endif
-      }
-
-#ifdef HAVE_OPENSSL
-      SSL_CTX* ssl_ctx;
-      std::multimap<std::string, Socket*> socket_map;
+      virtual void on_socket_shutdown (const StringView& host, int port, Socket*) {}
 
+#ifdef HAVE_GNUTLS
       // CertStore::Listener
-      virtual void on_verify_cert_failed(X509*, std::string, std::string, int);
-      virtual void on_valid_cert_added (X509*, std::string );
+      virtual void on_verify_cert_failed(gnutls_x509_crt_t, std::string, int);
+      virtual void on_valid_cert_added (gnutls_x509_crt_t, std::string );
 #endif
       CertStore & store;
       Data& data;
diff --git a/pan/tasks/socket-impl-openssl.cc b/pan/tasks/socket-impl-openssl.cc
index fec8cfd..74d48a6 100644
--- a/pan/tasks/socket-impl-openssl.cc
+++ b/pan/tasks/socket-impl-openssl.cc
@@ -5,7 +5,6 @@
  *
  * This file
  * Copyright (C) 2011 Heinrich Mü<sphemuel stud informatik uni-erlangen de>
- * SSL functions : Copyright (C) 2002 vjt (irssi project)
  *
  * 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
@@ -65,6 +64,7 @@ extern "C" {
 //    return buf;
 //  }
 
+
   const char*
   get_last_error (int err)
   {
@@ -116,32 +116,34 @@ extern t_freeaddrinfo p_freeaddrinfo;
 *****
 ****/
 
-#ifdef HAVE_OPENSSL // without libssl this class is just a stub....
+namespace
+{
+  static gboolean gnutls_inited = FALSE;
+}
 
-GIOChannelSocketSSL :: GIOChannelSocketSSL (ServerInfo& data, const Quark& server, SSL_CTX* ctx, CertStore& cs):
+#ifdef HAVE_GNUTLS // without gnutls this class is just a stub....
+
+GIOChannelSocketGnuTLS :: GIOChannelSocketGnuTLS (ServerInfo& data, const Quark& server, CertStore& cs):
    _channel (0),
    _tag_watch (0),
    _tag_timeout (0),
-   _handshake_timeout_tag(0),
    _listener (0),
    _out_buf (g_string_new (0)),
    _in_buf (g_string_new (0)),
    _io_performed (false),
-   _ctx(ctx),
    _certstore(cs),
-   _rehandshake(false),
    _server(server),
    _done(false),
    _data(data)
 {
-  debug ("GIOChannelSocketSSL ctor " << (void*)this);
+
+  debug ("GIOChannelSocketGnuTLS ctor " << (void*)this);
   cs.add_listener(this);
-  _session = cs.get_session();
 }
 
 
 GIOChannel *
-GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std::string& setme_err)
+GIOChannelSocketGnuTLS :: create_channel (const StringView& host_in, int port, std::string& setme_err)
 {
   int err;
   int sockfd;
@@ -268,8 +270,8 @@ GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std:
     g_io_channel_set_encoding (channel, 0, 0);
   g_io_channel_set_buffered (channel,true);
   g_io_channel_set_line_term (channel, "\n", 1);
-  GIOChannel* ret (ssl_get_iochannel(channel));
-  debug ("SocketSSL "<<ret);
+  GIOChannel* ret (gnutls_get_iochannel(channel, host_in.str));
+  debug ("########### SocketSSL "<<ret);
   return ret;
 }
 
@@ -291,24 +293,25 @@ namespace
     GIOChannel pad;
     gint fd;
     GIOChannel *giochan;
-    SSL *ssl;
-    SSL_CTX *ctx;
     char* host;
-    unsigned int verify;
-  } GIOSSLChannel;
+    bool verify;
+    gnutls_session_t session;
+    gnutls_certificate_credentials_t cred;
+    bool established;
+  } GIOGnuTLSChannel;
 
 
-  void ssl_free(GIOChannel *handle)
+  void _gnutls_free(GIOChannel *handle)
   {
-    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
+    GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)handle;
     g_io_channel_unref(chan->giochan);
-    SSL_shutdown(chan->ssl);
-    SSL_free(chan->ssl);
+    gnutls_deinit (chan->session);
+    g_free(chan->host);
     g_free(chan);
   }
 }
 
-GIOChannelSocketSSL :: ~GIOChannelSocketSSL ()
+GIOChannelSocketGnuTLS :: ~GIOChannelSocketGnuTLS ()
 {
 
   _certstore.remove_listener(this);
@@ -317,15 +320,12 @@ GIOChannelSocketSSL :: ~GIOChannelSocketSSL ()
 
   remove_source (_tag_watch);
   remove_source (_tag_timeout);
-  remove_source (_handshake_timeout_tag);
 
   if (_channel)
   {
-    GIOSSLChannel *chan = (GIOSSLChannel *)_channel;
-    _session = SSL_get1_session(chan->ssl);
-    _certstore.add_session(_session);
+    GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)_channel;
     g_io_channel_shutdown (_channel, true, 0);
-    ssl_free(_channel);
+    _gnutls_free(_channel);
     g_string_free(_channel->read_buf,true);
     _channel = 0;
   }
@@ -338,7 +338,7 @@ GIOChannelSocketSSL :: ~GIOChannelSocketSSL ()
 }
 
 bool
-GIOChannelSocketSSL :: open (const StringView& address, int port, std::string& setme_err)
+GIOChannelSocketGnuTLS :: open (const StringView& address, int port, std::string& setme_err)
 {
   _host.assign (address.str, address.len);
   _channel = create_channel (address, port, setme_err);
@@ -346,13 +346,13 @@ GIOChannelSocketSSL :: open (const StringView& address, int port, std::string& s
 }
 
 void
-GIOChannelSocketSSL :: get_host (std::string& setme) const
+GIOChannelSocketGnuTLS :: get_host (std::string& setme) const
 {
   setme = _host;
 }
 
 void
-GIOChannelSocketSSL :: write_command (const StringView& command, Socket::Listener * l)
+GIOChannelSocketGnuTLS :: write_command (const StringView& command, Socket::Listener * l)
 {
   _partial_read.clear ();
   _listener = l;
@@ -371,11 +371,13 @@ GIOChannelSocketSSL :: write_command (const StringView& command, Socket::Listene
 namespace
 {
 
-  static void set_blocking(SSL * ssl, bool val)
+  static void set_blocking(gnutls_session_t& session, bool val)
   {
-    int fd, flags;
-    /* SSL_get_rfd returns -1 on error */
-    if(fd = SSL_get_fd(ssl))
+    int fd(-1), flags;
+    gnutls_transport_ptr_t tmp = gnutls_transport_get_ptr (session);
+    fd = GPOINTER_TO_INT (tmp);
+
+    if(fd)
     {
 #ifndef G_OS_WIN32
       flags = fcntl(fd, F_GETFL);
@@ -390,222 +392,206 @@ namespace
       ioctlsocket(fd, FIONBIO, &block);
     }
 #endif
-
   }
 
+  GIOStatus _gnutls_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
+  {
+    return G_IO_STATUS_NORMAL;
+  }
 
-  static GIOStatus ssl_errno(gint e)
+  GIOStatus _gnutls_write(GIOChannel *handle, const gchar *buf, gsize len, gsize *ret, GError **gerr)
   {
-    switch(e)
-    {
-      case EINVAL:
-        return G_IO_STATUS_ERROR;
-      case EINTR:
-      case EAGAIN:
-        return G_IO_STATUS_AGAIN;
-      default:
-        return G_IO_STATUS_ERROR;
-    }
-    return G_IO_STATUS_ERROR;
+    return G_IO_STATUS_NORMAL;
   }
 
+  GIOStatus gnutls_seek(GIOChannel *handle, gint64 offset, GSeekType type, GError **gerr)
+  {
+    GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)handle;
+    GIOError e;
+    e = g_io_channel_seek(chan->giochan, offset, type);
+    return (e == G_IO_ERROR_NONE) ? G_IO_STATUS_NORMAL : G_IO_STATUS_ERROR;
+  }
 
-  int ssl_handshake(ServerInfo& data, const Quark& server, GIOChannel *handle, CertStore::Listener* listener,
-                    CertStore* cs, std::string host, SSL_SESSION* session, bool rehandshake)
+  GIOStatus gnutls_close(GIOChannel *handle, GError **gerr)
   {
+    debug("gnutls close "<<handle);
 
-    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
-    int ret(0);
-    int err(0);
-    X509 *cert;
-    const char *errstr;
-
-    /* init custom data for callback */
-    mydata_t mydata;
-    mydata.ctx = chan->ctx;
-    mydata.cs = cs;
-    mydata.ignore_all = 0;
-    mydata.l = listener;
-    /* build cert name from scratch or from Server* */
-    Quark setme;
-    data.find_server_by_hn(host, setme);
-    mydata.cert_name = data.get_server_cert(setme);
-    mydata.server = server;
-    SSL_set_ex_data(chan->ssl, SSL_get_fd(chan->ssl), &mydata);
-
-    if (session) ret = SSL_set_session(chan->ssl, session);
-//    if (rehandshake)
-//    {
-//      /* Stop the client from just resuming the un-authenticated session */
-//      SSL_set_session_id_context(chan->ssl, (void *)&s_server_auth_session_id_context, sizeof(s_server_auth_session_id_context));
-//
-//      if(SSL_renegotiate(ssl)<=0)
-//        return 1;
-//      if(SSL_do_handshake(ssl)<=0)
-//        ssl->state=SSL_ST_ACCEPT;
-//      if(SSL_do_handshake(ssl)<=0)
-//        return 1;
-//    }
-
-    ret = SSL_connect(chan->ssl);
-    if (ret <= 0) {
-      err = SSL_get_error(chan->ssl, ret);
-      return ret;
-      switch (err) {
-        case SSL_ERROR_WANT_READ:
-          debug("SSL handshake failed: wants to read");
-           if (SSL_pending (chan->ssl)) return 1;
-           return 1;
-        case SSL_ERROR_WANT_WRITE:
-          debug("SSL handshake failed: wants to write");
-          if (SSL_pending (chan->ssl)) return 1;
-          return 3;
-        case SSL_ERROR_ZERO_RETURN:
-          debug("SSL handshake failed: 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);
-          debug("SSL handshake failed: "<<(errstr != NULL ? errstr : "server closed connection unexpectedly"));
-          return -1;
-        default:
-          errstr = ERR_reason_error_string(ERR_get_error());
-          debug("SSL handshake failed: "<<(errstr != NULL ? errstr : "unknown SSL error"));
-          return -1;
-      }
-    }
+    GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *) handle;
 
-    cert = SSL_get_peer_certificate(chan->ssl);
-    if (!cert) {
-      debug("SSL server supplied no certificate");
-      return -1;
-    }
+    if (chan->established) {
+      int ret;
 
-    ret = !chan->verify || ssl_verify(cs, chan->ssl, chan->ctx, host.c_str(), cert);
-    X509_free(cert);
-    return ret ? 0 : -1;
+      do {
+        ret = gnutls_bye (chan->session, GNUTLS_SHUT_WR);
+      } while (ret == GNUTLS_E_INTERRUPTED);
+    }
 
+    return chan->giochan->funcs->io_close (handle, gerr);
   }
 
-  GIOStatus ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
+  GSource *gnutls_create_watch(GIOChannel *handle, GIOCondition cond)
   {
-    return G_IO_STATUS_NORMAL;
+    GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)handle;
+
+    return chan->giochan->funcs->io_create_watch(handle, cond);
   }
 
-  GIOStatus ssl_read_line_string(GIOChannel *handle, GString* g, gsize *ret, GError **gerr)
+  GIOStatus gnutls_set_flags(GIOChannel *handle, GIOFlags flags, GError **gerr)
   {
+      GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)handle;
 
-    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
-    gint err;
-    const size_t tmp_size(4096*128);
-    char tmp[tmp_size];
-    g_string_set_size(g,0);
+      return chan->giochan->funcs->io_set_flags(handle, flags, gerr);
+  }
 
-    if (handle->read_buf->len == 0)
-    {
-      err = SSL_read(chan->ssl, tmp, tmp_size*sizeof(char));
-      if (ret) *ret = err < 0 ? 0 : err;
-      if(err <= 0)
-      {
-        if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
-          return G_IO_STATUS_AGAIN;
-        return ssl_errno(errno);
-      }
-      else
-        g_string_append_len (handle->read_buf,tmp,err);
-    }
+  GIOFlags gnutls_get_flags(GIOChannel *handle)
+  {
+      GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)handle;
 
-    //fill in from read_buf
-    char * buf = handle->read_buf->str;
-    int pos(0);
-    bool found(false);
-    while (*buf) { if (*buf == '\n') { found = true; break; } ++pos; ++buf; }
-    if (found) {
-      int _pos(std::min(pos+1,(int)handle->read_buf->len));
-      g_string_append_len(g, handle->read_buf->str, _pos);
-      g_string_erase (handle->read_buf, 0, _pos);
-      return G_IO_STATUS_NORMAL;
-    }
-    // no linebreak, partial line. retry later...
-    return G_IO_STATUS_AGAIN;
+      return chan->giochan->funcs->io_get_flags(handle);
   }
 
-  GIOStatus ssl_write(GIOChannel *handle, const gchar *buf, gsize len, gsize *ret, GError **gerr)
-  {
-    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
-    gint err;
+  GIOFuncs gnutls_channel_funcs = {
+    _gnutls_read,
+    _gnutls_write,
+    gnutls_seek,
+    gnutls_close,
+    gnutls_create_watch,
+    _gnutls_free,
+    gnutls_set_flags,
+    gnutls_get_flags
+  };
 
-    err = SSL_write(chan->ssl, (const char *)buf, len);
-    if(err < 0)
-    {
-      *ret = 0;
-      if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
-        return G_IO_STATUS_AGAIN;
-      return ssl_errno(errno);
-    }
-    else
-    {
-      *ret = err;
-      return G_IO_STATUS_NORMAL;
-    }
-    return G_IO_STATUS_ERROR;
+
+}
+
+/***
+****
+***/
+
+GIOStatus
+GIOChannelSocketGnuTLS :: gnutls_write_line(GIOChannel *handle, const gchar *buf, gsize len, gsize *ret, GError **gerr)
+{
+
+  *ret = 0;
+
+  GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)handle;
+  gint err;
+  GIOStatus result;
+
+  if (!chan->established) {
+    result = _gnutls_handshake (handle);
+
+    if (result == G_IO_STATUS_AGAIN ||
+        result == G_IO_STATUS_ERROR)
+          return result;
+      chan->established = TRUE;
   }
 
-  GIOStatus ssl_seek(GIOChannel *handle, gint64 offset, GSeekType type, GError **gerr)
+  err = gnutls_record_send (chan->session, (const char *)buf, len);
+  if(err < 0)
   {
-    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
-    GIOError e;
-    e = g_io_channel_seek(chan->giochan, offset, type);
-    return (e == G_IO_ERROR_NONE) ? G_IO_STATUS_NORMAL : G_IO_STATUS_ERROR;
+    if ((err == GNUTLS_E_INTERRUPTED) ||
+        (err == GNUTLS_E_AGAIN))
+          return G_IO_STATUS_AGAIN;
+    g_set_error (gerr, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Received corrupted data");
   }
-
-  GIOStatus ssl_close(GIOChannel *handle, GError **gerr)
+  else
   {
-    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
-    g_io_channel_close(chan->giochan);
+    *ret = err;
     return G_IO_STATUS_NORMAL;
   }
+  return G_IO_STATUS_ERROR;
+}
 
-  GSource *ssl_create_watch(GIOChannel *handle, GIOCondition cond)
-  {
-    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
+GIOStatus
+GIOChannelSocketGnuTLS :: gnutls_read_line(GString* g, gsize *ret, GError **gerr)
+{
 
-    return chan->giochan->funcs->io_create_watch(handle, cond);
-  }
+  *ret = 0;
 
-  GIOStatus ssl_set_flags(GIOChannel *handle, GIOFlags flags, GError **gerr)
-  {
-      GIOSSLChannel *chan = (GIOSSLChannel *)handle;
+  GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)_channel;
 
-      return chan->giochan->funcs->io_set_flags(handle, flags, gerr);
+  if (!chan->established) {
+    GIOStatus result = _gnutls_handshake (_channel);
+
+    if (result == G_IO_STATUS_AGAIN ||
+        result == G_IO_STATUS_ERROR)
+      return result;
+
+    chan->established = true;
   }
 
-  GIOFlags ssl_get_flags(GIOChannel *handle)
-  {
-      GIOSSLChannel *chan = (GIOSSLChannel *)handle;
+  gint err;
+  const size_t tmp_size(4096*128);
+  char tmp[tmp_size];
+  g_string_set_size(g,0);
 
-      return chan->giochan->funcs->io_get_flags(handle);
+  if (_channel->read_buf->len == 0)
+  {
+    err = gnutls_record_recv (chan->session, tmp, tmp_size);
+    if (ret) *ret = err < 0 ? 0 : err;
+    if(err < 0)
+    {
+      if ((err == GNUTLS_E_INTERRUPTED) ||
+          (err == GNUTLS_E_AGAIN))
+            return G_IO_STATUS_AGAIN;
+      g_set_error (gerr, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Received corrupted data");
+      return G_IO_STATUS_ERROR;
+    }
+    else
+      g_string_append_len (_channel->read_buf,tmp,err);
   }
 
-  GIOFuncs ssl_channel_funcs = {
-    ssl_read,
-    ssl_write,
-    ssl_seek,
-    ssl_close,
-    ssl_create_watch,
-    ssl_free,
-    ssl_set_flags,
-    ssl_get_flags
-  };
+  //fill in from read_buf
+  char * buf = _channel->read_buf->str;
+  int pos(0);
+  bool found(false);
+  while (*buf) { if (*buf == '\n') { found = true; break; } ++pos; ++buf; }
+  if (found) {
+    int _pos(std::min(pos+1,(int)_channel->read_buf->len));
+    g_string_append_len(g, _channel->read_buf->str, _pos);
+    g_string_erase (_channel->read_buf, 0, _pos);
+    return G_IO_STATUS_NORMAL;
+  }
+  //no linebreak, partial line. retry later...
+  return G_IO_STATUS_AGAIN;
 }
 
-/***
-****
-***/
 
-GIOChannelSocketSSL :: DoResult
-GIOChannelSocketSSL :: do_read ()
+GIOStatus
+GIOChannelSocketGnuTLS :: _gnutls_handshake (GIOChannel *channel)
+{
+
+  GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)channel;
+
+  g_return_val_if_fail (channel, G_IO_STATUS_ERROR);
+  g_return_val_if_fail (chan, G_IO_STATUS_ERROR);
+  g_return_val_if_fail (chan->session, G_IO_STATUS_ERROR);
+
+  /* init custom data for callback */
+  mydata_t mydata;
+
+  mydata.cs = &_certstore;
+  Quark setme;
+  _data.find_server_by_hn(_host.c_str(), setme);
+  mydata.host = setme;
+  mydata.hostname_full = _host;
+  int res(0); // always trust server cert
+  _data.get_server_trust (setme, res);
+  mydata.always_trust = res;
+  gnutls_session_set_ptr (chan->session, (void *) &mydata);
+
+  int status = gnutls_handshake (chan->session);
+
+  bool r = !chan->verify || status == 0;
+  if (r) chan->established = true;
+
+  return r ? G_IO_STATUS_NORMAL : G_IO_STATUS_ERROR;
+
+}
+
+GIOChannelSocketGnuTLS :: DoResult
+GIOChannelSocketGnuTLS :: do_read ()
 {
   g_assert (!_out_buf->len);
 
@@ -614,13 +600,13 @@ GIOChannelSocketSSL :: do_read ()
 
   bool more (true);
 
-  GIOSSLChannel * chan = (GIOSSLChannel*)_channel;
+  GIOGnuTLSChannel * chan = (GIOGnuTLSChannel*)_channel;
 
   while (more && !_abort_flag)
   {
     _io_performed = true;
     gsize ret;
-    const GIOStatus status (ssl_read_line_string(_channel, g, &ret, &err));
+    const GIOStatus status (gnutls_read_line(g, &ret, &err));
 
     if (status == G_IO_STATUS_NORMAL)
     {
@@ -656,8 +642,8 @@ GIOChannelSocketSSL :: do_read ()
 }
 
 
-GIOChannelSocketSSL :: DoResult
-GIOChannelSocketSSL :: do_write ()
+GIOChannelSocketGnuTLS :: DoResult
+GIOChannelSocketGnuTLS :: do_write ()
 {
   g_assert (_partial_read.empty());
 
@@ -674,7 +660,7 @@ GIOChannelSocketSSL :: do_write ()
   GError * err = 0;
   gsize out = 0;
   GIOStatus status = g->len
-    ? ssl_write(_channel, g->str, g->len, &out, &err)
+    ? gnutls_write_line(_channel, g->str, g->len, &out, &err)
     : G_IO_STATUS_NORMAL;
   debug ("socket " << this << " channel " << _channel
                    << " maybe wrote [" << g->str << "]; status was " << status);
@@ -700,9 +686,9 @@ GIOChannelSocketSSL :: do_write ()
 }
 
 gboolean
-GIOChannelSocketSSL :: timeout_func (gpointer sock_gp)
+GIOChannelSocketGnuTLS :: timeout_func (gpointer sock_gp)
 {
-  GIOChannelSocketSSL * self (static_cast<GIOChannelSocketSSL*>(sock_gp));
+  GIOChannelSocketGnuTLS * self (static_cast<GIOChannelSocketGnuTLS*>(sock_gp));
 
   if (!self->_io_performed)
   {
@@ -717,15 +703,15 @@ GIOChannelSocketSSL :: timeout_func (gpointer sock_gp)
 }
 
 gboolean
-GIOChannelSocketSSL :: gio_func (GIOChannel   * channel,
+GIOChannelSocketGnuTLS :: gio_func (GIOChannel   * channel,
                               GIOCondition   cond,
                               gpointer       sock_gp)
 {
-  return static_cast<GIOChannelSocketSSL*>(sock_gp)->gio_func (channel, cond);
+  return static_cast<GIOChannelSocketGnuTLS*>(sock_gp)->gio_func (channel, cond);
 }
 
 gboolean
-GIOChannelSocketSSL :: gio_func (GIOChannel   * channel,
+GIOChannelSocketGnuTLS :: gio_func (GIOChannel   * channel,
                                  GIOCondition   cond)
 {
   set_watch_mode (IGNORE_NOW);
@@ -757,9 +743,9 @@ namespace
 }
 
 void
-GIOChannelSocketSSL :: set_watch_mode (WatchMode mode)
+GIOChannelSocketGnuTLS :: set_watch_mode (WatchMode mode)
 {
-  GIOSSLChannel *chan = (GIOSSLChannel *)_channel;
+  GIOGnuTLSChannel *chan = (GIOGnuTLSChannel *)_channel;
   debug ("socket " << this << " calling set_watch_mode " << mode << "; _channel is " << chan->giochan);
   remove_source (_tag_watch);
   remove_source (_tag_timeout);
@@ -793,71 +779,67 @@ GIOChannelSocketSSL :: set_watch_mode (WatchMode mode)
 }
 
 GIOChannel *
-GIOChannelSocketSSL :: ssl_get_iochannel(GIOChannel *handle, gboolean verify)
+GIOChannelSocketGnuTLS :: gnutls_get_iochannel(GIOChannel* channel, const char* host, gboolean verify)
 {
 
-	GIOSSLChannel *chan(0);
+  g_return_val_if_fail(channel, 0);
+
+	GIOGnuTLSChannel *chan(0);
 	GIOChannel *gchan(0);
 	int err(0), fd(0);
-	SSL *ssl(0);
-	SSL_CTX *ctx(_ctx);
 
-	g_return_val_if_fail(handle != 0, 0);
-	g_return_val_if_fail(ctx != 0, 0);
+	chan = g_new0(GIOGnuTLSChannel, 1);
+	g_return_val_if_fail(chan, 0);
 
-	if(!(fd = g_io_channel_unix_get_fd(handle)))
-	{
-    return 0;
-	}
+  gnutls_session_t session(NULL);
+
+	if(!(fd = g_io_channel_unix_get_fd(channel))) return 0;
 
-	if(!(ssl = SSL_new(ctx)))
-	{
-		g_warning("Failed to allocate SSL structure");
-		return 0;
-	}
-	SSL_set_verify(ssl,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0);
+  if (gnutls_init (&session, GNUTLS_CLIENT | GNUTLS_NONBLOCK) != 0) return 0;
+  if (gnutls_set_default_priority (session) != 0) return 0;
 
-	if(!(err = SSL_set_fd(ssl, fd)))
-	{
-		g_warning("Failed to associate socket to SSL stream");
-		SSL_free(ssl);
-		return 0;
-	}
+  gnutls_priority_set_direct (
+  session,
+  // "NONE:+VERS-SSL3.0:+CIPHER-ALL:+COMP-ALL:+RSA:+DHE-RSA:+DHE-DSS:+MAC-ALL"
+  "NONE:+VERS-TLS1.0:+CIPHER-ALL:+COMP-ALL:+RSA:+DHE-RSA:+DHE-DSS:+MAC-ALL", NULL);
 
-	chan = g_new0(GIOSSLChannel, 1);
-	if (!chan) return 0;
+  gnutls_certificate_credentials_t creds = _certstore.get_creds();
+  gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, creds);
+  gnutls_transport_set_ptr (session,  GINT_TO_POINTER (fd));
 
+	chan->host = g_strdup(host);
+	chan->session = session;
 	chan->fd = fd;
-	chan->giochan = handle;
-	chan->ssl = ssl;
-	chan->ctx = ctx;
-	chan->verify = verify ? 1 : 0;
+	chan->giochan = channel;
+	chan->verify = verify;
 
 	gchan = (GIOChannel *)chan;
-	gchan->funcs = &ssl_channel_funcs;
+	gchan->funcs = &gnutls_channel_funcs;
 	g_io_channel_init(gchan);
   gchan->read_buf = g_string_sized_new(4096*128);
 
   int ret;
-  set_blocking(ssl, true);
-  if (ssl_handshake(_data, _server, gchan, this, &_certstore,_host, _session,_rehandshake) == 0)
+  set_blocking(session, true);
+
+  if (_gnutls_handshake(gchan) == G_IO_STATUS_NORMAL)
   {
-    set_blocking(chan->ssl, false);
+    set_blocking(session, false);
     return gchan;
   }
-  set_blocking(chan->ssl, false);
+
+  set_blocking(session, false);
+
   return 0;
 }
 
 void
-GIOChannelSocketSSL :: on_verify_cert_failed (X509* cert, std::string server,
-                                              std::string cert_name, int nr)
+GIOChannelSocketGnuTLS :: on_verify_cert_failed (gnutls_x509_crt_t cert, std::string server, int nr)
 {
   _certstore.blacklist(server);
 }
 
 void
-GIOChannelSocketSSL :: on_valid_cert_added (X509* cert, std::string server)
+GIOChannelSocketGnuTLS :: on_valid_cert_added (gnutls_x509_crt_t cert, std::string server)
 {}
-#endif  //HAVE_OPENSSL
+#endif  //HAVE_GNUTLS
 
diff --git a/pan/tasks/socket-impl-openssl.h b/pan/tasks/socket-impl-openssl.h
index fbb877a..ff1da0d 100644
--- a/pan/tasks/socket-impl-openssl.h
+++ b/pan/tasks/socket-impl-openssl.h
@@ -31,15 +31,12 @@
 #include <pan/general/quark.h>
 #include <pan/tasks/socket.h>
 #include <pan/tasks/socket-impl-gio.h>
+#include <pan/tasks/socket-impl-main.h>
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
   #include <pan/data/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>
+  #include <gnutls/gnutls.h>
+  #include <gnutls/x509.h>
 #endif
 
 
@@ -50,14 +47,14 @@ namespace pan
    *
    * @ingroup tasks
    */
-#ifdef HAVE_OPENSSL
-  class GIOChannelSocketSSL:
+#ifdef HAVE_GNUTLS
+  class GIOChannelSocketGnuTLS:
     public Socket,
     private CertStore::Listener
   {
     public:
-      virtual ~GIOChannelSocketSSL ();
-      GIOChannelSocketSSL (ServerInfo&, const Quark&, SSL_CTX* ctx, CertStore& cs);
+      virtual ~GIOChannelSocketGnuTLS ();
+      GIOChannelSocketGnuTLS (ServerInfo&, const Quark&, CertStore& cs);
 
       virtual bool open (const StringView& address, int port, std::string& setme_err);
       virtual void write_command (const StringView& chars, Socket::Listener *);
@@ -68,24 +65,17 @@ namespace pan
       GIOChannel * _channel;
       unsigned int _tag_watch;
       unsigned int _tag_timeout;
-      unsigned int _handshake_timeout_tag;
       Socket::Listener * _listener;
       GString * _out_buf;
       GString * _in_buf;
       std::string _partial_read;
       std::string _host;
       bool _io_performed;
-      SSL_CTX * _ctx;
       CertStore& _certstore;
-      SSL_SESSION* _session;
       bool _rehandshake;
       Quark _server;
       bool _done;
 
-    public:
-      void set_rehandshake (bool setme) { _rehandshake = setme; }
-      bool get_done() { return _done; }
-
     private:
       enum WatchMode { READ_NOW, WRITE_NOW, IGNORE_NOW };
       void set_watch_mode (WatchMode mode);
@@ -97,23 +87,27 @@ namespace pan
       DoResult do_write ();
 
       // CertStore::Listener
-      virtual void on_verify_cert_failed (X509*, std::string, std::string, int) ;
-      virtual void on_valid_cert_added (X509*, std::string );
+      virtual void on_verify_cert_failed (gnutls_x509_crt_t, std::string, int) ;
+      virtual void on_valid_cert_added (gnutls_x509_crt_t, 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);
 
     private:
-      GIOChannel* ssl_get_iochannel(GIOChannel *handle, gboolean verify=true);
+      GIOChannel* gnutls_get_iochannel(GIOChannel* channel, const char* host, gboolean verify=true);
+      GIOStatus  _gnutls_handshake (GIOChannel *channel);
+      gboolean verify_certificate (gnutls_session_t session, GError **err);
       static gboolean handshake_cb(gpointer ptr);
+      GIOStatus gnutls_read_line(GString* g, gsize *ret, GError **gerr);
+      GIOStatus gnutls_write_line(GIOChannel *handle, const gchar *buf, gsize len, gsize *ret, GError **gerr);
 
 #else
-  class GIOChannelSocketSSL
+  class GIOChannelSocketGnuTLS
   {
     public:
-      virtual ~GIOChannelSocketSSL ();
-      GIOChannelSocketSSL () { debug("SocketSSL stub ctor"); }
-#endif  // HAVE_OPENSSL
+      virtual ~GIOChannelSocketGnuTLS ();
+      GIOChannelSocketGnuTLS () { debug("SocketSSL stub ctor"); }
+#endif  // HAVE_GNUTLS
   };
 }
 
diff --git a/pan/tasks/socket.h b/pan/tasks/socket.h
index efdf1f8..6947229 100644
--- a/pan/tasks/socket.h
+++ b/pan/tasks/socket.h
@@ -23,13 +23,8 @@
 #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>
+#ifdef HAVE_GNUTLS
+  #include <gnutls/gnutls.h>
 #endif
 
 namespace pan
diff --git a/pan/tasks/task-article.cc b/pan/tasks/task-article.cc
index 161f069..6a0bb8f 100644
--- a/pan/tasks/task-article.cc
+++ b/pan/tasks/task-article.cc
@@ -79,7 +79,9 @@ TaskArticle :: TaskArticle (const ServerRank          & server_rank,
                             ArticleRead               & read,
                             Progress::Listener        * listener,
                             SaveMode                    save_mode,
-                            const Quark               & save_path):
+                            const Quark               & save_path,
+                            const char                * filename,
+                            const SaveOptions         & options):
   Task (save_path.empty() ? "BODIES" : "SAVE", get_description (article, !save_path.empty())),
   _save_path (save_path),
   _server_rank (server_rank),
@@ -90,8 +92,11 @@ TaskArticle :: TaskArticle (const ServerRank          & server_rank,
   _save_mode (save_mode),
   _decoder(0),
   _decoder_has_run (false),
-  _groups(get_groups_str(article))
+  _groups(get_groups_str(article)),
+  _attachment(filename),
+  _options(options)
 {
+
   cache.reserve (article.get_part_mids());
 
   if (listener != 0)
@@ -330,8 +335,8 @@ TaskArticle :: use_decoder (Decoder* decoder)
   init_steps(100);
   _state.set_working();
   const Article::mid_sequence_t mids (_article.get_part_mids());
-  const ArticleCache :: strings_t filenames (_cache.get_filenames (mids));
-  _decoder->enqueue (this, _save_path, filenames, _save_mode);
+  ArticleCache :: strings_t filenames (_cache.get_filenames (mids));
+  _decoder->enqueue (this, _save_path, filenames, _save_mode, _options, _attachment);
   set_status_va (_("Decoding %s"), _article.subject.c_str());
   debug ("decoder thread was free, enqueued work");
 }
diff --git a/pan/tasks/task-article.h b/pan/tasks/task-article.h
index 01afcf6..4494161 100644
--- a/pan/tasks/task-article.h
+++ b/pan/tasks/task-article.h
@@ -48,6 +48,12 @@ namespace pan
   {
     public: // life cycle
 
+      enum SaveOptions
+      {
+        SAVE_ALL,
+        SAVE_AS
+      };
+
       enum SaveMode { NONE=0, DECODE=(1<<0), RAW=(1<<1) };
 
       TaskArticle (const ServerRank   & server_rank,
@@ -57,7 +63,9 @@ namespace pan
                    ArticleRead        & read,
                    Progress::Listener* l=0,
                    SaveMode             save_mode = NONE,
-                   const Quark        & save_path = Quark());
+                   const Quark        & save_path = Quark(),
+                   const char         * filename=0,
+                   const SaveOptions  & options=SAVE_ALL);
       virtual ~TaskArticle ();
       time_t get_time_posted () const { return _time_posted; }
       const Quark& get_save_path () const { return _save_path; }
@@ -95,6 +103,7 @@ namespace pan
       quarks_t _servers;
       const Article _article;
       const time_t _time_posted;
+      StringView _attachment;
 
     private: // implementation
       const SaveMode _save_mode;
@@ -102,20 +111,7 @@ namespace pan
       Decoder * _decoder;
       bool _decoder_has_run;
       std::string _groups;
-
-    private:
-
-//      typedef std::pair<std::string,StringView> lines_p;
-//      typedef std::vector<lines_p> lines_v;
-//
-//      struct CacheAdder
-//      {
-//        lines_v lines;
-//        void add(std::string& q, const StringView& v)
-//          { lines.push_back(lines_p(q,v)); }
-//      };
-//
-//      CacheAdder adder;
+      const SaveOptions _options;
 
       struct Needed {
         std::string message_id;
diff --git a/pan/usenet-utils/mime-utils.cc b/pan/usenet-utils/mime-utils.cc
index 34179ef..0433b53 100644
--- a/pan/usenet-utils/mime-utils.cc
+++ b/pan/usenet-utils/mime-utils.cc
@@ -1617,11 +1617,13 @@ namespace pan
 
     if (info.type == GPG_VERIFY)
     {
-      GMimeSignatureList * sig_list = g_mime_multipart_signed_verify (mps, gpg_ctx, &info.err);
-      if (sig_list) info.no_sigs = false;
-      if (!sig_list) return false;
-      fill_signer_info(info.signers, sig_list);
-
+      GMimeSignatureList * sigs = g_mime_multipart_signed_verify (mps, gpg_ctx, &info.err);
+      if (info.err || !sigs) return false;
+      if (sigs) info.no_sigs = false;
+      fill_signer_info(info.signers, sigs);
+      bool status = get_sig_status(sigs) == GMIME_SIGNATURE_STATUS_GOOD;
+      g_object_unref(sigs);
+      return status;
     }
 
     if (info.type == GPG_DECODE)
@@ -1635,12 +1637,14 @@ namespace pan
       {
         info.no_sigs = false;
         fill_signer_info(info.signers, sigs);
+        bool status = get_sig_status(info.result->signatures) == GMIME_SIGNATURE_STATUS_GOOD;
+        g_object_unref(sigs);
+        return status;
       }
-
+      return !info.err;
     }
 
-    return get_sig_status(info.result->signatures) == GMIME_SIGNATURE_STATUS_GOOD || !info.err;
-
+    return true;
   }
 
   enum
diff --git a/pan/usenet-utils/ssl-utils.h b/pan/usenet-utils/ssl-utils.h
index 51bf9a5..25ce687 100644
--- a/pan/usenet-utils/ssl-utils.h
+++ b/pan/usenet-utils/ssl-utils.h
@@ -5,11 +5,15 @@
  * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
  *
  * This file
- * Copyright (C) 2011 Heinrich Mïller <sphemuel stud informatik uni-erlangen de>
+ * Copyright (C) 2011 Heinrich MÃller <sphemuel stud informatik uni-erlangen de>
  * SSL functions : Copyright (C) 2002 vjt (irssi project)
  * getTimeFromASN1 : Copyright (C) 2003 Jay Case,
  * taken from : http://www.mail-archive.com/openssl-users openssl org/msg33365.html
  *
+ * GnuTLS functions and code
+ * Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2010 Free Software
+ * Foundation, Inc.
+ *
  * 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.
@@ -27,7 +31,7 @@
 #ifndef _SSL_UTILS_H_
 #define _SSL_UTILS_H_
 
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_GNUTLS
 
 #include <pan/data/cert-store.h>
 #include <pan/tasks/socket.h>
@@ -36,15 +40,12 @@
 #include <pan/general/string-view.h>
 #include <pan/tasks/socket.h>
 #include <pan/general/e-util.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>
+#include <gnutls/gnutls.h>
+#include <gnutls/openssl.h>
+#include <gnutls/x509.h>
 #include <map>
-#include <iostream>
 #include <sstream>
+#include <iostream>
 extern "C" {
   #include <glib/gi18n.h>
 }
@@ -52,385 +53,56 @@ extern "C" {
 namespace pan
 {
 
-  /* Checks if the given string has internal NUL characters. */
-  static gboolean has_internal_nul(const char* str, int len) {
-    /* Remove trailing nul characters. They would give false alarms */
-    while (len > 0 && str[len-1] == 0)
-      len--;
-    return strlen(str) != len;
-  }
-
-  /* tls_dns_name - Extract valid DNS name from subjectAltName value */
-  static const char *tls_dns_name(const GENERAL_NAME * gn)
-  {
-    const char *dnsname;
-
-    /* We expect the OpenSSL library to construct GEN_DNS extension objects as
-       ASN1_IA5STRING values. Check we got the right union member. */
-    if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) {
-      g_warning("Invalid ASN1 value type in subjectAltName");
-      return NULL;
-    }
-
-    /* Safe to treat as an ASCII string possibly holding a DNS name */
-    dnsname = (char *) ASN1_STRING_data(gn->d.ia5);
-
-    if (has_internal_nul(dnsname, ASN1_STRING_length(gn->d.ia5))) {
-      g_warning("Internal NUL in subjectAltName");
-      return NULL;
-    }
-
-    return dnsname;
-  }
-
-  /* tls_text_name - extract certificate property value by name */
-  static char *tls_text_name(X509_NAME *name, int nid)
-  {
-    int     pos;
-    X509_NAME_ENTRY *entry;
-    ASN1_STRING *entry_str;
-    int     utf8_length;
-    unsigned char *utf8_value;
-    char *result;
-
-    if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) {
-      return NULL;
-      }
-
-      entry = X509_NAME_get_entry(name, pos);
-      g_return_val_if_fail(entry != NULL, NULL);
-      entry_str = X509_NAME_ENTRY_get_data(entry);
-      g_return_val_if_fail(entry_str != NULL, NULL);
-
-      /* Convert everything into UTF-8. It's up to OpenSSL to do something
-       reasonable when converting ASCII formats that contain non-ASCII
-       content. */
-      if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) {
-        g_warning("Error decoding ASN.1 type=%d", ASN1_STRING_type(entry_str));
-        return NULL;
-      }
-
-      if (has_internal_nul((char *)utf8_value, utf8_length)) {
-        g_warning("NUL character in hostname in certificate");
-        OPENSSL_free(utf8_value);
-        return NULL;
-      }
-
-      result = g_strdup((char *) utf8_value);
-    OPENSSL_free(utf8_value);
-    return result;
-  }
-
-
-  /** check if a hostname in the certificate matches the hostname we used for the connection */
-  static gboolean match_hostname(const char *cert_hostname, const char *hostname)
-  {
-    const char *hostname_left;
-
-    if (!strcasecmp(cert_hostname, hostname)) { /* exact match */
-      return TRUE;
-    } else if (cert_hostname[0] == '*' && cert_hostname[1] == '.' && cert_hostname[2] != 0) { /* wildcard match */
-      /* The initial '*' matches exactly one hostname component */
-      hostname_left = strchr(hostname, '.');
-      if (hostname_left != NULL && ! strcasecmp(hostname_left + 1, cert_hostname + 2)) {
-        return TRUE;
-      }
-    }
-    return FALSE;
-  }
-
-  static gboolean ssl_verify_hostname(X509 *cert, const char *hostname)
-  {
-    int gen_index, gen_count;
-    gboolean matched = FALSE, has_dns_name = FALSE;
-    const char *cert_dns_name;
-    char *cert_subject_cn;
-    const GENERAL_NAME *gn;
-    STACK_OF(GENERAL_NAME) * gens;
-
-    /* Verify the dNSName(s) in the peer certificate against the hostname. */
-    gens = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0);
-    if (gens) {
-      gen_count = sk_GENERAL_NAME_num(gens);
-      for (gen_index = 0; gen_index < gen_count && !matched; ++gen_index) {
-        gn = sk_GENERAL_NAME_value(gens, gen_index);
-        if (gn->type != GEN_DNS)
-          continue;
-
-        /* Even if we have an invalid DNS name, we still ultimately
-           ignore the CommonName, because subjectAltName:DNS is
-           present (though malformed). */
-        has_dns_name = TRUE;
-        cert_dns_name = tls_dns_name(gn);
-        if (cert_dns_name && *cert_dns_name) {
-          matched = match_hostname(cert_dns_name, hostname);
-        }
-        }
-
-        /* Free stack *and* member GENERAL_NAME objects */
-        sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
-    }
-
-    if (has_dns_name) {
-      if (! matched) {
-        /* The CommonName in the issuer DN is obsolete when SubjectAltName is available. */
-        g_warning("None of the Subject Alt Names in the certificate match hostname '%s'", hostname);
-      }
-      return matched;
-    } else { /* No subjectAltNames, look at CommonName */
-      cert_subject_cn = tls_text_name(X509_get_subject_name(cert), NID_commonName);
-        if (cert_subject_cn && *cert_subject_cn) {
-          matched = match_hostname(cert_subject_cn, hostname);
-          if (! matched) {
-          g_warning("SSL certificate common name '%s' doesn't match host name '%s'", cert_subject_cn, hostname);
-          }
-        } else {
-          g_warning("No subjectAltNames and no valid common name in certificate");
-        }
-        free(cert_subject_cn);
-    }
-
-    return matched;
-  }
-
-  static gboolean ssl_verify(CertStore* cs, SSL *ssl, SSL_CTX *ctx, const char* hostname, X509 *cert)
-  {
-    long result;
-
-    result = SSL_get_verify_result(ssl);
-    if (result == X509_V_ERR_CERT_HAS_EXPIRED && cs->is_ignored(cert)) return true;
-    if (result != X509_V_OK) {
-      unsigned char md[EVP_MAX_MD_SIZE];
-      unsigned int n;
-      char *str(0);
-
-      g_warning("Could not verify SSL servers certificate: %s",
-          X509_verify_cert_error_string(result));
-      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;
-    } else if (! ssl_verify_hostname(cert, hostname)){
-      return FALSE;
-    }
-    return TRUE;
-  }
-
-  static std::map<int, Quark> ssl_err;
-  static int map_init(0);
-  typedef std::pair<int, Quark> err_p;
-
-  static void init_err_map()
-  {
-    ssl_err.insert(err_p(2,"X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"));
-    ssl_err.insert(err_p(3,"X509_V_ERR_UNABLE_TO_GET_CRL"));
-    ssl_err.insert(err_p(4,"X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"));
-    ssl_err.insert(err_p(5,"X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"));
-    ssl_err.insert(err_p(6,"X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"));
-    ssl_err.insert(err_p(7,"X509_V_ERR_CERT_SIGNATURE_FAILURE"));
-    ssl_err.insert(err_p(8,"X509_V_ERR_CRL_SIGNATURE_FAILURE"));
-    ssl_err.insert(err_p(9,"X509_V_ERR_CERT_NOT_YET_VALID"));
-    ssl_err.insert(err_p(10,"X509_V_ERR_CERT_HAS_EXPIRED"));
-    ssl_err.insert(err_p(11,"X509_V_ERR_CRL_NOT_YET_VALID"));
-    ssl_err.insert(err_p(12,"X509_V_ERR_CRL_HAS_EXPIRED"));
-    ssl_err.insert(err_p(13,"X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"));
-    ssl_err.insert(err_p(14,"X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"));
-    ssl_err.insert(err_p(15,"X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"));
-    ssl_err.insert(err_p(16,"X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"));
-    ssl_err.insert(err_p(17,"X509_V_ERR_OUT_OF_MEM"));
-    ssl_err.insert(err_p(18,"X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"));
-    ssl_err.insert(err_p(19,"X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"));
-    ssl_err.insert(err_p(20,"X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY "));
-    ssl_err.insert(err_p(21,"X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE	"));
-    ssl_err.insert(err_p(22,"X509_V_ERR_CERT_CHAIN_TOO_LONG"));
-    ssl_err.insert(err_p(23,"X509_V_ERR_CERT_REVOKED"));
-    ssl_err.insert(err_p(24,"X509_V_ERR_INVALID_CA"));
-    ssl_err.insert(err_p(25,"X509_V_ERR_PATH_LENGTH_EXCEEDED"));
-    ssl_err.insert(err_p(26,"X509_V_ERR_INVALID_PURPOSE"));
-    ssl_err.insert(err_p(27,"X509_V_ERR_CERT_UNTRUSTED"));
-    ssl_err.insert(err_p(28,"X509_V_ERR_CERT_REJECTED"));
-  }
-
-  static const Quark
-  ssl_err_to_string(int i)
-  {
-    if (map_init++ == 0) init_err_map();
-    Quark ret;
-    if (ssl_err.count(i)) return ssl_err[i];
-    return ret;
-  }
-
-  static time_t
-  getTimeFromASN1(const ASN1_TIME * aTime)
-  {
-    time_t lResult = 0;
-    char lBuffer[24];
-    char * pBuffer = lBuffer;
-    size_t lTimeLength = aTime->length;
-    char * pString = (char *)aTime->data;
-
-    if (aTime->type == V_ASN1_UTCTIME)
-    {
-      if ((lTimeLength < 11) || (lTimeLength > 17)) return 0;
-      memcpy(pBuffer, pString, 10);
-      pBuffer += 10;
-      pString += 10;
-    }
-    else
-    {
-      if (lTimeLength < 13) return 0;
-      memcpy(pBuffer, pString, 12);
-      pBuffer += 12;
-      pString += 12;
-    }
-
-
-    if ((*pString == 'Z') || (*pString == '-') || (*pString == '+'))
-    {
-      *(pBuffer++) = '0';
-      *(pBuffer++) = '0';
-    }
-    else
-    {
-      *(pBuffer++) = *(pString++);
-      *(pBuffer++) = *(pString++);
-      // Skip any fractional seconds...
-      if (*pString == '.')
-      {
-        pString++;
-        while ((*pString >= '0') && (*pString <= '9'))
-          pString++;
-      }
-    }
-
-    *(pBuffer++) = 'Z';
-    *(pBuffer++) = '\0';
-
-    time_t lSecondsFromUCT;
-    if (*pString == 'Z')
-      lSecondsFromUCT = 0;
-    else
-    {
-      if ((*pString != '+') && (pString[5] != '-')) return 0;
-      lSecondsFromUCT = ((pString[1]-'0') * 10 + (pString[2]-'0')) * 60;
-      lSecondsFromUCT += (pString[3]-'0') * 10 + (pString[4]-'0');
-      if (*pString == '-')
-        lSecondsFromUCT = -lSecondsFromUCT;
-    }
-
-    tm lTime;
-    lTime.tm_sec  = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
-    lTime.tm_min  = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
-    lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
-    lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0');
-    lTime.tm_mon  = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1;
-    lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0');
-    if (lTime.tm_year < 50)
-      lTime.tm_year += 100; // RFC 2459
-    lTime.tm_wday = 0;
-    lTime.tm_yday = 0;
-    lTime.tm_isdst = 0;  // No DST adjustment requested
-
-    lResult = mktime(&lTime);
-
-    if ((time_t)-1 != lResult)
-    {
-      if (0 != lTime.tm_isdst)
-        lResult -= 3600; // mktime may adjust for DST (OS dependent)
-      lResult += lSecondsFromUCT;
-    }
-    else
-      lResult = 0;
-    return lResult;
-  }
-
-
-  static 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;
-  }
-
   typedef std::pair<Quark,Quark> quarks_p;
   typedef std::map<Quark,Quark>::iterator tags_it;
 
   const static char* tags_idx[] =
   {
-    "/L=",
-    "/CN=",
-    "/C=",
-    "/OU=",
-    "/O=",
-    "/ST=",
-    "/EMAIL=",
-    "/emailAddress="
+    ",L=",
+    ",CN=",
+    ",C=",
+    ",OU=",
+    ",O=",
+    ",ST=",
+    ",EMAIL=",
+    ",emailAddress=",
+    ",serialNumber="
   };
 
   const static char* cleaned_tags[] =
   {
-    "L", "CN", "C", "OU", "O", "ST", "EMAIL", "emailAdress"
+    "L", "CN", "C", "OU", "O", "ST", "EMAIL", "emailAdress", "serialNumber"
   };
 
   struct CertParser
   {
-    X509* cert;
     std::map<Quark, Quark> tags;
-    char* issuer, * subject;
     std::string iss, sub;
     int pos1, pos2, tmp_pos1, idx;
-    char buf[256];
+    char buf[2048];
+    char * dn_buf;
     size_t num_tags;
     const char delim;
+    size_t len;
+    gnutls_datum_t d;
+    gnutls_x509_crt_t cert;
+    gnutls_x509_dn_t dn;
+    size_t size;
 
-    CertParser(X509* c) : cert(c), delim('/'), pos1(0), pos2(0), idx(0), num_tags(G_N_ELEMENTS(tags_idx))
+    CertParser(gnutls_x509_crt_t c) : cert(c), delim(','), pos1(0), pos2(0), idx(0), num_tags(G_N_ELEMENTS(tags_idx))
     {
-      issuer  = X509_NAME_oneline(X509_get_issuer_name(c),0, 0);
-      subject = X509_NAME_oneline(X509_get_subject_name(c), 0, 0);
-      iss = issuer;
-      sub = subject;
+
+      gnutls_x509_crt_get_issuer_dn(cert,NULL, &size);
+      dn_buf = new char[size];
+      gnutls_x509_crt_get_issuer_dn(cert,dn_buf, &size);
+      iss = dn_buf;
+      delete dn_buf;
+
+      gnutls_x509_crt_get_subject_unique_id(cert, NULL, &size);
+      dn_buf = new char[size];
+      gnutls_x509_crt_get_subject_unique_id(cert, dn_buf, &size);
+      sub = dn_buf;
+      delete dn_buf;
 
       /* init map */
       int i(0);
@@ -442,11 +114,12 @@ namespace pan
       tags.insert(quarks_p(cleaned_tags[i++],"State"));
       tags.insert(quarks_p(cleaned_tags[i++],"Email Address"));
       tags.insert(quarks_p(cleaned_tags[i],"Email Address"));
+      tags.insert(quarks_p(cleaned_tags[i],"serialNumber"));
     }
 
     void parse(std::vector<quarks_p>& i, std::vector<quarks_p>& s)
     {
-      while(idx<num_tags)
+      while(idx < num_tags)
       {
         std::string::size_type index = iss.find(tags_idx[idx]);
         if(index != std::string::npos)
@@ -505,29 +178,35 @@ namespace pan
 
     ~CertParser ()
     {
-        free(issuer);
-        free(subject);
     }
   };
 
+  static std::string escaped (const std::string& s)
+  {
+    char * pch = g_markup_escape_text (s.c_str(), s.size());
+    const std::string ret (pch);
+    g_free (pch);
+    return ret;
+  }
+
 
   static void
-  pretty_print_x509 (char* buf, size_t size, const Quark& server, X509* cert, bool on_connect)
+  pretty_print_x509 (char* buf, size_t size, const Quark& server, gnutls_x509_crt_t c, bool on_connect)
   {
 
-    if (!cert)
+    if (!c)
     {
       g_snprintf(buf,size, _("Error printing the server certificate for '%s'"), server.c_str());
       return;
     }
 
-    struct CertParser cp(cert);
+    CertParser cp(c);
     std::vector<quarks_p> p_issuer, p_subject;
     cp.parse(p_issuer, p_subject);
 
 
-    time_t t  = getTimeFromASN1(cert->cert_info->validity->notAfter);
-    time_t t2 = getTimeFromASN1(cert->cert_info->validity->notBefore);
+    time_t t  = gnutls_x509_crt_get_expiration_time(c);
+    time_t t2 = gnutls_x509_crt_get_activation_time(c);
     EvolutionDateMaker date_maker;
     char * until = date_maker.get_date_string (t);
     char * before = date_maker.get_date_string (t2);
@@ -536,54 +215,32 @@ namespace pan
     char tmp1[2048], tmp2[2048];
     g_snprintf(tmp1,sizeof(tmp1), "The current server <b>'%s'</b> sent this security certificate :\n\n", server.c_str());
     g_snprintf(tmp2,sizeof(tmp2), "Certificate information for server <b>'%s'</b> :\n\n", server.c_str());
+    size_t md5_size;
+    gnutls_x509_crt_get_fingerprint(c, GNUTLS_DIG_MD5, NULL, &md5_size);
+    char * md5_buf = new char[size];
+    gnutls_x509_crt_get_fingerprint(c, GNUTLS_DIG_MD5, md5_buf, &md5_size);
+
+    g_snprintf(buf,size, _("%s"
+                           "<b>Issuer information:</b>\n"
+                           "%s\n"
+                           //"<b>Subject information: </b>\n"
+                           //"%s\n"
+                           "<b>Valid until : </b>%s\n\n"
+                           "<b>Not valid before : </b>%s\n\n"),
+                           //"<b>Fingerprint (MD5) : </b>\n%s\n\n"),
+                           on_connect ? tmp1 : tmp2,
+                           cp.build_complete(p_issuer).c_str(),
+                           //cp.build_complete(p_subject).c_str(),
+                           until,
+                           before);
+//                           md5_buf);
+
+    g_free (before);
+    g_free (until);
+    delete md5_buf;
 
-    g_snprintf(buf,size, _(     "%s"
-                                "<b>Issuer information:</b>\n"
-                                "%s\n"
-                                "<b>Subject information: </b>\n"
-                                "%s\n"
-                                "<b>Valid until : </b>%s\n\n"
-                                "<b>Not valid before : </b>%s\n\n"
-                                "<b>Fingerprint (MD5) : </b>\n%s\n\n"),
-                                on_connect ? tmp1 : tmp2,
-                                cp.build_complete(p_issuer).c_str(),
-                                cp.build_complete(p_subject).c_str(),
-                                until,
-                                before,
-                                get_x509_fingerpint_md5(cert).c_str());
-
-  }
-
-
-  typedef std::multimap<std::string, Socket*> socks_m;
-  typedef std::pair<std::string, Socket*> socks_p;
-
-  static void delete_all_socks(socks_m& socket_map, std::string server)
-  {
-
-    for (socks_m::iterator it = socket_map.begin(); it != socket_map.end();)
-    {
-      std::cerr<<it->first<<" "<<it->second<<std::endl;
-      if (it->first == server)
-      {
-        it->second->set_abort_flag(true);
-        socket_map.erase(it++);
-      } else
-        ++it;
-    }
   }
 
-  static void delete_sock(socks_m& socket_map, Socket* sock)
-  {
-    for (socks_m::iterator it = socket_map.begin(); it != socket_map.end();)
-    {
-      if (it->second == sock)
-      {
-        delete it->second;
-        socket_map.erase(it);
-      }
-    }
-  }
 
 }
 



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