diff options
Diffstat (limited to 'mailnews/mime/src/mimemsig.cpp')
-rw-r--r-- | mailnews/mime/src/mimemsig.cpp | 775 |
1 files changed, 775 insertions, 0 deletions
diff --git a/mailnews/mime/src/mimemsig.cpp b/mailnews/mime/src/mimemsig.cpp new file mode 100644 index 000000000..d74cfb09a --- /dev/null +++ b/mailnews/mime/src/mimemsig.cpp @@ -0,0 +1,775 @@ +/* -*- 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/. */ + +#include "modmimee.h" +#include "mimemsig.h" +#include "nspr.h" + +#include "prmem.h" +#include "plstr.h" +#include "prerror.h" +#include "nsMimeTypes.h" +#include "msgCore.h" +#include "nsMimeStringResources.h" +#include "mimemoz2.h" +#include "nsIMimeConverter.h" // for MimeConverterOutputCallback +#include "mozilla/Attributes.h" + +#define MIME_SUPERCLASS mimeMultipartClass +MimeDefClass(MimeMultipartSigned, MimeMultipartSignedClass, + mimeMultipartSignedClass, &MIME_SUPERCLASS); + +static int MimeMultipartSigned_initialize (MimeObject *); +static int MimeMultipartSigned_create_child (MimeObject *); +static int MimeMultipartSigned_close_child(MimeObject *); +static int MimeMultipartSigned_parse_line (const char *, int32_t, MimeObject *); +static int MimeMultipartSigned_parse_child_line (MimeObject *, const char *, int32_t, + bool); +static int MimeMultipartSigned_parse_eof (MimeObject *, bool); +static void MimeMultipartSigned_finalize (MimeObject *); + +static int MimeMultipartSigned_emit_child (MimeObject *obj); + +static int +MimeMultipartSignedClassInitialize(MimeMultipartSignedClass *clazz) +{ + MimeObjectClass *oclass = (MimeObjectClass *) clazz; + MimeMultipartClass *mclass = (MimeMultipartClass *) clazz; + + oclass->initialize = MimeMultipartSigned_initialize; + oclass->parse_line = MimeMultipartSigned_parse_line; + oclass->parse_eof = MimeMultipartSigned_parse_eof; + oclass->finalize = MimeMultipartSigned_finalize; + mclass->create_child = MimeMultipartSigned_create_child; + mclass->parse_child_line = MimeMultipartSigned_parse_child_line; + mclass->close_child = MimeMultipartSigned_close_child; + + PR_ASSERT(!oclass->class_initialized); + return 0; +} + +static int +MimeMultipartSigned_initialize (MimeObject *object) +{ + MimeMultipartSigned *sig = (MimeMultipartSigned *) object; + + /* This is an abstract class; it shouldn't be directly instantiated. */ + PR_ASSERT(object->clazz != (MimeObjectClass *) &mimeMultipartSignedClass); + + sig->state = MimeMultipartSignedPreamble; + + return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object); +} + +static void +MimeMultipartSigned_cleanup (MimeObject *obj, bool finalizing_p) +{ + MimeMultipart *mult = (MimeMultipart *) obj; /* #58075. Fix suggested by jwz */ + MimeMultipartSigned *sig = (MimeMultipartSigned *) obj; + if (sig->part_buffer) + { + MimePartBufferDestroy(sig->part_buffer); + sig->part_buffer = 0; + } + if (sig->body_hdrs) + { + MimeHeaders_free (sig->body_hdrs); + sig->body_hdrs = 0; + } + if (sig->sig_hdrs) + { + MimeHeaders_free (sig->sig_hdrs); + sig->sig_hdrs = 0; + } + + mult->state = MimeMultipartEpilogue; /* #58075. Fix suggested by jwz */ + sig->state = MimeMultipartSignedEpilogue; + + if (finalizing_p && sig->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. */ + ((MimeMultipartSignedClass *) obj->clazz)->crypto_free (sig->crypto_closure); + sig->crypto_closure = 0; + } + + if (sig->sig_decoder_data) + { + MimeDecoderDestroy(sig->sig_decoder_data, true); + sig->sig_decoder_data = 0; + } +} + +static int +MimeMultipartSigned_parse_eof (MimeObject *obj, bool abort_p) +{ + MimeMultipartSigned *sig = (MimeMultipartSigned *) obj; + int status = 0; + + if (obj->closed_p) return 0; + + /* Close off the signature, if we've gotten that far. + */ + if (sig->state == MimeMultipartSignedSignatureHeaders || + sig->state == MimeMultipartSignedSignatureFirstLine || + sig->state == MimeMultipartSignedSignatureLine || + sig->state == MimeMultipartSignedEpilogue) + { + status = (((MimeMultipartSignedClass *) obj->clazz)->crypto_signature_eof) (sig->crypto_closure, abort_p); + if (status < 0) return status; + } + + if (!abort_p) + { + /* Now that we've read both the signed object and the signature (and + have presumably verified the signature) write out a blurb, and then + the signed object. + */ + status = MimeMultipartSigned_emit_child(obj); + if (status < 0) return status; + } + + MimeMultipartSigned_cleanup(obj, false); + return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p); +} + + +static void +MimeMultipartSigned_finalize (MimeObject *obj) +{ + MimeMultipartSigned_cleanup(obj, true); + ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj); +} + + +static int +MimeMultipartSigned_parse_line (const char *line, int32_t length, MimeObject *obj) +{ + MimeMultipart *mult = (MimeMultipart *) obj; + MimeMultipartSigned *sig = (MimeMultipartSigned *) obj; + MimeMultipartParseState old_state = mult->state; + bool hash_line_p = true; + bool no_headers_p = false; + int status = 0; + + /* First do the parsing for normal multipart/ objects by handing it off to + the superclass method. This includes calling the create_child and + close_child methods. + */ + status = (((MimeObjectClass *)(&MIME_SUPERCLASS)) + ->parse_line (line, length, obj)); + if (status < 0) return status; + + /* The instance variable MimeMultipartClass->state tracks motion through + the various stages of multipart/ parsing. The instance variable + MimeMultipartSigned->state tracks the difference between the first + part (the body) and the second part (the signature.) This second, + more specific state variable is updated by noticing the transitions + of the first, more general state variable. + */ + if (old_state != mult->state) /* there has been a state change */ + { + switch (mult->state) + { + case MimeMultipartPreamble: + PR_ASSERT(0); /* can't move *in* to preamble state. */ + sig->state = MimeMultipartSignedPreamble; + break; + + case MimeMultipartHeaders: + /* If we're moving in to the Headers state, then that means + that this line is the preceeding boundary string (and we + should ignore it.) + */ + hash_line_p = false; + + if (sig->state == MimeMultipartSignedPreamble) + sig->state = MimeMultipartSignedBodyFirstHeader; + else if (sig->state == MimeMultipartSignedBodyFirstLine || + sig->state == MimeMultipartSignedBodyLine) + sig->state = MimeMultipartSignedSignatureHeaders; + else if (sig->state == MimeMultipartSignedSignatureFirstLine || + sig->state == MimeMultipartSignedSignatureLine) + sig->state = MimeMultipartSignedEpilogue; + break; + + case MimeMultipartPartFirstLine: + if (sig->state == MimeMultipartSignedBodyFirstHeader) + { + sig->state = MimeMultipartSignedBodyFirstLine; + no_headers_p = true; + } + else if (sig->state == MimeMultipartSignedBodyHeaders) + sig->state = MimeMultipartSignedBodyFirstLine; + else if (sig->state == MimeMultipartSignedSignatureHeaders) + sig->state = MimeMultipartSignedSignatureFirstLine; + else + sig->state = MimeMultipartSignedEpilogue; + break; + + case MimeMultipartPartLine: + + PR_ASSERT(sig->state == MimeMultipartSignedBodyFirstLine || + sig->state == MimeMultipartSignedBodyLine || + sig->state == MimeMultipartSignedSignatureFirstLine || + sig->state == MimeMultipartSignedSignatureLine); + + if (sig->state == MimeMultipartSignedBodyFirstLine) + sig->state = MimeMultipartSignedBodyLine; + else if (sig->state == MimeMultipartSignedSignatureFirstLine) + sig->state = MimeMultipartSignedSignatureLine; + break; + + case MimeMultipartEpilogue: + sig->state = MimeMultipartSignedEpilogue; + break; + + default: /* bad state */ + NS_ERROR("bad state in MultipartSigned parse line"); + return -1; + break; + } + } + + + /* Perform multipart/signed-related actions on this line based on the state + of the parser. + */ + switch (sig->state) + { + case MimeMultipartSignedPreamble: + /* Do nothing. */ + break; + + case MimeMultipartSignedBodyFirstLine: + /* We have just moved out of the MimeMultipartSignedBodyHeaders + state, so cache away the headers that apply only to the body part. + */ + NS_ASSERTION(mult->hdrs, "null multipart hdrs"); + NS_ASSERTION(!sig->body_hdrs, "signed part shouldn't have already have body_hdrs"); + sig->body_hdrs = mult->hdrs; + mult->hdrs = 0; + + /* fall through. */ + MOZ_FALLTHROUGH; + case MimeMultipartSignedBodyFirstHeader: + case MimeMultipartSignedBodyHeaders: + case MimeMultipartSignedBodyLine: + + if (!sig->crypto_closure) + { + /* Set error change */ + PR_SetError(0, 0); + /* Initialize the signature verification library. */ + sig->crypto_closure = (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_init) (obj); + if (!sig->crypto_closure) + { + status = PR_GetError(); + NS_ASSERTION(status < 0, "got non-negative status"); + if (status >= 0) + status = -1; + return status; + } + } + + if (hash_line_p) + { + /* this is the first hashed line if this is the first header + (that is, if it's the first line in the header state after + a state change.) + */ + bool first_line_p + = (no_headers_p || + sig->state == MimeMultipartSignedBodyFirstHeader); + + if (sig->state == MimeMultipartSignedBodyFirstHeader) + sig->state = MimeMultipartSignedBodyHeaders; + + /* The newline issues here are tricky, since both the newlines + before and after the boundary string are to be considered part + of the boundary: this is so that a part can be specified such + that it does not end in a trailing newline. + + To implement this, we send a newline *before* each line instead + of after, except for the first line, which is not preceeded by a + newline. + + For purposes of cryptographic hashing, we always hash line + breaks as CRLF -- the canonical, on-the-wire linebreaks, since + we have no idea of knowing what line breaks were used on the + originating system (SMTP rightly destroys that information.) + */ + + /* Remove the trailing newline... */ + if (length > 0 && line[length-1] == '\n') length--; + if (length > 0 && line[length-1] == '\r') length--; + + PR_ASSERT(sig->crypto_closure); + + if (!first_line_p) + { + /* Push out a preceeding newline... */ + char nl[] = CRLF; + status = (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_data_hash (nl, 2, sig->crypto_closure)); + if (status < 0) return status; + } + + /* Now push out the line sans trailing newline. */ + if (length > 0) + status = (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_data_hash (line,length, sig->crypto_closure)); + if (status < 0) return status; + } + break; + + case MimeMultipartSignedSignatureHeaders: + + if (sig->crypto_closure && + old_state != mult->state) + { + /* We have just moved out of the MimeMultipartSignedBodyLine + state, so tell the signature verification library that we've + reached the end of the signed data. + */ + status = (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_data_eof) (sig->crypto_closure, false); + if (status < 0) return status; + } + break; + + case MimeMultipartSignedSignatureFirstLine: + /* We have just moved out of the MimeMultipartSignedSignatureHeaders + state, so cache away the headers that apply only to the sig part. + */ + PR_ASSERT(mult->hdrs); + PR_ASSERT(!sig->sig_hdrs); + sig->sig_hdrs = mult->hdrs; + mult->hdrs = 0; + + + /* If the signature block has an encoding, set up a decoder for it. + (Similar logic is in MimeLeafClass->parse_begin.) + */ + { + MimeDecoderData *(*fn) (MimeConverterOutputCallback, void*) = 0; + nsCString encoding; + encoding.Adopt(MimeHeaders_get (sig->sig_hdrs, + HEADER_CONTENT_TRANSFER_ENCODING, + true, false)); + if (encoding.IsEmpty()) + ; + else if (!PL_strcasecmp(encoding.get(), ENCODING_BASE64)) + fn = &MimeB64DecoderInit; + else if (!PL_strcasecmp(encoding.get(), ENCODING_QUOTED_PRINTABLE)) + { + sig->sig_decoder_data = + MimeQPDecoderInit (((MimeConverterOutputCallback) + (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_signature_hash)), + sig->crypto_closure); + if (!sig->sig_decoder_data) + return MIME_OUT_OF_MEMORY; + } + else if (!PL_strcasecmp(encoding.get(), ENCODING_UUENCODE) || + !PL_strcasecmp(encoding.get(), ENCODING_UUENCODE2) || + !PL_strcasecmp(encoding.get(), ENCODING_UUENCODE3) || + !PL_strcasecmp(encoding.get(), ENCODING_UUENCODE4)) + fn = &MimeUUDecoderInit; + else if (!PL_strcasecmp(encoding.get(), ENCODING_YENCODE)) + fn = &MimeYDecoderInit; + if (fn) + { + sig->sig_decoder_data = + fn (((MimeConverterOutputCallback) + (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_signature_hash)), + sig->crypto_closure); + if (!sig->sig_decoder_data) + return MIME_OUT_OF_MEMORY; + } + } + + /* Show these headers to the crypto module. */ + if (hash_line_p) + { + status = (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_signature_init) (sig->crypto_closure, + obj, sig->sig_hdrs); + if (status < 0) return status; + } + + /* fall through. */ + MOZ_FALLTHROUGH; + case MimeMultipartSignedSignatureLine: + if (hash_line_p) + { + /* Feed this line into the signature verification routines. */ + + if (sig->sig_decoder_data) + status = MimeDecoderWrite (sig->sig_decoder_data, line, length, nullptr); + else + status = (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_signature_hash (line, length, + sig->crypto_closure)); + if (status < 0) return status; + } + break; + + case MimeMultipartSignedEpilogue: + /* Nothing special to do here. */ + break; + + default: /* bad state */ + PR_ASSERT(0); + return -1; + } + + return status; +} + + +static int +MimeMultipartSigned_create_child (MimeObject *parent) +{ + /* Don't actually create a child -- we call the superclass create_child + method later, after we've fully parsed everything. (And we only call + it once, for part #1, and never for part #2 (the signature.)) + */ + MimeMultipart *mult = (MimeMultipart *) parent; + mult->state = MimeMultipartPartFirstLine; + return 0; +} + + +static int +MimeMultipartSigned_close_child (MimeObject *obj) +{ + /* The close_child method on MimeMultipartSigned doesn't actually do + anything to the children list, since the create_child method also + doesn't do anything. + */ + MimeMultipart *mult = (MimeMultipart *) obj; + MimeContainer *cont = (MimeContainer *) obj; + MimeMultipartSigned *msig = (MimeMultipartSigned *) obj; + + if (msig->part_buffer) + /* Closes the tmp file, if there is one: doesn't free the part_buffer. */ + MimePartBufferClose(msig->part_buffer); + + if (mult->hdrs) /* duplicated from MimeMultipart_close_child, ugh. */ + { + MimeHeaders_free(mult->hdrs); + mult->hdrs = 0; + } + + /* Should be no kids yet. */ + PR_ASSERT(cont->nchildren == 0); + if (cont->nchildren != 0) return -1; + + return 0; +} + + +static int +MimeMultipartSigned_parse_child_line (MimeObject *obj, + const char *line, int32_t length, + bool first_line_p) +{ + MimeMultipartSigned *sig = (MimeMultipartSigned *) obj; + MimeContainer *cont = (MimeContainer *) obj; + int status = 0; + + /* Shouldn't have made any sub-parts yet. */ + PR_ASSERT(cont->nchildren == 0); + if (cont->nchildren != 0) return -1; + + switch (sig->state) + { + case MimeMultipartSignedPreamble: + case MimeMultipartSignedBodyFirstHeader: + case MimeMultipartSignedBodyHeaders: + // How'd we get here? Oh well, fall through. + NS_ERROR("wrong state in parse child line"); + MOZ_FALLTHROUGH; + case MimeMultipartSignedBodyFirstLine: + PR_ASSERT(first_line_p); + if (!sig->part_buffer) + { + sig->part_buffer = MimePartBufferCreate(); + if (!sig->part_buffer) + return MIME_OUT_OF_MEMORY; + } + /* fall through */ + MOZ_FALLTHROUGH; + case MimeMultipartSignedBodyLine: + { + /* This is the first part; we are buffering it, and will emit it all + at the end (so that we know whether the signature matches before + showing anything to the user.) + */ + + /* The newline issues here are tricky, since both the newlines + before and after the boundary string are to be considered part + of the boundary: this is so that a part can be specified such + that it does not end in a trailing newline. + + To implement this, we send a newline *before* each line instead + of after, except for the first line, which is not preceeded by a + newline. + */ + + /* Remove the trailing newline... */ + if (length > 0 && line[length-1] == '\n') length--; + if (length > 0 && line[length-1] == '\r') length--; + + PR_ASSERT(sig->part_buffer); + PR_ASSERT(first_line_p == + (sig->state == MimeMultipartSignedBodyFirstLine)); + + if (!first_line_p) + { + /* Push out a preceeding newline... */ + char nl[] = MSG_LINEBREAK; + status = MimePartBufferWrite (sig->part_buffer, nl, MSG_LINEBREAK_LEN); + if (status < 0) return status; + } + + /* Now push out the line sans trailing newline. */ + if (length > 0) + status = MimePartBufferWrite (sig->part_buffer, line, length); + if (status < 0) return status; + } + break; + + case MimeMultipartSignedSignatureHeaders: + // How'd we get here? Oh well, fall through. + NS_ERROR("should have already parse sig hdrs"); + MOZ_FALLTHROUGH; + case MimeMultipartSignedSignatureFirstLine: + case MimeMultipartSignedSignatureLine: + /* Nothing to do here -- hashing of the signature part is handled up + in MimeMultipartSigned_parse_line(). + */ + break; + + case MimeMultipartSignedEpilogue: + /* Too many kids? MimeMultipartSigned_create_child() should have + prevented us from getting here. */ + NS_ERROR("too many kids?"); + return -1; + break; + + default: /* bad state */ + NS_ERROR("bad state in multipart signed parse line"); + return -1; + break; + } + + return status; +} + + +static int +MimeMultipartSigned_emit_child (MimeObject *obj) +{ + MimeMultipartSigned *sig = (MimeMultipartSigned *) obj; + MimeMultipart *mult = (MimeMultipart *) obj; + MimeContainer *cont = (MimeContainer *) obj; + int status = 0; + MimeObject *body; + + NS_ASSERTION(sig->crypto_closure, "no crypto closure"); + + /* Emit some HTML saying whether the signature was cool. + But don't emit anything if in FO_QUOTE_MESSAGE mode. + */ + if (obj->options && + obj->options->headers != MimeHeadersCitation && + obj->options->write_html_p && + obj->options->output_fn && + obj->options->headers != MimeHeadersCitation && + sig->crypto_closure) + { + char *html = (((MimeMultipartSignedClass *) obj->clazz) + ->crypto_generate_html (sig->crypto_closure)); +#if 0 // XXX For the moment, no HTML output. Fix this XXX // + if (!html) return -1; /* MIME_OUT_OF_MEMORY? */ + + status = MimeObject_write(obj, html, strlen(html), false); + PR_Free(html); + if (status < 0) return status; +#endif + + /* 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, + "should have already written some data"); + 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_Free(html); + if (status < 0) return status; + } + } + } + + + /* Oh, this is fairly nasty. We're skipping over our "create child" method + and using the one our superclass defines. Perhaps instead we should add + a new method on this class, and initialize that method to be the + create_child method of the superclass. Whatever. + */ + + + /* The superclass method expects to find the headers for the part that it's + to create in mult->hdrs, so ensure that they're there. */ + NS_ASSERTION(!mult->hdrs, "shouldn't already have hdrs for multipart"); + if (mult->hdrs) MimeHeaders_free(mult->hdrs); + mult->hdrs = sig->body_hdrs; + sig->body_hdrs = 0; + + /* Run the superclass create_child method. + */ + status = (((MimeMultipartClass *)(&MIME_SUPERCLASS))->create_child(obj)); + if (status < 0) return status; + + // Notify the charset of the first part. + if (obj->options && !(obj->options->override_charset)) { + MimeObject *firstChild = ((MimeContainer*) obj)->children[0]; + char *disposition = MimeHeaders_get (firstChild->headers, + HEADER_CONTENT_DISPOSITION, + true, + false); + // check if need to show as inline + if (!disposition) + { + const char *content_type = firstChild->content_type; + if (!PL_strcasecmp (content_type, TEXT_PLAIN) || + !PL_strcasecmp (content_type, TEXT_HTML) || + !PL_strcasecmp (content_type, TEXT_MDL) || + !PL_strcasecmp (content_type, MULTIPART_ALTERNATIVE) || + !PL_strcasecmp (content_type, MULTIPART_RELATED) || + !PL_strcasecmp (content_type, MESSAGE_NEWS) || + !PL_strcasecmp (content_type, MESSAGE_RFC822)) { + char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false, false); + if (ct) { + char *cset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL); + if (cset) { + mimeEmitterUpdateCharacterSet(obj->options, cset); + SetMailCharacterSetToMsgWindow(obj, cset); + PR_Free(cset); + } + PR_Free(ct); + } + } + } + } + + // The js emitter wants to know about the newly created child. Because + // MimeMultipartSigned dummies out its create_child operation, the logic + // in MimeMultipart_parse_line that would normally provide this notification + // does not get to fire. + if (obj->options && obj->options->notify_nested_bodies) { + MimeObject *kid = ((MimeContainer*) obj)->children[0]; + // The emitter is expecting the content type with parameters; not the fully + // parsed thing, so get it from raw. (We do not do it in the charset + // notification block that just happened because it already has complex + // if-checks that do not jive with us. + char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false, + false); + mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE, + ct ? ct : "text/plain"); + PR_Free(ct); + + char *part_path = mime_part_address(kid); + if (part_path) { + mimeEmitterAddHeaderField(obj->options, + "x-jsemitter-part-path", + part_path); + PR_Free(part_path); + } + } + + /* Retrieve the child that it created. + */ + NS_ASSERTION(cont->nchildren == 1, "should only have one child"); + if (cont->nchildren != 1) + return -1; + body = cont->children[0]; + NS_ASSERTION(body, "missing body"); + if (!body) + return -1; + +#ifdef MIME_DRAFTS + if (body->options->decompose_file_p) { + body->options->signed_p = true; + if (!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) && + body->options->decompose_file_init_fn) + body->options->decompose_file_init_fn ( body->options->stream_closure, body->headers ); + } +#endif /* MIME_DRAFTS */ + + /* If there's no part_buffer, this is a zero-length signed message? */ + if (sig->part_buffer) + { +#ifdef MIME_DRAFTS + if (body->options->decompose_file_p && + !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) && + body->options->decompose_file_output_fn) + status = MimePartBufferRead (sig->part_buffer, + /* The (MimeConverterOutputCallback) cast is to turn the + `void' argument into `MimeObject'. */ + ((MimeConverterOutputCallback) + body->options->decompose_file_output_fn), + body->options->stream_closure); + else +#endif /* MIME_DRAFTS */ + + status = MimePartBufferRead (sig->part_buffer, + /* The (MimeConverterOutputCallback) cast is to turn the + `void' argument into `MimeObject'. */ + ((MimeConverterOutputCallback) body->clazz->parse_buffer), + body); + if (status < 0) return status; + } + + MimeMultipartSigned_cleanup(obj, false); + + /* Done parsing. */ + 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 (body->options->decompose_file_p && + !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) && + body->options->decompose_file_close_fn) + body->options->decompose_file_close_fn(body->options->stream_closure); +#endif /* MIME_DRAFTS */ + + /* Put out a separator after every multipart/signed object. */ + status = MimeObject_write_separator(obj); + if (status < 0) return status; + + return 0; +} |