Bug 768928 - New implementation of nsIPgpMimeProxy to allow for Enigmail to receive data from mimelib. r=bienvenu,sr=Standard8 AURORA_BASE_20120716
authorPatrick Brunschwig <patrick@enigmail.net>
Mon, 16 Jul 2012 19:40:22 +0100
changeset 10666 f0e7508fd586861d18f0de9d43eb62da31503cbe
parent 10665 afea59d023bc76168da129c56e215944303fdb88
child 10667 d4252c5d2e246cc28cd6ff76e00995852244b296
push id8051
push userbugzilla@standard8.plus.com
push dateMon, 16 Jul 2012 18:40:51 +0000
treeherdercomm-central@f0e7508fd586 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbienvenu, Standard8
bugs768928
Bug 768928 - New implementation of nsIPgpMimeProxy to allow for Enigmail to receive data from mimelib. r=bienvenu,sr=Standard8
mail/app/profile/all-thunderbird.js
mail/locales/en-US/chrome/messenger/pgpmime.properties
mail/locales/jar.mn
mailnews/build/Makefile.in
mailnews/build/nsMailModule.cpp
mailnews/mailnews.js
mailnews/mime/cthandlers/Makefile.in
mailnews/mime/cthandlers/pgpmime/Makefile.in
mailnews/mime/cthandlers/pgpmime/nsPgpMimeProxy.cpp
mailnews/mime/cthandlers/pgpmime/nsPgpMimeProxy.h
mailnews/mime/public/Makefile.in
mailnews/mime/public/nsIMimeContentTypeHandler.h
mailnews/mime/public/nsIPgpMimeProxy.idl
suite/locales/en-US/chrome/mailnews/pgpmime.properties
suite/locales/jar.mn
--- a/mail/app/profile/all-thunderbird.js
+++ b/mail/app/profile/all-thunderbird.js
@@ -757,8 +757,11 @@ pref("mail.chat.enabled", true);
 pref("mail.chat.play_notification_sound", true);
 // Send typing notification in private conversations
 pref("purple.conversations.im.send_typing", true);
 
 // BigFiles
 pref("mail.cloud_files.enabled", true);
 pref("mail.cloud_files.inserted_urls.footer.link", "http://www.getthunderbird.com");
 pref("mail.cloud_files.learn_more_url", "https://support.mozillamessaging.com/kb/filelink-large-attachments");
+
+// PgpMime Proxy
+pref("mail.pgpmime.addon_url", "https://addons.mozilla.org/thunderbird/addon/enigmail/");
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/pgpmime.properties
@@ -0,0 +1,12 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# The following are used by the pgpmime content type handler
+#
+
+# LOCALIZATION NOTE(pgpMimeNeedsAddon): The text can contain HTML tags.
+# %S is the url to Enigmail on AMO supplied from preferences.
+pgpMimeNeedsAddon=This is an encrypted OpenPGP message.<br>In order to decrypt this mail, you need to install an <a href="%S">OpenPGP add-on</a>.
+
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -77,16 +77,17 @@
   locale/@AB_CD@/messenger/searchTermOverlay.dtd                        (%chrome/messenger/searchTermOverlay.dtd)
   locale/@AB_CD@/messenger/imapMsgs.properties                          (%chrome/messenger/imapMsgs.properties)
   locale/@AB_CD@/messenger/localMsgs.properties                         (%chrome/messenger/localMsgs.properties)
   locale/@AB_CD@/messenger/downloadheaders.dtd                          (%chrome/messenger/downloadheaders.dtd)
   locale/@AB_CD@/messenger/news.properties                              (%chrome/messenger/news.properties)
   locale/@AB_CD@/messenger/mime.properties                              (%chrome/messenger/mime.properties)
   locale/@AB_CD@/messenger/mimeheader.properties                        (%chrome/messenger/mimeheader.properties)
   locale/@AB_CD@/messenger/smime.properties                             (%chrome/messenger/smime.properties)
+  locale/@AB_CD@/messenger/pgpmime.properties                           (%chrome/messenger/pgpmime.properties)
   locale/@AB_CD@/messenger/markByDate.dtd                               (%chrome/messenger/markByDate.dtd)
   locale/@AB_CD@/messenger/am-mdn.dtd                                   (%chrome/messenger/am-mdn.dtd)
   locale/@AB_CD@/messenger/am-mdn.properties                            (%chrome/messenger/am-mdn.properties)
   locale/@AB_CD@/messenger/am-archiveoptions.dtd                        (%chrome/messenger/am-archiveoptions.dtd)
   locale/@AB_CD@/messenger/msgmdn.properties                            (%chrome/messenger/msgmdn.properties)
   locale/@AB_CD@/messenger/mailviews.properties                         (%chrome/messenger/mailviews.properties)
   locale/@AB_CD@/messenger/msgViewPickerOverlay.dtd                     (%chrome/messenger/msgViewPickerOverlay.dtd)
   locale/@AB_CD@/messenger/mailViewSetup.dtd                            (%chrome/messenger/mailViewSetup.dtd)
