static int MimeMessage_add_child (MimeObject *parent, MimeObject *child) { MimeContainer *cont = (MimeContainer *) parent; PR_ASSERT(parent && child); if (!parent || !child) return -1; /* message/rfc822 containers can only have one child. */ PR_ASSERT(cont->nchildren == 0); if (cont->nchildren != 0) return -1; #ifdef MIME_DRAFTS if ( parent->options && parent->options->decompose_file_p && ! parent->options->is_multipart_msg && ! mime_typep(child, (MimeObjectClass*) &mimeEncryptedClass) && parent->options->decompose_file_init_fn ) { int status = 0; status = parent->options->decompose_file_init_fn ( parent->options->stream_closure, ((MimeMessage*)parent)->hdrs ); if ( status < 0 ) return status; } #endif /* MIME_DRAFTS */ return ((MimeContainerClass*)&MIME_SUPERCLASS)->add_child (parent, child); }
static MimeMultipartBoundaryType MimeMultipart_check_boundary(MimeObject *obj, const char *line, PRInt32 length) { MimeMultipart *mult = (MimeMultipart *) obj; PRInt32 blen; PRBool term_p; if (!mult->boundary || line[0] != '-' || line[1] != '-') return MimeMultipartBoundaryTypeNone; /* This is a candidate line to be a boundary. Check it out... */ blen = strlen(mult->boundary); term_p = PR_FALSE; /* strip trailing whitespace (including the newline.) */ while(length > 2 && IS_SPACE(line[length-1])) length--; /* Could this be a terminating boundary? */ if (length == blen + 4 && line[length-1] == '-' && line[length-2] == '-') { term_p = PR_TRUE; } //looks like we have a separator but first, we need to check it's not for one of the part's children. MimeContainer *cont = (MimeContainer *) obj; if (cont->nchildren > 0) { MimeObject *kid = cont->children[cont->nchildren-1]; if (kid) if (mime_typep(kid, (MimeObjectClass*) &mimeMultipartClass)) { //Don't ask the kid to check the boundary if it has already detected a Teminator MimeMultipart *mult = (MimeMultipart *) kid; if (mult->state != MimeMultipartEpilogue) if (MimeMultipart_check_boundary(kid, line, length) != MimeMultipartBoundaryTypeNone) return MimeMultipartBoundaryTypeNone; } } if (term_p) length -= 2; if (blen == length-2 && !strncmp(line+2, mult->boundary, length-2)) return (term_p ? MimeMultipartBoundaryTypeTerminator : MimeMultipartBoundaryTypeSeparator); else return MimeMultipartBoundaryTypeNone; }
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; }
static int MimeMessage_parse_eof (MimeObject *obj, PRBool abort_p) { int status; PRBool outer_p; MimeMessage *msg = (MimeMessage *)obj; if (obj->closed_p) return 0; /* Run parent method first, to flush out any buffered data. */ status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p); if (status < 0) return status; outer_p = !obj->headers; /* is this the outermost message? */ // Hack for messages with truncated headers (bug 244722) // If there is no empty line in a message, the parser can't figure out where // the headers end, causing parsing to hang. So we insert an extra newline // to keep it happy. This is OK, since a message without any empty lines is // broken anyway... if(outer_p && msg->hdrs && ! msg->hdrs->done_p) { MimeMessage_parse_line("\n", 1, obj); } // Once we get to the end of parsing the message, we will notify // the emitter that we are done the the body. // Mark the end of the mail body if we are actually emitting the // body of the message (i.e. not Header ONLY) if (outer_p && obj->options && obj->options->write_html_p) { if (obj->options->generate_footer_html_fn) { mime_stream_data *msd = (mime_stream_data *) obj->options->stream_closure; if (msd) { char *html = obj->options->generate_footer_html_fn (msd->orig_url_name, obj->options->html_closure, msg->hdrs); if (html) { int lstatus = MimeObject_write(obj, html, strlen(html), PR_FALSE); PR_Free(html); if (lstatus < 0) return lstatus; } } } if ((!obj->options->part_to_load || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) && obj->options->headers != MimeHeadersOnly) mimeEmitterEndBody(obj->options); } #ifdef MIME_DRAFTS if ( obj->options && obj->options->decompose_file_p && obj->options->done_parsing_outer_headers && ! obj->options->is_multipart_msg && ! mime_typep(obj, (MimeObjectClass*) &mimeEncryptedClass) && obj->options->decompose_file_close_fn ) { status = obj->options->decompose_file_close_fn ( obj->options->stream_closure ); if ( status < 0 ) return status; } #endif /* MIME_DRAFTS */ /* Put out a separator after every message/rfc822 object. */ if (!abort_p && !outer_p) { status = MimeObject_write_separator(obj); if (status < 0) return status; } return 0; }
static int MimeMessage_close_headers (MimeObject *obj) { MimeMessage *msg = (MimeMessage *) obj; int status = 0; char *ct = 0; /* Content-Type header */ MimeObject *body; if (msg->hdrs) { PRBool outer_p = !obj->headers; /* is this the outermost message? */ #ifdef MIME_DRAFTS if (outer_p && obj->options && (obj->options->decompose_file_p || obj->options->caller_need_root_headers) && obj->options->decompose_headers_info_fn) { #ifdef ENABLE_SMIME if (obj->options->decrypt_p && !mime_crypto_object_p (msg->hdrs, PR_FALSE)) obj->options->decrypt_p = PR_FALSE; #endif /* ENABLE_SMIME */ if (!obj->options->caller_need_root_headers || (obj == obj->options->state->root)) status = obj->options->decompose_headers_info_fn ( obj->options->stream_closure, msg->hdrs ); } #endif /* MIME_DRAFTS */ /* If this is the outermost message, we need to run the `generate_header' callback. This happens here instead of in `parse_begin', because it's only now that we've parsed our headers. However, since this is the outermost message, we have yet to write any HTML, so that's fine. */ if (outer_p && obj->output_p && obj->options && obj->options->write_html_p && obj->options->generate_header_html_fn) { int lstatus = 0; char *html = 0; /* The generate_header_html_fn might return HTML, so it's important that the output stream be set up with the proper type before we make the MimeObject_write() call below. */ if (!obj->options->state->first_data_written_p) { lstatus = MimeObject_output_init (obj, TEXT_HTML); if (lstatus < 0) return lstatus; PR_ASSERT(obj->options->state->first_data_written_p); } html = obj->options->generate_header_html_fn(NULL, obj->options->html_closure, msg->hdrs); if (html) { lstatus = MimeObject_write(obj, html, strlen(html), PR_FALSE); PR_Free(html); if (lstatus < 0) return lstatus; } } /* Find the content-type of the body of this message. */ { PRBool ok = PR_TRUE; char *mv = MimeHeaders_get (msg->hdrs, HEADER_MIME_VERSION, PR_TRUE, PR_FALSE); #ifdef REQUIRE_MIME_VERSION_HEADER /* If this is the outermost message, it must have a MIME-Version header with the value 1.0 for us to believe what might be in the Content-Type header. If the MIME-Version header is not present, we must treat this message as untyped. */ ok = (mv && !strcmp(mv, "1.0")); #else /* #### actually, we didn't check this in Mozilla 2.0, and checking it now could cause some compatibility nonsense, so for now, let's just believe any Content-Type header we see. */ ok = PR_TRUE; #endif if (ok) { ct = MimeHeaders_get (msg->hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE); /* If there is no Content-Type header, but there is a MIME-Version header, then assume that this *is* in fact a MIME message. (I've seen messages with MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable and no Content-Type, and we should treat those as being of type MimeInlineTextPlain rather than MimeUntypedText.) */ if (mv && !ct) ct = strdup(TEXT_PLAIN); } PR_FREEIF(mv); /* done with this now. */ } /* If this message has a body which is encrypted and we're going to decrypt it (whithout converting it to HTML, since decrypt_p and write_html_p are never true at the same time) */ if (obj->output_p && obj->options && obj->options->decrypt_p #ifdef ENABLE_SMIME && !mime_crypto_object_p (msg->hdrs, PR_FALSE) #endif /* ENABLE_SMIME */ ) { /* The body of this message is not an encrypted object, so we need to turn off the decrypt_p flag (to prevent us from s#$%ing the body of the internal object up into one.) In this case, our output will end up being identical to our input. */ obj->options->decrypt_p = PR_FALSE; } /* Emit the HTML for this message's headers. Do this before creating the object representing the body. */ if (obj->output_p && obj->options && obj->options->write_html_p) { /* If citation headers are on, and this is not the outermost message, turn them off. */ if (obj->options->headers == MimeHeadersCitation && !outer_p) obj->options->headers = MimeHeadersSome; /* Emit a normal header block. */ status = MimeMessage_write_headers_html(obj); if (status < 0) return status; } else if (obj->output_p) { /* Dump the headers, raw. */ status = MimeObject_write(obj, "", 0, PR_FALSE); /* initialize */ if (status < 0) return status; status = MimeHeaders_write_raw_headers(msg->hdrs, obj->options, obj->options->decrypt_p); if (status < 0) return status; } #ifdef XP_UNIX if (outer_p && obj->output_p) /* Kludge from mimehdrs.c */ MimeHeaders_do_unix_display_hook_hack(msg->hdrs); #endif /* XP_UNIX */ } /* Never put out a separator after a message header block. */ if (obj->options && obj->options->state) obj->options->state->separator_suppressed_p = PR_TRUE; #ifdef MIME_DRAFTS if ( !obj->headers && /* outer most message header */ obj->options && obj->options->decompose_file_p && ct ) obj->options->is_multipart_msg = PL_strcasestr(ct, "multipart/") != NULL; #endif /* MIME_DRAFTS */ body = mime_create(ct, msg->hdrs, obj->options); 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; } // Only do this if this is a Text Object! if ( mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass) ) { ((MimeInlineText *) body)->needUpdateMsgWinCharset = PR_TRUE; } /* 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; // Now notify the emitter if this is the outer most message, unless // it is a part that is not the head of the message. If it's a part, // we need to figure out the content type/charset of the part // PRBool outer_p = !obj->headers; /* is this the outermost message? */ if ( outer_p && (!obj->options->part_to_load || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)) { // call SetMailCharacterSetToMsgWindow() to set a menu charset if (mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass)) { MimeInlineText *text = (MimeInlineText *) body; if (text && text->charset && *text->charset) SetMailCharacterSetToMsgWindow(body, text->charset); } char *msgID = MimeHeaders_get (msg->hdrs, HEADER_MESSAGE_ID, PR_FALSE, PR_FALSE); const char *outCharset = NULL; if (!obj->options->force_user_charset) /* Only convert if the user prefs is false */ outCharset = "UTF-8"; mimeEmitterStartBody(obj->options, (obj->options->headers == MimeHeadersNone), msgID, outCharset); PR_FREEIF(msgID); // setting up truncated message html fotter function char *xmoz = MimeHeaders_get(msg->hdrs, HEADER_X_MOZILLA_STATUS, PR_FALSE, PR_FALSE); if (xmoz) { PRUint32 flags = 0; char dummy = 0; if (sscanf(xmoz, " %lx %c", &flags, &dummy) == 1 && flags & MSG_FLAG_PARTIAL) { obj->options->html_closure = obj; obj->options->generate_footer_html_fn = MimeMessage_partial_message_html; } PR_FREEIF(xmoz); } } return 0; }
static int MimeMessage_parse_line (const char *aLine, PRInt32 aLength, MimeObject *obj) { const char * line = aLine; PRInt32 length = aLength; MimeMessage *msg = (MimeMessage *) obj; int status = 0; PR_ASSERT(line && *line); if (!line || !*line) return -1; #ifdef MOZ_SECURITY HG11013 #endif /* MOZ_SECURITY */ if (msg->grabSubject) { if ( (!PL_strncasecmp(line, "Subject: ", 9)) && (obj->parent) ) { if ( (obj->headers) && (!obj->headers->munged_subject) ) { obj->headers->munged_subject = (char *) PL_strndup(line + 9, length - 9); char *tPtr = obj->headers->munged_subject; while (*tPtr) { if ( (*tPtr == '\r') || (*tPtr == '\n') ) { *tPtr = '\0'; break; } tPtr++; } } } } /* If we already have a child object, 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 (msg->container.nchildren) { MimeObject *kid = msg->container.children[0]; PRBool nl; PR_ASSERT(kid); if (!kid) return -1; msg->bodyLength += length; /* Don't allow MimeMessage objects to not end in a newline, since it would be inappropriate for any following part to appear on the same line as the last line of the message. #### This assumes that the only time the `parse_line' method is called with a line that doesn't end in a newline is when that line is the last line. */ nl = (length > 0 && (line[length-1] == '\r' || line[length-1] == '\n')); #ifdef MIME_DRAFTS if ( !mime_typep (kid, (MimeObjectClass*) &mimeMessageClass) && obj->options && obj->options->decompose_file_p && ! obj->options->is_multipart_msg && obj->options->decompose_file_output_fn ) { if (!obj->options->decrypt_p) { //if we are processing a flowed plain text line, we need to remove any stuffed space if (length > 0 && ' ' == *line && mime_typep(kid, (MimeObjectClass *)&mimeInlineTextPlainFlowedClass)) { line ++; length --; } status = obj->options->decompose_file_output_fn (line, length, obj->options->stream_closure); if (status < 0) return status; if (!nl) { status = obj->options->decompose_file_output_fn (MSG_LINEBREAK, MSG_LINEBREAK_LEN, obj->options->stream_closure); if (status < 0) return status; } return status; } } #endif /* MIME_DRAFTS */ if (nl) return kid->clazz->parse_buffer (line, length, kid); else { /* Hack a newline onto the end. */ char *s = (char *)PR_MALLOC(length + MSG_LINEBREAK_LEN + 1); if (!s) return MIME_OUT_OF_MEMORY; memcpy(s, line, length); PL_strncpyz(s + length, MSG_LINEBREAK, MSG_LINEBREAK_LEN); status = kid->clazz->parse_buffer (s, length + MSG_LINEBREAK_LEN, kid); PR_Free(s); return status; } } /* Otherwise we don't yet have a child object, which means we're not done parsing our headers yet. */ if (!msg->hdrs) { msg->hdrs = MimeHeaders_new(); if (!msg->hdrs) return MIME_OUT_OF_MEMORY; } #ifdef MIME_DRAFTS if ( obj->options && obj->options->decompose_file_p && ! obj->options->is_multipart_msg && obj->options->done_parsing_outer_headers && obj->options->decompose_file_output_fn ) { status = obj->options->decompose_file_output_fn( line, length, obj->options->stream_closure ); if (status < 0) return status; } #endif /* MIME_DRAFTS */ status = MimeHeaders_parse_line(line, length, msg->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 = MimeMessage_close_headers(obj); if (status < 0) return status; } return 0; }
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"); /* 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) // && !mime_crypto_object_p(enc->hdrs, PR_TRUE)) // XXX fix later XXX // { char *html; #if 0 // XXX Fix this later XXX // char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html (enc->crypto_closure)); if (!html) return -1; /* MK_OUT_OF_MEMORY? */ status = MimeObject_write(obj, html, nsCRT::strlen(html), PR_FALSE); PR_FREEIF(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 = nsnull; 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"); html = obj->options->generate_post_header_html_fn(NULL, obj->options->html_closure, outer_headers); obj->options->state->post_header_html_run_p = PR_TRUE; if (html) { status = MimeObject_write(obj, html, strlen(html), PR_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, PR_TRUE, PR_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 = PR_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 = PR_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, PR_FALSE); /* initialize */ if (status < 0) return status; status = MimeHeaders_write_raw_headers(body->headers, obj->options, PR_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 (nsresult (*) ...) cast is to turn the `void' argument into `MimeObject'. */ ((nsresult (*) (const char *, PRInt32, void *)) obj->options->decompose_file_output_fn), obj->options->stream_closure); else #endif /* MIME_DRAFTS */ status = MimePartBufferRead(enc->part_buffer, /* The (nsresult (*) ...) cast is to turn the `void' argument into `MimeObject'. */ ((nsresult (*) (const char *, PRInt32, void *)) body->clazz->parse_buffer), body); if (status < 0) return status; /* The child has been fully processed. Close it off. */ status = body->clazz->parse_eof(body, PR_FALSE); if (status < 0) return status; status = body->clazz->parse_end(body, PR_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, PR_FALSE); return 0; }
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=nsnull; 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 (nsresult (*) ...) cast is to turn the `void' argument into `MimeObject'. */ ((nsresult (*) (const char *, PRInt32, void *)) body->options->decompose_file_output_fn), body->options->stream_closure); else #endif /* MIME_DRAFTS */ status = MimePartBufferRead (sig->part_buffer, /* The (nsresult (*) ...) cast is to turn the `void' argument into `MimeObject'. */ ((nsresult (*) (const char *, PRInt32, void *)) 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; }
static int MimeInlineText_convert_and_parse_line(char *line, int32_t length, MimeObject *obj) { int status; char *converted = 0; int32_t converted_len = 0; MimeInlineText *text = (MimeInlineText *) obj; //in case of charset autodetection, charset can be override by meta charset if (text->charsetOverridable) { if (mime_typep(obj, (MimeObjectClass *) &mimeInlineTextHTMLClass)) { MimeInlineTextHTML *textHTML = (MimeInlineTextHTML *) obj; if (textHTML->charset && *textHTML->charset && strcmp(textHTML->charset, text->charset)) { //if meta tag specified charset is different from our detected result, use meta charset. //but we don't want to redo previous lines MIME_get_unicode_decoder(textHTML->charset, getter_AddRefs(text->inputDecoder)); PR_FREEIF(text->charset); text->charset = strdup(textHTML->charset); //update MsgWindow charset if we are instructed to do so if (text->needUpdateMsgWinCharset && *text->charset) SetMailCharacterSetToMsgWindow(obj, text->charset); } } } //initiate decoder if not yet if (text->inputDecoder == nullptr) MIME_get_unicode_decoder(text->charset, getter_AddRefs(text->inputDecoder)); // If no decoder found, use ""UTF-8"", that will map most non-US-ASCII chars as invalid // A pure-ASCII only decoder would be better, but there is none if (text->inputDecoder == nullptr) MIME_get_unicode_decoder("UTF-8", getter_AddRefs(text->inputDecoder)); if (text->utf8Encoder == nullptr) MIME_get_unicode_encoder("UTF-8", getter_AddRefs(text->utf8Encoder)); bool useInputCharsetConverter = obj->options->m_inputCharsetToUnicodeDecoder && !PL_strcasecmp(text->charset, obj->options->charsetForCachedInputDecoder.get()); if (useInputCharsetConverter) status = obj->options->charset_conversion_fn(line, length, text->charset, "UTF-8", &converted, &converted_len, obj->options->stream_closure, obj->options->m_inputCharsetToUnicodeDecoder, obj->options->m_unicodeToUTF8Encoder); else status = obj->options->charset_conversion_fn(line, length, text->charset, "UTF-8", &converted, &converted_len, obj->options->stream_closure, (nsIUnicodeDecoder*)text->inputDecoder, (nsIUnicodeEncoder*)text->utf8Encoder); if (status < 0) { PR_FREEIF(converted); return status; } if (converted) { line = converted; length = converted_len; } /* Now that the line has been converted, call the subclass's parse_line method with the decoded data. */ status = obj->clazz->parse_line(line, length, obj); PR_FREEIF(converted); return status; }
static bool MimeMultipartRelated_output_child_p(MimeObject *obj, MimeObject* child) { MimeMultipartRelated *relobj = (MimeMultipartRelated *) obj; /* rhp - Changed from "if (relobj->head_loaded)" alone to support the start parameter */ if ( (relobj->head_loaded) || (MimeStartParamExists(obj, child) && !MimeThisIsStartPart(obj, child)) ) { /* This is a child part. Just remember the mapping between the URL it represents and the part-URL to get it back. */ char* location = MimeHeaders_get(child->headers, HEADER_CONTENT_LOCATION, false, false); if (!location) { char* tmp = MimeHeaders_get(child->headers, HEADER_CONTENT_ID, false, false); if (tmp) { char* tmp2 = tmp; if (*tmp2 == '<') { int length; tmp2++; length = strlen(tmp2); if (length > 0 && tmp2[length - 1] == '>') { tmp2[length - 1] = '\0'; } } location = PR_smprintf("cid:%s", tmp2); PR_Free(tmp); } } if (location) { char *absolute; char *base_url = MimeHeaders_get(child->headers, HEADER_CONTENT_BASE, false, false); absolute = MakeAbsoluteURL(base_url ? base_url : relobj->base_url, location); PR_FREEIF(base_url); PR_Free(location); if (absolute) { nsCAutoString partnum; nsCAutoString imappartnum; partnum.Adopt(mime_part_address(child)); if (!partnum.IsEmpty()) { if (obj->options->missing_parts) { char * imappart = mime_imap_part_address(child); if (imappart) imappartnum.Adopt(imappart); } /* AppleDouble part need special care: we need to output only the data fork part of it. The problem at this point is that we haven't yet decoded the children of the AppleDouble part therfore we will have to hope the datafork is the second one! */ if (mime_typep(child, (MimeObjectClass *) &mimeMultipartAppleDoubleClass)) partnum.Append(".2"); char* part; if (!imappartnum.IsEmpty()) part = mime_set_url_imap_part(obj->options->url, imappartnum.get(), partnum.get()); else { char *no_part_url = nullptr; if (obj->options->part_to_load && obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) no_part_url = mime_get_base_url(obj->options->url); if (no_part_url) { part = mime_set_url_part(no_part_url, partnum.get(), false); PR_Free(no_part_url); } else part = mime_set_url_part(obj->options->url, partnum.get(), false); } if (part) { char *name = MimeHeaders_get_name(child->headers, child->options); // let's stick the filename in the part so save as will work. if (name) { //char *savePart = part; //part = PR_smprintf("%s&filename=%s", savePart, name); //PR_Free(savePart); PR_Free(name); } char *temp = part; /* If there's a space in the url, escape the url. (This happens primarily on Windows and Unix.) */ if (PL_strchr(part, ' ') || PL_strchr(part, '>') || PL_strchr(part, '%')) temp = escape_for_mrel_subst(part); MimeHashValue * value = new MimeHashValue(child, temp); PL_HashTableAdd(relobj->hash, absolute, value); /* rhp - If this part ALSO has a Content-ID we need to put that into the hash table and this is what this code does */ { char *tloc; char *tmp = MimeHeaders_get(child->headers, HEADER_CONTENT_ID, false, false); if (tmp) { char* tmp2 = tmp; if (*tmp2 == '<') { int length; tmp2++; length = strlen(tmp2); if (length > 0 && tmp2[length - 1] == '>') { tmp2[length - 1] = '\0'; } } tloc = PR_smprintf("cid:%s", tmp2); PR_Free(tmp); if (tloc) { MimeHashValue *value; value = (MimeHashValue*)PL_HashTableLookup(relobj->hash, tloc); if (!value) { value = new MimeHashValue(child, temp); PL_HashTableAdd(relobj->hash, tloc, value); } else PR_smprintf_free(tloc); } } } /* rhp - End of putting more stuff into the hash table */ /* it's possible that temp pointer is the same than the part pointer, therefore be carefull to not freeing twice the same pointer */ if (temp && temp != part) PR_Free(temp); PR_Free(part); } } } } } else { /* Ah-hah! We're the head object. */ char* base_url; relobj->head_loaded = true; relobj->headobj = child; relobj->buffered_hdrs = MimeHeaders_copy(child->headers); base_url = MimeHeaders_get(child->headers, HEADER_CONTENT_BASE, false, false); /* rhp: need this for supporting Content-Location */ if (!base_url) { base_url = MimeHeaders_get(child->headers, HEADER_CONTENT_LOCATION, false, false); } /* rhp: need this for supporting Content-Location */ if (base_url) { /* If the head object has a base_url associated with it, use that instead of any base_url that may have been associated with the multipart/related. */ PR_FREEIF(relobj->base_url); relobj->base_url = base_url; } } if (obj->options && !obj->options->write_html_p #ifdef MIME_DRAFTS && !obj->options->decompose_file_p #endif /* MIME_DRAFTS */ ) { return true; } return false; /* Don't actually parse this child; we'll handle all that at eof time. */ }
static int MimeExternalBody_parse_eof (MimeObject *obj, bool abort_p) { int status = 0; MimeExternalBody *bod = (MimeExternalBody *) obj; if (obj->closed_p) return 0; /* Run parent method first, to flush out any buffered data. */ status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p); if (status < 0) return status; #ifdef XP_MACOSX if (obj->parent && mime_typep(obj->parent, (MimeObjectClass*) &mimeMultipartAppleDoubleClass)) goto done; #endif /* XP_MACOSX */ if (!abort_p && obj->output_p && obj->options && obj->options->write_html_p) { bool all_headers_p = obj->options->headers == MimeHeadersAll; MimeDisplayOptions *newopt = obj->options; /* copy it */ char *ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE); char *at, *lexp, *size, *perm; char *url, *dir, *mode, *name, *site, *svr, *subj; char *h = 0, *lname = 0, *lurl = 0, *body = 0; MimeHeaders *hdrs = 0; if (!ct) return MIME_OUT_OF_MEMORY; at = MimeHeaders_get_parameter(ct, "access-type", NULL, NULL); lexp = MimeHeaders_get_parameter(ct, "expiration", NULL, NULL); size = MimeHeaders_get_parameter(ct, "size", NULL, NULL); perm = MimeHeaders_get_parameter(ct, "permission", NULL, NULL); dir = MimeHeaders_get_parameter(ct, "directory", NULL, NULL); mode = MimeHeaders_get_parameter(ct, "mode", NULL, NULL); name = MimeHeaders_get_parameter(ct, "name", NULL, NULL); site = MimeHeaders_get_parameter(ct, "site", NULL, NULL); svr = MimeHeaders_get_parameter(ct, "server", NULL, NULL); subj = MimeHeaders_get_parameter(ct, "subject", NULL, NULL); url = MimeHeaders_get_parameter(ct, "url", NULL, NULL); PR_FREEIF(ct); /* the *internal* content-type */ ct = MimeHeaders_get(bod->hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE); PRUint32 hlen = ((at ? strlen(at) : 0) + (lexp ? strlen(lexp) : 0) + (size ? strlen(size) : 0) + (perm ? strlen(perm) : 0) + (dir ? strlen(dir) : 0) + (mode ? strlen(mode) : 0) + (name ? strlen(name) : 0) + (site ? strlen(site) : 0) + (svr ? strlen(svr) : 0) + (subj ? strlen(subj) : 0) + (ct ? strlen(ct) : 0) + (url ? strlen(url) : 0) + 100); h = (char *) PR_MALLOC(hlen); if (!h) { status = MIME_OUT_OF_MEMORY; goto FAIL; } /* If there's a URL parameter, remove all whitespace from it. (The URL parameter to one of these headers is stored with lines broken every 40 characters or less; it's assumed that all significant whitespace was URL-hex-encoded, and all the rest of it was inserted just to keep the lines short.) */ if (url) { char *in, *out; for (in = url, out = url; *in; in++) if (!IS_SPACE(*in)) *out++ = *in; *out = 0; } hdrs = MimeHeaders_new(); if (!hdrs) { status = MIME_OUT_OF_MEMORY; goto FAIL; } # define FROB(STR,VAR) \ if (VAR) \ { \ PL_strncpyz(h, STR ": ", hlen); \ PL_strcatn(h, hlen, VAR); \ PL_strcatn(h, hlen, MSG_LINEBREAK); \ status = MimeHeaders_parse_line(h, strlen(h), hdrs); \ if (status < 0) goto FAIL; \ } FROB("Access-Type", at); FROB("URL", url); FROB("Site", site); FROB("Server", svr); FROB("Directory", dir); FROB("Name", name); FROB("Type", ct); FROB("Size", size); FROB("Mode", mode); FROB("Permission", perm); FROB("Expiration", lexp); FROB("Subject", subj); # undef FROB PL_strncpyz(h, MSG_LINEBREAK, hlen); status = MimeHeaders_parse_line(h, strlen(h), hdrs); if (status < 0) goto FAIL; lurl = MimeExternalBody_make_url(ct, at, lexp, size, perm, dir, mode, name, url, site, svr, subj, bod->body); if (lurl) { lname = MimeGetStringByID(MIME_MSG_LINK_TO_DOCUMENT); } else { lname = MimeGetStringByID(MIME_MSG_DOCUMENT_INFO); all_headers_p = PR_TRUE; } all_headers_p = PR_TRUE; /* #### just do this all the time? */ if (bod->body && all_headers_p) { char *s = bod->body; while (IS_SPACE(*s)) s++; if (*s) { char *s2; const char *pre = "<P><PRE>"; const char *suf = "</PRE>"; PRInt32 i; for(i = strlen(s)-1; i >= 0 && IS_SPACE(s[i]); i--) s[i] = 0; s2 = MsgEscapeHTML(s); if (!s2) goto FAIL; body = (char *) PR_MALLOC(strlen(pre) + strlen(s2) + strlen(suf) + 1); if (!body) { NS_Free(s2); goto FAIL; } PL_strcpy(body, pre); PL_strcat(body, s2); PL_strcat(body, suf); } } newopt->fancy_headers_p = PR_TRUE; newopt->headers = (all_headers_p ? MimeHeadersAll : MimeHeadersSome); FAIL: if (hdrs) MimeHeaders_free(hdrs); PR_FREEIF(h); PR_FREEIF(lname); PR_FREEIF(lurl); PR_FREEIF(body); PR_FREEIF(ct); PR_FREEIF(at); PR_FREEIF(lexp); PR_FREEIF(size); PR_FREEIF(perm); PR_FREEIF(dir); PR_FREEIF(mode); PR_FREEIF(name); PR_FREEIF(url); PR_FREEIF(site); PR_FREEIF(svr); PR_FREEIF(subj); } #ifdef XP_MACOSX done: #endif return status; }
static int MimeObject_parse_begin (MimeObject *obj) { NS_ASSERTION (!obj->closed_p, "object shouldn't be already closed"); /* If we haven't set up the state object yet, then this should be the outermost object... */ if (obj->options && !obj->options->state) { NS_ASSERTION(!obj->headers, "headers should be null"); /* should be the outermost object. */ obj->options->state = new MimeParseStateObject; if (!obj->options->state) return MIME_OUT_OF_MEMORY; obj->options->state->root = obj; obj->options->state->separator_suppressed_p = PR_TRUE; /* no first sep */ const char *delParts = PL_strcasestr(obj->options->url, "&del="); const char *detachLocations = PL_strcasestr(obj->options->url, "&detachTo="); if (delParts) { const char *delEnd = PL_strcasestr(delParts + 1, "&"); if (!delEnd) delEnd = delParts + strlen(delParts); ParseString(Substring(delParts + 5, delEnd), ',', obj->options->state->partsToStrip); } if (detachLocations) { detachLocations += 10; // advance past "&detachTo=" ParseString(nsDependentCString(detachLocations), ',', obj->options->state->detachToFiles); } } /* Decide whether this object should be output or not... */ if (!obj->options || !obj->options->output_fn /* if we are decomposing the message in files and processing a multipart object, we must not output it without parsing it first */ || (obj->options->decompose_file_p && obj->options->decompose_file_output_fn && mime_typep(obj, (MimeObjectClass*) &mimeMultipartClass)) ) obj->output_p = PR_FALSE; else if (!obj->options->part_to_load) obj->output_p = PR_TRUE; else { char *id = mime_part_address(obj); if (!id) return MIME_OUT_OF_MEMORY; // We need to check if a part is the subpart of the part to load. // If so and this is a raw or body display output operation, then // we should mark the part for subsequent output. // // First, check for an exact match obj->output_p = !strcmp(id, obj->options->part_to_load); if (!obj->output_p && (obj->options->format_out == nsMimeOutput::nsMimeMessageRaw || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay || obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)) { // Then, check for subpart unsigned int partlen = strlen(obj->options->part_to_load); obj->output_p = (strlen(id) >= partlen + 2) && (id[partlen] == '.') && !strncmp(id, obj->options->part_to_load, partlen); } PR_Free(id); } return 0; }
static int MimeMultipartAlternative_display_cached_part(MimeObject *obj, MimeHeaders *hdrs, MimePartBufferData *buffer, bool do_display) { int status; char *ct = (hdrs ? MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, true, false) : 0); const char *dct = (((MimeMultipartClass *) obj->clazz)->default_part_type); MimeObject *body; /** Don't pass in NULL as the content-type (this means that the * auto-uudecode-hack won't ever be done for subparts of a * multipart, but only for untyped children of message/rfc822. */ const char *uct = (ct && *ct) ? ct : (dct ? dct: TEXT_PLAIN); // We always want to display the cached part inline. body = mime_create(uct, hdrs, obj->options, true); PR_FREEIF(ct); if (!body) return MIME_OUT_OF_MEMORY; body->output_p = do_display; status = ((MimeContainerClass *) obj->clazz)->add_child(obj, body); if (status < 0) { mime_free(body); return status; } /* We need to muck around with the options to prevent output when do_display is false. More about this below. */ /* add_child assigns body->options from obj->options, but that's just a pointer so if we muck with it in the child it'll modify the parent as well, which we definitely don't want. Therefore we need to make a copy. */ body->options = new MimeDisplayOptions; *body->options = *obj->options; /* But we have to be careful about getting into a situation where memory could be double-freed. All of this is a gross abstraction violation which could be avoided if it were possible to tell parse_begin what output_p should be. */ if (body->options->part_to_load) body->options->part_to_load = strdup(body->options->part_to_load); if (body->options->default_charset) body->options->default_charset = strdup(body->options->default_charset); /* parse_begin resets output_p. This is quite annoying. To convince it that we mean business, we set output_fn to null if we don't want output. */ if (! do_display) body->options->output_fn = nullptr; #ifdef MIME_DRAFTS /* if this object is a child of a multipart/related object, the parent is taking care of decomposing the whole part, don't need to do it at this level. However, we still have to call decompose_file_init_fn and decompose_file_close_fn in order to set the correct content-type. But don't call MimePartBufferRead */ bool multipartRelatedChild = mime_typep(obj->parent,(MimeObjectClass*)&mimeMultipartRelatedClass); bool decomposeFile = do_display && obj->options && obj->options->decompose_file_p && obj->options->decompose_file_init_fn && !mime_typep(body, (MimeObjectClass *) &mimeMultipartClass); if (decomposeFile) { status = obj->options->decompose_file_init_fn ( obj->options->stream_closure, hdrs); if (status < 0) return status; } #endif /* MIME_DRAFTS */ /* Now that we've added this new object to our list of children, notify emitters and start its parser going. */ MimeMultipart_notify_emitter(body); status = body->clazz->parse_begin(body); if (status < 0) return status; /* Now that parse_begin is done mucking with output_p, we can put body->options back to what it's supposed to be. Avoids a memory leak. */ delete body->options; body->options = obj->options; #ifdef MIME_DRAFTS if (decomposeFile && !multipartRelatedChild) status = MimePartBufferRead (buffer, obj->options->decompose_file_output_fn, obj->options->stream_closure); else #endif /* MIME_DRAFTS */ status = MimePartBufferRead (buffer, /* The MimeConverterOutputCallback cast is to turn the `void' argument into `MimeObject'. */ ((MimeConverterOutputCallback) body->clazz->parse_buffer), body); if (status < 0) return status; /* 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 (decomposeFile) { status = obj->options->decompose_file_close_fn ( obj->options->stream_closure ); if (status < 0) return status; } #endif /* MIME_DRAFTS */ return 0; }
static int MimeMultipart_parse_child_line (MimeObject *obj, const char *line, PRInt32 length, PRBool first_line_p) { MimeContainer *cont = (MimeContainer *) obj; int status; MimeObject *kid; PR_ASSERT(cont->nchildren > 0); if (cont->nchildren <= 0) return -1; kid = cont->children[cont->nchildren-1]; PR_ASSERT(kid); if (!kid) return -1; #ifdef MIME_DRAFTS if ( obj->options && obj->options->decompose_file_p && obj->options->is_multipart_msg && obj->options->decompose_file_output_fn ) { if (!mime_typep(obj,(MimeObjectClass*)&mimeMultipartAlternativeClass) && !mime_typep(obj,(MimeObjectClass*)&mimeMultipartRelatedClass) && !mime_typep(obj,(MimeObjectClass*)&mimeMultipartSignedClass) && #ifdef MIME_DETAIL_CHECK !mime_typep(kid,(MimeObjectClass*)&mimeMultipartAlternativeClass) && !mime_typep(kid,(MimeObjectClass*)&mimeMultipartRelatedClass) && !mime_typep(kid,(MimeObjectClass*)&mimeMultipartSignedClass) #else /* bug 21869 -- due to the fact that we are not generating the correct mime class object for content-typ multipart/signed part the above check failed. to solve the problem in general and not to cause early temination when parsing message for opening as draft we can simply make sure that the child is not a multipart mime object. this way we could have a proper decomposing message part functions set correctly */ !mime_typep(kid, (MimeObjectClass*) &mimeMultipartClass) #endif && !(mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(kid->content_type, "text/x-vcard")) ) return obj->options->decompose_file_output_fn (line, length, obj->options->stream_closure); } #endif /* MIME_DRAFTS */ /* 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--; if (!first_line_p) { /* Push out a preceeding newline... */ char nl[] = MSG_LINEBREAK; status = kid->clazz->parse_buffer (nl, MSG_LINEBREAK_LEN, kid); if (status < 0) return status; } /* Now push out the line sans trailing newline. */ return kid->clazz->parse_buffer (line, length, kid); }
static int MimeMultipart_close_child(MimeObject *object) { MimeMultipart *mult = (MimeMultipart *) object; MimeContainer *cont = (MimeContainer *) object; if (!mult->hdrs) return 0; MimeHeaders_free(mult->hdrs); mult->hdrs = 0; NS_ASSERTION(cont->nchildren > 0, "badly formed mime message"); if (cont->nchildren > 0) { MimeObject *kid = cont->children[cont->nchildren-1]; // If we have a child and it has not already been closed, process it. // The kid would be already be closed if we encounter a multipart section // that did not have a fully delineated header block. No header block means // no creation of a new child, but the termination case still happens and // we still end up here. Obviously, we don't want to close the child a // second time and the best thing we can do is nothing. if (kid && !kid->closed_p) { int status; status = kid->clazz->parse_eof(kid, PR_FALSE); if (status < 0) return status; status = kid->clazz->parse_end(kid, PR_FALSE); if (status < 0) return status; #ifdef MIME_DRAFTS if ( object->options && object->options->decompose_file_p && object->options->is_multipart_msg && object->options->decompose_file_close_fn ) { if ( !mime_typep(object,(MimeObjectClass*)&mimeMultipartRelatedClass) && !mime_typep(object,(MimeObjectClass*)&mimeMultipartAlternativeClass) && !mime_typep(object,(MimeObjectClass*)&mimeMultipartSignedClass) && #ifdef MIME_DETAIL_CHECK !mime_typep(kid,(MimeObjectClass*)&mimeMultipartRelatedClass) && !mime_typep(kid,(MimeObjectClass*)&mimeMultipartAlternativeClass) && !mime_typep(kid,(MimeObjectClass*)&mimeMultipartSignedClass) #else /* bug 21869 -- due to the fact that we are not generating the correct mime class object for content-typ multipart/signed part the above check failed. to solve the problem in general and not to cause early temination when parsing message for opening as draft we can simply make sure that the child is not a multipart mime object. this way we could have a proper decomposing message part functions set correctly */ !mime_typep(kid,(MimeObjectClass*) &mimeMultipartClass) #endif && !(mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(kid->content_type, "text/x-vcard")) ) { status = object->options->decompose_file_close_fn ( object->options->stream_closure ); if (status < 0) return status; } } #endif /* MIME_DRAFTS */ } } return 0; }
static int MimeMultipart_create_child(MimeObject *obj) { MimeMultipart *mult = (MimeMultipart *) obj; int status; char *ct = (mult->hdrs ? MimeHeaders_get (mult->hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE) : 0); const char *dct = (((MimeMultipartClass *) obj->clazz)->default_part_type); MimeObject *body = NULL; mult->state = MimeMultipartPartFirstLine; /* Don't pass in NULL as the content-type (this means that the auto-uudecode-hack won't ever be done for subparts of a multipart, but only for untyped children of message/rfc822. */ body = mime_create(((ct && *ct) ? ct : (dct ? dct: TEXT_PLAIN)), mult->hdrs, obj->options); 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; } #ifdef MIME_DRAFTS if ( obj->options && obj->options->decompose_file_p && obj->options->is_multipart_msg && obj->options->decompose_file_init_fn ) { if ( !mime_typep(obj,(MimeObjectClass*)&mimeMultipartRelatedClass) && !mime_typep(obj,(MimeObjectClass*)&mimeMultipartAlternativeClass) && !mime_typep(obj,(MimeObjectClass*)&mimeMultipartSignedClass) && #ifdef MIME_DETAIL_CHECK !mime_typep(body, (MimeObjectClass*)&mimeMultipartRelatedClass) && !mime_typep(body, (MimeObjectClass*)&mimeMultipartAlternativeClass) && !mime_typep(body,(MimeObjectClass*)&mimeMultipartSignedClass) #else /* bug 21869 -- due to the fact that we are not generating the correct mime class object for content-typ multipart/signed part the above check failed. to solve the problem in general and not to cause early temination when parsing message for opening as draft we can simply make sure that the child is not a multipart mime object. this way we could have a proper decomposing message part functions set correctly */ !mime_typep(body, (MimeObjectClass*) &mimeMultipartClass) #endif && ! (mime_typep(body, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(body->content_type, "text/x-vcard")) ) { status = obj->options->decompose_file_init_fn ( obj->options->stream_closure, mult->hdrs ); if (status < 0) return status; } } #endif /* MIME_DRAFTS */ /* Now that we've added this new object to our list of children, start its parser going (if we want to display it.) */ body->output_p = (((MimeMultipartClass *) obj->clazz)->output_child_p(obj, body)); if (body->output_p) { status = body->clazz->parse_begin(body); #ifdef XP_MACOSX /* if we are saving an apple double attachment, we need to set correctly the conten type of the channel */ if (mime_typep(obj, (MimeObjectClass *) &mimeMultipartAppleDoubleClass)) { struct mime_stream_data *msd = (struct mime_stream_data *)body->options->stream_closure; if (!body->options->write_html_p && body->content_type && !PL_strcasecmp(body->content_type, APPLICATION_APPLEFILE)) { if (msd && msd->channel) msd->channel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_APPLEFILE)); } } #endif if (status < 0) return status; } return 0; }