/* 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 "nsStringGlue.h" #include "mozilla/Services.h" #include "nsIRequest.h" #include "nsIStringBundle.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIURI.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" #include "nsComponentManagerUtils.h" #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 *, int32_t, void *), void *); static int MimePgpe_write (const char *, int32_t, void *); static int MimePgpe_eof (void *, bool); static char* MimePgpe_generate (void *); static void MimePgpe_free (void *); /* Returns a string describing the location of the part (like "2.5.3"). This is not a full URL, just a part-number. */ static nsCString determineMimePart(MimeObject* obj); #define PGPMIME_PROPERTIES_URL "chrome://messenger/locale/pgpmime.properties" #define PGPMIME_STR_NOT_SUPPORTED_ID u"pgpMimeNeedsAddon" #define PGPMIME_URL_PREF "mail.pgpmime.addon_url" static void PgpMimeGetNeedsAddonString(nsCString &aResult) { aResult.AssignLiteral("???"); nsCOMPtr stringBundleService = mozilla::services::GetStringBundleService(); nsCOMPtr stringBundle; nsresult rv = stringBundleService->CreateBundle(PGPMIME_PROPERTIES_URL, getter_AddRefs(stringBundle)); if (NS_FAILED(rv)) return; nsCOMPtr 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 char16_t *formatStrings[] = { url16.get() }; nsString result; rv = stringBundle->FormatStringFromName(PGPMIME_STR_NOT_SUPPORTED_ID, formatStrings, 1, getter_Copies(result)); if (NS_FAILED(rv)) return; aResult = NS_ConvertUTF16toUTF8(result); } static int MimeEncryptedPgpClassInitialize(MimeEncryptedPgpClass *clazz) { mozilla::DebugOnly oclass = (MimeObjectClass *) clazz; NS_ASSERTION(!oclass->class_initialized, "oclass is not initialized"); MimeEncryptedClass *eclass = (MimeEncryptedClass *) clazz; 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, int32_t buf_size, void *output_closure); void *output_closure; MimeObject *self; nsCOMPtr mimeDecrypt; MimePgpeData() : output_fn(nullptr), output_closure(nullptr) { } private: virtual ~MimePgpeData() { } }; NS_IMPL_ISUPPORTS0(MimePgpeData) static void* MimePgpe_init(MimeObject *obj, int (*output_fn) (const char *buf, int32_t buf_size, void *output_closure), void *output_closure) { if (!(obj && obj->options && output_fn)) return nullptr; MimePgpeData* data = new MimePgpeData(); NS_ENSURE_TRUE(data, nullptr); data->self = obj; data->output_fn = output_fn; data->output_closure = output_closure; data->mimeDecrypt = nullptr; nsresult rv; data->mimeDecrypt = do_CreateInstance(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 nullptr; nsCString mimePart = determineMimePart(obj); rv = data->mimeDecrypt->SetMimePart(mimePart); if (NS_FAILED(rv)) return nullptr; mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure); nsIChannel *channel = msd->channel; nsCOMPtr uri; if (channel) channel->GetURI(getter_AddRefs(uri)); if (NS_FAILED(data->mimeDecrypt->SetMimeCallback(output_fn, output_closure, uri))) return nullptr; return data; } static int MimePgpe_write(const char *buf, int32_t 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; if (NS_FAILED(data->mimeDecrypt->Finish())) return -1; data->mimeDecrypt = nullptr; return 0; } static char* MimePgpe_generate(void *output_closure) { const char htmlMsg[] = "GEN MSG"; char* msg = (char *) PR_MALLOC(strlen(htmlMsg) + 1); if (msg) PL_strcpy(msg, htmlMsg); return msg; } static void MimePgpe_free(void *output_closure) { } /* Returns a string describing the location of the part (like "2.5.3"). This is not a full URL, just a part-number. */ static nsCString determineMimePart(MimeObject* obj) { char mimePartNum[20]; MimeObject *kid; MimeContainer *cont; int32_t i; nsCString mimePart; while (obj->parent) { cont = (MimeContainer *) obj->parent; for (i = 0; i < cont->nchildren; i++) { kid = cont->children[i]; if (kid == obj) { sprintf(mimePartNum, ".%d", i + 1); mimePart.Insert(mimePartNum, 0); } } obj = obj->parent; } // remove leading "." if (mimePart.Length() > 0) mimePart.Cut(0, 1); return mimePart; } //////////////////////////////////////////////////////////////////////////// NS_IMPL_ISUPPORTS(nsPgpMimeProxy, nsIPgpMimeProxy, nsIRequestObserver, nsIStreamListener, nsIRequest, nsIInputStream) // nsPgpMimeProxy implementation nsPgpMimeProxy::nsPgpMimeProxy() : mInitialized(false), mDecryptor(nullptr), mLoadGroup(nullptr), mLoadFlags(LOAD_NORMAL), mCancelStatus(NS_OK) { } nsPgpMimeProxy::~nsPgpMimeProxy() { Finalize(); } nsresult nsPgpMimeProxy::Finalize() { return NS_OK; } NS_IMETHODIMP nsPgpMimeProxy::SetMimeCallback(MimeDecodeCallbackFun outputFun, void* outputClosure, nsIURI* myUri) { 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, myUri); return NS_OK; } NS_IMETHODIMP nsPgpMimeProxy::Init() { mByteBuf.Truncate(); nsresult rv; nsCOMPtr pbi(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; mDecryptor = do_CreateInstance(PGPMIME_JS_DECRYPTOR_CONTRACTID, &rv); if (NS_FAILED(rv)) mDecryptor = nullptr; return NS_OK; } NS_IMETHODIMP nsPgpMimeProxy::Write(const char *buf, uint32_t buf_size) { NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); mByteBuf.Assign(buf, buf_size); mStreamOffset = 0; if (mDecryptor) return mDecryptor->OnDataAvailable((nsIRequest*) this, nullptr, (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, nullptr, NS_OK); } else { nsCString temp; temp.Append("Content-Type: text/html\r\nCharset: UTF-8\r\n\r\n"); temp.Append("
"); temp.Append("
"); nsCString tString; PgpMimeGetNeedsAddonString(tString); temp.Append(tString); temp.Append("

\r\n"); PR_SetError(0,0); int status = mOutputFun(temp.get(), temp.Length(), mOutputClosure); if (status < 0) { PR_SetError(status, 0); mOutputFun = nullptr; 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; } NS_IMETHODIMP nsPgpMimeProxy::GetMimePart(nsACString &aMimePart) { aMimePart = mMimePart; return NS_OK; } NS_IMETHODIMP nsPgpMimeProxy::SetMimePart(const nsACString &aMimePart) { mMimePart = aMimePart; 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 = NS_SUCCEEDED(mCancelStatus); 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 (NS_SUCCEEDED(status)) return NS_ERROR_FAILURE; if (NS_SUCCEEDED(mCancelStatus)) 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(uint64_t* _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, uint32_t count, uint32_t *readCount) { NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); if (!buf || !readCount) return NS_ERROR_NULL_POINTER; int32_t avail = (mByteBuf.Length() > mStreamOffset) ? mByteBuf.Length() - mStreamOffset : 0; uint32_t readyCount = ((uint32_t) 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, uint32_t count, uint32_t *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, uint64_t aSourceOffset, uint32_t aLength) { NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_ARG(aInputStream); char buf[kCharMax]; uint32_t 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 = nullptr; return NS_ERROR_FAILURE; } aLength -= readCount; } return NS_OK; }