--- a/mailnews/build/Makefile.in
+++ b/mailnews/build/Makefile.in
@@ -51,16 +51,17 @@ SHARED_LIBRARY_LIBS = \
 	        ../mime/src/$(LIB_PREFIX)mime_s.$(LIB_SUFFIX) \
 	        ../mime/emitters/src/$(LIB_PREFIX)emitterutil_s.$(LIB_SUFFIX) \
 	        ../extensions/bayesian-spam-filter/src/$(LIB_PREFIX)bayesflt_s.$(LIB_SUFFIX) \
 	        ../extensions/fts3/src/$(LIB_PREFIX)fts3tok_s.$(LIB_SUFFIX) \
 	        ../extensions/mailviews/src/$(LIB_PREFIX)mailview_s.$(LIB_SUFFIX) \
                 ../extensions/mdn/src/$(LIB_PREFIX)msgmdn_s.$(LIB_SUFFIX) \
                 ../mime/cthandlers/vcard/$(LIB_PREFIX)vcard_s.$(LIB_SUFFIX) \
                 ../mime/cthandlers/glue/$(LIB_PREFIX)mimecthglue_s.$(LIB_SUFFIX) \
+                ../mime/cthandlers/pgpmime/$(LIB_PREFIX)pgpmime_s.$(LIB_SUFFIX) \
                 $(NULL)
 
 ifdef MOZILLA_INTERNAL_API
 EXTRA_DSO_LDOPTS = \
 		$(MOZDEPTH)/rdf/util/src/internal/$(LIB_PREFIX)rdfutil_s.$(LIB_SUFFIX) \
 		$(MOZ_JS_LIBS) \
 		$(MOZ_UNICHARUTIL_LIBS) \
 		$(MOZ_COMPONENT_LIBS) \
--- a/mailnews/build/nsMailModule.cpp
+++ b/mailnews/build/nsMailModule.cpp
@@ -291,16 +291,21 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 // FTS3 Tokenizer
 ///////////////////////////////////////////////////////////////////////////////
 #include "nsFts3TokenizerCID.h"
 #include "nsFts3Tokenizer.h"
 
 ////////////////////////////////////////////////////////////////////////////////
+// PGP/MIME includes
+////////////////////////////////////////////////////////////////////////////////
+#include "nsPgpMimeProxy.h"
+
+////////////////////////////////////////////////////////////////////////////////
 // mailnews base factories
 ////////////////////////////////////////////////////////////////////////////////
 using namespace mozilla::mailnews;
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMessengerBootstrap)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMsgMailSession, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMessenger)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMsgAccountManager, Init)
@@ -767,16 +772,48 @@ static nsresult nsVCardMimeContentTypeHa
 
   NS_ADDREF(inst);
   rv = inst->QueryInterface(aIID,aResult);
   NS_RELEASE(inst);
 
   return rv;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// PGP/MIME factories
