From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mailnews/mime/src/mimecryp.cpp | 507 ++++++++++++++++++++++++++++++++++++ 1 file changed, 507 insertions(+) create mode 100644 comm/mailnews/mime/src/mimecryp.cpp (limited to 'comm/mailnews/mime/src/mimecryp.cpp') diff --git a/comm/mailnews/mime/src/mimecryp.cpp b/comm/mailnews/mime/src/mimecryp.cpp new file mode 100644 index 0000000000..b985c53fbe --- /dev/null +++ b/comm/mailnews/mime/src/mimecryp.cpp @@ -0,0 +1,507 @@ +/* -*- 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/. */ + +#ifdef MOZ_LOGGING +# include "mozilla/Logging.h" +#endif +#include "mimecryp.h" +#include "mimemoz2.h" +#include "nsMimeTypes.h" +#include "nsMimeStringResources.h" +#include "mimebuf.h" +#include "prmem.h" +#include "plstr.h" +#include "prlog.h" +#include "mimemult.h" +#include "modmimee.h" // for MimeConverterOutputCallback + +using namespace mozilla; + +static mozilla::LazyLogModule gMimeCryptLog("MIMECRYPT"); + +#define MIME_SUPERCLASS mimeContainerClass +MimeDefClass(MimeEncrypted, MimeEncryptedClass, mimeEncryptedClass, + &MIME_SUPERCLASS); + +static int MimeEncrypted_initialize(MimeObject*); +static void MimeEncrypted_finalize(MimeObject*); +static int MimeEncrypted_parse_begin(MimeObject*); +static int MimeEncrypted_parse_buffer(const char*, int32_t, MimeObject*); +static int MimeEncrypted_parse_line(const char*, int32_t, MimeObject*); +static int MimeEncrypted_parse_decoded_buffer(const char*, int32_t, + MimeObject*); +static int MimeEncrypted_parse_eof(MimeObject*, bool); +static int MimeEncrypted_parse_end(MimeObject*, bool); +static int MimeEncrypted_add_child(MimeObject*, MimeObject*); + +static int MimeHandleDecryptedOutput(const char*, int32_t, void*); +static int MimeHandleDecryptedOutputLine(char*, int32_t, MimeObject*); +static int MimeEncrypted_close_headers(MimeObject*); +static int MimeEncrypted_emit_buffered_child(MimeObject*); + +static int MimeEncryptedClassInitialize(MimeEncryptedClass* clazz) { + MimeObjectClass* oclass = (MimeObjectClass*)clazz; + MimeContainerClass* cclass = (MimeContainerClass*)clazz; + + NS_ASSERTION(!oclass->class_initialized, + "1.2 01 Nov 2001 17:59"); + oclass->initialize = MimeEncrypted_initialize; + oclass->finalize = MimeEncrypted_finalize; + oclass->parse_begin = MimeEncrypted_parse_begin; + oclass->parse_buffer = MimeEncrypted_parse_buffer; + oclass->parse_line = MimeEncrypted_parse_line; + oclass->parse_eof = MimeEncrypted_parse_eof; + oclass->parse_end = MimeEncrypted_parse_end; + + cclass->add_child = MimeEncrypted_add_child; + + clazz->parse_decoded_buffer = MimeEncrypted_parse_decoded_buffer; + + return 0; +} + +static int MimeEncrypted_initialize(MimeObject* obj) { + return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj); +} + +static int MimeEncrypted_parse_begin(MimeObject* obj) { + MimeEncrypted* enc = (MimeEncrypted*)obj; + MimeDecoderData* (*fn)(MimeConverterOutputCallback, void*) = 0; + + if (enc->crypto_closure) return -1; + + enc->crypto_closure = (((MimeEncryptedClass*)obj->clazz)->crypto_init)( + obj, MimeHandleDecryptedOutput, obj); + if (!enc->crypto_closure) return -1; + + /* (Mostly duplicated from MimeLeaf, see comments in mimecryp.h.) + Initialize a decoder if necessary. + */ + if (!obj->encoding) + ; + else if (!PL_strcasecmp(obj->encoding, ENCODING_BASE64)) + fn = &MimeB64DecoderInit; + else if (!PL_strcasecmp(obj->encoding, ENCODING_QUOTED_PRINTABLE)) { + enc->decoder_data = + MimeQPDecoderInit(/* The (MimeConverterOutputCallback) cast is to turn + the `void' argument into `MimeObject'. */ + ((MimeConverterOutputCallback)((MimeEncryptedClass*) + obj->clazz) + ->parse_decoded_buffer), + obj); + + if (!enc->decoder_data) return MIME_OUT_OF_MEMORY; + } else if (!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE) || + !PL_strcasecmp(obj->encoding, ENCODING_UUENCODE2) || + !PL_strcasecmp(obj->encoding, ENCODING_UUENCODE3) || + !PL_strcasecmp(obj->encoding, ENCODING_UUENCODE4)) + fn = &MimeUUDecoderInit; + else if (!PL_strcasecmp(obj->encoding, ENCODING_YENCODE)) + fn = &MimeYDecoderInit; + if (fn) { + enc->decoder_data = + fn(/* The (MimeConverterOutputCallback) cast is to turn the `void' + argument into `MimeObject'. */ + ((MimeConverterOutputCallback)((MimeEncryptedClass*)obj->clazz) + ->parse_decoded_buffer), + obj); + + if (!enc->decoder_data) return MIME_OUT_OF_MEMORY; + } + + return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj); +} + +static int MimeEncrypted_parse_buffer(const char* buffer, int32_t size, + MimeObject* obj) { + /* (Duplicated from MimeLeaf, see comments in mimecryp.h.) + */ + + MimeEncrypted* enc = (MimeEncrypted*)obj; + + if (obj->closed_p) return -1; + + /* Don't consult output_p here, since at this point we're behaving as a + simple container object -- the output_p decision should be made by + the child of this object. */ + + if (enc->decoder_data) + return MimeDecoderWrite(enc->decoder_data, buffer, size, nullptr); + else + return ((MimeEncryptedClass*)obj->clazz) + ->parse_decoded_buffer(buffer, size, obj); +} + +static int MimeEncrypted_parse_line(const char* line, int32_t length, + MimeObject* obj) { + NS_ERROR("This method shouldn't ever be called."); + return -1; +} + +static int MimeEncrypted_parse_decoded_buffer(const char* buffer, int32_t size, + MimeObject* obj) { + MimeEncrypted* enc = (MimeEncrypted*)obj; + return ((MimeEncryptedClass*)obj->clazz) + ->crypto_write(buffer, size, enc->crypto_closure); +} + +static int MimeEncrypted_parse_eof(MimeObject* obj, bool abort_p) { + int status = 0; + MimeEncrypted* enc = (MimeEncrypted*)obj; + + if (obj->closed_p) return 0; + NS_ASSERTION(!obj->parsed_p, "1.2 01 Nov 2001 17:59"); + + /* (Duplicated from MimeLeaf, see comments in mimecryp.h.) + Close off the decoder, to cause it to give up any buffered data that + it is still holding. + */ + if (enc->decoder_data) { + int status = MimeDecoderDestroy(enc->decoder_data, false); + enc->decoder_data = 0; + if (status < 0) return status; + } + + /* If there is still data in the ibuffer, that means that the last + *decrypted* line of this part didn't end in a newline; so push it out + anyway (this means that the parse_line method will be called with a + string with no trailing newline, which isn't the usual case.) */ + if (!abort_p && obj->ibuffer_fp > 0) { + int status = + MimeHandleDecryptedOutputLine(obj->ibuffer, obj->ibuffer_fp, obj); + obj->ibuffer_fp = 0; + if (status < 0) { + obj->closed_p = true; + return status; + } + } + + /* Now run the superclass's parse_eof, which (because we've already taken + care of ibuffer in a way appropriate for this class, immediately above) + will only set closed_p to true. + */ + status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p); + if (status < 0) return status; + + /* Now close off the underlying crypto module. At this point, the crypto + module has all of the input. (DecoderDestroy called parse_decoded_buffer + which called crypto_write, with the last of the data.) + */ + if (enc->crypto_closure) { + status = ((MimeEncryptedClass*)obj->clazz) + ->crypto_eof(enc->crypto_closure, abort_p); + if (status < 0 && !abort_p) return status; + } + + /* Now we have the entire child part in the part buffer. + We are now able to verify its signature, emit a blurb, and then + emit the part. + */ + if (abort_p) + return 0; + else + return MimeEncrypted_emit_buffered_child(obj); +} + +static int MimeEncrypted_parse_end(MimeObject* obj, bool abort_p) { + return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_end(obj, abort_p); +} + +static void MimeEncrypted_cleanup(MimeObject* obj, bool finalizing_p) { + MimeEncrypted* enc = (MimeEncrypted*)obj; + + if (enc->part_buffer) { + MimePartBufferDestroy(enc->part_buffer); + enc->part_buffer = 0; + } + + if (finalizing_p && enc->crypto_closure) { + /* Don't free these until this object is really going away -- keep them + around for the lifetime of the MIME object, so that we can get at the + security info of sub-parts of the currently-displayed message. */ + ((MimeEncryptedClass*)obj->clazz)->crypto_free(enc->crypto_closure); + enc->crypto_closure = 0; + } + + /* (Duplicated from MimeLeaf, see comments in mimecryp.h.) + Free the decoder data, if it's still around. */ + if (enc->decoder_data) { + MimeDecoderDestroy(enc->decoder_data, true); + enc->decoder_data = 0; + } + + if (enc->hdrs) { + MimeHeaders_free(enc->hdrs); + enc->hdrs = 0; + } +} + +static void MimeEncrypted_finalize(MimeObject* obj) { + MimeEncrypted_cleanup(obj, true); + ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj); +} + +static int MimeHandleDecryptedOutput(const char* buf, int32_t buf_size, + void* output_closure) { + /* This method is invoked by the underlying decryption module. + The module is assumed to return a MIME object, and its associated + headers. For example, if a text/plain document was encrypted, + the encryption module would return the following data: + + Content-Type: text/plain + + Decrypted text goes here. + + This function will then extract a header block (up to the first + blank line, as usual) and will then handle the included data as + appropriate. + */ + MimeObject* obj = (MimeObject*)output_closure; + + /* Is it truly safe to use ibuffer here? I think so... */ + return mime_LineBuffer(buf, buf_size, &obj->ibuffer, &obj->ibuffer_size, + &obj->ibuffer_fp, true, + ((int (*)(char*, int32_t, void*)) + /* This cast is to turn void into MimeObject */ + MimeHandleDecryptedOutputLine), + obj); +} + +static int MimeHandleDecryptedOutputLine(char* line, int32_t length, + MimeObject* obj) { + /* Largely the same as MimeMessage_parse_line (the other MIME container + type which contains exactly one child.) + */ + MimeEncrypted* enc = (MimeEncrypted*)obj; + int status = 0; + + if (!line || !*line) return -1; + + /* If we're supposed to write this object, but aren't supposed to convert + it to HTML, simply pass it through unaltered. */ + if (obj->output_p && obj->options && !obj->options->write_html_p && + obj->options->output_fn) + return MimeObject_write(obj, line, length, true); + + /* If we already have a child object in the buffer, then we're done parsing + headers, and all subsequent lines get passed to the inferior object + without further processing by us. (Our parent will stop feeding us + lines when this MimeMessage part is out of data.) + */ + if (enc->part_buffer) + return MimePartBufferWrite(enc->part_buffer, line, length); + + /* Otherwise we don't yet have a child object in the buffer, which means + we're not done parsing our headers yet. + */ + if (!enc->hdrs) { + enc->hdrs = MimeHeaders_new(); + if (!enc->hdrs) return MIME_OUT_OF_MEMORY; + } + + status = MimeHeaders_parse_line(line, length, enc->hdrs); + if (status < 0) return status; + + /* If this line is blank, we're now done parsing headers, and should + examine our content-type to create our "body" part. + */ + if (*line == '\r' || *line == '\n') { + status = MimeEncrypted_close_headers(obj); + if (status < 0) return status; + } + + return 0; +} + +static int MimeEncrypted_close_headers(MimeObject* obj) { + MimeEncrypted* enc = (MimeEncrypted*)obj; + + // Notify the JS Mime Emitter that this was an encrypted part that it should + // hopefully not analyze for indexing... + if (obj->options && obj->options->notify_nested_bodies) + mimeEmitterAddHeaderField(obj->options, "x-jsemitter-encrypted", "1"); + + if (enc->part_buffer) return -1; + enc->part_buffer = MimePartBufferCreate(); + if (!enc->part_buffer) return MIME_OUT_OF_MEMORY; + + return 0; +} + +static int MimeEncrypted_add_child(MimeObject* parent, MimeObject* child) { + MimeContainer* cont = (MimeContainer*)parent; + if (!parent || !child) return -1; + + /* Encryption containers can only have one child. */ + if (cont->nchildren != 0) return -1; + + return ((MimeContainerClass*)&MIME_SUPERCLASS)->add_child(parent, child); +} + +#ifdef MOZ_LOGGING +static int DebugOut(const char* buf, int32_t size, void* closure) { + MOZ_LOG(gMimeCryptLog, LogLevel::Debug, + ("MimeEncrypted_emit_buffered_child: (partial) decrypted body\n%.*s", + size, buf)); + return 0; +} +#endif + +static int MimeEncrypted_emit_buffered_child(MimeObject* obj) { + MimeEncrypted* enc = (MimeEncrypted*)obj; + int status = 0; + char* ct = 0; + MimeObject* body; + + NS_ASSERTION(enc->crypto_closure, + "1.2 01 Nov 2001 17:59"); + +#ifdef MOZ_LOGGING + if (enc->hdrs && enc->hdrs->all_headers) { + MOZ_LOG(gMimeCryptLog, LogLevel::Debug, + ("MimeEncrypted_emit_buffered_child: decrypted headers:\n%.*s", + enc->hdrs->all_headers_fp, enc->hdrs->all_headers)); + } + + if (enc->part_buffer) { + status = MimePartBufferRead(enc->part_buffer, DebugOut, 0); + if (status < 0) return status; + } +#endif + + /* Emit some HTML saying whether the signature was cool. + But don't emit anything if in FO_QUOTE_MESSAGE mode. + + Also, don't emit anything if the enclosed object is itself a signed + object -- in the case of an encrypted object which contains a signed + object, we only emit the HTML once (since the normal way of encrypting + and signing is to nest the signature inside the crypto envelope.) + */ + if (enc->crypto_closure && obj->options && + obj->options->headers != MimeHeadersCitation && + obj->options->write_html_p && obj->options->output_fn) { + /* Now that we have written out the crypto stamp, the outermost header + block is well and truly closed. If this is in fact the outermost + message, then run the post_header_html_fn now. + */ + if (obj->options && obj->options->state && + obj->options->generate_post_header_html_fn && + !obj->options->state->post_header_html_run_p) { + MimeHeaders* outer_headers = nullptr; + MimeObject* p; + for (p = obj; p->parent; p = p->parent) outer_headers = p->headers; + NS_ASSERTION(obj->options->state->first_data_written_p, + "1.2 01 Nov 2001 17:59"); + char* html = obj->options->generate_post_header_html_fn( + NULL, obj->options->html_closure, outer_headers); + obj->options->state->post_header_html_run_p = true; + if (html) { + status = MimeObject_write(obj, html, strlen(html), false); + PR_FREEIF(html); + if (status < 0) return status; + } + } + } else if (enc->crypto_closure && obj->options && obj->options->decrypt_p) { + /* Do this just to cause `mime_set_crypto_stamp' to be called, and to + cause the various `decode_error' and `verify_error' slots to be set: + we don't actually use the returned HTML, because we're not emitting + HTML. It's maybe not such a good thing that the determination of + whether it was encrypted or not is tied up with generating HTML, + but oh well. */ + char* html = (((MimeEncryptedClass*)obj->clazz) + ->crypto_generate_html(enc->crypto_closure)); + PR_FREEIF(html); + } + + if (enc->hdrs) + ct = MimeHeaders_get(enc->hdrs, HEADER_CONTENT_TYPE, true, false); + body = mime_create((ct ? ct : TEXT_PLAIN), enc->hdrs, obj->options); + +#ifdef MIME_DRAFTS + if (obj->options->decompose_file_p) { + if (mime_typep(body, (MimeObjectClass*)&mimeMultipartClass)) + obj->options->is_multipart_msg = true; + else if (obj->options->decompose_file_init_fn) + obj->options->decompose_file_init_fn(obj->options->stream_closure, + enc->hdrs); + } +#endif /* MIME_DRAFTS */ + + PR_FREEIF(ct); + + if (!body) return MIME_OUT_OF_MEMORY; + status = ((MimeContainerClass*)obj->clazz)->add_child(obj, body); + if (status < 0) { + mime_free(body); + return status; + } + + /* Now that we've added this new object to our list of children, + start its parser going. */ + status = body->clazz->parse_begin(body); + if (status < 0) return status; + + /* If this object (or the parent) is being output, then by definition + the child is as well. (This is only necessary because this is such + a funny sort of container...) + */ + if (!body->output_p && + (obj->output_p || (obj->parent && obj->parent->output_p))) + body->output_p = true; + + /* If the body is being written raw (not as HTML) then make sure to + write its headers as well. */ + if (body->output_p && obj->output_p && !obj->options->write_html_p) { + status = MimeObject_write(body, "", 0, false); /* initialize */ + if (status < 0) return status; + status = MimeHeaders_write_raw_headers(body->headers, obj->options, false); + if (status < 0) return status; + } + + if (enc->part_buffer) /* part_buffer is 0 for 0-length encrypted data. */ + { +#ifdef MIME_DRAFTS + if (obj->options->decompose_file_p && !obj->options->is_multipart_msg) { + status = MimePartBufferRead( + enc->part_buffer, + /* The (MimeConverterOutputCallback) cast is to turn the `void' + argument into `MimeObject'. */ + ((MimeConverterOutputCallback)obj->options->decompose_file_output_fn), + obj->options->stream_closure); + } else { +#endif /* MIME_DRAFTS */ + + status = MimePartBufferRead( + enc->part_buffer, + /* The (MimeConverterOutputCallback) cast is to turn the `void' + argument into `MimeObject'. */ + ((MimeConverterOutputCallback)body->clazz->parse_buffer), body); +#ifdef MIME_DRAFTS + } +#endif /* MIME_DRAFTS */ + } + if (status < 0) return status; + + /* The child has been fully processed. Close it off. + */ + status = body->clazz->parse_eof(body, false); + if (status < 0) return status; + + status = body->clazz->parse_end(body, false); + if (status < 0) return status; + +#ifdef MIME_DRAFTS + if (obj->options->decompose_file_p && !obj->options->is_multipart_msg) + obj->options->decompose_file_close_fn(obj->options->stream_closure); +#endif /* MIME_DRAFTS */ + + /* Put out a separator after every encrypted object. */ + status = MimeObject_write_separator(obj); + if (status < 0) return status; + + MimeEncrypted_cleanup(obj, false); + + return 0; +} -- cgit v1.2.3