1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "msgCore.h"
7 #include "mimemult.h"
8 #include "mimemoz2.h"
9 #include "mimeeobj.h"
10
11 #include "prlog.h"
12 #include "prmem.h"
13 #include "plstr.h"
14 #include "prio.h"
15 #include "nsMimeStringResources.h"
16 #include "nsMimeTypes.h"
17 #include <ctype.h>
18
19 #ifdef XP_MACOSX
20 extern MimeObjectClass mimeMultipartAppleDoubleClass;
21 #endif
22
23 #define MIME_SUPERCLASS mimeContainerClass
24 MimeDefClass(MimeMultipart, MimeMultipartClass, mimeMultipartClass,
25 &MIME_SUPERCLASS);
26
27 static int MimeMultipart_initialize(MimeObject*);
28 static void MimeMultipart_finalize(MimeObject*);
29 static int MimeMultipart_parse_line(const char* line, int32_t length,
30 MimeObject*);
31 static int MimeMultipart_parse_eof(MimeObject* object, bool abort_p);
32
33 static MimeMultipartBoundaryType MimeMultipart_check_boundary(MimeObject*,
34 const char*,
35 int32_t);
36 static int MimeMultipart_create_child(MimeObject*);
37 static bool MimeMultipart_output_child_p(MimeObject*, MimeObject*);
38 static int MimeMultipart_parse_child_line(MimeObject*, const char*, int32_t,
39 bool);
40 static int MimeMultipart_close_child(MimeObject*);
41
42 extern "C" MimeObjectClass mimeMultipartAlternativeClass;
43 extern "C" MimeObjectClass mimeMultipartRelatedClass;
44 extern "C" MimeObjectClass mimeMultipartSignedClass;
45 extern "C" MimeObjectClass mimeInlineTextVCardClass;
46 extern "C" MimeExternalObjectClass mimeExternalObjectClass;
47 extern "C" MimeSuppressedCryptoClass mimeSuppressedCryptoClass;
48
49 #if defined(DEBUG) && defined(XP_UNIX)
50 static int MimeMultipart_debug_print(MimeObject*, PRFileDesc*, int32_t);
51 #endif
52
MimeMultipartClassInitialize(MimeMultipartClass * clazz)53 static int MimeMultipartClassInitialize(MimeMultipartClass* clazz) {
54 MimeObjectClass* oclass = (MimeObjectClass*)clazz;
55 MimeMultipartClass* mclass = (MimeMultipartClass*)clazz;
56
57 PR_ASSERT(!oclass->class_initialized);
58 oclass->initialize = MimeMultipart_initialize;
59 oclass->finalize = MimeMultipart_finalize;
60 oclass->parse_line = MimeMultipart_parse_line;
61 oclass->parse_eof = MimeMultipart_parse_eof;
62
63 mclass->check_boundary = MimeMultipart_check_boundary;
64 mclass->create_child = MimeMultipart_create_child;
65 mclass->output_child_p = MimeMultipart_output_child_p;
66 mclass->parse_child_line = MimeMultipart_parse_child_line;
67 mclass->close_child = MimeMultipart_close_child;
68
69 #if defined(DEBUG) && defined(XP_UNIX)
70 oclass->debug_print = MimeMultipart_debug_print;
71 #endif
72
73 return 0;
74 }
75
MimeMultipart_initialize(MimeObject * object)76 static int MimeMultipart_initialize(MimeObject* object) {
77 MimeMultipart* mult = (MimeMultipart*)object;
78 char* ct;
79
80 /* This is an abstract class; it shouldn't be directly instantiated. */
81 PR_ASSERT(object->clazz != (MimeObjectClass*)&mimeMultipartClass);
82
83 ct = MimeHeaders_get(object->headers, HEADER_CONTENT_TYPE, false, false);
84 mult->boundary =
85 (ct ? MimeHeaders_get_parameter(ct, HEADER_PARM_BOUNDARY, NULL, NULL)
86 : 0);
87 PR_FREEIF(ct);
88 mult->state = MimeMultipartPreamble;
89 return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
90 }
91
MimeMultipart_finalize(MimeObject * object)92 static void MimeMultipart_finalize(MimeObject* object) {
93 MimeMultipart* mult = (MimeMultipart*)object;
94
95 object->clazz->parse_eof(object, false);
96
97 PR_FREEIF(mult->boundary);
98 if (mult->hdrs) MimeHeaders_free(mult->hdrs);
99 mult->hdrs = 0;
100 ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
101 }
102
MimeWriteAString(MimeObject * obj,const nsACString & string)103 int MimeWriteAString(MimeObject* obj, const nsACString& string) {
104 const nsCString& flatString = PromiseFlatCString(string);
105 return MimeObject_write(obj, flatString.get(), flatString.Length(), true);
106 }
107
MimeMultipart_parse_line(const char * line,int32_t length,MimeObject * obj)108 static int MimeMultipart_parse_line(const char* line, int32_t length,
109 MimeObject* obj) {
110 MimeMultipart* mult = (MimeMultipart*)obj;
111 MimeContainer* container = (MimeContainer*)obj;
112 int status = 0;
113 MimeMultipartBoundaryType boundary;
114
115 NS_ASSERTION(line && *line, "empty line in multipart parse_line");
116 if (!line || !*line) return -1;
117
118 NS_ASSERTION(!obj->closed_p, "obj shouldn't already be closed");
119 if (obj->closed_p) return -1;
120
121 /* If we're supposed to write this object, but aren't supposed to convert
122 it to HTML, simply pass it through unaltered. */
123 if (obj->output_p && obj->options && !obj->options->write_html_p &&
124 obj->options->output_fn &&
125 obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
126 return MimeObject_write(obj, line, length, true);
127
128 if (mult->state == MimeMultipartEpilogue) /* already done */
129 boundary = MimeMultipartBoundaryTypeNone;
130 else
131 boundary =
132 ((MimeMultipartClass*)obj->clazz)->check_boundary(obj, line, length);
133
134 if (boundary == MimeMultipartBoundaryTypeTerminator ||
135 boundary == MimeMultipartBoundaryTypeSeparator) {
136 /* Match! Close the currently-open part, move on to the next
137 state, and discard this line.
138 */
139 bool endOfPart = (mult->state != MimeMultipartPreamble);
140 if (endOfPart) status = ((MimeMultipartClass*)obj->clazz)->close_child(obj);
141 if (status < 0) return status;
142
143 if (boundary == MimeMultipartBoundaryTypeTerminator)
144 mult->state = MimeMultipartEpilogue;
145 else {
146 mult->state = MimeMultipartHeaders;
147
148 /* Reset the header parser for this upcoming part. */
149 NS_ASSERTION(!mult->hdrs, "mult->hdrs should be null here");
150 if (mult->hdrs) MimeHeaders_free(mult->hdrs);
151 mult->hdrs = MimeHeaders_new();
152 if (!mult->hdrs) return MIME_OUT_OF_MEMORY;
153 if (obj->options && obj->options->state &&
154 obj->options->state->partsToStrip.Length() > 0) {
155 nsAutoCString newPart(mime_part_address(obj));
156 newPart.Append('.');
157 newPart.AppendInt(container->nchildren + 1);
158 obj->options->state->strippingPart = false;
159 // check if this is a sub-part of a part we're stripping.
160 for (uint32_t partIndex = 0;
161 partIndex < obj->options->state->partsToStrip.Length();
162 partIndex++) {
163 nsCString& curPartToStrip =
164 obj->options->state->partsToStrip[partIndex];
165 if (newPart.Find(curPartToStrip) == 0 &&
166 (newPart.Length() == curPartToStrip.Length() ||
167 newPart.CharAt(curPartToStrip.Length()) == '.')) {
168 obj->options->state->strippingPart = true;
169 if (partIndex < obj->options->state->detachToFiles.Length())
170 obj->options->state->detachedFilePath =
171 obj->options->state->detachToFiles[partIndex];
172 break;
173 }
174 }
175 }
176 }
177
178 // if stripping out attachments, write the boundary line. Otherwise, return
179 // to ignore it.
180 if (obj->options &&
181 obj->options->format_out == nsMimeOutput::nsMimeMessageAttach) {
182 // Because MimeMultipart_parse_child_line strips out the
183 // the CRLF of the last line before the end of a part, we need to add that
184 // back in here.
185 if (endOfPart) MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
186
187 status = MimeObject_write(obj, line, length, true);
188 }
189 return 0;
190 }
191
192 /* Otherwise, this isn't a boundary string. So do whatever it is we
193 should do with this line (parse it as a header, feed it to the
194 child part, ignore it, etc.) */
195
196 switch (mult->state) {
197 case MimeMultipartPreamble:
198 case MimeMultipartEpilogue:
199 /* Ignore this line. */
200 break;
201
202 case MimeMultipartHeaders:
203 /* Parse this line as a header for the sub-part. */
204 {
205 status = MimeHeaders_parse_line(line, length, mult->hdrs);
206 bool stripping = false;
207
208 if (status < 0) return status;
209
210 // If this line is blank, we're now done parsing headers, and should
211 // now examine the content-type to create this "body" part.
212 //
213 if (*line == '\r' || *line == '\n') {
214 if (obj->options && obj->options->state &&
215 obj->options->state->strippingPart) {
216 stripping = true;
217 bool detachingPart =
218 obj->options->state->detachedFilePath.Length() > 0;
219
220 nsAutoCString fileName;
221 fileName.Adopt(MimeHeaders_get_name(mult->hdrs, obj->options));
222 // clang-format off
223 if (detachingPart) {
224 char *contentType =
225 MimeHeaders_get(mult->hdrs, "Content-Type", false, false);
226 if (contentType) {
227 MimeWriteAString(obj, "Content-Type: "_ns);
228 MimeWriteAString(obj, nsDependentCString(contentType));
229 PR_Free(contentType);
230 }
231 MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
232 MimeWriteAString(obj, "Content-Disposition: attachment; filename=\""_ns);
233 MimeWriteAString(obj, fileName);
234 MimeWriteAString(obj, "\""_ns MSG_LINEBREAK);
235 MimeWriteAString(obj, "X-Mozilla-External-Attachment-URL: "_ns);
236 MimeWriteAString(obj, obj->options->state->detachedFilePath);
237 MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
238 MimeWriteAString(obj, "X-Mozilla-Altered: AttachmentDetached; date=\""_ns);
239 } else {
240 nsAutoCString header("Content-Type: text/x-moz-deleted; name=\"Deleted: ");
241 header.Append(fileName);
242 status = MimeWriteAString(obj, header);
243 if (status < 0) return status;
244 status = MimeWriteAString(obj, "\""_ns MSG_LINEBREAK
245 "Content-Transfer-Encoding: 8bit"_ns MSG_LINEBREAK);
246 MimeWriteAString(obj, "Content-Disposition: inline; filename=\"Deleted: "_ns);
247 MimeWriteAString(obj, fileName);
248 MimeWriteAString(obj, "\""_ns MSG_LINEBREAK
249 "X-Mozilla-Altered: AttachmentDeleted; date=\""_ns);
250 }
251 nsCString result;
252 char timeBuffer[128];
253 PRExplodedTime now;
254 PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
255 PR_FormatTimeUSEnglish(timeBuffer, sizeof(timeBuffer),
256 "%a %b %d %H:%M:%S %Y", &now);
257 MimeWriteAString(obj, nsDependentCString(timeBuffer));
258 MimeWriteAString(obj, "\""_ns MSG_LINEBREAK);
259 MimeWriteAString(obj, MSG_LINEBREAK
260 "You deleted an attachment from this message. The original "_ns
261 "MIME headers for the attachment were:"_ns MSG_LINEBREAK);
262 MimeHeaders_write_raw_headers(mult->hdrs, obj->options, false);
263 // clang-format on
264 }
265 int32_t old_nchildren = container->nchildren;
266 status = ((MimeMultipartClass*)obj->clazz)->create_child(obj);
267 if (status < 0) return status;
268 NS_ASSERTION(mult->state != MimeMultipartHeaders,
269 "mult->state shouldn't be MimeMultipartHeaders");
270
271 if (!stripping && container->nchildren > old_nchildren &&
272 obj->options &&
273 !mime_typep(obj,
274 (MimeObjectClass*)&mimeMultipartAlternativeClass)) {
275 // Notify emitter about content type and part path.
276 MimeObject* kid = container->children[container->nchildren - 1];
277 MimeMultipart_notify_emitter(kid);
278 }
279 }
280 break;
281 }
282
283 case MimeMultipartPartFirstLine:
284 /* Hand this line off to the sub-part. */
285 status = (((MimeMultipartClass*)obj->clazz)
286 ->parse_child_line(obj, line, length, true));
287 if (status < 0) return status;
288 mult->state = MimeMultipartPartLine;
289 break;
290
291 case MimeMultipartPartLine:
292 /* Hand this line off to the sub-part. */
293 status = (((MimeMultipartClass*)obj->clazz)
294 ->parse_child_line(obj, line, length, false));
295 if (status < 0) return status;
296 break;
297
298 default:
299 NS_ERROR("unexpected state in parse line");
300 return -1;
301 }
302
303 if (obj->options &&
304 obj->options->format_out == nsMimeOutput::nsMimeMessageAttach &&
305 (!(obj->options->state && obj->options->state->strippingPart) &&
306 mult->state != MimeMultipartPartLine))
307 return MimeObject_write(obj, line, length, false);
308 return 0;
309 }
310
MimeMultipart_notify_emitter(MimeObject * obj)311 void MimeMultipart_notify_emitter(MimeObject* obj) {
312 char* ct = nullptr;
313
314 NS_ASSERTION(obj->options,
315 "MimeMultipart_notify_emitter called with null options");
316 if (!obj->options) return;
317
318 ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, false, false);
319 if (obj->options->notify_nested_bodies) {
320 mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
321 ct ? ct : TEXT_PLAIN);
322 char* part_path = mime_part_address(obj);
323 if (part_path) {
324 mimeEmitterAddHeaderField(obj->options, "x-jsemitter-part-path",
325 part_path);
326 PR_Free(part_path);
327 }
328 }
329
330 // Examine the headers and see if there is a special charset
331 // (i.e. non US-ASCII) for this message. If so, we need to
332 // tell the emitter that this is the case for use in any
333 // possible reply or forward operation.
334 if (ct &&
335 (obj->options->notify_nested_bodies || MimeObjectIsMessageBody(obj))) {
336 char* cset = MimeHeaders_get_parameter(ct, "charset", NULL, NULL);
337 if (cset) {
338 mimeEmitterUpdateCharacterSet(obj->options, cset);
339 if (!obj->options->override_charset)
340 // Also set this charset to msgWindow
341 SetMailCharacterSetToMsgWindow(obj, cset);
342 PR_Free(cset);
343 }
344 }
345
346 PR_FREEIF(ct);
347 }
348
MimeMultipart_check_boundary(MimeObject * obj,const char * line,int32_t length)349 static MimeMultipartBoundaryType MimeMultipart_check_boundary(MimeObject* obj,
350 const char* line,
351 int32_t length) {
352 MimeMultipart* mult = (MimeMultipart*)obj;
353 int32_t blen;
354 bool term_p;
355
356 if (!mult->boundary || line[0] != '-' || line[1] != '-')
357 return MimeMultipartBoundaryTypeNone;
358
359 /* This is a candidate line to be a boundary. Check it out... */
360 blen = strlen(mult->boundary);
361 term_p = false;
362
363 /* strip trailing whitespace (including the newline.) */
364 while (length > 2 && IS_SPACE(line[length - 1])) length--;
365
366 /* Could this be a terminating boundary? */
367 if (length == blen + 4 && line[length - 1] == '-' &&
368 line[length - 2] == '-') {
369 term_p = true;
370 }
371
372 // looks like we have a separator but first, we need to check it's not for one
373 // of the part's children.
374 MimeContainer* cont = (MimeContainer*)obj;
375 if (cont->nchildren > 0) {
376 MimeObject* kid = cont->children[cont->nchildren - 1];
377 if (kid)
378 if (mime_typep(kid, (MimeObjectClass*)&mimeMultipartClass)) {
379 // Don't ask the kid to check the boundary if it has already detected a
380 // Teminator
381 MimeMultipart* mult = (MimeMultipart*)kid;
382 if (mult->state != MimeMultipartEpilogue)
383 if (MimeMultipart_check_boundary(kid, line, length) !=
384 MimeMultipartBoundaryTypeNone)
385 return MimeMultipartBoundaryTypeNone;
386 }
387 }
388
389 if (term_p) length -= 2;
390
391 if (blen == length - 2 && !strncmp(line + 2, mult->boundary, length - 2))
392 return (term_p ? MimeMultipartBoundaryTypeTerminator
393 : MimeMultipartBoundaryTypeSeparator);
394 else
395 return MimeMultipartBoundaryTypeNone;
396 }
397
MimeMultipart_create_child(MimeObject * obj)398 static int MimeMultipart_create_child(MimeObject* obj) {
399 MimeMultipart* mult = (MimeMultipart*)obj;
400 int status;
401 char* ct = (mult->hdrs ? MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE,
402 true, false)
403 : 0);
404 const char* dct = (((MimeMultipartClass*)obj->clazz)->default_part_type);
405 MimeObject* body = NULL;
406
407 mult->state = MimeMultipartPartFirstLine;
408 if (obj->options) obj->options->is_child = true;
409
410 /* Don't pass in NULL as the content-type (this means that the
411 auto-uudecode-hack won't ever be done for subparts of a
412 multipart, but only for untyped children of message/rfc822.
413 */
414 body = mime_create(((ct && *ct) ? ct : (dct ? dct : TEXT_PLAIN)), mult->hdrs,
415 obj->options);
416 PR_FREEIF(ct);
417 if (!body) return MIME_OUT_OF_MEMORY;
418 status = ((MimeContainerClass*)obj->clazz)->add_child(obj, body);
419 if (status < 0) {
420 mime_free(body);
421 return status;
422 }
423
424 #ifdef MIME_DRAFTS
425 if (obj->options && obj->options->decompose_file_p &&
426 obj->options->is_multipart_msg && obj->options->decompose_file_init_fn) {
427 if (!mime_typep(obj, (MimeObjectClass*)&mimeMultipartRelatedClass) &&
428 !mime_typep(obj, (MimeObjectClass*)&mimeMultipartAlternativeClass) &&
429 !mime_typep(obj, (MimeObjectClass*)&mimeMultipartSignedClass) &&
430 /* bug 21869 -- due to the fact that we are not generating the
431 correct mime class object for content-typ multipart/signed part
432 the above check failed. to solve the problem in general and not
433 to cause early termination when parsing message for opening as
434 draft we can simply make sure that the child is not a multipart
435 mime object. this way we could have a proper decomposing message
436 part functions set correctly */
437 !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
438 !((mime_typep(body, (MimeObjectClass*)&mimeExternalObjectClass) ||
439 mime_typep(body, (MimeObjectClass*)&mimeSuppressedCryptoClass)) &&
440 (!strcmp(body->content_type, "text/vcard") ||
441 !strcmp(body->content_type, "text/x-vcard")))) {
442 status = obj->options->decompose_file_init_fn(
443 obj->options->stream_closure, mult->hdrs);
444 if (status < 0) return status;
445 }
446 }
447 #endif /* MIME_DRAFTS */
448
449 /* Now that we've added this new object to our list of children,
450 start its parser going (if we want to display it.)
451 */
452 body->output_p =
453 (((MimeMultipartClass*)obj->clazz)->output_child_p(obj, body));
454 if (body->output_p) {
455 status = body->clazz->parse_begin(body);
456
457 #ifdef XP_MACOSX
458 /* if we are saving an apple double attachment, we need to set correctly the
459 * content type of the channel */
460 if (mime_typep(obj, (MimeObjectClass*)&mimeMultipartAppleDoubleClass)) {
461 mime_stream_data* msd = (mime_stream_data*)body->options->stream_closure;
462 if (!body->options->write_html_p && body->content_type &&
463 !PL_strcasecmp(body->content_type, APPLICATION_APPLEFILE)) {
464 if (msd && msd->channel)
465 msd->channel->SetContentType(nsLiteralCString(APPLICATION_APPLEFILE));
466 }
467 }
468 #endif
469
470 if (status < 0) return status;
471 }
472
473 return 0;
474 }
475
MimeMultipart_output_child_p(MimeObject * obj,MimeObject * child)476 static bool MimeMultipart_output_child_p(MimeObject* obj, MimeObject* child) {
477 /* We don't output a child if we're stripping it. */
478 if (obj->options && obj->options->state && obj->options->state->strippingPart)
479 return false;
480 /* if we are saving an apple double attachment, ignore the appledouble wrapper
481 * part */
482 return (obj->options && obj->options->write_html_p) ||
483 PL_strcasecmp(child->content_type, MULTIPART_APPLEDOUBLE);
484 }
485
MimeMultipart_close_child(MimeObject * object)486 static int MimeMultipart_close_child(MimeObject* object) {
487 MimeMultipart* mult = (MimeMultipart*)object;
488 MimeContainer* cont = (MimeContainer*)object;
489
490 if (!mult->hdrs) return 0;
491
492 MimeHeaders_free(mult->hdrs);
493 mult->hdrs = 0;
494
495 NS_ASSERTION(cont->nchildren > 0, "badly formed mime message");
496 if (cont->nchildren > 0) {
497 MimeObject* kid = cont->children[cont->nchildren - 1];
498 // If we have a child and it has not already been closed, process it.
499 // The kid would be already be closed if we encounter a multipart section
500 // that did not have a fully delineated header block. No header block means
501 // no creation of a new child, but the termination case still happens and
502 // we still end up here. Obviously, we don't want to close the child a
503 // second time and the best thing we can do is nothing.
504 if (kid && !kid->closed_p) {
505 int status;
506 status = kid->clazz->parse_eof(kid, false);
507 if (status < 0) return status;
508 status = kid->clazz->parse_end(kid, false);
509 if (status < 0) return status;
510
511 #ifdef MIME_DRAFTS
512 if (object->options && object->options->decompose_file_p &&
513 object->options->is_multipart_msg &&
514 object->options->decompose_file_close_fn) {
515 // clang-format off
516 if (!mime_typep(object, (MimeObjectClass *)&mimeMultipartRelatedClass) &&
517 !mime_typep(object, (MimeObjectClass *)&mimeMultipartAlternativeClass) &&
518 !mime_typep(object, (MimeObjectClass *)&mimeMultipartSignedClass) &&
519 /* bug 21869 -- due to the fact that we are not generating the
520 correct mime class object for content-typ multipart/signed part
521 the above check failed. to solve the problem in general and not
522 to cause early termination when parsing message for opening as
523 draft we can simply make sure that the child is not a multipart
524 mime object. this way we could have a proper decomposing message
525 part functions set correctly */
526 !mime_typep(kid, (MimeObjectClass *)&mimeMultipartClass) &&
527 !((mime_typep(kid, (MimeObjectClass *)&mimeExternalObjectClass) ||
528 mime_typep(kid, (MimeObjectClass *)&mimeSuppressedCryptoClass)) &&
529 (!strcmp(kid->content_type, "text/vcard") ||
530 !strcmp(kid->content_type, "text/x-vcard")))) {
531 status = object->options->decompose_file_close_fn(
532 object->options->stream_closure);
533 if (status < 0) return status;
534 }
535 // clang-format on
536 }
537 #endif /* MIME_DRAFTS */
538 }
539 }
540 return 0;
541 }
542
MimeMultipart_parse_child_line(MimeObject * obj,const char * line,int32_t length,bool first_line_p)543 static int MimeMultipart_parse_child_line(MimeObject* obj, const char* line,
544 int32_t length, bool first_line_p) {
545 MimeContainer* cont = (MimeContainer*)obj;
546 int status;
547 MimeObject* kid;
548
549 PR_ASSERT(cont->nchildren > 0);
550 if (cont->nchildren <= 0) return -1;
551
552 kid = cont->children[cont->nchildren - 1];
553 PR_ASSERT(kid);
554 if (!kid) return -1;
555
556 #ifdef MIME_DRAFTS
557 if (obj->options && obj->options->decompose_file_p &&
558 obj->options->is_multipart_msg &&
559 obj->options->decompose_file_output_fn) {
560 if (!mime_typep(obj, (MimeObjectClass*)&mimeMultipartAlternativeClass) &&
561 !mime_typep(obj, (MimeObjectClass*)&mimeMultipartRelatedClass) &&
562 !mime_typep(obj, (MimeObjectClass*)&mimeMultipartSignedClass) &&
563 /* bug 21869 -- due to the fact that we are not generating the
564 correct mime class object for content-typ multipart/signed part
565 the above check failed. to solve the problem in general and not
566 to cause early termination when parsing message for opening as
567 draft we can simply make sure that the child is not a multipart
568 mime object. this way we could have a proper decomposing message
569 part functions set correctly */
570 !mime_typep(kid, (MimeObjectClass*)&mimeMultipartClass) &&
571 !((mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) ||
572 mime_typep(kid, (MimeObjectClass*)&mimeSuppressedCryptoClass)) &&
573 (!strcmp(kid->content_type, "text/vcard") ||
574 !strcmp(kid->content_type, "text/x-vcard"))))
575 return obj->options->decompose_file_output_fn(
576 line, length, obj->options->stream_closure);
577 }
578 #endif /* MIME_DRAFTS */
579
580 /* The newline issues here are tricky, since both the newlines before
581 and after the boundary string are to be considered part of the
582 boundary: this is so that a part can be specified such that it
583 does not end in a trailing newline.
584
585 To implement this, we send a newline *before* each line instead
586 of after, except for the first line, which is not preceded by a
587 newline.
588 */
589
590 /* Remove the trailing newline... */
591 if (length > 0 && line[length - 1] == '\n') length--;
592 if (length > 0 && line[length - 1] == '\r') length--;
593
594 if (!first_line_p) {
595 /* Push out a preceding newline... */
596 char nl[] = MSG_LINEBREAK;
597 status = kid->clazz->parse_buffer(nl, MSG_LINEBREAK_LEN, kid);
598 if (status < 0) return status;
599 }
600
601 /* Now push out the line sans trailing newline. */
602 return kid->clazz->parse_buffer(line, length, kid);
603 }
604
MimeMultipart_parse_eof(MimeObject * obj,bool abort_p)605 static int MimeMultipart_parse_eof(MimeObject* obj, bool abort_p) {
606 MimeMultipart* mult = (MimeMultipart*)obj;
607 MimeContainer* cont = (MimeContainer*)obj;
608
609 if (obj->closed_p) return 0;
610
611 /* Push out the last trailing line if there's one in the buffer. If
612 this happens, this object does not end in a trailing newline (and
613 the parse_line method will be called with a string with no trailing
614 newline, which isn't the usual case.)
615 */
616 if (!abort_p && obj->ibuffer_fp > 0) {
617 /* There is leftover data without a terminating newline. */
618 int status = obj->clazz->parse_line(obj->ibuffer, obj->ibuffer_fp, obj);
619 obj->ibuffer_fp = 0;
620 if (status < 0) {
621 obj->closed_p = true;
622 return status;
623 }
624 }
625
626 /* Now call parse_eof for our active child, if there is one.
627 */
628 if (cont->nchildren > 0 && (mult->state == MimeMultipartPartLine ||
629 mult->state == MimeMultipartPartFirstLine)) {
630 MimeObject* kid = cont->children[cont->nchildren - 1];
631 NS_ASSERTION(kid, "not expecting null kid");
632 if (kid) {
633 int status = kid->clazz->parse_eof(kid, abort_p);
634 if (status < 0) return status;
635 }
636 }
637
638 return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
639 }
640
641 #if defined(DEBUG) && defined(XP_UNIX)
MimeMultipart_debug_print(MimeObject * obj,PRFileDesc * stream,int32_t depth)642 static int MimeMultipart_debug_print(MimeObject* obj, PRFileDesc* stream,
643 int32_t depth) {
644 /* MimeMultipart *mult = (MimeMultipart *) obj; */
645 MimeContainer* cont = (MimeContainer*)obj;
646 char* addr = mime_part_address(obj);
647 int i;
648 for (i = 0; i < depth; i++) PR_Write(stream, " ", 2);
649 /**
650 fprintf(stream, "<%s %s (%d kid%s) boundary=%s 0x%08X>\n",
651 obj->clazz->class_name,
652 addr ? addr : "???",
653 cont->nchildren, (cont->nchildren == 1 ? "" : "s"),
654 (mult->boundary ? mult->boundary : "(none)"),
655 (uint32_t) mult);
656 **/
657 PR_FREEIF(addr);
658
659 /*
660 if (cont->nchildren > 0)
661 fprintf(stream, "\n");
662 */
663
664 for (i = 0; i < cont->nchildren; i++) {
665 MimeObject* kid = cont->children[i];
666 int status = kid->clazz->debug_print(kid, stream, depth + 1);
667 if (status < 0) return status;
668 }
669
670 /*
671 if (cont->nchildren > 0)
672 fprintf(stream, "\n");
673 */
674
675 return 0;
676 }
677 #endif
678