summaryrefslogtreecommitdiffstats
path: root/mailnews/mime/src/mimecms.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
commit302bf1b523012e11b60425d6eee1221ebc2724eb (patch)
treeb191a895f8716efcbe42f454f37597a545a6f421 /mailnews/mime/src/mimecms.cpp
parent21b3f6247403c06f85e1f45d219f87549862198f (diff)
downloadUXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'mailnews/mime/src/mimecms.cpp')
-rw-r--r--mailnews/mime/src/mimecms.cpp716
1 files changed, 716 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimecms.cpp b/mailnews/mime/src/mimecms.cpp
new file mode 100644
index 000000000..18d88acaa
--- /dev/null
+++ b/mailnews/mime/src/mimecms.cpp
@@ -0,0 +1,716 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "nsICMSMessage.h"
+#include "nsICMSMessage2.h"
+#include "nsICMSMessageErrors.h"
+#include "nsICMSDecoder.h"
+#include "mimecms.h"
+#include "mimemsig.h"
+#include "nspr.h"
+#include "mimemsg.h"
+#include "mimemoz2.h"
+#include "nsIURI.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMimeMiscStatus.h"
+#include "nsIMsgSMIMEHeaderSink.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIX509Cert.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+
+using namespace mozilla::mailnews;
+
+
+#define MIME_SUPERCLASS mimeEncryptedClass
+MimeDefClass(MimeEncryptedCMS, MimeEncryptedCMSClass,
+ mimeEncryptedCMSClass, &MIME_SUPERCLASS);
+
+static void *MimeCMS_init(MimeObject *, int (*output_fn) (const char *, int32_t, void *), void *);
+static int MimeCMS_write (const char *, int32_t, void *);
+static int MimeCMS_eof (void *, bool);
+static char * MimeCMS_generate (void *);
+static void MimeCMS_free (void *);
+
+extern int SEC_ERROR_CERT_ADDR_MISMATCH;
+
+static int MimeEncryptedCMSClassInitialize(MimeEncryptedCMSClass *clazz)
+{
+#ifdef DEBUG
+ MimeObjectClass *oclass = (MimeObjectClass *) clazz;
+ NS_ASSERTION(!oclass->class_initialized, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
+#endif
+
+ MimeEncryptedClass *eclass = (MimeEncryptedClass *) clazz;
+ eclass->crypto_init = MimeCMS_init;
+ eclass->crypto_write = MimeCMS_write;
+ eclass->crypto_eof = MimeCMS_eof;
+ eclass->crypto_generate_html = MimeCMS_generate;
+ eclass->crypto_free = MimeCMS_free;
+
+ return 0;
+}
+
+
+typedef struct MimeCMSdata
+{
+ int (*output_fn) (const char *buf, int32_t buf_size, void *output_closure);
+ void *output_closure;
+ nsCOMPtr<nsICMSDecoder> decoder_context;
+ nsCOMPtr<nsICMSMessage> content_info;
+ bool ci_is_encrypted;
+ char *sender_addr;
+ bool decoding_failed;
+ uint32_t decoded_bytes;
+ MimeObject *self;
+ bool parent_is_encrypted_p;
+ bool parent_holds_stamp_p;
+ nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
+
+ MimeCMSdata()
+ :output_fn(nullptr),
+ output_closure(nullptr),
+ ci_is_encrypted(false),
+ sender_addr(nullptr),
+ decoding_failed(false),
+ decoded_bytes(0),
+ self(nullptr),
+ parent_is_encrypted_p(false),
+ parent_holds_stamp_p(false)
+ {
+ }
+
+ ~MimeCMSdata()
+ {
+ if(sender_addr)
+ PR_Free(sender_addr);
+
+ // Do an orderly release of nsICMSDecoder and nsICMSMessage //
+ if (decoder_context)
+ {
+ nsCOMPtr<nsICMSMessage> cinfo;
+ decoder_context->Finish(getter_AddRefs(cinfo));
+ }
+ }
+} MimeCMSdata;
+
+/* SEC_PKCS7DecoderContentCallback for SEC_PKCS7DecoderStart() */
+static void MimeCMS_content_callback (void *arg, const char *buf, unsigned long length)
+{
+ int status;
+ MimeCMSdata *data = (MimeCMSdata *) arg;
+ if (!data) return;
+
+ if (!data->output_fn)
+ return;
+
+ PR_SetError(0,0);
+ status = data->output_fn (buf, length, data->output_closure);
+ if (status < 0)
+ {
+ PR_SetError(status, 0);
+ data->output_fn = 0;
+ return;
+ }
+
+ data->decoded_bytes += length;
+}
+
+bool MimeEncryptedCMS_encrypted_p (MimeObject *obj)
+{
+ bool encrypted;
+
+ if (!obj) return false;
+ if (mime_typep(obj, (MimeObjectClass *) &mimeEncryptedCMSClass))
+ {
+ MimeEncrypted *enc = (MimeEncrypted *) obj;
+ MimeCMSdata *data = (MimeCMSdata *) enc->crypto_closure;
+ if (!data || !data->content_info) return false;
+ data->content_info->ContentIsEncrypted(&encrypted);
+ return encrypted;
+ }
+ return false;
+}
+
+
+bool MimeCMSHeadersAndCertsMatch(nsICMSMessage *content_info,
+ nsIX509Cert *signerCert,
+ const char *from_addr,
+ const char *from_name,
+ const char *sender_addr,
+ const char *sender_name,
+ bool *signing_cert_without_email_address)
+{
+ nsCString cert_addr;
+ bool match = true;
+ bool foundFrom = false;
+ bool foundSender = false;
+
+ /* Find the name and address in the cert.
+ */
+ if (content_info)
+ {
+ // Extract any address contained in the cert.
+ // This will be used for testing, whether the cert contains no addresses at all.
+ content_info->GetSignerEmailAddress (getter_Copies(cert_addr));
+ }
+
+ if (signing_cert_without_email_address)
+ *signing_cert_without_email_address = cert_addr.IsEmpty();
+
+ /* Now compare them --
+ consider it a match if the address in the cert matches the
+ address in the From field (or as a fallback, the Sender field)
+ */
+
+ /* If there is no addr in the cert at all, it can not match and we fail. */
+ if (cert_addr.IsEmpty())
+ {
+ match = false;
+ }
+ else
+ {
+ if (signerCert)
+ {
+ if (from_addr && *from_addr)
+ {
+ NS_ConvertASCIItoUTF16 ucs2From(from_addr);
+ if (NS_FAILED(signerCert->ContainsEmailAddress(ucs2From, &foundFrom)))
+ {
+ foundFrom = false;
+ }
+ }
+ else if (sender_addr && *sender_addr)
+ {
+ NS_ConvertASCIItoUTF16 ucs2Sender(sender_addr);
+ if (NS_FAILED(signerCert->ContainsEmailAddress(ucs2Sender, &foundSender)))
+ {
+ foundSender = false;
+ }
+ }
+ }
+
+ if (!foundSender && !foundFrom)
+ {
+ match = false;
+ }
+ }
+
+ return match;
+}
+
+class nsSMimeVerificationListener : public nsISMimeVerificationListener
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISMIMEVERIFICATIONLISTENER
+
+ nsSMimeVerificationListener(const char *aFromAddr, const char *aFromName,
+ const char *aSenderAddr, const char *aSenderName,
+ nsIMsgSMIMEHeaderSink *aHeaderSink, int32_t aMimeNestingLevel);
+
+protected:
+ virtual ~nsSMimeVerificationListener() {}
+
+ /**
+ * It is safe to declare this implementation as thread safe,
+ * despite not using a lock to protect the members.
+ * Because of the way the object will be used, we don't expect a race.
+ * After construction, the object is passed to another thread,
+ * but will no longer be accessed on the original thread.
+ * The other thread is unable to access/modify self's data members.
+ * When the other thread is finished, it will call into the "Notify"
+ * callback. Self's members will be accessed on the other thread,
+ * but this is fine, because there is no race with the original thread.
+ * Race-protection for XPCOM reference counting is sufficient.
+ */
+ bool mSinkIsNull;
+ nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> mHeaderSink;
+ int32_t mMimeNestingLevel;
+
+ nsCString mFromAddr;
+ nsCString mFromName;
+ nsCString mSenderAddr;
+ nsCString mSenderName;
+};
+
+class SignedStatusRunnable : public mozilla::Runnable
+{
+public:
+ SignedStatusRunnable(const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> &aSink, int32_t aNestingLevel,
+ int32_t aSignatureStatus, nsIX509Cert *aSignerCert);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> m_sink;
+ int32_t m_nestingLevel;
+ int32_t m_signatureStatus;
+ nsCOMPtr<nsIX509Cert> m_signerCert;
+};
+
+SignedStatusRunnable::SignedStatusRunnable(const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> &aSink,
+ int32_t aNestingLevel,
+ int32_t aSignatureStatus,
+ nsIX509Cert *aSignerCert) :
+ m_sink(aSink), m_nestingLevel(aNestingLevel),
+ m_signatureStatus(aSignatureStatus), m_signerCert(aSignerCert)
+{
+}
+
+NS_IMETHODIMP SignedStatusRunnable::Run()
+{
+ return m_sink->SignedStatus(m_nestingLevel, m_signatureStatus, m_signerCert);
+}
+
+
+nsresult ProxySignedStatus(const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> &aSink,
+ int32_t aNestingLevel,
+ int32_t aSignatureStatus,
+ nsIX509Cert *aSignerCert)
+{
+ RefPtr<SignedStatusRunnable> signedStatus =
+ new SignedStatusRunnable(aSink, aNestingLevel, aSignatureStatus, aSignerCert);
+ return NS_DispatchToMainThread(signedStatus, NS_DISPATCH_SYNC);
+}
+
+NS_IMPL_ISUPPORTS(nsSMimeVerificationListener, nsISMimeVerificationListener)
+
+nsSMimeVerificationListener::nsSMimeVerificationListener(const char *aFromAddr, const char *aFromName,
+ const char *aSenderAddr, const char *aSenderName,
+ nsIMsgSMIMEHeaderSink *aHeaderSink, int32_t aMimeNestingLevel)
+{
+ mHeaderSink = new nsMainThreadPtrHolder<nsIMsgSMIMEHeaderSink>(aHeaderSink);
+ mSinkIsNull = !aHeaderSink;
+ mMimeNestingLevel = aMimeNestingLevel;
+
+ mFromAddr = aFromAddr;
+ mFromName = aFromName;
+ mSenderAddr = aSenderAddr;
+ mSenderName = aSenderName;
+}
+
+NS_IMETHODIMP nsSMimeVerificationListener::Notify(nsICMSMessage2 *aVerifiedMessage,
+ nsresult aVerificationResultCode)
+{
+ // Only continue if we have a valid pointer to the UI
+ NS_ENSURE_FALSE(mSinkIsNull, NS_OK);
+
+ NS_ENSURE_TRUE(aVerifiedMessage, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsICMSMessage> msg = do_QueryInterface(aVerifiedMessage);
+ NS_ENSURE_TRUE(msg, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIX509Cert> signerCert;
+ msg->GetSignerCert(getter_AddRefs(signerCert));
+
+ int32_t signature_status = nsICMSMessageErrors::GENERAL_ERROR;
+
+ if (NS_FAILED(aVerificationResultCode))
+ {
+ if (NS_ERROR_MODULE_SECURITY == NS_ERROR_GET_MODULE(aVerificationResultCode))
+ signature_status = NS_ERROR_GET_CODE(aVerificationResultCode);
+ else if (NS_ERROR_NOT_IMPLEMENTED == aVerificationResultCode)
+ signature_status = nsICMSMessageErrors::VERIFY_ERROR_PROCESSING;
+ }
+ else
+ {
+ bool signing_cert_without_email_address;
+
+ bool good_p = MimeCMSHeadersAndCertsMatch(msg, signerCert,
+ mFromAddr.get(), mFromName.get(),
+ mSenderAddr.get(), mSenderName.get(),
+ &signing_cert_without_email_address);
+ if (!good_p)
+ {
+ if (signing_cert_without_email_address)
+ signature_status = nsICMSMessageErrors::VERIFY_CERT_WITHOUT_ADDRESS;
+ else
+ signature_status = nsICMSMessageErrors::VERIFY_HEADER_MISMATCH;
+ }
+ else
+ signature_status = nsICMSMessageErrors::SUCCESS;
+ }
+
+ ProxySignedStatus(mHeaderSink, mMimeNestingLevel, signature_status, signerCert);
+
+ return NS_OK;
+}
+
+int MIMEGetRelativeCryptoNestLevel(MimeObject *obj)
+{
+ /*
+ the part id of any mimeobj is mime_part_address(obj)
+ our currently displayed crypto part is obj
+ the part shown as the toplevel object in the current window is
+ obj->options->part_to_load
+ possibly stored in the toplevel object only ???
+ but hopefully all nested mimeobject point to the same displayooptions
+
+ we need to find out the nesting level of our currently displayed crypto object
+ wrt the shown part in the toplevel window
+ */
+
+ // if we are showing the toplevel message, aTopMessageNestLevel == 0
+ int aTopMessageNestLevel = 0;
+ MimeObject *aTopShownObject = nullptr;
+ if (obj && obj->options->part_to_load) {
+ bool aAlreadyFoundTop = false;
+ for (MimeObject *walker = obj; walker; walker = walker->parent) {
+ if (aAlreadyFoundTop) {
+ if (!mime_typep(walker, (MimeObjectClass *) &mimeEncryptedClass)
+ && !mime_typep(walker, (MimeObjectClass *) &mimeMultipartSignedClass)) {
+ ++aTopMessageNestLevel;
+ }
+ }
+ if (!aAlreadyFoundTop && !strcmp(mime_part_address(walker), walker->options->part_to_load)) {
+ aAlreadyFoundTop = true;
+ aTopShownObject = walker;
+ }
+ if (!aAlreadyFoundTop && !walker->parent) {
+ // The mime part part_to_load is not a parent of the
+ // the crypto mime part passed in to this function as parameter obj.
+ // That means the crypto part belongs to another branch of the mime tree.
+ return -1;
+ }
+ }
+ }
+
+ bool CryptoObjectIsChildOfTopShownObject = false;
+ if (!aTopShownObject) {
+ // no sub part specified, top message is displayed, and
+ // our crypto object is definitively a child of it
+ CryptoObjectIsChildOfTopShownObject = true;
+ }
+
+ // if we are the child of the topmost message, aCryptoPartNestLevel == 1
+ int aCryptoPartNestLevel = 0;
+ if (obj) {
+ for (MimeObject *walker = obj; walker; walker = walker->parent) {
+ // Crypto mime objects are transparent wrt nesting.
+ if (!mime_typep(walker, (MimeObjectClass *) &mimeEncryptedClass)
+ && !mime_typep(walker, (MimeObjectClass *) &mimeMultipartSignedClass)) {
+ ++aCryptoPartNestLevel;
+ }
+ if (aTopShownObject && walker->parent == aTopShownObject) {
+ CryptoObjectIsChildOfTopShownObject = true;
+ }
+ }
+ }
+
+ if (!CryptoObjectIsChildOfTopShownObject) {
+ return -1;
+ }
+
+ return aCryptoPartNestLevel - aTopMessageNestLevel;
+}
+
+static void *MimeCMS_init(MimeObject *obj,
+ int (*output_fn) (const char *buf, int32_t buf_size, void *output_closure),
+ void *output_closure)
+{
+ MimeCMSdata *data;
+ nsresult rv;
+
+ if (!(obj && obj->options && output_fn)) return 0;
+
+ data = new MimeCMSdata;
+ if (!data) return 0;
+
+ data->self = obj;
+ data->output_fn = output_fn;
+ data->output_closure = output_closure;
+ PR_SetError(0, 0);
+ data->decoder_context = do_CreateInstance(NS_CMSDECODER_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ {
+ delete data;
+ return 0;
+ }
+
+ rv = data->decoder_context->Start(MimeCMS_content_callback, data);
+ if (NS_FAILED(rv))
+ {
+ delete data;
+ return 0;
+ }
+
+ // XXX Fix later XXX //
+ data->parent_holds_stamp_p =
+ (obj->parent &&
+ (mime_crypto_stamped_p(obj->parent) ||
+ mime_typep(obj->parent, (MimeObjectClass *) &mimeEncryptedClass)));
+
+ data->parent_is_encrypted_p =
+ (obj->parent && MimeEncryptedCMS_encrypted_p (obj->parent));
+
+ /* If the parent of this object is a crypto-blob, then it's the grandparent
+ who would have written out the headers and prepared for a stamp...
+ (This shit sucks.)
+ */
+ if (data->parent_is_encrypted_p &&
+ !data->parent_holds_stamp_p &&
+ obj->parent && obj->parent->parent)
+ data->parent_holds_stamp_p =
+ mime_crypto_stamped_p (obj->parent->parent);
+
+ mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure);
+ if (msd)
+ {
+ nsIChannel *channel = msd->channel; // note the lack of ref counting...
+ if (channel)
+ {
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ nsCOMPtr<nsIMsgMailNewsUrl> msgurl;
+ nsCOMPtr<nsISupports> securityInfo;
+ channel->GetURI(getter_AddRefs(uri));
+ if (uri)
+ {
+ nsAutoCString urlSpec;
+ rv = uri->GetSpec(urlSpec);
+
+ // We only want to update the UI if the current mime transaction
+ // is intended for display.
+ // If the current transaction is intended for background processing,
+ // we can learn that by looking at the additional header=filter
+ // string contained in the URI.
+ //
+ // If we find something, we do not set smimeHeaderSink,
+ // which will prevent us from giving UI feedback.
+ //
+ // If we do not find header=filter, we assume the result of the
+ // processing will be shown in the UI.
+
+ if (!strstr(urlSpec.get(), "?header=filter") &&
+ !strstr(urlSpec.get(), "&header=filter") &&
+ !strstr(urlSpec.get(), "?header=attach") &&
+ !strstr(urlSpec.get(), "&header=attach"))
+ {
+ msgurl = do_QueryInterface(uri);
+ if (msgurl)
+ msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
+ if (msgWindow)
+ msgWindow->GetMsgHeaderSink(getter_AddRefs(headerSink));
+ if (headerSink)
+ headerSink->GetSecurityInfo(getter_AddRefs(securityInfo));
+ if (securityInfo)
+ data->smimeHeaderSink = do_QueryInterface(securityInfo);
+ }
+ }
+ } // if channel
+ } // if msd
+
+ return data;
+}
+
+static int
+MimeCMS_write (const char *buf, int32_t buf_size, void *closure)
+{
+ MimeCMSdata *data = (MimeCMSdata *) closure;
+ nsresult rv;
+
+ if (!data || !data->output_fn || !data->decoder_context) return -1;
+
+ PR_SetError(0, 0);
+ rv = data->decoder_context->Update(buf, buf_size);
+ data->decoding_failed = NS_FAILED(rv);
+
+ return 0;
+}
+
+void MimeCMSGetFromSender(MimeObject *obj,
+ nsCString &from_addr,
+ nsCString &from_name,
+ nsCString &sender_addr,
+ nsCString &sender_name)
+{
+ MimeHeaders *msg_headers = 0;
+
+ /* Find the headers of the MimeMessage which is the parent (or grandparent)
+ of this object (remember, crypto objects nest.) */
+ MimeObject *o2 = obj;
+ msg_headers = o2->headers;
+ while (o2 &&
+ o2->parent &&
+ !mime_typep(o2->parent, (MimeObjectClass *) &mimeMessageClass))
+ {
+ o2 = o2->parent;
+ msg_headers = o2->headers;
+ }
+
+ if (!msg_headers)
+ return;
+
+ /* Find the names and addresses in the From and/or Sender fields.
+ */
+ nsCString s;
+
+ /* Extract the name and address of the "From:" field. */
+ s.Adopt(MimeHeaders_get(msg_headers, HEADER_FROM, false, false));
+ if (!s.IsEmpty())
+ ExtractFirstAddress(EncodedHeader(s), from_name, from_addr);
+
+ /* Extract the name and address of the "Sender:" field. */
+ s.Adopt(MimeHeaders_get(msg_headers, HEADER_SENDER, false, false));
+ if (!s.IsEmpty())
+ ExtractFirstAddress(EncodedHeader(s), sender_name, sender_addr);
+}
+
+void MimeCMSRequestAsyncSignatureVerification(nsICMSMessage *aCMSMsg,
+ const char *aFromAddr, const char *aFromName,
+ const char *aSenderAddr, const char *aSenderName,
+ nsIMsgSMIMEHeaderSink *aHeaderSink, int32_t aMimeNestingLevel,
+ unsigned char* item_data, uint32_t item_len)
+{
+ nsCOMPtr<nsICMSMessage2> msg2 = do_QueryInterface(aCMSMsg);
+ if (!msg2)
+ return;
+
+ RefPtr<nsSMimeVerificationListener> listener =
+ new nsSMimeVerificationListener(aFromAddr, aFromName, aSenderAddr, aSenderName,
+ aHeaderSink, aMimeNestingLevel);
+ if (!listener)
+ return;
+
+ if (item_data)
+ msg2->AsyncVerifyDetachedSignature(listener, item_data, item_len);
+ else
+ msg2->AsyncVerifySignature(listener);
+}
+
+static int
+MimeCMS_eof (void *crypto_closure, bool abort_p)
+{
+ MimeCMSdata *data = (MimeCMSdata *) crypto_closure;
+ nsresult rv;
+ int32_t status = nsICMSMessageErrors::SUCCESS;
+
+ if (!data || !data->output_fn || !data->decoder_context) {
+ return -1;
+ }
+
+ int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self);
+
+ /* Hand an EOF to the crypto library. It may call data->output_fn.
+ (Today, the crypto library has no flushing to do, but maybe there
+ will be someday.)
+
+ We save away the value returned and will use it later to emit a
+ blurb about whether the signature validation was cool.
+ */
+
+ PR_SetError(0, 0);
+ rv = data->decoder_context->Finish(getter_AddRefs(data->content_info));
+ if (NS_FAILED(rv))
+ status = nsICMSMessageErrors::GENERAL_ERROR;
+
+ data->decoder_context = nullptr;
+
+ nsCOMPtr<nsIX509Cert> certOfInterest;
+
+ if (!data->smimeHeaderSink)
+ return 0;
+
+ if (aRelativeNestLevel < 0)
+ return 0;
+
+ int32_t maxNestLevel = 0;
+ data->smimeHeaderSink->MaxWantedNesting(&maxNestLevel);
+
+ if (aRelativeNestLevel > maxNestLevel)
+ return 0;
+
+ if (data->decoding_failed)
+ status = nsICMSMessageErrors::GENERAL_ERROR;
+
+ if (!data->content_info)
+ {
+ if (!data->decoded_bytes)
+ {
+ // We were unable to decode any data.
+ status = nsICMSMessageErrors::GENERAL_ERROR;
+ }
+ else
+ {
+ // Some content got decoded, but we failed to decode
+ // the final summary, probably we got truncated data.
+ status = nsICMSMessageErrors::ENCRYPT_INCOMPLETE;
+ }
+
+ // Although a CMS message could be either encrypted or opaquely signed,
+ // what we see is most likely encrypted, because if it were
+ // signed only, we probably would have been able to decode it.
+
+ data->ci_is_encrypted = true;
+ }
+ else
+ {
+ rv = data->content_info->ContentIsEncrypted(&data->ci_is_encrypted);
+
+ if (NS_SUCCEEDED(rv) && data->ci_is_encrypted) {
+ data->content_info->GetEncryptionCert(getter_AddRefs(certOfInterest));
+ }
+ else {
+ // Existing logic in mimei assumes, if !ci_is_encrypted, then it is signed.
+ // Make sure it indeed is signed.
+
+ bool testIsSigned;
+ rv = data->content_info->ContentIsSigned(&testIsSigned);
+
+ if (NS_FAILED(rv) || !testIsSigned) {
+ // Neither signed nor encrypted?
+ // We are unable to understand what we got, do not try to indicate S/Mime status.
+ return 0;
+ }
+
+ nsCString from_addr;
+ nsCString from_name;
+ nsCString sender_addr;
+ nsCString sender_name;
+
+ MimeCMSGetFromSender(data->self,
+ from_addr, from_name,
+ sender_addr, sender_name);
+
+ MimeCMSRequestAsyncSignatureVerification(data->content_info,
+ from_addr.get(), from_name.get(),
+ sender_addr.get(), sender_name.get(),
+ data->smimeHeaderSink, aRelativeNestLevel,
+ nullptr, 0);
+ }
+ }
+
+ if (data->ci_is_encrypted)
+ {
+ data->smimeHeaderSink->EncryptionStatus(
+ aRelativeNestLevel,
+ status,
+ certOfInterest
+ );
+ }
+
+ return 0;
+}
+
+static void
+MimeCMS_free (void *crypto_closure)
+{
+ MimeCMSdata *data = (MimeCMSdata *) crypto_closure;
+ if (!data) return;
+
+ delete data;
+}
+
+static char *
+MimeCMS_generate (void *crypto_closure)
+{
+ return nullptr;
+}
+