[pan2: 228/268] * cleanups and fixes + pgp encrypt and sign messages inline (text/plain) (todo : decode signed) + re



commit 056337d5a21a4f9fd2df8c64cddcad87d0f087c9
Author: Heinrich MÃller <henmull src gnome org>
Date:   Tue Dec 6 19:58:24 2011 +0100

    * cleanups and fixes
    + pgp encrypt and sign messages inline (text/plain) (todo : decode signed)
    + revert "download all flagged", is unneeded

 configure.in                   |   10 +-
 gtkspell/gtkspell/gtkspell.c   |   44 ++--
 pan/data-impl/data-impl.cc     |   47 +---
 pan/data-impl/data-impl.h      |    4 +-
 pan/data-impl/profiles.cc      |   26 ++-
 pan/data-impl/server.cc        |   57 +++--
 pan/data/data.h                |   11 +-
 pan/gui/Makefile.am            |   10 +-
 pan/gui/actions.cc             |    6 -
 pan/gui/body-pane.cc           |   10 +-
 pan/gui/gui.cc                 |   36 +--
 pan/gui/gui.h                  |    1 -
 pan/gui/pan-file-entry.cc      |    2 +-
 pan/gui/pan-ui.h               |    1 -
 pan/gui/pan.cc                 |   10 +-
 pan/gui/pan.ui.h               |    4 -
 pan/gui/post-ui.cc             |  294 +++++++++++------------
 pan/gui/post-ui.h              |    7 +-
 pan/gui/post.ui.h              |    2 +
 pan/gui/prefs-ui.cc            |    4 +-
 pan/gui/profiles-dialog.cc     |  138 +++++++++--
 pan/gui/profiles-dialog.h      |   10 +-
 pan/gui/server-ui.cc           |    4 +-
 pan/tasks/nntp.cc              |   20 +-
 pan/tasks/nntp.h               |    2 +-
 pan/tasks/task-upload.h        |   16 --
 pan/usenet-utils/Makefile.am   |    4 +-
 pan/usenet-utils/mime-utils.cc |  517 ++++++++++++++++++++++++++--------------
 pan/usenet-utils/mime-utils.h  |   11 +
 uulib/uulib.c                  |   32 ++--
 30 files changed, 786 insertions(+), 554 deletions(-)
---
diff --git a/configure.in b/configure.in
index b95f646..9175c81 100644
--- a/configure.in
+++ b/configure.in
@@ -46,14 +46,15 @@ dnl GtkSpell is optional: GTKSPELL_REQUIRED refers to the minimum version
 dnl needed if you want to build Pan with spellchecking in the Post window.
 
 GLIB_REQUIRED=2.14.0
-GMIME_REQUIRED=2.4.0
+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
 LIBNOTIFY_REQUIRED=0.4.1
 LIBGSASL_REQUIRED=1.6.1
-LIBGKR_REQUIRED=6.2.2
+LIBGKR_REQUIRED=3.2.2
+LIBGPGME_REQUIRED=1.3.0
 AC_SUBST(GLIB_REQUIRED)
 AC_SUBST(GMIME_REQUIRED)
 AC_SUBST(GTK_REQUIRED)
@@ -147,6 +148,11 @@ if test "x$HAVE_GKR" = "xyes"; then
   AC_DEFINE([HAVE_GKR],[1],[gnome-keyring-1 for password storage])
 fi
 
+dnl Check for gpgme for message encryption / signing
+AM_PATH_GPGME([$LIBGPGME_REQUIRED],[HAVE_GPGME="yes"],[HAVE_GPGME="no"])
+if test "x$HAVE_GPGME" = "xyes"; then
+  AC_DEFINE([HAVE_GPGME],[1],[gpgme for message encryption / signing])
+fi
 
 dnl Check to see if strftime supports the use of %l and %k
 AC_MSG_CHECKING(for %l and %k support in strftime)