+////////////////////////////////////////////////////////////////////////////////
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPgpMimeProxy, Init)
+
+NS_DEFINE_NAMED_CID(NS_PGPMIMEPROXY_CID);
+
+NS_DEFINE_NAMED_CID(NS_PGPMIME_CONTENT_TYPE_HANDLER_CID);
+
+extern "C" MimeObjectClass *
+MIME_PgpMimeCreateContentTypeHandlerClass(const char *content_type,
+                                        contentTypeHandlerInitStruct *initStruct);
+
+static nsresult
+nsPgpMimeMimeContentTypeHandlerConstructor(nsISupports *aOuter,
+                                         REFNSIID aIID,
+                                         void **aResult)
+{
+  NS_ENSURE_ARG_POINTER(aResult);
+  NS_ENSURE_FALSE(aOuter, NS_ERROR_NO_AGGREGATION);
+  *aResult = nsnull;
+
+  nsRefPtr<nsMimeContentTypeHandler> inst(
+    new nsMimeContentTypeHandler("mulitpart/encrypted",
+                                 &MIME_PgpMimeCreateContentTypeHandlerClass));
+
+  NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);
+
+  return inst->QueryInterface(aIID, aResult);
+}
+
 const mozilla::Module::CIDEntry kMailNewsCIDs[] = {
   // MailNews Base Entries
   { &kNS_MESSENGERBOOTSTRAP_CID, false, NULL, nsMessengerBootstrapConstructor },
   { &kNS_MESSENGERWINDOWSERVICE_CID, false, NULL, nsMessengerBootstrapConstructor},
   { &kNS_MSGMAILSESSION_CID, false, NULL, nsMsgMailSessionConstructor},
   { &kNS_MESSENGER_CID, false, NULL,nsMessengerConstructor},
   { &kNS_MSGACCOUNTMANAGER_CID, false, NULL, nsMsgAccountManagerConstructor},
   { &kNS_MSGACCOUNT_CID, false, NULL, nsMsgAccountConstructor},
@@ -966,16 +1003,19 @@ const mozilla::Module::CIDEntry kMailNew
   { &kNS_NNTPARTICLELIST_CID, false, NULL, nsNNTPArticleListConstructor },
   { &kNS_NEWSDOWNLOADDIALOGARGS_CID, false, NULL, nsNewsDownloadDialogArgsConstructor },
   // Mail View Entries
   { &kNS_MSGMAILVIEWLIST_CID, false, NULL, nsMsgMailViewListConstructor },
   // mdn Entries
   { &kNS_MSGMDNGENERATOR_CID, false, NULL, nsMsgMdnGeneratorConstructor },
   // Vcard Entries
   { &kNS_VCARD_CONTENT_TYPE_HANDLER_CID, false, NULL, nsVCardMimeContentTypeHandlerConstructor},
+  // PGP/MIME Entries
+  { &kNS_PGPMIME_CONTENT_TYPE_HANDLER_CID, false, NULL, nsPgpMimeMimeContentTypeHandlerConstructor },
+  { &kNS_PGPMIMEPROXY_CID, false, NULL, nsPgpMimeProxyConstructor },
   // Tokenizer Entries
   { NULL }
 };
 
 const mozilla::Module::ContractIDEntry kMailNewsContracts[] = {
   // MailNews Base Entries
   { NS_MESSENGERBOOTSTRAP_CONTRACTID, &kNS_MESSENGERBOOTSTRAP_CID },
   { NS_MESSENGERWINDOWSERVICE_CONTRACTID, &kNS_MESSENGERWINDOWSERVICE_CID },
@@ -1196,16 +1236,19 @@ const mozilla::Module::ContractIDEntry k
   { NS_NNTPARTICLELIST_CONTRACTID, &kNS_NNTPARTICLELIST_CID },
   { NS_NEWSDOWNLOADDIALOGARGS_CONTRACTID, &kNS_NEWSDOWNLOADDIALOGARGS_CID },
   // Mail View Entries
   { NS_MSGMAILVIEWLIST_CONTRACTID, &kNS_MSGMAILVIEWLIST_CID },
   // mdn Entries
   { NS_MSGMDNGENERATOR_CONTRACTID, &kNS_MSGMDNGENERATOR_CID },
   // Vcard Entries
   { "@mozilla.org/mimecth;1?type=text/x-vcard", &kNS_VCARD_CONTENT_TYPE_HANDLER_CID },
+  // PGP/MIME Entries
+  { "@mozilla.org/mimecth;1?type=multipart/encrypted", &kNS_PGPMIME_CONTENT_TYPE_HANDLER_CID },
+  { NS_PGPMIMEPROXY_CONTRACTID, &kNS_PGPMIMEPROXY_CID },
   // Tokenizer Entries
   { NULL }
 };
 
 static const mozilla::Module::CategoryEntry kMailNewsCategories[] = {
   // MailNews Base Entries
   { XPCOM_DIRECTORY_PROVIDER_CATEGORY, "mail-directory-provider", NS_MAILDIRPROVIDER_CONTRACTID },
   { "content-policy", NS_MSGCONTENTPOLICY_CONTRACTID, NS_MSGCONTENTPOLICY_CONTRACTID },
--- a/mailnews/mailnews.js
+++ b/mailnews/mailnews.js
@@ -809,8 +809,11 @@ pref("mailnews.import.text.fieldmap", "+
 // Section 4.8 "High-Throughput Data Service Class"
 pref("mail.pop3.qos", 0);
 pref("mail.smtp.qos", 0);
 pref("mail.nntp.qos", 0);
 
 // default value for IMAP4
 // in a DSCP environment this should be 56 (0x38, or AF13), ibid.
 pref("mail.imap.qos", 0);
+
+// PgpMime Addon
+pref("mail.pgpmime.addon_url", "https://addons.mozilla.org/addon/enigmail/");
--- a/mailnews/mime/cthandlers/Makefile.in
+++ b/mailnews/mime/cthandlers/Makefile.in
@@ -11,15 +11,18 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 ifdef MOZ_PSM
 BUILD_SMIME=1
 endif
 
 PARALLEL_DIRS += glue vcard
 
+# pgpmime depends on glue
+DIRS += pgpmime
+
 ifndef BUILD_SMIME
 # smimestub always depends on glue, so always build that afterwards
 DIRS += smimestub
 endif
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/mailnews/mime/cthandlers/pgpmime/Makefile.in
@@ -0,0 +1,49 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH     = ../../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE         = pgpmime
+LIBRARY_NAME   = pgpmime_s
+EXPORT_LIBRARY = 1
+IS_COMPONENT   = 1
+MODULE_NAME    = nsPgpMimeModule
+ifndef MOZ_INCOMPLETE_EXTERNAL_LINKAGE
+MOZILLA_INTERNAL_API = 1
+LIBXUL_LIBRARY = 1
+endif
+
+EXTRA_DSO_LIBS = mimecthglue_s
+
+CPPSRCS = \
+  nsPgpMimeProxy.cpp \
+  $(NULL)
+
+EXPORTS	= \
+  nsPgpMimeProxy.h \
+  $(NULL)
+
+ifdef MOZILLA_INTERNAL_API
+EXTRA_DSO_LDOPTS = \
+  $(LIBS_DIR) \
+  $(EXTRA_DSO_LIBS) \
+  $(MOZ_COMPONENT_LIBS) \
+  $(NULL)
+else
+EXTRA_DSO_LDOPTS = \
+  $(LIBS_DIR) \
+  $(EXTRA_DSO_LIBS) \
+  $(XPCOM_GLUE_LDOPTS) \
+  $(NSPR_LIBS) \
+  $(NULL)
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+LOCAL_INCLUDES = -I$(srcdir)/../glue
new file mode 100644
--- /dev/null
+++ b/mailnews/mime/cthandlers/pgpmime/nsPgpMimeProxy.cpp
@@ -0,0 +1,568 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsPgpMimeProxy.h"
+#include "nspr.h"
+#include "plstr.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsNetUtil.h"
+#include "nsIRequest.h"
+#include "nsIStringBundle.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "mimexpcom.h"
+#include "nsMsgUtils.h"
+
+#include "nsMsgMimeCID.h"
+
+#include "mimecth.h"
+#include "mimemoz2.h"
+#include "nspr.h"
+#include "plstr.h"
+#include "nsIPgpMimeProxy.h"
+
+static NS_DEFINE_CID(kMimeObjectClassAccessCID, NS_MIME_OBJECT_CLASS_ACCESS_CID);
+
+#define MIME_SUPERCLASS mimeEncryptedClass
+MimeDefClass(MimeEncryptedPgp, MimeEncryptedPgpClass,
+             mimeEncryptedPgpClass, &MIME_SUPERCLASS);
+
+#define kCharMax 1024
+
+extern "C" MimeObjectClass *
+MIME_PgpMimeCreateContentTypeHandlerClass(
+                                    const char *content_type,
+                                    contentTypeHandlerInitStruct *initStruct)
+{
+  MimeObjectClass *objClass = (MimeObjectClass *) &mimeEncryptedPgpClass;
+
+  initStruct->force_inline_display = false;
+
+  return objClass;
+}
+
+static void *MimePgpe_init(MimeObject *,
+                           int (*output_fn) (const char *, PRInt32, void *),
+                           void *);
+static int MimePgpe_write (const char *, PRInt32, void *);
+static int MimePgpe_eof (void *, bool);
+static char* MimePgpe_generate (void *);
+static void MimePgpe_free (void *);
+
+#define PGPMIME_PROPERTIES_URL        "chrome://messenger/locale/pgpmime.properties"
+#define PGPMIME_STR_NOT_SUPPORTED_ID  "pgpMimeNeedsAddon"
+#define PGPMIME_URL_PREF              "mail.pgpmime.addon_url"
+
+static void PgpMimeGetNeedsAddonString(nsCString &aResult)
+{
+  aResult.AssignLiteral("???");
+
+  nsCOMPtr<nsIStringBundleService> stringBundleService =
+    mozilla::services::GetStringBundleService();
+
+  nsCOMPtr<nsIStringBundle> stringBundle;
+  nsresult rv = stringBundleService->CreateBundle(PGPMIME_PROPERTIES_URL,
+                                                  getter_AddRefs(stringBundle));
+  if (NS_FAILED(rv))
+    return;
+
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv))
+    return;
+
+  nsCString url;
+  if (NS_FAILED(prefs->GetCharPref("mail.pgpmime.addon_url",
+                                   getter_Copies(url))))
+    return;
+
+  NS_ConvertUTF8toUTF16 url16(url);
+  const PRUnichar *formatStrings[] = { url16.get() };
+
+  nsString result;
+  rv = stringBundle->FormatStringFromName(NS_LITERAL_STRING(PGPMIME_STR_NOT_SUPPORTED_ID).get(),
+                                          formatStrings, 1, getter_Copies(result));
+  if (NS_FAILED(rv))
+    return;
+  aResult = NS_ConvertUTF16toUTF8(result);
+}
+
+static int
+MimeEncryptedPgpClassInitialize(MimeEncryptedPgpClass *clazz)
+{
+  MimeObjectClass    *oclass = (MimeObjectClass *)    clazz;
+  MimeEncryptedClass *eclass = (MimeEncryptedClass *) clazz;
+
+  NS_ASSERTION(!oclass->class_initialized, "oclass is not initialized");
+  eclass->crypto_init          = MimePgpe_init;
+  eclass->crypto_write         = MimePgpe_write;
+  eclass->crypto_eof           = MimePgpe_eof;
+  eclass->crypto_generate_html = MimePgpe_generate;
+  eclass->crypto_free          = MimePgpe_free;
+
+  return 0;
+}
+
+class MimePgpeData : public nsISupports
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  int (*output_fn) (const char *buf, PRInt32 buf_size, void *output_closure);
+  void *output_closure;
+  MimeObject *self;
+
+  nsCOMPtr<nsIPgpMimeProxy> mimeDecrypt;
+
+  MimePgpeData()
+    : output_fn(nsnull),
+      output_closure(nsnull)
+  {
+  }
+
+  virtual ~MimePgpeData()
+  {
+  }
+};
+
+NS_IMPL_ISUPPORTS0(MimePgpeData)
+
+static void*
+MimePgpe_init(MimeObject *obj,
+              int (*output_fn) (const char *buf, PRInt32 buf_size,
+                                void *output_closure),
+              void *output_closure)
+{
+  if (!(obj && obj->options && output_fn))
+    return nsnull;
+
+  MimeDisplayOptions *opts;
+  opts = obj->options;
+
+  MimePgpeData* data = new MimePgpeData();
+  NS_ENSURE_TRUE(data, nsnull);
+
+  data->self = obj;
+  data->output_fn = output_fn;
+  data->output_closure = output_closure;
+  data->mimeDecrypt = nsnull;
+
+  nsresult rv;
+  data->mimeDecrypt = do_GetService(NS_PGPMIMEPROXY_CONTRACTID, &rv);
+  if (NS_FAILED(rv))
+    return data;
+
+  char *ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, false, false);
+
+  rv = (ct ? data->mimeDecrypt->SetContentType(nsDependentCString(ct))
+        : data->mimeDecrypt->SetContentType(EmptyCString()));
+
+  PR_Free(ct);
+
+  if (NS_FAILED(rv))
+    return nsnull;
+
+  if (NS_FAILED(data->mimeDecrypt->SetMimeCallback(output_fn, output_closure)))
+    return nsnull;
+
+  return data;
+}
+
+static int
+MimePgpe_write(const char *buf, PRInt32 buf_size, void *output_closure)
+{
+  MimePgpeData* data = (MimePgpeData *) output_closure;
+
+  if (!data || !data->output_fn)
+    return -1;
+
+  if (!data->mimeDecrypt)
+    return 0;
+
+  return (NS_SUCCEEDED(data->mimeDecrypt->Write(buf, buf_size)) ? 0 : -1);
+}
+
+static int
+MimePgpe_eof(void* output_closure, bool abort_p)
+{
+  MimePgpeData* data = (MimePgpeData *) output_closure;
+
+  if (!data || !data->output_fn)
+    return -1;
+
+  mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure);
+
+  if (NS_FAILED(data->mimeDecrypt->Finish()))
+    return -1;
+
+  data->mimeDecrypt = nsnull;
+  return 0;
+}
+
+static char*
+MimePgpe_generate(void *output_closure)
+{
+  const char htmlMsg[] = "<html><body><b>GEN MSG<b></body></html>";
+  char* msg = (char *) PR_MALLOC(strlen(htmlMsg) + 1);
+  if (msg)
+    PL_strcpy(msg, htmlMsg);
+
+  return msg;
+}
+
+static void
+MimePgpe_free(void *output_closure)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+NS_IMPL_THREADSAFE_ISUPPORTS5(nsPgpMimeProxy,
+                              nsIPgpMimeProxy,
+                              nsIRequestObserver,
+                              nsIStreamListener,
+                              nsIRequest,
+                              nsIInputStream)
+
+// nsPgpMimeProxy implementation
+nsPgpMimeProxy::nsPgpMimeProxy()
+  : mInitialized(false),
+    mDecryptor(nsnull),
+    mLoadGroup(nsnull),
+    mLoadFlags(LOAD_NORMAL),
+    mCancelStatus(NS_OK)
+{
+  NS_INIT_ISUPPORTS();
+}
+
+nsPgpMimeProxy::~nsPgpMimeProxy()
+{
+  Finalize();
+}
+
+nsresult
+nsPgpMimeProxy::Finalize()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::SetMimeCallback(MimeDecodeCallbackFun outputFun,
+                        void* outputClosure)
+{
+  if (!outputFun || !outputClosure)
+    return NS_ERROR_NULL_POINTER;
+
+  mOutputFun     = outputFun;
+  mOutputClosure = outputClosure;
+  mInitialized   = true;
+
+  mStreamOffset = 0;
+  mByteBuf.Truncate();
+
+  if (mDecryptor)
+    return mDecryptor->OnStartRequest((nsIRequest*) this, nsnull);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Init()
+{
+  mByteBuf.Truncate();
+
+  nsresult rv;
+  nsCOMPtr<nsIPrefBranch> pbi(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv))
+    return rv;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Write(const char *buf, PRUint32 buf_size)
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  mByteBuf.Assign(buf, buf_size);
+  mStreamOffset = 0;
+
+  if (mDecryptor)
+    return mDecryptor->OnDataAvailable((nsIRequest*) this, nsnull, (nsIInputStream*) this,
+                                      0, buf_size);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Finish() {
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  if (mDecryptor) {
+    return mDecryptor->OnStopRequest((nsIRequest*) this, nsnull, NS_OK);
+  }
+  else {
+    nsCString temp;
+    temp.Append("Content-Type: text/html\r\nCharset: UTF-8\r\n\r\n<html><body>");
+    temp.Append("<BR><text=\"#000000\" bgcolor=\"#FFFFFF\" link=\"#FF0000\" vlink=\"#800080\" alink=\"#0000FF\">");
+    temp.Append("<center><table BORDER=1 ><tr><td><CENTER>");
+
+    nsCString tString;
+    PgpMimeGetNeedsAddonString(tString);
+    temp.Append(tString);
+    temp.Append("</CENTER></td></tr></table></center><BR></body></html>\r\n");
+
+    PR_SetError(0,0);
+    int status = mOutputFun(temp.get(), temp.Length(), mOutputClosure);
+    if (status < 0) {
+      PR_SetError(status, 0);
+      mOutputFun = nsnull;
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::GetDecryptor(nsIStreamListener **aDecryptor)
+{
+  NS_IF_ADDREF(*aDecryptor = mDecryptor);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::SetDecryptor(nsIStreamListener *aDecryptor)
+{
+  mDecryptor = aDecryptor;
+
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsPgpMimeProxy::GetContentType(nsACString &aContentType)
+{
+  aContentType = mContentType;
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsPgpMimeProxy::SetContentType(const nsACString &aContentType)
+{
+  mContentType = aContentType;
+
+  return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIRequest methods
+///////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsPgpMimeProxy::GetName(nsACString &result)
+{
+  result = "pgpmimeproxy";
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::IsPending(bool *result)
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  *result = (mCancelStatus == NS_OK);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::GetStatus(nsresult *status)
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  *status = mCancelStatus;
+  return NS_OK;
+}
+
+// NOTE: We assume that OnStopRequest should not be called if
+// request is canceled. This may be wrong!
+NS_IMETHODIMP
+nsPgpMimeProxy::Cancel(nsresult status)
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  // Need a non-zero status code to cancel
+  if (status == NS_OK)
+    return NS_ERROR_FAILURE;
+
+  if (mCancelStatus == NS_OK)
+    mCancelStatus = status;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Suspend(void)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Resume(void)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::GetLoadGroup(nsILoadGroup * *aLoadGroup)
+{
+  NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+  mLoadGroup = aLoadGroup;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::GetLoadFlags(nsLoadFlags *aLoadFlags)
+{
+  *aLoadFlags = mLoadFlags;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::SetLoadFlags(nsLoadFlags aLoadFlags)
+{
+  mLoadFlags = aLoadFlags;
+  return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIInputStream methods
+///////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Available(PRUint32* _retval)
+{
+  NS_ENSURE_ARG(_retval);
+
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  *_retval = (mByteBuf.Length() > mStreamOffset) ?
+              mByteBuf.Length() - mStreamOffset : 0;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Read(char* buf, PRUint32 count,
+                         PRUint32 *readCount)
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  if (!buf || !readCount)
+    return NS_ERROR_NULL_POINTER;
+
+  PRInt32 avail = (mByteBuf.Length() > mStreamOffset) ?
+                   mByteBuf.Length() - mStreamOffset : 0;
+
+  PRUint32 readyCount = ((PRUint32) avail > count) ? count : avail;
+
+  if (readyCount) {
+    memcpy(buf, mByteBuf.get()+mStreamOffset, readyCount);
+    *readCount = readyCount;
+  }
+
+  mStreamOffset += *readCount;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::ReadSegments(nsWriteSegmentFun writer,
+                          void * aClosure, PRUint32 count,
+                          PRUint32 *readCount)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::IsNonBlocking(bool *aNonBlocking)
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  *aNonBlocking = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::Close()
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  mStreamOffset = 0;
+  mByteBuf.Truncate();
+
+  return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIStreamListener methods
+///////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsPgpMimeProxy::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPgpMimeProxy::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+                             nsresult aStatus)
+{
+  return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIStreamListener method
+///////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsPgpMimeProxy::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
+                              nsIInputStream *aInputStream,
+                              PRUint32 aSourceOffset,
+                              PRUint32 aLength)
+{
+  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
+
+  NS_ENSURE_ARG(aInputStream);
+  NS_ENSURE_ARG_MIN(aLength, 0);
+
+  char buf[kCharMax];
+  PRUint32 readCount, readMax;
+
+  while (aLength > 0) {
+    readMax = (aLength < kCharMax) ? aLength : kCharMax;
+
+    nsresult rv;
+    rv = aInputStream->Read((char *) buf, readMax, &readCount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    int status = mOutputFun(buf, readCount, mOutputClosure);
+    if (status < 0) {
+      PR_SetError(status, 0);
+      mOutputFun = nsnull;
+      return NS_ERROR_FAILURE;
+    }
+
+    aLength -= readCount;
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/mailnews/mime/cthandlers/pgpmime/nsPgpMimeProxy.h
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsPgpmimeDecrypt_h_
+#define _nsPgpmimeDecrypt_h_
+
+#include "mimecth.h"
+#include "nsIPgpMimeProxy.h"
+#include "nsCOMPtr.h"
+#include "nsIStreamListener.h"
+#include "nsIInputStream.h"
+#include "nsILoadGroup.h"
+
+typedef struct MimeEncryptedPgpClass MimeEncryptedPgpClass;
+typedef struct MimeEncryptedPgp      MimeEncryptedPgp;
+
+struct MimeEncryptedPgpClass {
+  MimeEncryptedClass encrypted;
+};
+
+//extern MimeEncryptedPgpClass mimeEncryptedPgpClass;
+
+struct MimeEncryptedPgp {
+  MimeEncrypted encrypted;
+};
+
+class nsPgpMimeProxy : public nsIPgpMimeProxy,
+                       public nsIRequest,
+                       public nsIInputStream
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPGPMIMEPROXY
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIREQUEST
+  NS_DECL_NSIINPUTSTREAM
+
+  nsPgpMimeProxy();
+  virtual ~nsPgpMimeProxy();
+
+  // Define a Create method to be used with a factory:
+  static NS_METHOD
+  Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+protected:
+  bool                          mInitialized;
+  nsCOMPtr<nsIStreamListener>   mDecryptor;
+
+  MimeDecodeCallbackFun         mOutputFun;
+  void*                         mOutputClosure;
+
+  nsCOMPtr<nsILoadGroup>        mLoadGroup;
+  nsLoadFlags                   mLoadFlags;
+  nsresult                      mCancelStatus;
+
+  PRUint32                      mStreamOffset;
+  nsCString                     mByteBuf;
+  nsCString                     mContentType;
+
+  nsresult Finalize();
+};
+
+#endif
--- a/mailnews/mime/public/Makefile.in
+++ b/mailnews/mime/public/Makefile.in
@@ -22,12 +22,13 @@ EXPORTS		= \
 XPIDLSRCS	= \
 		nsIMimeStreamConverter.idl \
 		nsIMimeEmitter.idl \
 		nsIMsgHeaderParser.idl \
 		nsIMimeMiscStatus.idl \
 		nsIMimeHeaders.idl \
 		nsIMimeConverter.idl \
 		nsISimpleMimeConverter.idl \
+		nsIPgpMimeProxy.idl \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
--- a/mailnews/mime/public/nsIMimeContentTypeHandler.h
+++ b/mailnews/mime/public/nsIMimeContentTypeHandler.h
@@ -1,23 +1,23 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- 
+
 /*
  * This interface is implemented by content type handlers that will be
  * called upon by libmime to process various attachments types. The primary
  * purpose of these handlers will be to represent the attached data in a
  * viewable HTML format that is useful for the user
  *
  * Note: These will all register by their content type prefixed by the
  *       following:  mimecth:text/vcard
- * 
- *       libmime will then use the XPCOM Component Manager to 
+ *
+ *       libmime will then use the XPCOM Component Manager to
  *       locate the appropriate Content Type handler
  */
 #ifndef nsIMimeContentTypeHandler_h_
 #define nsIMimeContentTypeHandler_h_
 
 typedef struct {
   bool        force_inline_display;
 } contentTypeHandlerInitStruct;
@@ -39,23 +39,28 @@ typedef struct {
 #define NS_SMIME_CONTENT_TYPE_HANDLER_CID \
       { 0x20dabdac, 0xf8b5, 0x11d2, \
       { 0xFF, 0xe0, 0x0, 0xa0, 0x24, 0xa7, 0xd1, 0x44 } }
 
 #define NS_SIGNED_CONTENT_TYPE_HANDLER_CID \
       { 0x20dabdac, 0xf8b5, 0x11d2, \
       { 0xFF, 0xe0, 0x0, 0xaf, 0x19, 0xa7, 0xd1, 0x44 } }
 
+#define NS_PGPMIME_CONTENT_TYPE_HANDLER_CID \
+      { 0x212f415f, 0xf8b5, 0x11d2, \
+      { 0xFF, 0xe0, 0x0, 0xaf, 0x19, 0xa7, 0xd1, 0x44 } }
+
+
 class nsIMimeContentTypeHandler : public nsISupports {
-public: 
+public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMIME_CONTENT_TYPE_HANDLER_IID)
 
   NS_IMETHOD    GetContentType(char **contentType) = 0;
 
-  NS_IMETHOD    CreateContentTypeHandlerClass(const char *content_type, 
-                                              contentTypeHandlerInitStruct *initStruct, 
+  NS_IMETHOD    CreateContentTypeHandlerClass(const char *content_type,
+                                              contentTypeHandlerInitStruct *initStruct,
                                               MimeObjectClass **objClass) = 0;
-}; 
+};
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIMimeContentTypeHandler,
                               NS_IMIME_CONTENT_TYPE_HANDLER_IID)
 
 #endif /* nsIMimeContentTypeHandler_h_ */
new file mode 100644
--- /dev/null
+++ b/mailnews/mime/public/nsIPgpMimeProxy.idl
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIStreamListener.idl"
+
+%{C++
+typedef int (*MimeDecodeCallbackFun)(const char *buf, PRInt32 buf_size, void *output_closure);
+
+#define NS_PGPMIMEPROXY_CLASSNAME "PGP/Mime Decryption"
+#define NS_PGPMIMEPROXY_CONTRACTID "@mozilla.org/mime/pgp-mime-decrypt;1"
+
+#define NS_PGPMIMEPROXY_CID                     \
+{ /* c200d239-6dad-434f-a614-1b58a58c61ec */      \
+   0xc200d239, 0x6dad, 0x434f,                    \
+{0xa6, 0x14, 0x1b, 0x58, 0xa5, 0x8c, 0x61, 0xec } }
+%}
+
+native MimeDecodeCallbackFun(MimeDecodeCallbackFun);
+
+/**
+ * nsIPgpMimeProxy is a proxy for a (JS-)addon for OpenPGP/MIME decryption
+ */
+
+[scriptable, uuid(c200d239-6dad-434f-a614-1b58a58c61ec)]
+interface nsIPgpMimeProxy : nsIStreamListener
+{
+  /**
+   * set the decoder callback into mimelib
+   */
+  [noscript] void setMimeCallback(in MimeDecodeCallbackFun outputFun,
+                                  in voidPtr outputClosure);
+
+  /**
+   * init function
+   */
+  void init();
+
+  /**
+   * process encoded data received from mimelib
+   */
+  void write(in string buf, in unsigned long count);
+
+  /**
+   * finish writing (EOF) from mimelib
+   */
+  void finish();
+
+  /**
+   * the listener that receives the OpenPGP/MIME data stream and decrypts
+   * the message
+   */
+  attribute nsIStreamListener     decryptor;
+
+  attribute ACString              contentType;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
new file mode 100644
--- /dev/null
+++ b/suite/locales/en-US/chrome/mailnews/pgpmime.properties
@@ -0,0 +1,13 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# The following are used by the pgpmime content type handler
+#
+
+# LOCALIZATION NOTE(pgpMimeNeedsAddon): The text can contain HTML tags.
+# %S is the url to Enigmail on AMO supplied from preferences.
+pgpMimeNeedsAddon=This is an encrypted OpenPGP message.<br>In order to decrypt this mail, you need to install an <a href="%S">OpenPGP add-on</a>.
+
+
--- a/suite/locales/jar.mn
+++ b/suite/locales/jar.mn
@@ -310,16 +310,17 @@
   locale/@AB_CD@/messenger/renameFolderDialog.dtd                           (%chrome/mailnews/renameFolderDialog.dtd)
   locale/@AB_CD@/messenger/search.properties                                (%chrome/mailnews/search.properties)
   locale/@AB_CD@/messenger/SearchDialog.dtd                                 (%chrome/mailnews/SearchDialog.dtd)
   locale/@AB_CD@/messenger/searchTermOverlay.dtd                            (%chrome/mailnews/searchTermOverlay.dtd)
   locale/@AB_CD@/messenger/search-attributes.properties                     (%chrome/mailnews/search-attributes.properties)
   locale/@AB_CD@/messenger/search-operators.properties                      (%chrome/mailnews/search-operators.properties)
   locale/@AB_CD@/messenger/shutdownWindow.properties                        (%chrome/mailnews/shutdownWindow.properties)
   locale/@AB_CD@/messenger/smime.properties                                 (%chrome/mailnews/smime.properties)
+  locale/@AB_CD@/messenger/pgpmime.properties                               (%chrome/mailnews/pgpmime.properties)
   locale/@AB_CD@/messenger/smtpEditOverlay.dtd                              (%chrome/mailnews/pref/smtpEditOverlay.dtd)
   locale/@AB_CD@/messenger/start.dtd                                        (%chrome/mailnews/start.dtd)
   locale/@AB_CD@/messenger/subscribe.dtd                                    (%chrome/mailnews/subscribe.dtd)
   locale/@AB_CD@/messenger/subscribe.properties                             (%chrome/mailnews/subscribe.properties)
   locale/@AB_CD@/messenger/textImportMsgs.properties                        (%chrome/mailnews/textImportMsgs.properties)
   locale/@AB_CD@/messenger/threadpane.dtd                                   (%chrome/mailnews/threadpane.dtd)
   locale/@AB_CD@/messenger/vCardImportMsgs.properties                       (%chrome/mailnews/vCardImportMsgs.properties)
   locale/@AB_CD@/messenger/viewLog.dtd                                      (%chrome/mailnews/viewLog.dtd)