diff --git a/gtkspell/gtkspell/gtkspell.c b/gtkspell/gtkspell/gtkspell.c
index ecca064..a648f1e 100644
--- a/gtkspell/gtkspell/gtkspell.c
+++ b/gtkspell/gtkspell/gtkspell.c
@@ -51,7 +51,7 @@ static gboolean
 gtkspell_text_iter_forward_word_end(GtkTextIter *i) {
 	GtkTextIter iter;
 
-/* heuristic: 
+/* heuristic:
  * if we're on an singlequote/apostrophe and
  * if the next letter is alphanumeric,
  * this is an apostrophe. */
@@ -122,7 +122,7 @@ static void
 check_range(GtkSpell *spell, GtkTextBuffer *buffer,
             GtkTextIter start, GtkTextIter end, gboolean force_all) {
 	/* we need to "split" on word boundaries.
-	 * luckily, pango knows what "words" are 
+	 * luckily, pango knows what "words" are
 	 * so we don't have to figure it out. */
 
 	GtkTextIter wstart, wend, cursor, precursor;
@@ -134,7 +134,7 @@ check_range(GtkSpell *spell, GtkTextBuffer *buffer,
 	if (gtk_text_iter_inside_word(&end))
 		gtk_text_iter_forward_word_end(&end);
 	if (!gtk_text_iter_starts_word(&start)) {
-		if (gtk_text_iter_inside_word(&start) || 
+		if (gtk_text_iter_inside_word(&start) ||
 				gtk_text_iter_ends_word(&start)) {
 			gtk_text_iter_backward_word_start(&start);
 		} else {
@@ -153,7 +153,7 @@ check_range(GtkSpell *spell, GtkTextBuffer *buffer,
 	gtk_text_iter_backward_char(&precursor);
 	highlight = gtk_text_iter_has_tag(&cursor, spell->tag_highlight) ||
 			gtk_text_iter_has_tag(&precursor, spell->tag_highlight);
-	
+
 	gtk_text_buffer_remove_tag(buffer, spell->tag_highlight, &start, &end);
 
 	/* Fix a corner case when replacement occurs at beginning of buffer:
@@ -173,11 +173,11 @@ check_range(GtkSpell *spell, GtkTextBuffer *buffer,
 		wend = wstart;
 		gtk_text_iter_forward_word_end(&wend);
 
-		inword = (gtk_text_iter_compare(&wstart, &cursor) < 0) && 
+		inword = (gtk_text_iter_compare(&wstart, &cursor) < 0) &&
 				(gtk_text_iter_compare(&cursor, &wend) <= 0);
 
 		if (inword && !force_all) {
-			/* this word is being actively edited, 
+			/* this word is being actively edited,
 			 * only check if it's already highligted,
 			 * otherwise defer this check until later. */
 			if (highlight)
@@ -232,7 +232,7 @@ insert_text_after(GtkTextBuffer *buffer, GtkTextIter *iter,
 	/* we need to check a range of text. */
 	gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start);
 	check_range(spell, buffer, start, *iter, FALSE);
-	
+
 	gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, iter);
 }
 
@@ -251,7 +251,7 @@ delete_range_after(GtkTextBuffer *buffer,
 }
 
 static void
-mark_set(GtkTextBuffer *buffer, GtkTextIter *iter, 
+mark_set(GtkTextBuffer *buffer, GtkTextIter *iter,
 		 GtkTextMark *mark, GtkSpell *spell) {
 	/* if the cursor has moved and there is a deferred check so handle it now */
 	if ((mark == gtk_text_buffer_get_insert(buffer)) && spell->deferred_check)
@@ -262,7 +262,7 @@ static void
 get_word_extents_from_mark(GtkTextBuffer *buffer,
                      GtkTextIter *start, GtkTextIter *end, GtkTextMark *mark) {
 	gtk_text_buffer_get_iter_at_mark(buffer, start, mark);
-	if (!gtk_text_iter_starts_word(start)) 
+	if (!gtk_text_iter_starts_word(start))
 		gtk_text_iter_backward_word_start(start);
 	*end = *start;
 	if (gtk_text_iter_inside_word(end))
@@ -276,7 +276,7 @@ add_to_dictionary(GtkWidget *menuitem, GtkSpell *spell) {
 
 	get_word_extents_from_mark(spell->buffer, &start, &end, spell->mark_click);
 	word = gtk_text_buffer_get_text(spell->buffer, &start, &end, FALSE);
-	
+
 	enchant_dict_add_to_pwl( spell->speller, word, strlen(word));
 
 	gtkspell_recheck_all(spell);
@@ -291,7 +291,7 @@ ignore_all(GtkWidget *menuitem, GtkSpell *spell) {
 
 	get_word_extents_from_mark(spell->buffer, &start, &end, spell->mark_click);
 	word = gtk_text_buffer_get_text(spell->buffer, &start, &end, FALSE);
-	
+
 	enchant_dict_add_to_session(spell->speller, word, strlen(word));
 
 	gtkspell_recheck_all(spell);
@@ -304,7 +304,7 @@ replace_word(GtkWidget *menuitem, GtkSpell *spell) {
 	char *oldword;
 	const char *newword;
 	GtkTextIter start, end;
-	
+
 	if (!spell->speller)
 		return;
 
@@ -324,7 +324,7 @@ replace_word(GtkWidget *menuitem, GtkSpell *spell) {
 	gtk_text_buffer_insert(spell->buffer, &start, newword, -1);
 	gtk_text_buffer_end_user_action(spell->buffer);
 
-	enchant_dict_store_replacement(spell->speller, 
+	enchant_dict_store_replacement(spell->speller,
 			oldword, strlen(oldword),
 			newword, strlen(newword));
 
@@ -340,7 +340,7 @@ add_suggestion_menus(GtkSpell *spell, GtkTextBuffer *buffer,
 	char **suggestions;
 	size_t n_suggs, i;
 	char *label;
-	
+
 	menu = topmenu;
 
 	if (!spell->speller)
@@ -389,7 +389,7 @@ add_suggestion_menus(GtkSpell *spell, GtkTextBuffer *buffer,
 	label = g_strdup_printf(_("Add \"%s\" to Dictionary"), word);
 	mi = gtk_image_menu_item_new_with_label(label);
 	g_free(label);
-	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), 
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi),
 			gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
 	g_signal_connect(G_OBJECT(mi), "activate",
 			G_CALLBACK(add_to_dictionary), spell);
@@ -398,7 +398,7 @@ add_suggestion_menus(GtkSpell *spell, GtkTextBuffer *buffer,
 
 	/* - Ignore All */
 	mi = gtk_image_menu_item_new_with_label(_("Ignore All"));
-	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), 
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi),
 			gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
 	g_signal_connect(G_OBJECT(mi), "activate",
 			G_CALLBACK(ignore_all), spell);
@@ -500,7 +500,7 @@ populate_popup(GtkTextView *textview, GtkMenu *menu, GtkSpell *spell) {
 	/* we need to figure out if they picked a misspelled word. */
 	get_word_extents_from_mark(spell->buffer, &start, &end, spell->mark_click);
 
-	/* if our highlight algorithm ever messes up, 
+	/* if our highlight algorithm ever messes up,
 	 * this isn't correct, either. */
 	if (!gtk_text_iter_has_tag(&start, spell->tag_highlight))
 		return; /* word wasn't misspelled. */
@@ -524,8 +524,8 @@ button_press_event(GtkTextView *view, GdkEventButton *event, GtkSpell *spell) {
 		if (spell->deferred_check)
 			check_deferred_range(spell, spell->buffer, TRUE);
 
-		gtk_text_view_window_to_buffer_coords(view, 
-				GTK_TEXT_WINDOW_TEXT, 
+		gtk_text_view_window_to_buffer_coords(view,
+				GTK_TEXT_WINDOW_TEXT,
 				event->x, event->y,
 				&x, &y);
 		gtk_text_view_get_iter_at_location(view, &iter, x, y);
@@ -542,7 +542,7 @@ static gboolean
 popup_menu_event(GtkTextView *view, GtkSpell *spell) {
 	GtkTextIter iter;
 
-	gtk_text_buffer_get_iter_at_mark(spell->buffer, &iter, 
+	gtk_text_buffer_get_iter_at_mark(spell->buffer, &iter,
 			gtk_text_buffer_get_insert(spell->buffer));
 	gtk_text_buffer_move_mark(spell->buffer, spell->mark_click, &iter);
 	return FALSE; /* false: let gtk process this event, too. */
@@ -693,7 +693,7 @@ gtkspell_set_buffer(GtkSpell *spell, GtkTextBuffer *buffer)
 #ifdef HAVE_PANGO_UNDERLINE_ERROR
 					"underline", PANGO_UNDERLINE_ERROR,
 #else
-					"foreground", "red", 
+					"foreground", "red",
 					"underline", PANGO_UNDERLINE_SINGLE,
 #endif
 					NULL);
@@ -711,7 +711,7 @@ gtkspell_set_buffer(GtkSpell *spell, GtkTextBuffer *buffer)
 		spell->mark_click = gtk_text_buffer_create_mark(spell->buffer,
 				"gtkspell-click",
 				&start, TRUE);
-			
+
 		spell->deferred_check = FALSE;
 
 		/* now check the entire text buffer. */
diff --git a/pan/data-impl/data-impl.cc b/pan/data-impl/data-impl.cc
index 65e7e73..6d822ee 100644
--- a/pan/data-impl/data-impl.cc
+++ b/pan/data-impl/data-impl.cc
@@ -125,35 +125,9 @@ DataImpl :: save_state ()
   }
 }
 
-
-namespace
-{
-
-  static void
-  stored_password (GnomeKeyringResult res, gpointer user_data)
-  {
-    if (res == GNOME_KEYRING_RESULT_OK)
-    g_print ("password saved successfully!\n");
-    else
-      g_print ("couldn't save password: %s", gnome_keyring_result_to_message (res));
-  }
-
-  static void
-  found_password (GnomeKeyringResult res, const gchar* password, gpointer user_data)
-  {
-    if (res == GNOME_KEYRING_RESULT_OK)
-      g_print ("password found was: %s\n", password);
-    else
-      g_print ("couldn't find password: %s", gnome_keyring_result_to_message (res));
-
-    /* Once this function returns |password| will be freed */
-  }
-
-}
-
 #ifdef HAVE_GKR
 GnomeKeyringResult
-DataImpl :: password_encrypt (const PasswordData& pw)
+DataImpl :: password_encrypt (const PasswordData* pw)
 {
   g_return_val_if_fail (gnome_keyring_is_available(), GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON);
 
@@ -161,17 +135,17 @@ DataImpl :: password_encrypt (const PasswordData& pw)
     gnome_keyring_store_password_sync (
       GNOME_KEYRING_NETWORK_PASSWORD,
       GNOME_KEYRING_DEFAULT,
-      _("Pan server password"),
-      pw.pw.str,
-      "user", pw.user.str,
-      "server", pw.server.c_str(),
+      _("Pan newsreader server passwords"),
+      pw->pw.str,
+      "user", pw->user.str,
+      "server", pw->server.c_str(),
       NULL)
     );
 
 }
 
 GnomeKeyringResult
-DataImpl :: password_decrypt (PasswordData& pw) const
+DataImpl :: password_decrypt (PasswordData* pw) const
 {
 
   gchar* pwd(0);
@@ -181,13 +155,14 @@ DataImpl :: password_decrypt (PasswordData& pw) const
     gnome_keyring_find_password_sync (
     GNOME_KEYRING_NETWORK_PASSWORD,
     &pwd,
-    "user", pw.user.str,
-    "server", pw.server.c_str(),
+    "user", pw->user.str,
+    "server", pw->server.c_str(),
     NULL);
 
-  std::string tmp(pwd);
+  std::string tmp;
+  if (pwd) tmp = pwd;
   gnome_keyring_free_password(pwd);
-  pw.pw = tmp;
+  pw->pw = tmp;
 
   return ret;
 }
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index 8590fd9..2929477 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -111,8 +111,8 @@ namespace pan
 
     public:
 #ifdef HAVE_GKR
-      GnomeKeyringResult password_encrypt (const PasswordData&);
-      GnomeKeyringResult password_decrypt (PasswordData&) const;
+      GnomeKeyringResult password_encrypt (const PasswordData*);
+      GnomeKeyringResult password_decrypt (PasswordData*) const;
 #endif
     private:
 
diff --git a/pan/data-impl/profiles.cc b/pan/data-impl/profiles.cc
index 3ad36b3..b31c9b4 100644
--- a/pan/data-impl/profiles.cc
+++ b/pan/data-impl/profiles.cc
@@ -86,7 +86,7 @@ namespace
         mc.profiles[mc.profile_name].clear ();
     }
 
-    if ((element_name == "signature_file") && !mc.profile_name.empty()) {
+    if ((element_name == "signature-file") && !mc.profile_name.empty()) {
       Profile& p (mc.profiles[mc.profile_name]);
       for (const char **k(attribute_names), **v(attribute_vals); *k; ++k, ++v) {
         if (!strcmp(*k,"active"))
@@ -98,6 +98,15 @@ namespace
         }
       }
     }
+    if ((element_name == "gpg-signature") && !mc.profile_name.empty()) {
+      Profile& p (mc.profiles[mc.profile_name]);
+      for (const char **k(attribute_names), **v(attribute_vals); *k; ++k, ++v) {
+        if (!strcmp(*k,"active"))
+          p.use_sigfile = false;
+        p.sig_type = p.GPGSIG;
+        p.use_gpgsig = true;
+      }
+    }
   }
 
   // Called for close tags </foo>
@@ -113,7 +122,8 @@ namespace
 
     if (!mc.profile_name.empty()) {
       Profile& p (mc.profiles[mc.profile_name]);
-      if (element_name == "signature_file") p.signature_file.assign (t.str, t.len);
+      if (element_name == "signature-file") p.signature_file.assign (t.str, t.len);
+      else if (element_name == "gpg-signature") p.gpg_sig_uid.assign (t.str, t.len);
       else if (element_name == "attribution") p.attribution.assign (t.str, t.len);
       else if (element_name == "fqdn") p.fqdn.assign (t.str, t.len);
       else if (element_name == "xface") p.xface.assign (t.str, t.len);
@@ -200,17 +210,23 @@ ProfilesImpl :: serialize (std::ostream& out) const
     out << indent(depth) << "<address>" << escaped(it->second.address) << "</address>\n";
     out << indent(depth) << "<server>" << escaped(it->second.posting_server.to_view()) << "</server>\n";
     out << indent(depth) << "<xface>" << escaped(it->second.xface) << "</xface>\n";
-    if (!it->second.signature_file.empty()) {
+    if (!it->second.signature_file.empty() && it->second.sig_type != Profile::GPGSIG) {
       const char * type;
       switch (it->second.sig_type) {
         case Profile::FILE: type = "file"; break;
         case Profile::COMMAND: type = "command"; break;
         default: type = "text"; break;
       }
-      out << indent(depth) << "<signature_file"
+      out << indent(depth) << "<signature-file"
                            << " active=\"" << (it->second.use_sigfile ? "true" : "false") << '"'
                            << " type=\"" << type << '"'
-                           << ">" << escaped(it->second.signature_file) << "</signature_file>\n";
+                           << ">" << escaped(it->second.signature_file) << "</signature-file>\n";
+    }
+    if (it->second.use_gpgsig && !it->second.gpg_sig_uid.empty())
+    {
+      out << indent(depth) << "<gpg-signature"
+                           << " active=\"" << (it->second.use_sigfile ? "true" : "false") << '"'
+                           << ">" << escaped(it->second.gpg_sig_uid) << "</gpg-signature>\n";
     }
     if (!it->second.attribution.empty())
       out << indent(depth) << "<attribution>" << escaped(it->second.attribution) << "</attribution>\n";
diff --git a/pan/data-impl/server.cc b/pan/data-impl/server.cc
index ff6cb3f..898f606 100644
--- a/pan/data-impl/server.cc
+++ b/pan/data-impl/server.cc
@@ -130,10 +130,11 @@ DataImpl :: set_server_auth (const Quark       & server,
 #ifndef HAVE_GKR
   s->password = password;
 #else
-  PasswordData pw ;
+  PasswordData pw;
   pw.server = s->host;
   pw.user = username;
   pw.pw = password;
+  password_encrypt(&pw);
 #endif
 
 }
@@ -211,7 +212,31 @@ DataImpl :: get_server_auth (const Quark   & server,
   bool found (s);
   if (found) {
     setme_username = s->username;
+#ifndef HAVE_GKR
     setme_password = s->password;
+#else
+    PasswordData pw;
+    pw.server = s->host;
+    pw.user = s->username;
+    GnomeKeyringResult res (password_decrypt(&pw));
+    switch (res)
+    {
+      case GNOME_KEYRING_RESULT_NO_MATCH:
+        Log::add_info_va(_("There seems to be no Password set for Server %s."), s->host.c_str());
+        break;
+
+      case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
+        Log::add_urgent_va (_("The Gnome keyring denied access to the Passwords."), s->host.c_str());
+        break;
+
+      case GNOME_KEYRING_RESULT_OK:
+        setme_password.assign(pw.pw.str, pw.pw.len);
+        break;
+
+      default:
+        break;
+    }
+#endif
   }
   return found;
 
@@ -393,29 +418,6 @@ DataImpl :: load_server_properties (const DataIO& source)
     s.username = kv["username"];
 #ifndef HAVE_GKR
     s.password = kv["password"];
-#else
-    PasswordData pw ;
-    pw.server = s.host;
-    pw.user = s.username;
-    GnomeKeyringResult res (password_decrypt(pw));
-    switch (res)
-    {
-      case GNOME_KEYRING_RESULT_NO_MATCH:
-        Log::add_info_va(_("There seems to be no password set for server %s."), s.host.c_str());
-        break;
-
-      case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
-        Log::add_urgent_va (_("The gnome keyring denied access to the passwords."), s.host.c_str());
-        break;
-
-      case GNOME_KEYRING_RESULT_OK:
-        s.password.assign(pw.pw.str, pw.pw.len);
-        break;
-
-      default:
-        Log::add_info_va ("%s %s",gnome_keyring_result_to_message (res),res);
-        break;
-    }
 #endif
     s.port = to_int (kv["port"], STD_NNTP_PORT);
     s.max_connections = to_int (kv["connection-limit"], 2);
@@ -467,12 +469,13 @@ DataImpl :: save_server_properties (DataIO& data_io) const
   *out << indent(depth++) << "<server-properties>\n";
   foreach_const (alpha_quarks_t, servers, it) {
     const Server* s (find_server (*it));
-    debug("saving server, "<<s->cert);
+    std::string user, pass;
+    get_server_auth(*it, user, pass);
     *out << indent(depth++) << "<server id=\"" << escaped(it->to_string()) << "\">\n";
     *out << indent(depth) << "<host>" << escaped(s->host) << "</host>\n"
          << indent(depth) << "<port>" << s->port << "</port>\n"
-         << indent(depth) << "<username>" << escaped(s->username) << "</username>\n"
-         << indent(depth) << "<password>" << escaped(s->password) << "</password>\n"
+         << indent(depth) << "<username>" << escaped(user) << "</username>\n"
+         << indent(depth) << "<password>" << escaped(pass) << "</password>\n"
          << indent(depth) << "<expire-articles-n-days-old>" << s->article_expiration_age << "</expire-articles-n-days-old>\n"
          << indent(depth) << "<connection-limit>" << s->max_connections << "</connection-limit>\n"
          << indent(depth) << "<newsrc>" << s->newsrc_filename << "</newsrc>\n"
diff --git a/pan/data/data.h b/pan/data/data.h
index 79dd046..f9d94ea 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -94,8 +94,10 @@ namespace pan
     std::string username;
     std::string address;
     bool use_sigfile;
-    enum { TEXT, FILE, COMMAND };
+    bool use_gpgsig;
+    enum { TEXT, FILE, COMMAND, GPGSIG };
     int sig_type;
+    std::string gpg_sig_uid;
     std::string signature_file;
     std::string attribution;
     std::string fqdn;
@@ -113,10 +115,11 @@ namespace pan
       return it==headers.end() ? nil : it->second;
     }
 
-    Profile(): use_sigfile(false), sig_type(TEXT) {}
+    Profile(): use_sigfile(false), use_gpgsig(false), sig_type(TEXT) {}
 
     void clear() { username.clear(); address.clear();
                    use_sigfile = false;
+                   use_gpgsig = false;
                    sig_type = TEXT;
                    signature_file.clear(); attribution.clear(); }
   };
@@ -216,8 +219,8 @@ namespace pan
 
     public:
 #ifdef HAVE_GKR
-      virtual GnomeKeyringResult password_encrypt (const PasswordData&) = 0;
-      virtual GnomeKeyringResult password_decrypt (PasswordData&) const = 0;
+      virtual GnomeKeyringResult password_encrypt (const PasswordData*) = 0;
+      virtual GnomeKeyringResult password_decrypt (PasswordData*) const = 0;
 #endif
       /** Gets a quark to the provided hostname */
       virtual bool find_server_by_hn (const Quark& server, Quark& setme) const = 0;
diff --git a/pan/gui/Makefile.am b/pan/gui/Makefile.am
index c5517f8..2e930cf 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)"\"
+							@OPENSSL_CFLAGS@ @LIBNOTIFY_CFLAGS@ @LIBGNOME_KEYRING_1_CFLAGS@ @GPGME_CFLAGS@ -DPANLOCALEDIR=\""$(panlocaledir)"\"
 
 noinst_LIBRARIES = libpangui.a
 
@@ -33,7 +33,8 @@ libpangui_a_SOURCES = \
  server-ui.cc \
  task-pane.cc \
  xface.c \
- url.cc
+ url.cc \
+ gpg.cc
 
 noinst_HEADERS = \
  action-manager.h \
@@ -75,7 +76,8 @@ noinst_HEADERS = \
  task-pane.h \
  url.h \
  wait.h \
- xface.h
+ xface.h \
+ gpg.h
 
 EXTRA_DIST = \
  panrc.rc
@@ -95,7 +97,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@ @OPENSSL_LIBS@ @LIBNOTIFY_LIBS@ @LIBGNOME_KEYRING_1_LIBS@ @GPGME_LIBS@
 
 if HAVE_WIN32
 pan_LDFLAGS = -mwindows
diff --git a/pan/gui/actions.cc b/pan/gui/actions.cc
index e553163..a2e0ea5 100644
--- a/pan/gui/actions.cc
+++ b/pan/gui/actions.cc
@@ -150,7 +150,6 @@ namespace
   void do_flag_off                     (GtkAction*) { pan_ui->do_flag_off(); }
   void do_next_flag                    (GtkAction*) { pan_ui->do_next_flag(); }
   void do_last_flag                    (GtkAction*) { pan_ui->do_last_flag(); }
-  void do_download_flagged             (GtkAction*) { pan_ui->do_download_flagged(); }
   void do_invert_selection             (GtkAction*) { pan_ui->do_invert_selection(); }
   void do_mark_all_flagged             (GtkAction*) { pan_ui->do_mark_all_flagged(); }
   void do_show_score_dialog            (GtkAction*) { pan_ui->do_show_score_dialog(); }
@@ -577,11 +576,6 @@ namespace
       N_("_Goto last flagged Thread"),
       G_CALLBACK(do_last_flag) },
 
-    { "get-all-flagged", NULL,
-      N_("_Get all flagged Thread"), NULL, ///TODO invent shortcut
-      N_("_Get all flagged Thread"),
-      G_CALLBACK(do_download_flagged) },
-
     { "invert-selection", NULL,
       N_("_Invert Selection"), "<control>I",
       N_("_Invert Selection"),
diff --git a/pan/gui/body-pane.cc b/pan/gui/body-pane.cc
index 3819aee..bdd4aff 100644
--- a/pan/gui/body-pane.cc
+++ b/pan/gui/body-pane.cc
@@ -969,6 +969,11 @@ BodyPane :: append_part (GMimeObject * obj, GtkAllocation * widget_size)
     is_done = true;
   }
 
+  else if (g_mime_content_type_is_type (type, "text", "*"))
+  {
+
+  }
+
   // otherwise, bitch and moan.
   if (!is_done) {
     const char * filename = g_mime_part_get_filename (part);
@@ -1049,7 +1054,7 @@ BodyPane :: set_text_from_message (GMimeMessage * message)
 {
   const char * fallback_charset (_charset.empty() ? 0 : _charset.c_str());
 
-  // manditory headers...
+  // mandatory headers...
   std::string s;
   size_t w(0), l(0);
   l = add_header_line (s, message, _("Subject"), "Subject", fallback_charset);
@@ -1098,6 +1103,7 @@ BodyPane :: set_text_from_message (GMimeMessage * message)
     GdkPixbuf *pixbuf = NULL;
     pixbuf = pan_gdk_pixbuf_create_from_x_face (pch);
     gtk_image_set_from_pixbuf (GTK_IMAGE(_xface), pixbuf);
+    gtk_widget_show_all(_verbose);
     g_object_unref (pixbuf);
   }
   // set the face
@@ -1416,7 +1422,7 @@ BodyPane :: BodyPane (Data& data, ArticleCache& cache, Prefs& prefs):
   gtk_label_set_ellipsize (GTK_LABEL(w), PANGO_ELLIPSIZE_MIDDLE);
   gtk_label_set_use_markup (GTK_LABEL(w), true);
   gtk_box_pack_start (GTK_BOX(hbox), w, true, true, PAD_SMALL);
-  w = _xface = gtk_image_new ();
+  w = _xface = gtk_image_new();
   gtk_widget_set_size_request (w, 48, 48);
   gtk_box_pack_start (GTK_BOX(hbox), w, false, false, PAD_SMALL);
   w = _face = gtk_image_new ();
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index bb1d060..975fe90 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -69,6 +69,10 @@ extern "C" {
 
 #include "profiles-dialog.h"
 
+#ifdef HAVE_GPGME
+  #include "gpg.h"
+#endif
+
 namespace pan
 {
   void
@@ -170,20 +174,6 @@ namespace
   }
 }
 
-//namespace
-//{
-//  enum {
-//        TARGET_STRING,
-//        TARGET_ROOTWIN
-//  };
-//
-//  static GtkTargetEntry target_list[] = {
-//          { const_cast<char*>("STRING"),     0, TARGET_STRING },
-//          { const_cast<char*>("text/plain"), 0, TARGET_STRING },
-//          { const_cast<char*>("application/x-rootwindow-drop"), 0, TARGET_ROOTWIN }
-//  };
-//}
-
 GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs):
   _data (data),
   _queue (queue),
@@ -354,6 +344,9 @@ GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs):
         on_queue_task_active_changed (queue, *(*it), true);
     }
   }
+#ifdef HAVE_GPGME
+  init_gpg();
+#endif
 }
 
 namespace
@@ -372,7 +365,7 @@ GUI :: ~GUI ()
 
   const std::string accel_filename (get_accel_filename());
   gtk_accel_map_save (accel_filename.c_str());
-  chmod (accel_filename.c_str(), 0600);
+  ::chmod (accel_filename.c_str(), 0600);
 
   if (hpane)
     _prefs.set_int ("main-window-hpane-position", gtk_paned_get_position(GTK_PANED(hpane)));
@@ -402,6 +395,10 @@ GUI :: ~GUI ()
   foreach (std::set<GtkWidget*>, unref, it)
     g_object_unref (*it);
   g_object_unref (G_OBJECT(_ui_manager));
+
+#ifdef HAVE_GPGME
+  deinit_gpg();
+#endif
 }
 
 /***
@@ -1191,14 +1188,6 @@ GUI :: do_last_flag ()
 }
 
 void
-GUI :: do_download_flagged ()
-{
-  do_mark_all_flagged();
-  do_download_selected_article();
-  do_unselect_all_articles();
-}
-
-void
 GUI :: do_invert_selection ()
 {
 //  std::cerr<<__LINE__<< " "<<__FILE__<<" : implement me.\n";
@@ -1646,7 +1635,6 @@ void GUI :: do_tabbed_layout (bool tabbed)
     vpane = 0;
   }
 
-  //gtk_widget_hide_all (_workarea_bin);
   gtk_widget_hide (_workarea_bin);
 
   GtkWidget * group_w (_group_pane->root());
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index 1f38559..9bb36ad 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -140,7 +140,6 @@ namespace pan
       virtual void do_next_flag ();
       virtual void do_last_flag ();
       virtual void do_mark_all_flagged ();
-      virtual void do_download_flagged ();
       virtual void do_invert_selection ();
       virtual void do_show_score_dialog ();
       virtual void do_show_new_score_dialog ();
diff --git a/pan/gui/pan-file-entry.cc b/pan/gui/pan-file-entry.cc
index ded3008..a732bbb 100644
--- a/pan/gui/pan-file-entry.cc
+++ b/pan/gui/pan-file-entry.cc
@@ -81,7 +81,7 @@ pan :: file_entry_new (const char * title, int chooser_action)
   g_object_set_data (G_OBJECT(hbox), "chooser-action", GINT_TO_POINTER(chooser_action));
   g_object_set_data (G_OBJECT(hbox), "entry", e);
   file_entry_set (hbox, g_get_home_dir());
-  
+
   gtk_widget_show (e);
   gtk_widget_show (b);
   return hbox;
diff --git a/pan/gui/pan-ui.h b/pan/gui/pan-ui.h
index a32be83..1921c17 100644
--- a/pan/gui/pan-ui.h
+++ b/pan/gui/pan-ui.h
@@ -71,7 +71,6 @@ namespace pan
     virtual void do_next_flag () = 0;
     virtual void do_last_flag () = 0;
     virtual void do_mark_all_flagged () = 0;
-    virtual void do_download_flagged () = 0;
     virtual void do_invert_selection () = 0;
     virtual void do_cancel_article () = 0;
     virtual void do_supersede_article () = 0;
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index 53a64ee..271131f 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -51,6 +51,12 @@ extern "C" {
   #include <pan/tasks/socket-impl-openssl.h>
 #endif
 
+#ifdef HAVE_GPGME
+  #include <gpgme.h>
+  #define GPG_DEF 1
+  #include <pan/gui/gpg.h>
+#endif
+
 #include <pan/data/cert-store.h>
 #include <pan/tasks/socket-impl-gio.h>
 #include <pan/tasks/socket-impl-main.h>
@@ -82,8 +88,6 @@ namespace
 
 namespace
 {
-//  bool keyring_active(gnome_keyring_is_available());
-
   GMainLoop * nongui_gmainloop (0);
 
   void mainloop ()
@@ -544,7 +548,7 @@ _("General Options\n"
   }
 
   /***
-   ** DBUS STUFF
+   ** DBUS
    ***/
 
   #define PAN_DBUS_SERVICE_NAME      "news.pan.NZB"
diff --git a/pan/gui/pan.ui.h b/pan/gui/pan.ui.h
index 3be86ae..abecb3e 100644
--- a/pan/gui/pan.ui.h
+++ b/pan/gui/pan.ui.h
@@ -138,8 +138,6 @@ const char * fallback_ui_file =
 "        <menuitem action='last-flagged' />\n"
 "        <menuitem action='mark-all-flagged' />\n"
 "        <separator />\n"
-"        <menuitem action='get-all-flagged' />\n"
-"        <separator />\n"
 "        <menuitem action='plonk' />\n"
 "        <menuitem action='view-article-score' />\n"
 "        <separator />\n"
@@ -216,8 +214,6 @@ const char * fallback_ui_file =
 "    <menuitem action='last-flagged' />\n"
 "    <menuitem action='mark-all-flagged' />\n"
 "    <separator />\n"
-"    <menuitem action='get-all-flagged' />\n"
-"    <separator />\n"
 "    <menuitem action='plonk' />\n"
 "    <menuitem action='view-article-score' />\n"
 "    <separator />\n"
diff --git a/pan/gui/post-ui.cc b/pan/gui/post-ui.cc
index 7947e72..2231ad9 100644
--- a/pan/gui/post-ui.cc
+++ b/pan/gui/post-ui.cc
@@ -59,6 +59,11 @@ extern "C" {
 #define DEFAULT_SPELLCHECK_FLAG false
 #endif
 
+#ifdef HAVE_GPGME
+  #include <gpgme.h>
+  #include <pan/gui/gpg.h>
+#endif
+
 using namespace pan;
 
 #define USER_AGENT_PREFS_KEY "add-user-agent-header-when-posting"
@@ -97,6 +102,7 @@ namespace
 
   bool remember_charsets (true);
   bool master_reply (true);
+  bool gpg_enc (false);
 
   void on_remember_charset_toggled (GtkToggleAction * toggle, gpointer)
   {
@@ -108,6 +114,11 @@ namespace
     master_reply = gtk_toggle_action_get_active (toggle);
   }
 
+  void on_enc_toggled (GtkToggleAction * toggle, gpointer)
+  {
+    gpg_enc = gtk_toggle_action_get_active (toggle);
+  }
+
   void on_spellcheck_toggled (GtkToggleAction * toggle, gpointer post_g)
   {
     const bool enabled = gtk_toggle_action_get_active (toggle);
@@ -259,9 +270,12 @@ PostUI :: set_spellcheck_enabled (bool enabled)
   {
 #ifdef HAVE_GTKSPELL
     GtkTextView * view = GTK_TEXT_VIEW(_body_view);
-    GtkSpell * spell = gtkspell_get_from_text_view (view);
-    if (spell)
-      gtkspell_detach (spell);
+    if (view)
+    {
+      GtkSpell * spell = gtkspell_get_from_text_view (view);
+      if (spell)
+        gtkspell_detach (spell);
+    }
 #endif
   }
 }
@@ -311,6 +325,68 @@ PostUI :: set_always_run_editor (bool run)
   _prefs.set_flag ("always-run-editor", run);
 }
 
+std::string
+PostUI :: gpg_sign_and_encrypt(const std::string& body, bool& fail)
+{
+  fail = true;
+
+  const Profile profile (get_current_profile ());
+  std::string uid = profile.gpg_sig_uid;
+  if (uid.empty()) return "";
+
+  gpgme_data_t gpg_buf, gpg_out_buf;
+  gpgme_error_t err;
+  gpgme_key_t mykey(0), key;
+
+  StringView v(body);
+  gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
+  gpgme_data_new (&gpg_out_buf);
+  gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_BASE64);
+
+  /* find key to uid */
+  err = gpgme_op_keylist_start (gpg_ctx, 0, 0);
+  while (!err)
+  {
+    err = gpgme_op_keylist_next (gpg_ctx, &key);
+    if (err) break;
+    if (strcmp(key->subkeys->keyid, uid.c_str()) == 0) { mykey = key; break; }
+  }
+  if (!mykey) return std::string("");
+
+  gpgme_key_t keys[] = { key, NULL} ;
+
+  if (gpg_enc)
+  {
+    gpgme_encrypt_result_t enc_res;
+    err = gpgme_op_encrypt (gpg_ctx, keys, GPGME_ENCRYPT_EXPECT_SIGN, gpg_buf, gpg_out_buf);
+    enc_res = gpgme_op_encrypt_result (gpg_ctx);
+  }
+  else if (profile.use_sigfile)
+  {
+    gpgme_sign_result_t sign_res;
+    gpgme_op_sign (gpg_ctx, gpg_buf, gpg_out_buf, GPGME_SIG_MODE_CLEAR);
+    sign_res = gpgme_op_sign_result (gpg_ctx);
+  }
+
+  gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
+  /* print buf */
+  ssize_t ret;
+  std::stringstream ret_str;
+  char buffer[4096]={0};
+
+  while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer)) > 0))
+  {
+    ret_str << buffer;
+    ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer));
+  }
+
+  gpgme_data_release(gpg_buf);
+  gpgme_data_release(gpg_out_buf);
+
+  fail = false;
+  return ret_str.str();
+}
+
 void
 PostUI :: set_wrap_mode (bool wrap)
 {
@@ -427,6 +503,7 @@ namespace
     { "always-run-editor", 0, N_("Always Run Editor"), 0, 0, G_CALLBACK(do_edit2), false },
     { "remember-charset", 0, N_("Remember Character Encoding for this Group"), 0, 0, G_CALLBACK(on_remember_charset_toggled), true },
     { "master-reply", 0, N_("Thread attached replies"), 0, 0, G_CALLBACK(on_mr_toggled), true },
+    { "gpg-encrypt", 0, N_("GPG-Encrypt the message"), 0, 0, G_CALLBACK(on_enc_toggled), true },
     { "spellcheck", 0, N_("Check _Spelling"), 0, 0, G_CALLBACK(on_spellcheck_toggled), true }
   };
 
@@ -612,7 +689,7 @@ namespace
     return true; // don't invoke the default handler that destroys the widget
   }
 
-   gboolean delete_parts_cb (GtkWidget* w, GdkEvent*, gpointer user_data)
+  gboolean delete_parts_cb (GtkWidget* w, GdkEvent*, gpointer user_data)
   {
     return false;
   }
@@ -984,9 +1061,18 @@ PostUI :: maybe_post_message (GMimeMessage * message)
 
   if(_file_queue_empty)
   {
-    _post_task = new TaskPost (server, message);
-    _post_task->add_listener (this);
-    _queue.add_task (_post_task, Queue::TOP);
+    bool fail;
+    std::string res = gpg_sign_and_encrypt(get_body(), fail);
+    if (!fail)
+    {
+      gtk_text_buffer_set_text (_body_buf, res.c_str(), res.size());
+      GMimeMessage* msg = new_message_from_ui(POSTING);
+      _post_task = new TaskPost (server, msg);
+      _post_task->add_listener (this);
+      _queue.add_task (_post_task, Queue::TOP);
+    } else
+      Log::add_urgent_va("Failed to sign the Message with your public key.");
+      return false;
   } else {
 
     // prepend header for xml file (if one was chosen)
@@ -1367,27 +1453,18 @@ GMimeMessage*
 PostUI :: new_message_from_ui (Mode mode, bool copy_body)
 {
   GMimeMessage * msg(0);
-  if (mode!=MULTI)
-    msg = g_mime_message_new (false);
-  else
-    msg = (GMimeMessage*)g_mime_multipart_new_with_subtype("mixed");
+  msg = g_mime_message_new (false);
 
   // headers from the ui: From
   const Profile profile (get_current_profile ());
   std::string s;
   profile.get_from_header (s);
-  if (mode!=MULTI)
-    g_mime_message_set_sender (msg, s.c_str());
-  else
-    g_mime_object_set_header ((GMimeObject *) msg, "From", s.c_str());
+  g_mime_message_set_sender (msg, s.c_str());
 
   // headers from the ui: Subject
   const char * cpch (gtk_entry_get_text (GTK_ENTRY(_subject_entry)));
   if (cpch) {
-    if (mode!=MULTI)
-      g_mime_message_set_subject (msg, cpch);
-    else
-      g_mime_object_set_header ((GMimeObject *) msg, "Subject", cpch);
+    g_mime_message_set_subject (msg, cpch);
   }
 
   // headers from the ui: To
@@ -1439,11 +1516,11 @@ PostUI :: new_message_from_ui (Mode mode, bool copy_body)
   g_free (pch);
 
   // User-Agent
-  if ((mode==POSTING || mode == UPLOADING || mode == MULTI) && _prefs.get_flag (USER_AGENT_PREFS_KEY, true))
+  if ((mode==POSTING || mode == UPLOADING) && _prefs.get_flag (USER_AGENT_PREFS_KEY, true))
     g_mime_object_set_header ((GMimeObject *) msg, "User-Agent", get_user_agent());
 
   // Message-ID for single text-only posts
-  if ((mode==POSTING || mode==UPLOADING || mode == MULTI) && _prefs.get_flag (MESSAGE_ID_PREFS_KEY, false)) {
+  if ((mode==POSTING || mode==UPLOADING) && _prefs.get_flag (MESSAGE_ID_PREFS_KEY, false)) {
     const std::string message_id = !profile.fqdn.empty()
       ? GNKSA::generate_message_id (profile.fqdn)
       : GNKSA::generate_message_id_from_email_address (profile.address);
@@ -1451,138 +1528,43 @@ PostUI :: new_message_from_ui (Mode mode, bool copy_body)
   }
 
     // body & charset
-    if (mode != MULTI)
-    {
-      std::string body;
-      if (copy_body) body = get_body();
-      GMimeStream * stream = g_mime_stream_mem_new_with_buffer (body.c_str(), body.size());
-      const std::string charset ((mode==POSTING && !_charset.empty()) ? _charset : "UTF-8");
-      if (charset != "UTF-8") {
-        // add a wrapper to convert from UTF-8 to $charset
-        GMimeStream * tmp = g_mime_stream_filter_new (stream);
-        g_object_unref (stream);
-        GMimeFilter * filter = g_mime_filter_charset_new ("UTF-8", charset.c_str());
-        g_mime_stream_filter_add (GMIME_STREAM_FILTER(tmp), filter);
-        g_object_unref (filter);
-        stream = tmp;
-      }
-      GMimeDataWrapper * content_object = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
+  {
+    std::string body;
+    if (copy_body) body = get_body();
+    GMimeStream * stream = g_mime_stream_mem_new_with_buffer (body.c_str(), body.size());
+    const std::string charset ((mode==POSTING && !_charset.empty()) ? _charset : "UTF-8");
+    if (charset != "UTF-8") {
+      // add a wrapper to convert from UTF-8 to $charset
+      GMimeStream * tmp = g_mime_stream_filter_new (stream);
       g_object_unref (stream);
-      GMimePart * part = g_mime_part_new ();
-      pch = g_strdup_printf ("text/plain; charset=%s", charset.c_str());
-
-      GMimeContentType * type = g_mime_content_type_new_from_string (pch);
-      g_free (pch);
-      g_mime_object_set_content_type ((GMimeObject *) part, type); // part owns type now. type isn't refcounted.
-      g_mime_part_set_content_object (part, content_object);
-      g_mime_part_set_content_encoding (part, GMIME_CONTENT_ENCODING_8BIT);
-      g_object_unref (content_object);
-      g_mime_message_set_mime_part (msg, GMIME_OBJECT(part));
-      g_object_unref (part);
+      GMimeFilter * filter = g_mime_filter_charset_new ("UTF-8", charset.c_str());
+      g_mime_stream_filter_add (GMIME_STREAM_FILTER(tmp), filter);
+      g_object_unref (filter);
+      stream = tmp;
     }
+    GMimeDataWrapper * content_object = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
+    g_object_unref (stream);
+    GMimePart * part = g_mime_part_new ();
+    pch = g_strdup_printf ("text/plain; charset=%s", charset.c_str());
+//    pch = g_strdup_printf ("multipart/encrypted;"
+//                           "protocol=\"application/pgp-encrypted\";"
+//                           "boundary=\"------------24i8m5cwm904t8v\"; charset=%s", charset.c_str());
 
-  return msg;
-}
-
-GMimeMultipart*
-PostUI :: new_multipart_from_ui (bool copy_body)
-{
-  GMimeMultipart * msg(g_mime_multipart_new_with_subtype("mixed"));
-
-  // headers from the ui: From
-  const Profile profile (get_current_profile ());
-  std::string s;
-  profile.get_from_header (s);
-  g_mime_object_set_header ((GMimeObject *) msg, "From", s.c_str());
-
-  // headers from the ui: Subject
-  const char * cpch (gtk_entry_get_text (GTK_ENTRY(_subject_entry)));
-  if (cpch)
-    g_mime_object_set_header ((GMimeObject *) msg, "Subject", cpch);
-
-  // headers from the ui: To
-  const StringView to (gtk_entry_get_text (GTK_ENTRY(_to_entry)));
-  if (!to.empty())
-    pan_g_mime_message_add_recipients_from_string ((GMimeMessage*)msg, GMIME_RECIPIENT_TYPE_TO, to.str);
-
-  // headers from the ui: Newsgroups
-  const StringView groups (gtk_entry_get_text (GTK_ENTRY(_groups_entry)));
-  if (!groups.empty())
-    g_mime_object_set_header ((GMimeObject *) msg, "Newsgroups", groups.str);
-
-  // headers from the ui: Followup-To
-  const StringView followupto (gtk_entry_get_text (GTK_ENTRY(_followupto_entry)));
-  if (!followupto.empty())
-    g_mime_object_set_header ((GMimeObject *) msg, "Followup-To", followupto.str);
-
-  // headers from the ui: Reply-To
-  const StringView replyto (gtk_entry_get_text (GTK_ENTRY(_replyto_entry)));
-  if (!replyto.empty())
-    g_mime_object_set_header ((GMimeObject *) msg, "Reply-To", replyto.str);
+    g_mime_object_set_header ((GMimeObject *) msg, "Content-Transfer-Encoding", "Base64");
 
-  // build headers from the 'more headers' entry field
-  std::map<std::string,std::string> headers;
-  GtkTextBuffer * buf (_headers_buf);
-  GtkTextIter start, end;
-  gtk_text_buffer_get_bounds (buf, &start, &end);
-  char * pch = gtk_text_buffer_get_text (buf, &start, &end, false);
-  StringView key, val, v(pch);
-  v.trim ();
-  while (v.pop_token (val, '\n') && val.pop_token(key,':')) {
-    key.trim ();
-    val.eat_chars (1);
-    val.trim ();
-    std::string key_str (key.to_string());
-    if (extra_header_is_editable (key, val))
-      g_mime_object_set_header ((GMimeObject *) msg, key.to_string().c_str(),
-                                val.to_string().c_str());
-  }
-  g_free (pch);
-
-  // User-Agent
-  if (_prefs.get_flag (USER_AGENT_PREFS_KEY, true))
-    g_mime_object_set_header ((GMimeObject *) msg, "User-Agent", get_user_agent());
-
-  // Message-ID for single text-only posts
-  if (_prefs.get_flag (MESSAGE_ID_PREFS_KEY, false)) {
-    const std::string message_id = !profile.fqdn.empty()
-      ? GNKSA::generate_message_id (profile.fqdn)
-      : GNKSA::generate_message_id_from_email_address (profile.address);
-    pan_g_mime_message_set_message_id ((GMimeMessage*)msg, message_id.c_str());
+    GMimeContentType * type = g_mime_content_type_new_from_string (pch);
+    g_free (pch);
+    g_mime_object_set_content_type ((GMimeObject *) part, type); // part owns type now. type isn't refcounted.
+    g_mime_part_set_content_object (part, content_object);
+    g_mime_part_set_content_encoding (part, GMIME_CONTENT_ENCODING_8BIT);
+    g_object_unref (content_object);
+    g_mime_message_set_mime_part (msg, GMIME_OBJECT(part));
+    g_object_unref (part);
   }
 
-    // body & charset
-      std::string body;
-      if (copy_body) body = get_body();
-      GMimeStream * stream = g_mime_stream_mem_new_with_buffer (body.c_str(), body.size());
-      const std::string charset (_charset.empty() ? _charset : "UTF-8");
-      if (charset != "UTF-8") {
-        // add a wrapper to convert from UTF-8 to $charset
-        GMimeStream * tmp = g_mime_stream_filter_new (stream);
-        g_object_unref (stream);
-        GMimeFilter * filter = g_mime_filter_charset_new ("UTF-8", charset.c_str());
-        g_mime_stream_filter_add (GMIME_STREAM_FILTER(tmp), filter);
-        g_object_unref (filter);
-        stream = tmp;
-      }
-      GMimeDataWrapper * content_object = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
-      g_object_unref (stream);
-      GMimePart * part = g_mime_part_new ();
-      pch = g_strdup_printf ("text/plain; charset=%s", charset.c_str());
-
-      GMimeContentType * type = g_mime_content_type_new_from_string (pch);
-      g_free (pch);
-      g_mime_object_set_content_type ((GMimeObject *) part, type); // part owns type now. type isn't refcounted.
-      g_mime_part_set_content_object (part, content_object);
-      g_mime_part_set_content_encoding (part, GMIME_CONTENT_ENCODING_8BIT);
-      g_object_unref (content_object);
-      g_mime_message_set_mime_part ((GMimeMessage*)msg, GMIME_OBJECT(part));
-      g_object_unref (part);
-
   return msg;
 }
 
-
 void
 PostUI :: save_draft ()
 {
@@ -1767,7 +1749,7 @@ namespace
     {
       file :: get_text_file_contents (pch, sig);
     }
-    else // command
+    else if (type == Profile::COMMAND)
     {
       int argc = 0;
       char ** argv = 0;
@@ -1807,6 +1789,10 @@ namespace
 
       g_strfreev (argv);
     }
+    else if (type == Profile::GPGSIG)
+    {
+
+    }
 
     /* Convert signature to UTF-8. Since the signature is a local file,
      * we assume the contents is in the user's locale's charset.
@@ -1926,11 +1912,11 @@ PostUI :: apply_profile_to_body ()
 
   // get the new signature
   std::string sig;
-  if (profile.use_sigfile) {
-    load_signature (profile.signature_file, profile.sig_type, sig);
-    int ignored;
-    if (GNKSA::find_signature_delimiter (sig, ignored) == GNKSA::SIG_NONE)
-      sig = std::string("\n\n-- \n") + sig;
+  if (profile.use_sigfile && !profile.use_gpgsig) {
+      load_signature (profile.signature_file, profile.sig_type, sig);
+      int ignored;
+      if (GNKSA::find_signature_delimiter (sig, ignored) == GNKSA::SIG_NONE)
+        sig = std::string("\n\n-- \n") + sig;
   }
   _current_signature = sig;
 
@@ -2942,18 +2928,16 @@ PostUI :: PostUI (GtkWindow    * parent,
   _body_changed_id(0),
   _body_changed_idle_tag(0),
   _filequeue_eventbox (0),
-  _filequeue_label (0),
-  _multipart(g_mime_multipart_new_with_subtype("mixed"))
-{
+  _filequeue_label (0)
 
-  g_mime_multipart_set_boundary(_multipart,"$pan_multipart_gmime_message$");
+{
 
   rng.seed();
 
   _upload_queue.add_listener (this);
 
   /* init timer for autosave */
-  set_draft_autosave_timeout( prefs.get_int("draft-autosave-timeout-min", 10 ));
+//  set_draft_autosave_timeout( prefs.get_int("draft-autosave-timeout-min", 10 ));
 //  _draft_autosave_id = g_timeout_add_seconds( _draft_autosave_timeout * 60, draft_save_cb, this);
 
   g_assert (profiles.has_profiles());
diff --git a/pan/gui/post-ui.h b/pan/gui/post-ui.h
index d6d6792..9d15665 100644
--- a/pan/gui/post-ui.h
+++ b/pan/gui/post-ui.h
@@ -84,6 +84,8 @@ namespace pan
       void set_wrap_mode (bool wrap);
       void set_always_run_editor (bool);
 
+      std::string gpg_sign_and_encrypt(const std::string& body, bool&);
+
       void update_parts_tab();
 
       //popup action entries
@@ -94,7 +96,6 @@ namespace pan
       void move_down     ();
       void move_top      ();
       void move_bottom   ();
-//      void select_encode (GtkAction*);  deactivated for now
 
       static void do_popup_menu (GtkWidget*, GdkEventButton *event, gpointer pane_g);
       static gboolean on_button_pressed (GtkWidget * treeview, GdkEventButton *event, gpointer userdata);
@@ -161,7 +162,6 @@ namespace pan
       int _total_parts;
       std::string _save_file;
       MTRand rng;
-      GMimeMultipart * _multipart;
 
     private:
       friend class UploadQueue;
@@ -173,9 +173,8 @@ namespace pan
       void add_actions (GtkWidget* box);
       void apply_profile_to_body ();
       void apply_profile_to_headers ();
-      enum Mode { DRAFTING, POSTING, UPLOADING, MULTI };
+      enum Mode { DRAFTING, POSTING, UPLOADING, MULTI};
       GMimeMessage * new_message_from_ui (Mode mode, bool copy_body=true);
-      GMimeMultipart* new_multipart_from_ui (bool copy_body=true);
       Profile get_current_profile ();
       bool check_message (const Quark& server, GMimeMessage*, bool binpost=false);
       bool check_charset ();
diff --git a/pan/gui/post.ui.h b/pan/gui/post.ui.h
index d873cb1..7e91bcb 100644
--- a/pan/gui/post.ui.h
+++ b/pan/gui/post.ui.h
@@ -22,6 +22,8 @@ const char * fallback_post_ui =
 "      <separator />\n"
 "      <menuitem action='master-reply' />\n"
 "      <separator />\n"
+"      <menuitem action='gpg-encrypt' />\n"
+"      <separator />\n"
 "      <menuitem action='run-editor' />\n"
 "      <menuitem action='always-run-editor' />\n"
 "      <separator />\n"
diff --git a/pan/gui/prefs-ui.cc b/pan/gui/prefs-ui.cc
index 73f973a..5e113d3 100644
--- a/pan/gui/prefs-ui.cc
+++ b/pan/gui/prefs-ui.cc
@@ -558,9 +558,9 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
     HIG :: workarea_add_section_title (t, &row, _("Articles"));
     HIG :: workarea_add_section_spacer (t, row, 5);
     w = new_check_button (_("Space selects next article rather than next unread"), "space-selects-next-article", true, prefs);
-    HIG :: workarea_add_wide_control (t, &row, w);        
+    HIG :: workarea_add_wide_control (t, &row, w);
     w = new_check_button (_("Expand threads upon selection."), "expand-selected-articles", false, prefs);
-    HIG :: workarea_add_wide_control (t, &row, w);        
+    HIG :: workarea_add_wide_control (t, &row, w);
     w = new_check_button (_("Always show article deletion confirmation dialog"), "show-deletion-confirm-dialog", true, prefs);
     HIG :: workarea_add_wide_control (t, &row, w);
     w = new_check_button (_("Smooth scrolling"), "smooth-scrolling", true, prefs);
diff --git a/pan/gui/profiles-dialog.cc b/pan/gui/profiles-dialog.cc
index d2496ce..db8e265 100644
--- a/pan/gui/profiles-dialog.cc
+++ b/pan/gui/profiles-dialog.cc
@@ -35,6 +35,11 @@ extern "C" {
 #include "pan-file-entry.h"
 #include "profiles-dialog.h"
 
+#ifdef HAVE_GPGME
+  #include <gpgme.h>
+  #include <pan/gui/gpg.h>
+#endif
+
 using namespace pan;
 
 /***
@@ -61,6 +66,24 @@ namespace
     gtk_widget_set_sensitive (GTK_WIDGET(sensitize), gtk_toggle_button_get_active(tb));
   }
 
+  void on_signature_type_changed (GtkComboBox* w, gpointer g)
+  {
+    ProfileDialog* d(static_cast<ProfileDialog*>(g));
+    g_return_if_fail(d);
+    gint row = gtk_combo_box_get_active (w);
+
+    if (row == 2) // GPG
+    {
+      gtk_widget_hide (d->_signature_file);
+      gtk_widget_show (d->_gpg_sig_entry);
+    }
+    else
+    {
+      gtk_widget_show (d->_signature_file);
+      gtk_widget_hide (d->_gpg_sig_entry);
+    }
+  }
+
   GtkWidget* make_servers_combo (const Data& data, const Quark sel)
   {
     const quarks_t servers (data.get_servers ());
@@ -92,6 +115,8 @@ namespace
   }
 }
 
+/// TODO (perhaps) beautify this!
+
 ProfileDialog :: ProfileDialog (const Data         & data,
                                 const StringView   & profile_name,
                                 const Profile      & profile,
@@ -105,6 +130,8 @@ ProfileDialog :: ProfileDialog (const Data         & data,
   gtk_dialog_set_default_response (GTK_DIALOG(_root), GTK_RESPONSE_OK);
   gtk_window_set_role (GTK_WINDOW(_root), "pan-edit-profile-dialog");
 
+  GtkWidget* hbox(0), * l(0);
+
   int row (0);
   GtkWidget *t = HIG :: workarea_create ();
   HIG :: workarea_add_section_title (t, &row, _("Profile Information"));
@@ -136,33 +163,85 @@ ProfileDialog :: ProfileDialog (const Data         & data,
     w = _signature_file = pan:: file_entry_new (_("Signature File"), GTK_FILE_CHOOSER_ACTION_OPEN);
     g_signal_connect (_signature_file_check, "toggled", G_CALLBACK(on_sig_file_toggled), w);
     file_entry_set (w, profile.signature_file.c_str());
-    HIG :: workarea_add_row (t, &row, _("_Signature:"), w);
 
-    GtkListStore * store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
     GtkTreeIter iter;
+
+    std::map<std::string, int> author_numbers;
+    GtkListStore * store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+    int cnt(0);
+    foreach (signers_m, gpg_signers, it)
+    {
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter, 0, it->second.real_name.c_str(),  1, it->first.c_str(), -1);
+      author_numbers.insert(std::pair<std::string, int>(it->first,cnt++));
+    }
+    w = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
+    hbox = gtk_hbox_new(FALSE, 3);
+    l = gtk_label_new(_("Signer : "));
+    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+    gtk_box_pack_start(GTK_BOX(hbox), l, false, false, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), w, true, true, 0);
+    _gpg_sig_entry = hbox;
+    _gpg_sig_entry_box = w;
+
+    int signer_no(0);
+    if (!profile.gpg_sig_uid.empty())
+    {
+      if (author_numbers.count(profile.gpg_sig_uid) != 0)
+        signer_no = author_numbers.find(profile.gpg_sig_uid)->second;
+    }
+    gtk_combo_box_set_active (GTK_COMBO_BOX(w), signer_no);
+
+
+    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", 0, NULL);
+
+    store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
     gtk_list_store_append (store, &iter);
-    gtk_list_store_set (store, &iter, 0, _("Text File"), 1, Profile::FILE, -1);
+    gtk_list_store_set (store, &iter, 0, _("Text File"),    1, Profile::FILE, -1);
     gtk_list_store_append (store, &iter);
-    gtk_list_store_set (store, &iter, 0, _("Text"),      1, Profile::TEXT, -1);
+    gtk_list_store_set (store, &iter, 0, _("Text"),         1, Profile::TEXT, -1);
     gtk_list_store_append (store, &iter);
-    gtk_list_store_set (store, &iter, 0, _("Command"),   1, Profile::COMMAND, -1);
-    w = _signature_file_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
-    g_signal_connect (_signature_file_check, "toggled", G_CALLBACK(on_sig_file_toggled), w);
-    GtkCellRenderer * renderer (gtk_cell_renderer_text_new ());
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer, TRUE);
+    gtk_list_store_set (store, &iter, 0, _("GPG Signature"),1, Profile::GPGSIG, -1);
+    gtk_list_store_append (store, &iter);
+    gtk_list_store_set (store, &iter, 0, _("Command"),      1, Profile::COMMAND, -1);
+    w = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
+    hbox = gtk_hbox_new(FALSE, 3);
+    l = gtk_label_new(_("Signature Type : "));
+    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+    gtk_box_pack_start(GTK_BOX(hbox), l, false, false, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), w, true, true, 0);
+    _signature_file_combo = hbox;
+    _signature_file_combo_box = w;
+
+    g_signal_connect (w, "changed", G_CALLBACK(on_signature_type_changed), this);
+
+    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", 0, NULL);
+
     int active = 0;
     if (profile.sig_type == profile.TEXT) active = 1;
-    if (profile.sig_type == profile.COMMAND) active = 2;
+    if (profile.sig_type == profile.GPGSIG) active = 2;
+    if (profile.sig_type == profile.COMMAND) active = 3;
+
     gtk_combo_box_set_active (GTK_COMBO_BOX(w), active);
-    HIG :: workarea_add_row (t, &row, _("Signature _Type:"), w);
+
+    GtkWidget* vbox = gtk_vbox_new(TRUE, 3);
+
+    gtk_box_pack_start(GTK_BOX(vbox), _signature_file_combo, false, false, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), _signature_file, false, false, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), _gpg_sig_entry, false, false, 0);
+    HIG :: workarea_add_row (t, &row, "",vbox);
 
   HIG :: workarea_add_section_divider (t, &row);
   HIG :: workarea_add_section_title (t, &row, _("X-Face (Avatar)"));
     w = _xface_entry = gtk_entry_new ();
     set_entry (w, profile.xface);
-    gtk_widget_set_tooltip_markup (w, _("You can add an avatar icon to your postings with a unique X-Face code. \n\
-Add the code without the trailing <b>\"X-Face:\"</b> \n if it was generated by a helper program (for example : http://www.dairiki.org/xface/xface.php)."));
+    gtk_widget_set_tooltip_markup (w, _("You can add an avatar icon to your postings with a unique X-Face code. \n"
+                                        "Add the code without the trailing <b>\"X-Face:\"</b> \n if it was generated "
+                                        "by a helper program (for example : http://www.dairiki.org/xface/xface.php)."));
     HIG :: workarea_add_row (t, &row, _("_X-Face:"), w, NULL);
   HIG :: workarea_add_section_divider (t, &row);
   HIG :: workarea_add_section_title (t, &row, _("Optional Information"));
@@ -170,8 +249,9 @@ Add the code without the trailing <b>\"X-Face:\"</b> \n if it was generated by a
 
     w = _msgid_fqdn_entry = gtk_entry_new ();
     set_entry (w, profile.fqdn);
-    gtk_widget_set_tooltip_text (w, _("When posting to Usenet, your article's Message-ID contains a domain name.  \n\
-You can set a custom domain name here, or leave it blank to let Pan use the domain name from your email address."));
+    gtk_widget_set_tooltip_text (w, _("When posting to Usenet, your article's Message-ID contains a domain name.  \n"
+                                      "You can set a custom domain name here, or leave it blank to let Pan use the "
+                                      "domain name from your email address."));
     HIG :: workarea_add_row (t, &row, _("Message-ID _Domain Name:"), w, NULL);
 
     w = _attribution_entry = gtk_entry_new ();
@@ -200,7 +280,8 @@ You can set a custom domain name here, or leave it blank to let Pan use the doma
       s += it->first + ": " + it->second + "\n";
     gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW(w)), s.c_str(), s.size());
     GtkWidget * eventbox = gtk_event_box_new ();
-    gtk_widget_set_tooltip_text (eventbox, _("Extra headers to be included in your posts, such as\nReply-To: \"Your Name\" <yourname somewhere com>\nOrganization: Your Organization"));
+    gtk_widget_set_tooltip_text (eventbox, _("Extra headers to be included in your posts, such as\nReply-To: \"Your Name\""
+                                             "<yourname somewhere com>\nOrganization: Your Organization\n"));
     GtkWidget * scrolled_window = gtk_scrolled_window_new (NULL, NULL);
     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scrolled_window),
                                          GTK_SHADOW_IN);
@@ -211,8 +292,11 @@ You can set a custom domain name here, or leave it blank to let Pan use the doma
     gtk_container_add (GTK_CONTAINER(eventbox), scrolled_window);
     HIG :: workarea_add_row (t, &row, _("E_xtra Headers:"), eventbox, w);
 
-  on_sig_file_toggled (GTK_TOGGLE_BUTTON(_signature_file_check), _signature_file);
-  on_sig_file_toggled (GTK_TOGGLE_BUTTON(_signature_file_check), _signature_file_combo);
+  /// REMOVED for now!
+//  on_sig_file_toggled (GTK_TOGGLE_BUTTON(_signature_file_check), _gpg_sig_entry_box);
+//  on_sig_file_toggled (GTK_TOGGLE_BUTTON(_signature_file_check), _signature_file);
+//  on_sig_file_toggled (GTK_TOGGLE_BUTTON(_signature_file_check), _signature_file_combo_box);
+
   gtk_box_pack_start (GTK_BOX( gtk_dialog_get_content_area( GTK_DIALOG(_root))), t, true, true, 0);
   gtk_widget_show_all (t);
 
@@ -220,6 +304,8 @@ You can set a custom domain name here, or leave it blank to let Pan use the doma
     gtk_window_set_transient_for (GTK_WINDOW(_root), parent);
     gtk_window_set_position (GTK_WINDOW(_root), GTK_WIN_POS_CENTER_ON_PARENT);
   }
+
+  on_signature_type_changed(GTK_COMBO_BOX(_signature_file_combo_box), this);
 }
 
 ProfileDialog :: ~ProfileDialog ()
@@ -271,16 +357,28 @@ ProfileDialog :: get_profile (std::string& profile_name, Profile& profile)
   from_entry (_attribution_entry, profile.attribution);
 
   profile.use_sigfile = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(_signature_file_check));
-  from_entry (file_entry_gtk_entry(_signature_file), profile.signature_file);
 
   int type;
   GtkTreeIter iter;
-  GtkComboBox * combo = GTK_COMBO_BOX (_signature_file_combo);
+  GtkComboBox * combo = GTK_COMBO_BOX (_signature_file_combo_box);
   gtk_combo_box_get_active_iter (combo, &iter);
   GtkTreeModel * model (gtk_combo_box_get_model (combo));
   gtk_tree_model_get (model, &iter, 1, &type, -1);
   profile.sig_type = type;
 
+  profile.use_gpgsig = (type == profile.GPGSIG);
+  if (!profile.use_gpgsig)
+    from_entry (file_entry_gtk_entry(_signature_file), profile.signature_file);
+
+  std::cerr<<"use gpgsig get profile "<<profile.use_gpgsig<<"\n";
+
+  char* uid;
+  combo = GTK_COMBO_BOX (_gpg_sig_entry_box);
+  gtk_combo_box_get_active_iter (combo, &iter);
+  model = gtk_combo_box_get_model (combo);
+  gtk_tree_model_get (model, &iter, 1, &uid, -1);
+  profile.gpg_sig_uid = uid;
+
   char * pch;
   combo = GTK_COMBO_BOX (_server_combo);
   gtk_combo_box_get_active_iter (combo, &iter);
diff --git a/pan/gui/profiles-dialog.h b/pan/gui/profiles-dialog.h
index 44ce27c..6ec6779 100644
--- a/pan/gui/profiles-dialog.h
+++ b/pan/gui/profiles-dialog.h
@@ -46,12 +46,16 @@ namespace pan
       GtkWidget * _xface_entry;
       GtkWidget * _attribution_entry;
       GtkWidget * _signature_file_check;
-      GtkWidget * _signature_file;
-      GtkWidget * _signature_file_combo;
       GtkWidget * _server_combo;
       GtkWidget * _extra_headers_tv;
-
+      GtkWidget * _signature_file_combo_box;
+      GtkWidget * _signature_file_combo;
+      GtkWidget * _gpg_sig_entry_box;
     public:
+
+      GtkWidget * _signature_file;
+      GtkWidget * _gpg_sig_entry;
+
       static bool run_until_valid_or_cancel (ProfileDialog& p);
   };
 
diff --git a/pan/gui/server-ui.cc b/pan/gui/server-ui.cc
index 35ea2c8..b6cabac 100644
--- a/pan/gui/server-ui.cc
+++ b/pan/gui/server-ui.cc
@@ -137,6 +137,7 @@ namespace
     pan_spin_button_set (d->connection_limit_spin, max_conn);
     pan_entry_set_text (d->auth_username_entry, user);
     pan_entry_set_text (d->auth_password_entry, pass);
+    d->cert = cert;
 
     // set the age combobox
     GtkComboBox * combo (GTK_COMBO_BOX (d->expiration_age_combo));
@@ -183,7 +184,6 @@ namespace
   server_edit_response_cb (GtkDialog * w, int response, gpointer user_data)
   {
     bool destroy (true);
-    bool bf_set (false);
 
     ServerEditDialog * d (static_cast<ServerEditDialog*>(user_data));
     g_assert (d!=NULL);
@@ -208,7 +208,9 @@ 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);
+
       StringView cert(d->cert);
+
 #ifdef HAVE_OPENSSL
       combo = GTK_COMBO_BOX (d->ssl_combo);
       if (gtk_combo_box_get_active_iter (combo, &iter))
diff --git a/pan/tasks/nntp.cc b/pan/tasks/nntp.cc
index 15030c9..8142b84 100644
--- a/pan/tasks/nntp.cc
+++ b/pan/tasks/nntp.cc
@@ -302,10 +302,10 @@ NNTP :: help (Listener * l)
 }
 
 void
-NNTP :: get_group (Listener * l)
+NNTP :: get_group (Listener * l, const Quark& group)
 {
    _listener = l;
-   _commands.push_back ("GROUP \r\n");
+   _commands.push_back (build_command ("GROUP %s\r\n",group.c_str()));
    write_next_command();
 }
 
@@ -318,7 +318,7 @@ NNTP :: xover (const Quark   & group,
 {
    _listener = l;
 
-   get_group(l);
+   get_group(l,group);
 
    _commands.push_back (build_command ("XOVER %"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT"\r\n", low, high));
 
@@ -331,7 +331,7 @@ NNTP :: xover_count_only (const Quark   & group,
 {
    _listener = l;
 
-   get_group(l);
+   get_group(l,group);
 
    _commands.push_back (build_command ("XOVER"));
 
@@ -361,7 +361,7 @@ NNTP :: article (const Quark     & group,
 {
    _listener = l;
 
-   get_group(l);
+   get_group(l,group);
 
    _commands.push_back (build_command ("ARTICLE %"G_GUINT64_FORMAT"\r\n", article_number));
 
@@ -375,7 +375,7 @@ NNTP :: article (const Quark     & group,
 {
    _listener = l;
 
-   get_group(l);
+   get_group(l,group);
 
    _commands.push_back (build_command ("ARTICLE %s\r\n", message_id));
 
@@ -389,7 +389,7 @@ NNTP :: get_headers (const Quark     & group,
 {
   _listener = l;
 
-   get_group(l);
+   get_group(l,group);
 
    _commands.push_back (build_command ("HEAD %s\r\n", message_id));
 
@@ -403,7 +403,7 @@ NNTP :: get_headers (const Quark     & group,
 {
    _listener = l;
 
-   get_group(l);
+   get_group(l,group);
 
    _commands.push_back (build_command ("HEAD %"G_GUINT64_FORMAT"\r\n", article_number));
 
@@ -417,7 +417,7 @@ NNTP :: get_body (const Quark     & group,
 {
   _listener = l;
 
-   get_group(l);
+   get_group(l,group);
 
    _commands.push_back (build_command ("BODY %s\r\n", message_id));
 
@@ -431,7 +431,7 @@ NNTP :: get_body (const Quark     & group,
 {
    _listener = l;
 
-   if (group != _group) get_group(l);
+   if (group != _group) get_group(l,group);
 
    _commands.push_back (build_command ("BODY %"G_GUINT64_FORMAT"\r\n", article_number));
 
diff --git a/pan/tasks/nntp.h b/pan/tasks/nntp.h
index a6bdcdc..60e6d51 100644
--- a/pan/tasks/nntp.h
+++ b/pan/tasks/nntp.h
@@ -162,7 +162,7 @@ namespace pan
     public:
 
       /* Internal only */
-      void get_group (Listener * l);
+      void get_group (Listener * l, const Quark& group);
       void get_headers (const Quark & group, const char * message_id, Listener  * l);
       void get_headers (const Quark & group, uint64_t article_number, Listener * l);
       void get_body (const Quark & group, const char * message_id, Listener  * l);
diff --git a/pan/tasks/task-upload.h b/pan/tasks/task-upload.h
index 9048c71..8e425f2 100644
--- a/pan/tasks/task-upload.h
+++ b/pan/tasks/task-upload.h
@@ -101,22 +101,6 @@ namespace pan
       unsigned long get_bytes_remaining () const;
       void stop ();
       const std::string& get_basename()  { return  _basename; }
-//      std::string enc_mode_to_str(EncodeMode& e)
-//      {
-//        std::string res;
-//        switch (e)
-//        {
-//            case YENC:
-//              res += "yEnc";
-//              break;
-//            case PLAIN:
-//              break;
-//            default:
-//              res += "yEnc";
-//              break;
-//        }
-//        return res;
-//      }
 
       /** only call this for tasks in the NEED_ENCODE state
        * attempts to acquire the encoder thread and start encoding
diff --git a/pan/usenet-utils/Makefile.am b/pan/usenet-utils/Makefile.am
index 7ee5aed..1ebac86 100644
--- a/pan/usenet-utils/Makefile.am
+++ b/pan/usenet-utils/Makefile.am
@@ -1,4 +1,6 @@
-AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@
+AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @GPGME_CFLAGS@
+
+AM_LDFLAGS = @GPGME_LIBS@
 
 noinst_LIBRARIES = libusenetutils.a
 
diff --git a/pan/usenet-utils/mime-utils.cc b/pan/usenet-utils/mime-utils.cc
index 4b24ff6..182c894 100644
--- a/pan/usenet-utils/mime-utils.cc
+++ b/pan/usenet-utils/mime-utils.cc
@@ -22,6 +22,7 @@
 #include <cstring>
 #include <cstdlib>
 #include <iostream>
+#include <sstream>
 extern "C"
 {
   #include <unistd.h>
@@ -36,6 +37,11 @@ extern "C"
 
 #define is_nonempty_string(a) ((a) && (*a))
 
+#ifdef HAVE_GPGME
+  #include <gpgme.h>
+  #include <pan/gui/gpg.h>
+#endif
+
 using namespace pan;
 
 namespace
@@ -329,6 +335,37 @@ namespace
    }
 }
 
+/***
+**** GPG
+***/
+
+namespace
+{
+
+  bool
+  gpg_is_beginning_line (const StringView& line)
+  {
+    if (line.len <= 1) return false;//g_return_val_if_fail(line.len > 1, false);
+    StringView l(line);
+//    l.ltrim(); l.ltrim();
+    return !strncmp (l.str, GPG_MARKER_BEGIN, GPG_MARKER_BEGIN_LEN);
+  }
+
+  bool
+  gpg_is_ending_line (const StringView& line)
+  {
+    if (line.len <= 1) return false;//g_return_val_if_fail(line.len > 1, false);
+    StringView l(line);
+//    l.ltrim(); l.ltrim();
+    return !strncmp (l.str, GPG_MARKER_END, GPG_MARKER_END_LEN);
+  }
+
+}
+
+/***
+****
+***/
+
 namespace
 {
   guint
@@ -359,9 +396,11 @@ namespace
 
 enum EncType
 {
-	ENC_PLAIN,
-	ENC_YENC,
-	ENC_UU
+	ENC_PLAIN = 1 ,
+	ENC_YENC = 1 << 1,
+	ENC_UU = 1 << 2,
+	ENC_GPG = 1 << 3,
+	ENC_BASE64 = 1<< 4
 };
 
 namespace
@@ -423,22 +462,81 @@ namespace
     return true;
   }
 
+  GMimeStream* gpg_decrypt (gpgme_ctx_t gpg_ctx, GMimeStream* s, TempPart* part)
+  {
+
+    GMimeStream* decrypted = g_mime_stream_mem_new ();
+
+    ssize_t stream_len = g_mime_stream_length(s);
+    char* streambuf = new char[stream_len];
+    g_mime_stream_read(s, streambuf, stream_len);
+    g_object_unref(s);
+
+    gpgme_data_t gpg_buf, gpg_out_buf;
+    gpgme_key_t key;
+    gpgme_error_t err;
+    bool found(false);
+
+    StringView v(streambuf);
+    gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
+
+    gpgme_strerror(gpgme_data_new (&gpg_out_buf));
+
+    gpgme_signers_clear(gpg_ctx);
+
+    gpgme_set_armor (gpg_ctx, 1);
+    gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_NONE);
+
+    err = gpgme_op_decrypt (gpg_ctx, gpg_buf, gpg_out_buf);
+//    gpgme_op_decrypt_result
+
+    delete streambuf;
+
+    gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
+
+    ssize_t ret(0);
+    char buffer[4096]={0};
+    gint64 len(0);
+
+    while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
+    {
+      len += g_mime_stream_write(decrypted, buffer, ret);
+    }
+
+    g_mime_stream_flush (decrypted);
+
+    gpgme_data_release(gpg_buf);
+    gpgme_data_release(gpg_out_buf);
+
+    g_mime_stream_reset(decrypted);
+
+    return decrypted;
+  }
+
   void apply_source_and_maybe_filter (TempPart * part, GMimeStream * s)
   {
+
     if (!part->stream) {
       part->stream = g_mime_stream_mem_new ();
-      if (part->type != ENC_PLAIN) {
-        part->filter_stream =
-          g_mime_stream_filter_new (part->stream);
-        part->filter = part->type == ENC_UU
-          ? g_mime_filter_basic_new (GMIME_CONTENT_ENCODING_UUENCODE, FALSE)
-          : g_mime_filter_yenc_new (FALSE);
+      if (part->type != ENC_PLAIN && part->type != ENC_GPG) {
+        part->filter_stream = g_mime_stream_filter_new (part->stream);
+        switch (part->type)
+        {
+          case ENC_UU:
+            part->filter = g_mime_filter_basic_new (GMIME_CONTENT_ENCODING_UUENCODE, FALSE);
+            break;
+
+          case ENC_YENC:
+            part->filter = g_mime_filter_yenc_new (FALSE);
+            break;
+        }
+
         g_mime_stream_filter_add (GMIME_STREAM_FILTER(part->filter_stream),
                                   part->filter);
       }
     }
 
-    g_mime_stream_write_to_stream (s, part->type == ENC_PLAIN ?
+    g_mime_stream_write_to_stream (s, (part->type == ENC_PLAIN || part->type == ENC_GPG) ?
 				   part->stream : part->filter_stream);
     g_object_unref (s);
   }
@@ -452,212 +550,245 @@ namespace
     sep_state():uu_temp(NULL) {};
   };
 
-bool
-separate_encoded_parts (GMimeStream  * istream, sep_state &state)
-{
-  temp_parts_t& master(state.master_list);
-  temp_parts_t& appendme(state.current_list);
-  TempPart * cur = NULL;
-  EncType type = ENC_PLAIN;
-  GByteArray * line;
-  gboolean yenc_looking_for_part_line = FALSE;
-  gint64 linestart_pos = 0;
-  gint64 sub_begin = 0;
-  guint line_len;
-  bool found = false;
-
-  /* sanity clause */
-  pan_return_val_if_fail (istream!=NULL,false);
-
-  sub_begin = 0;
-  line = g_byte_array_sized_new (4096);
-
-  while ((line_len = stream_readln (istream, line, &linestart_pos)))
+  bool
+  separate_encoded_parts (GMimeStream  * istream, sep_state &state)
   {
-    char * line_str = (char*) line->data;
-    char * pch = strchr (line_str, '\n');
-    if (pch != NULL) {
-      pch[1] = '\0';
-      line_len = pch - line_str;
-    }
+    temp_parts_t& master(state.master_list);
+    temp_parts_t& appendme(state.current_list);
+    TempPart * cur = NULL;
+    EncType type = ENC_PLAIN;
+    GByteArray * line;
+    gboolean yenc_looking_for_part_line = FALSE;
+    gboolean gpg_looking_for_line = FALSE;
+    gint64 linestart_pos = 0;
+    gint64 sub_begin = 0;
+    guint line_len;
+    bool found = false;
 
-    switch (type)
+    /* sanity clause */
+    pan_return_val_if_fail (istream!=NULL,false);
+
+    sub_begin = 0;
+    line = g_byte_array_sized_new (4096);
+
+    while ((line_len = stream_readln (istream, line, &linestart_pos)))
     {
-      case ENC_PLAIN:
-      {
-        const StringView line_pstr (line_str, line_len);
+      char * line_str = (char*) line->data;
+      char * pch = strchr (line_str, '\n');
+      if (pch != NULL) {
+        pch[1] = '\0';
+        line_len = pch - line_str;
+      }
 
-        if (uu_is_beginning_line (line_pstr))
+      switch (type)
+      {
+        case ENC_PLAIN:
         {
-          gulong mode = 0ul;
-          char * filename = NULL;
+          const StringView line_pstr (line_str, line_len);
 
-          found=true;
-          // flush the current entry
-          if (cur != NULL) {
-            GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+          if (uu_is_beginning_line (line_pstr))
+          {
+            gulong mode = 0ul;
+            char * filename = NULL;
+
+            found=true;
+            // flush the current entry
+            if (cur != NULL) {
+              GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
               apply_source_and_maybe_filter (cur, s);
-            if ( append_if_not_present (master, cur) )
-              append_if_not_present (appendme, cur);
-            cur = NULL;
-          }
 
-          // start a new section (or, if filename matches, continue an existing one)
-          sub_begin = linestart_pos;
-          uu_parse_begin_line (line_pstr, &filename, &mode);
-          cur = find_filename_part (master, filename);
-          if (cur)
-            g_free (filename);
-          else
-            cur = new TempPart (type=ENC_UU, filename);
-          state.uu_temp = cur;
-        }
-        else if (yenc_is_beginning_line (line_str))
-        {
-          found = true;
-          // flush the current entry
-          if (cur != NULL) {
-            GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+              if ( append_if_not_present (master, cur) )
+                append_if_not_present (appendme, cur);
+              cur = NULL;
+            }
+
+            // start a new section (or, if filename matches, continue an existing one)
+            sub_begin = linestart_pos;
+            uu_parse_begin_line (line_pstr, &filename, &mode);
+            cur = find_filename_part (master, filename);
+            if (cur)
+              g_free (filename);
+            else
+              cur = new TempPart (type=ENC_UU, filename);
+            state.uu_temp = cur;
+          }
+          else if (yenc_is_beginning_line (line_str))
+          {
+            found = true;
+            // flush the current entry
+            if (cur != NULL) {
+              GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
               apply_source_and_maybe_filter (cur, s);
-            if ( append_if_not_present (master, cur) )
-              append_if_not_present (appendme, cur);
-            cur = NULL;
+
+              if ( append_if_not_present (master, cur) )
+                append_if_not_present (appendme, cur);
+              cur = NULL;
+            }
+            sub_begin = linestart_pos;
+
+            // start a new section (or, if filename matches, continue an existing one)
+            char * fname;
+            int line_len, attach_size, part;
+            yenc_parse_begin_line (line_str, &fname, &line_len, &attach_size, &part);
+            cur = find_filename_part (master, fname);
+            if (cur) {
+              g_free (fname);
+              g_mime_filter_yenc_set_state (GMIME_FILTER_YENC (cur->filter),
+                                            GMIME_YDECODE_STATE_INIT);
+            }
+            else
+            {
+              cur = new TempPart (type=ENC_YENC, fname);
+              cur->y_line_len = line_len;
+              cur->y_attach_size = attach_size;
+              cur->y_part = part;
+              yenc_looking_for_part_line = cur->y_part!=0;
+            }
+          }
+          else if (gpg_is_beginning_line (line_str))
+          {
+
+            found = true;
+            cur = new TempPart (type = ENC_GPG);
+            gpg_looking_for_line = true;
+            sub_begin = linestart_pos;
           }
-          sub_begin = linestart_pos;
-
-          // start a new section (or, if filename matches, continue an existing one)
-          char * fname;
-          int line_len, attach_size, part;
-          yenc_parse_begin_line (line_str, &fname, &line_len, &attach_size, &part);
-          cur = find_filename_part (master, fname);
-          if (cur) {
-            g_free (fname);
-            g_mime_filter_yenc_set_state (GMIME_FILTER_YENC (cur->filter),
-                                          GMIME_YDECODE_STATE_INIT);
+          else if (state.uu_temp != NULL && is_uu_line(line_str, line_len) )
+          {
+            // continue an incomplete uu decode
+            found = true;
+            // flush the current entry
+            if (cur != NULL) {
+              GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+              apply_source_and_maybe_filter (cur, s);
+
+              if ( append_if_not_present (master, cur) )
+                append_if_not_present (appendme, cur);
+              cur = NULL;
+            }
+            sub_begin = linestart_pos;
+            cur = state.uu_temp;
+            ++cur->valid_lines;
+            type = ENC_UU;
           }
-          else
+          else if (cur == NULL)
           {
-            cur = new TempPart (type=ENC_YENC, fname);
-            cur->y_line_len = line_len;
-            cur->y_attach_size = attach_size;
-            cur->y_part = part;
-            yenc_looking_for_part_line = cur->y_part!=0;
+            sub_begin = linestart_pos;
+
+            cur = new TempPart (type = ENC_PLAIN);
           }
+          break;
         }
-        else if (state.uu_temp != NULL && is_uu_line(line_str, line_len) )
+        case ENC_UU:
         {
-          // continue an incomplete uu decode
-          found = true;
-          // flush the current entry
-          if (cur != NULL) {
-            GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
-              apply_source_and_maybe_filter (cur, s);
-            if ( append_if_not_present (master, cur) )
+          if (uu_is_ending_line(line_str))
+          {
+            GMimeStream * stream;
+            if (sub_begin < 0)
+              sub_begin = linestart_pos;
+            stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
+            apply_source_and_maybe_filter (cur, stream);
+            if( append_if_not_present (master, cur) )
               append_if_not_present (appendme, cur);
+
             cur = NULL;
+            type = ENC_PLAIN;
+            state.uu_temp = NULL;
           }
-          sub_begin = linestart_pos;
-          cur = state.uu_temp;
-          ++cur->valid_lines;
-          type = ENC_UU;
-        }
-        else if (cur == NULL)
-        {
-          sub_begin = linestart_pos;
+          else if (!is_uu_line(line_str, line_len))
+          {
+            /* hm, this isn't a uenc line, so ending the cat and setting sub_begin to -1 */
+            if (sub_begin >= 0)
+            {
+              GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+                apply_source_and_maybe_filter (cur, stream);
+            }
 
-          cur = new TempPart (type = ENC_PLAIN);
-        }
-        break;
-      }
-      case ENC_UU:
-      {
-        if (uu_is_ending_line(line_str))
-        {
-          GMimeStream * stream;
-          if (sub_begin < 0)
+            sub_begin = -1;
+          }
+          else if (sub_begin == -1)
+          {
+            /* looks like they decided to start using uu lines again. */
+            ++cur->valid_lines;
             sub_begin = linestart_pos;
-          stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
-          apply_source_and_maybe_filter (cur, stream);
-          if( append_if_not_present (master, cur) )
-            append_if_not_present (appendme, cur);
-
-          cur = NULL;
-          type = ENC_PLAIN;
-          state.uu_temp = NULL;
-        }
-        else if (!is_uu_line(line_str, line_len))
-        {
-          /* hm, this isn't a uenc line, so ending the cat and setting sub_begin to -1 */
-          if (sub_begin >= 0)
+          }
+          else
           {
-            GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
-              apply_source_and_maybe_filter (cur, stream);
+            ++cur->valid_lines;
           }
-
-          sub_begin = -1;
-        }
-        else if (sub_begin == -1)
-        {
-          /* looks like they decided to start using uu lines again. */
-          ++cur->valid_lines;
-          sub_begin = linestart_pos;
+          break;
         }
-        else
+        case ENC_YENC:
         {
-          ++cur->valid_lines;
-        }
-        break;
-      }
-      case ENC_YENC:
-      {
-        if (yenc_is_ending_line (line_str))
-        {
-          GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
+          if (yenc_is_ending_line (line_str))
+          {
+            GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
             apply_source_and_maybe_filter (cur, stream);
-          yenc_parse_end_line (line_str, &cur->y_size, NULL, &cur->y_pcrc, &cur->y_crc);
-          if( append_if_not_present (master, cur) )
-            append_if_not_present (appendme, cur);
+            yenc_parse_end_line (line_str, &cur->y_size, NULL, &cur->y_pcrc, &cur->y_crc);
+            if( append_if_not_present (master, cur) )
+              append_if_not_present (appendme, cur);
 
-          cur = NULL;
-          type = ENC_PLAIN;
-        }
-        else if (yenc_looking_for_part_line && yenc_is_part_line(line_str))
-        {
-          yenc_parse_part_line (line_str, &cur->y_offset_begin, &cur->y_offset_end);
-          yenc_looking_for_part_line = FALSE;
-          ++cur->valid_lines;
+            cur = NULL;
+            type = ENC_PLAIN;
+          }
+          else if (yenc_looking_for_part_line && yenc_is_part_line(line_str))
+          {
+            yenc_parse_part_line (line_str, &cur->y_offset_begin, &cur->y_offset_end);
+            yenc_looking_for_part_line = FALSE;
+            ++cur->valid_lines;
+          }
+          else
+          {
+            ++cur->valid_lines;
+          }
+          break;
         }
-        else
+        case ENC_GPG:
         {
-          ++cur->valid_lines;
+          if (gpg_is_ending_line(line_str))
+          {
+            GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
+            GMimeStream * dec = gpg_decrypt(gpg_ctx, stream, cur);
+            apply_source_and_maybe_filter (cur, dec);
+            gpg_looking_for_line = false;
+            if( append_if_not_present (master, cur) )
+              append_if_not_present (appendme, cur);
+
+            cur = NULL;
+            type = ENC_PLAIN;
+            sub_begin = -1;
+          }
+          else if (gpg_looking_for_line)
+          {
+            ++cur->valid_lines;
+          }
+          break;
         }
-        break;
       }
     }
-  }
 
-  /* close last entry */
-  if (cur != NULL)
-  {
-    if (sub_begin >= 0)
+    /* close last entry */
+    if (cur != NULL)
     {
-      GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+      if (sub_begin >= 0)
+      {
+        GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
         apply_source_and_maybe_filter (cur, stream);
-    }
+      }
 
-    /* just in case someone started with "yenc" or "begin 644 asf" in a text message to fuck with unwary newsreaders */
-    if (cur->valid_lines < 10u)
-      cur->type = ENC_PLAIN;
+      /* just in case someone started with "yenc" or "begin 644 asf" in a text message to fuck with unwary newsreaders */
+      if (cur->valid_lines < 10u)
+        cur->type = ENC_PLAIN;
 
-    if( append_if_not_present (master, cur) )
-      append_if_not_present (appendme, cur);
-    cur = NULL;
-    type = ENC_PLAIN;
-  }
+      if( append_if_not_present (master, cur) )
+        append_if_not_present (appendme, cur);
+      cur = NULL;
+      type = ENC_PLAIN;
+    }
 
-  g_byte_array_free (line, TRUE);
-  return found;
-}
+    g_byte_array_free (line, TRUE);
+    return found;
+  }
 
 }
 
@@ -782,7 +913,7 @@ namespace
     // break it into separate parts for text, uu, and yenc pieces.
     sep_state &state(*(sep_state*)data);
     temp_parts_t &parts(state.current_list);
-    bool split=separate_encoded_parts (istream, state);
+    bool split = separate_encoded_parts (istream, state);
     g_mime_stream_reset (istream);
 
     // split?
@@ -883,6 +1014,21 @@ namespace{
       return;
     v->push_back(temp_p(parent,part) );
   }
+
+  void find_gpg_parts_cb(GMimeObject *parent, GMimeObject *part, gpointer data)
+  {
+    if(!GMIME_IS_PART(part))
+      return;
+
+    temp_p_t *v( (temp_p_t *) data);
+
+    GMimeContentType * content_type = g_mime_object_get_content_type (part);
+    if (!(g_mime_content_type_is_type (content_type, "multipart", "encrypted") ||
+          g_mime_content_type_is_type (content_type, "text", "plain")))
+      return;
+    v->push_back(temp_p(parent,part) );
+  }
+
 }
 
 /***
@@ -940,6 +1086,15 @@ mime :: construct_message (GMimeStream  ** istreams,
     handle_uu_and_yenc_in_text_plain_cb(data.parent, data.part, &state);
   }
 
+//  partslist.clear();
+//  if (retval != NULL)
+//    g_mime_message_foreach(retval, find_text_cb, &partslist);
+//  foreach(temp_p_t, partslist, it)
+//  {
+//    temp_p &data(*it);
+//    handle_gpg_encrypted_parts_cb(data.parent, data.part, &state);
+//  }
+
   // cleanup
   foreach (temp_parts_t, state.master_list, it)
   {
@@ -1186,7 +1341,7 @@ namespace
     g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
 
     if (!mime_part->content || !mime_part->content->stream) {
-      g_warning ("no content set on this mime part");
+//      g_warning ("no content set on this mime part");  // dbg
       return NULL;
     }
 
diff --git a/pan/usenet-utils/mime-utils.h b/pan/usenet-utils/mime-utils.h
index 403503b..93a838e 100644
--- a/pan/usenet-utils/mime-utils.h
+++ b/pan/usenet-utils/mime-utils.h
@@ -55,6 +55,17 @@
 #define YENC_SHIFT             42
 #define YENC_QUOTE_SHIFT       64
 
+/***
+**** GPG
+***/
+
+#define GPG_MARKER_BEGIN          "-----BEGIN PGP MESSAGE-----"
+#define GPG_MARKER_BEGIN_LEN      27
+#define GPG_MARKER_END            "-----END PGP MESSAGE-----"
+#define GPG_MARKER_END_LEN        25
+#define GPG_MARKER_SIGNED_BEGIN   "-----BEGIN PGP SIGNED MESSAGE-----"
+#define GPG_MARKER_SIGNED_LEN
+
 namespace pan
 {
   /**
diff --git a/uulib/uulib.c b/uulib/uulib.c
index de8add7..1b7060b 100644
--- a/uulib/uulib.c
+++ b/uulib/uulib.c
@@ -84,7 +84,7 @@
 char * uulib_id = "$Id$";
 
 #ifdef SYSTEM_WINDLL
-BOOL _export WINAPI 
+BOOL _export WINAPI
 DllEntryPoint (HINSTANCE hInstance, DWORD seginfo,
 	       LPVOID lpCmdLine)
 {
@@ -574,7 +574,7 @@ UUstrerror (int code)
  */
 
 int UUEXPORT
-UUSetMsgCallback (void *opaque, 
+UUSetMsgCallback (void *opaque,
 		  void (*func) _ANSI_ARGS_((void *, char *, int)))
 {
   uu_MsgCallback = func;
@@ -719,7 +719,7 @@ UULoadFileWithPartNo (char *filename, char *fileid, int delflag, int partno)
     fileid = filename;
 
   while (!feof (datei) && !ferror (datei)) {
-    /* 
+    /*
      * Peek file, or some systems won't detect EOF
      */
     res = fgetc (datei);
@@ -727,7 +727,7 @@ UULoadFileWithPartNo (char *filename, char *fileid, int delflag, int partno)
       break;
     else
       ungetc (res, datei);
-    
+
     if ((loaded = ScanPart (datei, fileid, &sr)) == NULL) {
       if (sr != UURET_NODATA && sr != UURET_OK && sr != UURET_CONT) {
 	UUkillfread (loaded);
@@ -778,7 +778,7 @@ UULoadFileWithPartNo (char *filename, char *fileid, int delflag, int partno)
       if (uu_fast_scanning && sr != UURET_CONT) break;
       continue;
     }
-    
+
     if ((fload = UUPreProcessPart (loaded, &res)) == NULL) {
       /*
        * no useful data found
@@ -808,7 +808,7 @@ UULoadFileWithPartNo (char *filename, char *fileid, int delflag, int partno)
 		 (loaded->end)      ? "end"   : "",
 		 codenames[loaded->uudet]);
     }
-    
+
     if ((res = UUInsertPartToList (fload))) {
       /*
        * couldn't use the data
@@ -915,7 +915,7 @@ UUDecodeFile (uulist *thefile, char *destname)
 
   /*
    * Determine the name of the target file according to the rules:
-   * 
+   *
    * IF (destname!=NULL) THEN filename=destname;
    * ELSE
    *   filename = thefile->filename
@@ -1068,7 +1068,7 @@ UUInfoFile (uulist *thefile, void *opaque,
    */
 
   if (uu_FileCallback) {
-    if ((res = (*uu_FileCallback) (uu_FileCBArg, 
+    if ((res = (*uu_FileCallback) (uu_FileCBArg,
 				   thefile->thisfile->data->sfname,
 				   uugen_fnbuffer,
 				   1)) != UURET_OK)
@@ -1085,7 +1085,7 @@ UUInfoFile (uulist *thefile, void *opaque,
   else {
     if ((inpfile = fopen (thefile->thisfile->data->sfname, "rb")) == NULL) {
       UUMessage (uulib_id, __LINE__, UUMSG_ERROR,
-		 uustring (S_NOT_OPEN_FILE), 
+		 uustring (S_NOT_OPEN_FILE),
 		 thefile->thisfile->data->sfname,
 		 strerror (uu_errno=errno));
       return UURET_IOERR;
@@ -1100,7 +1100,7 @@ UUInfoFile (uulist *thefile, void *opaque,
   fseek (inpfile, thefile->thisfile->data->startpos, SEEK_SET);
   maxpos = thefile->thisfile->data->startpos + thefile->thisfile->data->length;
 
-  while (!feof (inpfile) && 
+  while (!feof (inpfile) &&
 	 (uu_fast_scanning || ftell(inpfile) < maxpos)) {
     if (_FP_fgets (uugen_inbuffer, 511, inpfile) == NULL)
       break;
@@ -1136,7 +1136,7 @@ UUInfoFile (uulist *thefile, void *opaque,
   fclose (inpfile);
 
   if (uu_FileCallback)
-    (*uu_FileCallback) (uu_FileCBArg, 
+    (*uu_FileCallback) (uu_FileCBArg,
 			thefile->thisfile->data->sfname,
 			uugen_fnbuffer, 0);
 
@@ -1145,7 +1145,7 @@ UUInfoFile (uulist *thefile, void *opaque,
 
   return UURET_OK;
 }
-	    
+
 int UUEXPORT
 UURenameFile (uulist *thefile, char *newname)
 {
@@ -1262,10 +1262,10 @@ UUCleanUp (void)
    * clean up the malloc'ed stuff
    */
 
-  for (aiter=toallocate; aiter->ptr; aiter++) {
-    _FP_free (*(aiter->ptr));
-    *(aiter->ptr) = NULL;
-  }
+//  for (aiter=toallocate; aiter->ptr; aiter++) {
+//    _FP_free (*(aiter->ptr));
+//    *(aiter->ptr) = NULL;
+//   }
 
   return UURET_OK;
 }



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