Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
*
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
* This Original Code has been modified by IBM Corporation. Modifications made
7
* by IBM described herein are Copyright (c) International Business Machines
8
* Corporation, 2000. Modifications to Mozilla code or documentation identified
9
* per MPL Section 3.3
10
*
11
* Date Modified by Description of modification
12
* 04/20/2000 IBM Corp. OS/2 VisualAge build.
13
*/
14
15
// clang-format off
16
#include "nsCOMPtr.h"
17
#include "mimeobj.h" /* MimeObject (abstract) */
18
#include "mimecont.h" /* |--- MimeContainer (abstract) */
19
#include "mimemult.h" /* | |--- MimeMultipart (abstract) */
20
#include "mimemmix.h" /* | | |--- MimeMultipartMixed */
21
#include "mimemdig.h" /* | | |--- MimeMultipartDigest */
22
#include "mimempar.h" /* | | |--- MimeMultipartParallel */
23
#include "mimemalt.h" /* | | |--- MimeMultipartAlternative */
24
#include "mimemrel.h" /* | | |--- MimeMultipartRelated */
25
#include "mimemapl.h" /* | | |--- MimeMultipartAppleDouble */
26
#include "mimesun.h" /* | | |--- MimeSunAttachment */
27
#include "mimemsig.h" /* | | |--- MimeMultipartSigned (abstract)*/
28
#ifdef ENABLE_SMIME
29
#include "mimemcms.h" /* | | |---MimeMultipartSignedCMS */
30
#endif
31
#include "mimecryp.h" /* | |--- MimeEncrypted (abstract) */
32
#ifdef ENABLE_SMIME
33
#include "mimecms.h" /* | | |--- MimeEncryptedPKCS7 */
34
#endif
35
#include "mimemsg.h" /* | |--- MimeMessage */
36
#include "mimeunty.h" /* | |--- MimeUntypedText */
37
#include "mimeleaf.h" /* |--- MimeLeaf (abstract) */
38
#include "mimetext.h" /* | |--- MimeInlineText (abstract) */
39
#include "mimetpla.h" /* | | |--- MimeInlineTextPlain */
40
#include "mimethpl.h" /* | | | |--- M.I.TextHTMLAsPlaintext */
41
#include "mimetpfl.h" /* | | |--- MimeInlineTextPlainFlowed */
42
#include "mimethtm.h" /* | | |--- MimeInlineTextHTML */
43
#include "mimethsa.h" /* | | | |--- M.I.TextHTMLSanitized */
44
#include "mimeTextHTMLParsed.h" /*| | |--- M.I.TextHTMLParsed */
45
#include "mimetric.h" /* | | |--- MimeInlineTextRichtext */
46
#include "mimetenr.h" /* | | | |--- MimeInlineTextEnriched */
47
/* SUPPORTED VIA PLUGIN | | |--- MimeInlineTextVCard */
48
#include "mimeiimg.h" /* | |--- MimeInlineImage */
49
#include "mimeeobj.h" /* | |--- MimeExternalObject */
50
#include "mimeebod.h" /* |--- MimeExternalBody */
51
/* If you add classes here,also add them to mimei.h */
52
// clang-format on
53
54
#include "prlog.h"
55
#include "prmem.h"
56
#include "prenv.h"
57
#include "plstr.h"
58
#include "prlink.h"
59
#include "prprf.h"
60
#include "mimecth.h"
61
#include "mimebuf.h"
62
#include "mimemoz2.h"
63
#include "nsIMimeContentTypeHandler.h"
64
#include "nsICategoryManager.h"
65
#include "nsCategoryManagerUtils.h"
66
#include "nsXPCOMCID.h"
67
#include "nsISimpleMimeConverter.h"
68
#include "nsSimpleMimeConverterStub.h"
69
#include "nsTArray.h"
70
#include "nsMimeStringResources.h"
71
#include "nsMimeTypes.h"
72
#include "nsMsgUtils.h"
73
#include "nsIPrefBranch.h"
74
#include "mozilla/Preferences.h"
75
#include "imgLoader.h"
76
77
#include "nsIMsgMailNewsUrl.h"
78
#include "nsIMsgHdr.h"
79
80
using namespace mozilla;
81
82
// forward declaration
83
void getMsgHdrForCurrentURL(MimeDisplayOptions *opts, nsIMsgDBHdr **aMsgHdr);
84
85
#define IMAP_EXTERNAL_CONTENT_HEADER "X-Mozilla-IMAP-Part"
86
#define EXTERNAL_ATTACHMENT_URL_HEADER "X-Mozilla-External-Attachment-URL"
87
88
/* ==========================================================================
89
Allocation and destruction
90
==========================================================================
91
*/
92
static int mime_classinit(MimeObjectClass *clazz);
93
94
/*
95
* These are the necessary defines/variables for doing
96
* content type handlers in external plugins.
97
*/
98
typedef struct {
99
char content_type[128];
100
bool force_inline_display;
101
} cthandler_struct;
102
103
nsTArray<cthandler_struct *> *ctHandlerList = nullptr;
104
105
/*
106
* This will return TRUE if the content_type is found in the
107
* list, FALSE if it is not found.
108
*/
109
bool find_content_type_attribs(const char *content_type,
110
bool *force_inline_display) {
111
*force_inline_display = false;
112
if (!ctHandlerList) return false;
113
114
for (size_t i = 0; i < ctHandlerList->Length(); i++) {
115
cthandler_struct *ptr = ctHandlerList->ElementAt(i);
116
if (PL_strcasecmp(content_type, ptr->content_type) == 0) {
117
*force_inline_display = ptr->force_inline_display;
118
return true;
119
}
120
}
121
122
return false;
123
}
124
125
void add_content_type_attribs(const char *content_type,
126
contentTypeHandlerInitStruct *ctHandlerInfo) {
127
cthandler_struct *ptr = nullptr;
128
bool force_inline_display;
129
130
if (find_content_type_attribs(content_type, &force_inline_display)) return;
131
132
if (!content_type || !ctHandlerInfo) return;
133
134
if (!ctHandlerList) ctHandlerList = new nsTArray<cthandler_struct *>();
135
136
if (!ctHandlerList) return;
137
138
ptr = (cthandler_struct *)PR_MALLOC(sizeof(cthandler_struct));
139
if (!ptr) return;
140
141
PL_strncpy(ptr->content_type, content_type, sizeof(ptr->content_type));
142
ptr->force_inline_display = ctHandlerInfo->force_inline_display;
143
ctHandlerList->AppendElement(ptr);
144
}
145
146
/*
147
* This routine will find all content type handler for a specific content
148
* type (if it exists)
149
*/
150
bool force_inline_display(const char *content_type) {
151
bool force_inline_disp;
152
153
find_content_type_attribs(content_type, &force_inline_disp);
154
return force_inline_disp;
155
}
156
157
/*
158
* This routine will find all content type handler for a specific content
159
* type (if it exists) and is defined to the nsRegistry
160
*/
161
MimeObjectClass *mime_locate_external_content_handler(
162
const char *content_type, contentTypeHandlerInitStruct *ctHandlerInfo) {
163
if (!content_type || !*(content_type)) // null or empty content type
164
return nullptr;
165
166
MimeObjectClass *newObj = nullptr;
167
nsresult rv;
168
169
nsAutoCString lookupID("@mozilla.org/mimecth;1?type=");
170
nsAutoCString contentType;
171
ToLowerCase(nsDependentCString(content_type), contentType);
172
lookupID += contentType;
173
174
nsCOMPtr<nsIMimeContentTypeHandler> ctHandler =
175
do_CreateInstance(lookupID.get(), &rv);
176
if (NS_FAILED(rv) || !ctHandler) {
177
nsCOMPtr<nsICategoryManager> catman =
178
do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
179
if (NS_FAILED(rv)) return nullptr;
180
181
nsCString value;
182
rv = catman->GetCategoryEntry(NS_SIMPLEMIMECONVERTERS_CATEGORY, contentType,
183
value);
184
if (NS_FAILED(rv) || value.IsEmpty()) return nullptr;
185
rv = MIME_NewSimpleMimeConverterStub(contentType.get(),
186
getter_AddRefs(ctHandler));
187
if (NS_FAILED(rv) || !ctHandler) return nullptr;
188
}
189
190
rv = ctHandler->CreateContentTypeHandlerClass(contentType.get(),
191
ctHandlerInfo, &newObj);
192
if (NS_FAILED(rv)) return nullptr;
193
194
add_content_type_attribs(contentType.get(), ctHandlerInfo);
195
return newObj;
196
}
197
198
/* This is necessary to expose the MimeObject method outside of this DLL */
199
int MIME_MimeObject_write(MimeObject *obj, const char *output, int32_t length,
200
bool user_visible_p) {
201
return MimeObject_write(obj, output, length, user_visible_p);
202
}
203
204
MimeObject *mime_new(MimeObjectClass *clazz, MimeHeaders *hdrs,
205
const char *override_content_type) {
206
int size = clazz->instance_size;
207
MimeObject *object;
208
int status;
209
210
/* Some assertions to verify that this isn't random junk memory... */
211
NS_ASSERTION(clazz->class_name && strlen(clazz->class_name) > 0,
212
"1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
213
NS_ASSERTION(size > 0 && size < 1000,
214
"1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
215
216
if (!clazz->class_initialized) {
217
status = mime_classinit(clazz);
218
if (status < 0) return 0;
219
}
220
221
NS_ASSERTION(clazz->initialize && clazz->finalize,
222
"1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
223
224
if (hdrs) {
225
hdrs = MimeHeaders_copy(hdrs);
226
if (!hdrs) return 0;
227
}
228
229
object = (MimeObject *)PR_MALLOC(size);
230
if (!object) return 0;
231
232
memset(object, 0, size);
233
object->clazz = clazz;
234
object->headers = hdrs;
235
object->dontShowAsAttachment = false;
236
237
if (override_content_type && *override_content_type)
238
object->content_type = strdup(override_content_type);
239
240
status = clazz->initialize(object);
241
if (status < 0) {
242
clazz->finalize(object);
243
PR_Free(object);
244
return 0;
245
}
246
247
return object;
248
}
249
250
void mime_free(MimeObject *object) {
251
#ifdef DEBUG__
252
int i, size = object->clazz->instance_size;
253
uint32_t *array = (uint32_t *)object;
254
#endif /* DEBUG */
255
256
object->clazz->finalize(object);
257
258
#ifdef DEBUG__
259
for (i = 0; i < (size / sizeof(*array)); i++) array[i] = (uint32_t)0xDEADBEEF;
260
#endif /* DEBUG */
261
262
PR_Free(object);
263
}
264
265
bool mime_is_allowed_class(const MimeObjectClass *clazz,
266
int32_t types_of_classes_to_disallow) {
267
if (types_of_classes_to_disallow == 0) return true;
268
bool avoid_html = (types_of_classes_to_disallow >= 1);
269
bool avoid_images = (types_of_classes_to_disallow >= 2);
270
bool avoid_strange_content = (types_of_classes_to_disallow >= 3);
271
bool allow_only_vanilla_classes = (types_of_classes_to_disallow == 100);
272
273
if (allow_only_vanilla_classes)
274
/* A "safe" class is one that is unlikely to have security bugs or to
275
allow security exploits or one that is essential for the usefulness
276
of the application, even for paranoid users.
277
What's included here is more personal judgement than following
278
strict rules, though, unfortunately.
279
The function returns true only for known good classes, i.e. is a
280
"whitelist" in this case.
281
This idea comes from Georgi Guninski.
282
*/
283
return (clazz == (MimeObjectClass *)&mimeInlineTextPlainClass ||
284
clazz == (MimeObjectClass *)&mimeInlineTextPlainFlowedClass ||
285
clazz == (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass ||
286
clazz == (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass ||
287
/* The latter 2 classes bear some risk, because they use the Gecko
288
HTML parser, but the user has the option to make an explicit
289
choice in this case, via html_as. */
290
clazz == (MimeObjectClass *)&mimeMultipartMixedClass ||
291
clazz == (MimeObjectClass *)&mimeMultipartAlternativeClass ||
292
clazz == (MimeObjectClass *)&mimeMultipartDigestClass ||
293
clazz == (MimeObjectClass *)&mimeMultipartAppleDoubleClass ||
294
clazz == (MimeObjectClass *)&mimeMessageClass ||
295
clazz == (MimeObjectClass *)&mimeExternalObjectClass ||
296
/* mimeUntypedTextClass? -- does uuencode */
297
#ifdef ENABLE_SMIME
298
clazz == (MimeObjectClass *)&mimeMultipartSignedCMSClass ||
299
clazz == (MimeObjectClass *)&mimeEncryptedCMSClass ||
300
#endif
301
clazz == 0);
302
303
/* Contrairy to above, the below code is a "blacklist", i.e. it
304
*excludes* some "bad" classes. */
305
return !(
306
(avoid_html && (clazz == (MimeObjectClass *)&mimeInlineTextHTMLParsedClass
307
/* Should not happen - we protect against that in
308
mime_find_class(). Still for safety... */
309
)) ||
310
(avoid_images && (clazz == (MimeObjectClass *)&mimeInlineImageClass)) ||
311
(avoid_strange_content &&
312
(clazz == (MimeObjectClass *)&mimeInlineTextEnrichedClass ||
313
clazz == (MimeObjectClass *)&mimeInlineTextRichtextClass ||
314
clazz == (MimeObjectClass *)&mimeSunAttachmentClass ||
315
clazz == (MimeObjectClass *)&mimeExternalBodyClass)));
316
}
317
318
void getMsgHdrForCurrentURL(MimeDisplayOptions *opts, nsIMsgDBHdr **aMsgHdr) {
319
*aMsgHdr = nullptr;
320
321
if (!opts) return;
322
323
mime_stream_data *msd = (mime_stream_data *)(opts->stream_closure);
324
if (!msd) return;
325
326
nsCOMPtr<nsIChannel> channel =
327
msd->channel; // note the lack of ref counting...
328
if (channel) {
329
nsCOMPtr<nsIURI> uri;
330
nsCOMPtr<nsIMsgMessageUrl> msgURI;
331
channel->GetURI(getter_AddRefs(uri));
332
if (uri) {
333
msgURI = do_QueryInterface(uri);
334
if (msgURI) {
335
msgURI->GetMessageHeader(aMsgHdr);
336
if (*aMsgHdr) return;
337
nsCString rdfURI;
338
msgURI->GetUri(getter_Copies(rdfURI));
339
if (!rdfURI.IsEmpty()) {
340
nsCOMPtr<nsIMsgDBHdr> msgHdr;
341
GetMsgDBHdrFromURI(rdfURI.get(), getter_AddRefs(msgHdr));
342
NS_IF_ADDREF(*aMsgHdr = msgHdr);
343
}
344
}
345
}
346
}
347
348
return;
349
}
350
351
MimeObjectClass *mime_find_class(const char *content_type, MimeHeaders *hdrs,
352
MimeDisplayOptions *opts, bool exact_match_p) {
353
MimeObjectClass *clazz = 0;
354
MimeObjectClass *tempClass = 0;
355
contentTypeHandlerInitStruct ctHandlerInfo;
356
357
// Read some prefs
358
nsIPrefBranch *prefBranch = GetPrefBranch(opts);
359
int32_t html_as = 0; // def. see below
360
int32_t types_of_classes_to_disallow = 0; /* Let only a few libmime classes
361
process incoming data. This protects from bugs (e.g. buffer overflows)
362
and from security loopholes (e.g. allowing unchecked HTML in some
363
obscure classes, although the user has html_as > 0).
364
This option is mainly for the UI of html_as.
365
0 = allow all available classes
366
1 = Use hardcoded blacklist to avoid rendering (incoming) HTML
367
2 = ... and images
368
3 = ... and some other uncommon content types
369
4 = show all body parts
370
100 = Use hardcoded whitelist to avoid even more bugs(buffer overflows).
371
This mode will limit the features available (e.g. uncommon
372
attachment types and inline images) and is for paranoid users.
373
*/
374
if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer &&
375
opts->format_out != nsMimeOutput::nsMimeMessageDecrypt &&
376
opts->format_out != nsMimeOutput::nsMimeMessageAttach)
377
if (prefBranch) {
378
prefBranch->GetIntPref("mailnews.display.html_as", &html_as);
379
prefBranch->GetIntPref("mailnews.display.disallow_mime_handlers",
380
&types_of_classes_to_disallow);
381
if (types_of_classes_to_disallow > 0 && html_as == 0)
382
// We have non-sensical prefs. Do some fixup.
383
html_as = 1;
384
}
385
386
// First, check to see if the message has been marked as JUNK. If it has,
387
// then force the message to be rendered as simple, unless this has been
388
// called by a filtering routine.
389
bool sanitizeJunkMail = false;
390
391
// it is faster to read the pref first then figure out the msg hdr for the
392
// current url only if we have to
393
// XXX instead of reading this pref every time, part of mime should be an
394
// observer listening to this pref change and updating internal state
395
// accordingly. But none of the other prefs in this file seem to be doing
396
// that...=(
397
if (prefBranch)
398
prefBranch->GetBoolPref("mail.spam.display.sanitize", &sanitizeJunkMail);
399
400
if (sanitizeJunkMail &&
401
!(opts && opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer)) {
402
nsCOMPtr<nsIMsgDBHdr> msgHdr;
403
getMsgHdrForCurrentURL(opts, getter_AddRefs(msgHdr));
404
if (msgHdr) {
405
nsCString junkScoreStr;
406
(void)msgHdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
407
if (html_as == 0 && junkScoreStr.get() && atoi(junkScoreStr.get()) > 50)
408
html_as = 3; // 3 == Simple HTML
409
} // if msgHdr
410
} // if we are supposed to sanitize junk mail
411
412
/*
413
* What we do first is check for an external content handler plugin.
414
* This will actually extend the mime handling by calling a routine
415
* which will allow us to load an external content type handler
416
* for specific content types. If one is not found, we will drop back
417
* to the default handler.
418
*/
419
if ((tempClass = mime_locate_external_content_handler(
420
content_type, &ctHandlerInfo)) != nullptr) {
421
#ifdef MOZ_THUNDERBIRD
422
// This is a case where we only want to add this property if we are a
423
// thunderbird build AND we have found an external mime content handler for
424
// text/calendar This will enable iMIP support in Lightning
425
if (hdrs && (!PL_strncasecmp(content_type, "text/calendar", 13))) {
426
char *full_content_type =
427
MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false);
428
if (full_content_type) {
429
char *imip_method = MimeHeaders_get_parameter(
430
full_content_type, "method", nullptr, nullptr);
431
nsCOMPtr<nsIMsgDBHdr> msgHdr;
432
getMsgHdrForCurrentURL(opts, getter_AddRefs(msgHdr));
433
if (msgHdr)
434
msgHdr->SetStringProperty("imip_method",
435
(imip_method) ? imip_method : "nomethod");
436
// PR_Free checks for null
437
PR_Free(imip_method);
438
PR_Free(full_content_type);
439
}
440
}
441
#endif
442
443
if (types_of_classes_to_disallow > 0 &&
444
!PL_strncasecmp(content_type, "text/x-vcard", 12))
445
/* Use a little hack to prevent some dangerous plugins, which ship
446
with Mozilla, to run.
447
For the truly user-installed plugins, we rely on the judgement
448
of the user. */
449
{
450
if (!exact_match_p)
451
clazz = (MimeObjectClass *)&mimeExternalObjectClass; // As attachment
452
} else
453
clazz = (MimeObjectClass *)tempClass;
454
} else {
455
if (!content_type || !*content_type ||
456
!PL_strcasecmp(content_type, "text")) /* with no / in the type */
457
clazz = (MimeObjectClass *)&mimeUntypedTextClass;
458
459
/* Subtypes of text...
460
*/
461
else if (!PL_strncasecmp(content_type, "text/", 5)) {
462
if (!PL_strcasecmp(content_type + 5, "html")) {
463
if (opts &&
464
(opts->format_out == nsMimeOutput::nsMimeMessageSaveAs ||
465
opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer ||
466
opts->format_out == nsMimeOutput::nsMimeMessageDecrypt ||
467
opts->format_out == nsMimeOutput::nsMimeMessageAttach))
468
// SaveAs in new modes doesn't work yet.
469
{
470
// Don't use the parsed HTML class if we're ...
471
// - saving the HTML of a message
472
// - getting message content for filtering
473
// - snarfing attachments (nsMimeMessageDecrypt used in
474
// SnarfMsgAttachment)
475
// - processing attachments (like deleting attachments).
476
clazz = (MimeObjectClass *)&mimeInlineTextHTMLClass;
477
types_of_classes_to_disallow = 0;
478
} else if (html_as == 0 || html_as == 4) // Render sender's HTML
479
clazz = (MimeObjectClass *)&mimeInlineTextHTMLParsedClass;
480
else if (html_as == 1) // convert HTML to plaintext
481
// Do a HTML->TXT->HTML conversion, see mimethpl.h.
482
clazz = (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass;
483
else if (html_as == 2) // display HTML source
484
/* This is for the freaks. Treat HTML as plaintext,
485
which will cause the HTML source to be displayed.
486
Not very user-friendly, but some seem to want this. */
487
clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
488
else if (html_as == 3) // Sanitize
489
// Strip all but allowed HTML
490
clazz = (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass;
491
else // Goofy pref
492
/* User has an unknown pref value. Maybe he used a newer Mozilla
493
with a new alternative to avoid HTML. Defaulting to option 1,
494
which is less dangerous than defaulting to the raw HTML. */
495
clazz = (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass;
496
} else if (!PL_strcasecmp(content_type + 5, "enriched"))
497
clazz = (MimeObjectClass *)&mimeInlineTextEnrichedClass;
498
else if (!PL_strcasecmp(content_type + 5, "richtext"))
499
clazz = (MimeObjectClass *)&mimeInlineTextRichtextClass;
500
else if (!PL_strcasecmp(content_type + 5, "rtf"))
501
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
502
else if (!PL_strcasecmp(content_type + 5, "plain")) {
503
// Preliminary use the normal plain text
504
clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
505
506
if (opts &&
507
opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer &&
508
opts->format_out != nsMimeOutput::nsMimeMessageAttach &&
509
opts->format_out != nsMimeOutput::nsMimeMessageRaw) {
510
bool disable_format_flowed = false;
511
if (prefBranch)
512
prefBranch->GetBoolPref(
513
"mailnews.display.disable_format_flowed_support",
514
&disable_format_flowed);
515
516
if (!disable_format_flowed) {
517
// Check for format=flowed, damn, it is already stripped away from
518
// the contenttype!
519
// Look in headers instead even though it's expensive and clumsy
520
// First find Content-Type:
521
char *content_type_row =
522
hdrs ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false)
523
: 0;
524
// Then the format parameter if there is one.
525
// I would rather use a PARAM_FORMAT but I can't find the right
526
// place to put the define. The others seems to be in net.h
527
// but is that really really the right place? There is also
528
// a nsMimeTypes.h but that one isn't included. Bug?
529
char *content_type_format =
530
content_type_row
531
? MimeHeaders_get_parameter(content_type_row, "format",
532
nullptr, nullptr)
533
: 0;
534
535
if (content_type_format &&
536
!PL_strcasecmp(content_type_format, "flowed"))
537
clazz = (MimeObjectClass *)&mimeInlineTextPlainFlowedClass;
538
PR_FREEIF(content_type_format);
539
PR_FREEIF(content_type_row);
540
}
541
}
542
} else if (!exact_match_p)
543
clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
544
}
545
546
/* Subtypes of multipart...
547
*/
548
else if (!PL_strncasecmp(content_type, "multipart/", 10)) {
549
// When html_as is 4, we want all MIME parts of the message to
550
// show up in the displayed message body, if they are MIME types
551
// that we know how to display, and also in the attachment pane
552
// if it's appropriate to put them there. Both
553
// multipart/alternative and multipart/related play games with
554
// hiding various MIME parts, and we don't want that to happen,
555
// so we prevent that by parsing those MIME types as
556
// multipart/mixed, which won't mess with anything.
557
//
558
// When our output format is nsMimeOutput::nsMimeMessageAttach,
559
// i.e., we are reformatting the message to remove attachments,
560
// we are in a similar boat. The code for deleting
561
// attachments properly in that mode is in mimemult.cpp
562
// functions which are inherited by mimeMultipartMixedClass but
563
// not by mimeMultipartAlternativeClass or
564
// mimeMultipartRelatedClass. Therefore, to ensure that
565
// everything is handled properly, in this context too we parse
566
// those MIME types as multipart/mixed.
567
bool basic_formatting =
568
(html_as == 4) ||
569
(opts && opts->format_out == nsMimeOutput::nsMimeMessageAttach);
570
if (!PL_strcasecmp(content_type + 10, "alternative"))
571
clazz = basic_formatting
572
? (MimeObjectClass *)&mimeMultipartMixedClass
573
: (MimeObjectClass *)&mimeMultipartAlternativeClass;
574
else if (!PL_strcasecmp(content_type + 10, "related"))
575
clazz = basic_formatting
576
? (MimeObjectClass *)&mimeMultipartMixedClass
577
: (MimeObjectClass *)&mimeMultipartRelatedClass;
578
else if (!PL_strcasecmp(content_type + 10, "digest"))
579
clazz = (MimeObjectClass *)&mimeMultipartDigestClass;
580
else if (!PL_strcasecmp(content_type + 10, "appledouble") ||
581
!PL_strcasecmp(content_type + 10, "header-set"))
582
clazz = (MimeObjectClass *)&mimeMultipartAppleDoubleClass;
583
else if (!PL_strcasecmp(content_type + 10, "parallel"))
584
clazz = (MimeObjectClass *)&mimeMultipartParallelClass;
585
else if (!PL_strcasecmp(content_type + 10, "mixed"))
586
clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
587
#ifdef ENABLE_SMIME
588
else if (!PL_strcasecmp(content_type + 10, "signed")) {
589
/* Check that the "protocol" and "micalg" parameters are ones we
590
know about. */
591
char *ct =
592
hdrs ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false) : 0;
593
char *proto =
594
ct ? MimeHeaders_get_parameter(ct, PARAM_PROTOCOL, nullptr, nullptr)
595
: 0;
596
char *micalg =
597
ct ? MimeHeaders_get_parameter(ct, PARAM_MICALG, nullptr, nullptr)
598
: 0;
599
600
if (proto && ((/* is a signature */
601
!PL_strcasecmp(proto, APPLICATION_XPKCS7_SIGNATURE) ||
602
!PL_strcasecmp(proto, APPLICATION_PKCS7_SIGNATURE)) &&
603
micalg &&
604
(!PL_strcasecmp(micalg, PARAM_MICALG_MD5) ||
605
!PL_strcasecmp(micalg, PARAM_MICALG_MD5_2) ||
606
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1) ||
607
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_2) ||
608
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_3) ||
609
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_4) ||
610
!PL_strcasecmp(micalg, PARAM_MICALG_SHA1_5) ||
611
!PL_strcasecmp(micalg, PARAM_MICALG_SHA256) ||
612
!PL_strcasecmp(micalg, PARAM_MICALG_SHA256_2) ||
613
!PL_strcasecmp(micalg, PARAM_MICALG_SHA256_3) ||
614
!PL_strcasecmp(micalg, PARAM_MICALG_SHA384) ||
615
!PL_strcasecmp(micalg, PARAM_MICALG_SHA384_2) ||
616
!PL_strcasecmp(micalg, PARAM_MICALG_SHA384_3) ||
617
!PL_strcasecmp(micalg, PARAM_MICALG_SHA512) ||
618
!PL_strcasecmp(micalg, PARAM_MICALG_SHA512_2) ||
619
!PL_strcasecmp(micalg, PARAM_MICALG_SHA512_3) ||
620
!PL_strcasecmp(micalg, PARAM_MICALG_MD2))))
621
clazz = (MimeObjectClass *)&mimeMultipartSignedCMSClass;
622
else
623
clazz = 0;
624
625
PR_FREEIF(proto);
626
PR_FREEIF(micalg);
627
PR_FREEIF(ct);
628
}
629
#endif
630
631
if (!clazz && !exact_match_p)
632
/* Treat all unknown multipart subtypes as "multipart/mixed" */
633
clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
634
635
/* If we are sniffing a message, let's treat alternative parts as mixed */
636
if (opts && opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer)
637
if (clazz == (MimeObjectClass *)&mimeMultipartAlternativeClass)
638
clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
639
}
640
641
/* Subtypes of message...
642
*/
643
else if (!PL_strncasecmp(content_type, "message/", 8)) {
644
if (!PL_strcasecmp(content_type + 8, "rfc822") ||
645
!PL_strcasecmp(content_type + 8, "news"))
646
clazz = (MimeObjectClass *)&mimeMessageClass;
647
else if (!PL_strcasecmp(content_type + 8, "external-body"))
648
clazz = (MimeObjectClass *)&mimeExternalBodyClass;
649
else if (!PL_strcasecmp(content_type + 8, "partial"))
650
/* I guess these are most useful as externals, for now... */
651
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
652
else if (!exact_match_p)
653
/* Treat all unknown message subtypes as "text/plain" */
654
clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
655
}
656
657
/* The magic image types which we are able to display internally...
658
*/
659
else if (!PL_strncasecmp(content_type, "image/", 6)) {
660
if (imgLoader::SupportImageWithMimeType(
661
content_type, AcceptedMimeTypes::IMAGES_AND_DOCUMENTS))
662
clazz = (MimeObjectClass *)&mimeInlineImageClass;
663
else
664
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
665
}
666
#ifdef ENABLE_SMIME
667
else if (!PL_strcasecmp(content_type, APPLICATION_XPKCS7_MIME) ||
668
!PL_strcasecmp(content_type, APPLICATION_PKCS7_MIME)) {
669
670
if (opts->is_child) {
671
// We do not allow encrypted parts except as top level.
672
// Allowing them would leak the plain text in case the part is
673
// cleverly hidden and the decrypted content gets included in
674
// replies and forwards.
675
clazz = (MimeObjectClass *)&mimeSuppressedCryptoClass;
676
} else {
677
char *ct =
678
hdrs ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false)
679
: nullptr;
680
char *st =
681
ct ? MimeHeaders_get_parameter(ct, "smime-type", nullptr, nullptr)
682
: nullptr;
683
684
/* by default, assume that it is an encrypted message */
685
clazz = (MimeObjectClass *)&mimeEncryptedCMSClass;
686
687
/* if the smime-type parameter says that it's a certs-only or
688
compressed file, then show it as an attachment, however
689
(MimeEncryptedCMS doesn't handle these correctly) */
690
if (st && (!PL_strcasecmp(st, "certs-only") ||
691
!PL_strcasecmp(st, "compressed-data")))
692
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
693
else {
694
/* look at the file extension... less reliable, but still covered
695
by the S/MIME specification (RFC 3851, section 3.2.1) */
696
char *name = (hdrs ? MimeHeaders_get_name(hdrs, opts) : nullptr);
697
if (name) {
698
char *suf = PL_strrchr(name, '.');
699
bool p7mExternal = false;
700
701
if (prefBranch)
702
prefBranch->GetBoolPref("mailnews.p7m_external", &p7mExternal);
703
if (suf &&
704
((!PL_strcasecmp(suf, ".p7m") && p7mExternal) ||
705
!PL_strcasecmp(suf, ".p7c") || !PL_strcasecmp(suf, ".p7z")))
706
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
707
}
708
PR_Free(name);
709
}
710
PR_Free(st);
711
PR_Free(ct);
712
}
713
}
714
#endif
715
/* A few types which occur in the real world and which we would otherwise
716
treat as non-text types (which would be bad) without this special-case...
717
*/
718
else if (!PL_strcasecmp(content_type, APPLICATION_PGP) ||
719
!PL_strcasecmp(content_type, APPLICATION_PGP2))
720
clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
721
722
else if (!PL_strcasecmp(content_type, SUN_ATTACHMENT))
723
clazz = (MimeObjectClass *)&mimeSunAttachmentClass;
724
725
/* Everything else gets represented as a clickable link.
726
*/
727
else if (!exact_match_p)
728
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
729
730
if (!mime_is_allowed_class(clazz, types_of_classes_to_disallow)) {
731
/* Do that check here (not after the if block), because we want to allow
732
user-installed plugins. */
733
if (!exact_match_p)
734
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
735
else
736
clazz = 0;
737
}
738
}
739
740
#ifdef ENABLE_SMIME
741
// see bug #189988
742
if (opts && opts->format_out == nsMimeOutput::nsMimeMessageDecrypt &&
743
(clazz != (MimeObjectClass *)&mimeEncryptedCMSClass)) {
744
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
745
}
746
#endif
747
748
if (!exact_match_p)
749
NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
750
if (!clazz) return 0;
751
752
NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
753
754
if (clazz && !clazz->class_initialized) {
755
int status = mime_classinit(clazz);
756
if (status < 0) return 0;
757
}
758
759
return clazz;
760
}
761
762
MimeObject *mime_create(const char *content_type, MimeHeaders *hdrs,
763
MimeDisplayOptions *opts,
764
bool forceInline /* = false */) {
765
/* If there is no Content-Disposition header, or if the Content-Disposition
766
is ``inline'', then we display the part inline (and let mime_find_class()
767
decide how.)
768
769
If there is any other Content-Disposition (either ``attachment'' or some
770
disposition that we don't recognise) then we always display the part as
771
an external link, by using MimeExternalObject to display it.
772
773
But Content-Disposition is ignored for all containers except `message'.
774
(including multipart/mixed, and multipart/digest.) It's not clear if
775
this is to spec, but from a usability standpoint, I think it's necessary.
776
*/
777
778
MimeObjectClass *clazz = 0;
779
char *content_disposition = 0;
780
MimeObject *obj = 0;
781
char *override_content_type = 0;
782
783
/* We've had issues where the incoming content_type is invalid, of a format:
784
content_type="=?windows-1252?q?application/pdf" (bug 659355)
785
We decided to fix that by simply trimming the stuff before the ?
786
*/
787
if (content_type) {
788
const char *lastQuestion = strrchr(content_type, '?');
789
if (lastQuestion)
790
content_type = lastQuestion + 1; // the substring after the last '?'
791
}
792
793
/* There are some clients send out all attachments with a content-type
794
of application/octet-stream. So, if we have an octet-stream attachment,
795
try to guess what type it really is based on the file extension. I HATE
796
that we have to do this...
797
*/
798
if (hdrs && opts && opts->file_type_fn &&
799
800
/* ### mwelch - don't override AppleSingle */
801
(content_type ? PL_strcasecmp(content_type, APPLICATION_APPLEFILE)
802
: true) &&
803
/* ## davidm Apple double shouldn't use this #$%& either. */
804
(content_type ? PL_strcasecmp(content_type, MULTIPART_APPLEDOUBLE)
805
: true) &&
806
(!content_type ||
807
!PL_strcasecmp(content_type, APPLICATION_OCTET_STREAM) ||
808
!PL_strcasecmp(content_type, UNKNOWN_CONTENT_TYPE))) {
809
char *name = MimeHeaders_get_name(hdrs, opts);
810
if (name) {
811
override_content_type = opts->file_type_fn(name, opts->stream_closure);
812
// appledouble isn't a valid override content type, and makes
813
// attachments invisible.
814
if (!PL_strcasecmp(override_content_type, MULTIPART_APPLEDOUBLE))
815
override_content_type = nullptr;
816
PR_FREEIF(name);
817
818
// Workaround for saving '.eml" file encoded with base64.
819
// Do not override with message/rfc822 whenever Transfer-Encoding is
820
// base64 since base64 encoding of message/rfc822 is invalid.
821
// Our MimeMessageClass has no capability to decode it.
822
if (!PL_strcasecmp(override_content_type, MESSAGE_RFC822)) {
823
nsCString encoding;
824
encoding.Adopt(MimeHeaders_get(hdrs, HEADER_CONTENT_TRANSFER_ENCODING,
825
true, false));
826
if (encoding.LowerCaseEqualsLiteral(ENCODING_BASE64))
827
override_content_type = nullptr;
828
}
829
830
// If we get here and it is not the unknown content type from the
831
// file name, let's do some better checking not to inline something bad
832
if (override_content_type && *override_content_type &&
833
(PL_strcasecmp(override_content_type, UNKNOWN_CONTENT_TYPE)))
834
content_type = override_content_type;
835
}
836
}
837
838
clazz = mime_find_class(content_type, hdrs, opts, false);
839
840
NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
841
if (!clazz) goto FAIL;
842
843
if (opts && opts->part_to_load)
844
/* Always ignore Content-Disposition when we're loading some specific
845
sub-part (which may be within some container that we wouldn't otherwise
846
descend into, if the container itself had a Content-Disposition of
847
`attachment'. */
848
content_disposition = 0;
849
850
else if (mime_subclass_p(clazz, (MimeObjectClass *)&mimeContainerClass) &&
851
!mime_subclass_p(clazz, (MimeObjectClass *)&mimeMessageClass))
852
/* Ignore Content-Disposition on all containers except `message'.
853
That is, Content-Disposition is ignored for multipart/mixed objects,
854
but is obeyed for message/rfc822 objects. */
855
content_disposition = 0;
856
857
else {
858
/* Check to see if the plugin should override the content disposition
859
to make it appear inline. One example is a vcard which has a content
860
disposition of an "attachment;" */
861
if (force_inline_display(content_type))
862
NS_MsgSACopy(&content_disposition, "inline");
863
else
864
content_disposition =
865
hdrs ? MimeHeaders_get(hdrs, HEADER_CONTENT_DISPOSITION, true, false)
866
: 0;
867
}
868
869
if (!content_disposition || !PL_strcasecmp(content_disposition, "inline"))
870
; /* Use the class we've got. */
871
else {
872
// override messages that have content disposition set to "attachment"
873
// even though we probably should show them inline.
874
if ((clazz != (MimeObjectClass *)&mimeMessageClass) &&
875
(clazz != (MimeObjectClass *)&mimeInlineImageClass) &&
876
(!opts->show_attachment_inline_text ||
877
((clazz != (MimeObjectClass *)&mimeInlineTextHTMLClass) &&
878
(clazz != (MimeObjectClass *)&mimeInlineTextHTMLParsedClass) &&
879
(clazz != (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass) &&
880
(clazz != (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass) &&
881
(clazz != (MimeObjectClass *)&mimeInlineTextRichtextClass) &&
882
(clazz != (MimeObjectClass *)&mimeInlineTextEnrichedClass) &&
883
(clazz != (MimeObjectClass *)&mimeInlineTextClass) &&
884
(clazz != (MimeObjectClass *)&mimeInlineTextPlainClass) &&
885
(clazz != (MimeObjectClass *)&mimeInlineTextPlainFlowedClass)))) {
886
// not a special inline type, so show as attachment
887
// However, mimeSuppressedCryptoClass is treated identically as
888
// mimeExternalObjectClass, let's not lose that type information.
889
if (clazz != (MimeObjectClass *)&mimeSuppressedCryptoClass) {
890
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
891
}
892
}
893
}
894
895
/* If the option `Show Attachments Inline' is off, now would be the time to
896
* change our mind... */
897
/* Also, if we're doing a reply (i.e. quoting the body), then treat that
898
* according to preference. */
899
if (opts &&
900
((!opts->show_attachment_inline_p && !forceInline) ||
901
(!opts->quote_attachment_inline_p &&
902
(opts->format_out == nsMimeOutput::nsMimeMessageQuoting ||
903
opts->format_out == nsMimeOutput::nsMimeMessageBodyQuoting)))) {
904
if (mime_subclass_p(clazz, (MimeObjectClass *)&mimeInlineTextClass)) {
905
/* It's a text type. Write it only if it's the *first* part
906
that we're writing, and then only if it has no "filename"
907
specified (the assumption here being, if it has a filename,
908
it wasn't simply typed into the text field -- it was actually
909
an attached document.) */
910
if (opts->state && opts->state->first_part_written_p)
911
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
912
else {
913
/* If there's a name, then write this as an attachment. */
914
char *name = (hdrs ? MimeHeaders_get_name(hdrs, opts) : nullptr);
915
if (name) {
916
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
917
PR_Free(name);
918
}
919
}
920
} else if (mime_subclass_p(clazz, (MimeObjectClass *)&mimeContainerClass) &&
921
!mime_subclass_p(clazz, (MimeObjectClass *)&mimeMessageClass))
922
/* Multipart subtypes are ok, except for messages; descend into
923
multiparts, and defer judgement.
924
925
Encrypted blobs are just like other containers (make the crypto
926
layer invisible, and treat them as simple containers. So there's
927
no easy way to save encrypted data directly to disk; it will tend
928
to always be wrapped inside a message/rfc822. That's ok.) */
929
;
930
else if (opts && opts->part_to_load &&
931
mime_subclass_p(clazz, (MimeObjectClass *)&mimeMessageClass))
932
/* Descend into messages only if we're looking for a specific sub-part. */
933
;
934
else {
935
/* Anything else, and display it as a link (and cause subsequent
936
text parts to also be displayed as links.) */
937
clazz = (MimeObjectClass *)&mimeExternalObjectClass;
938
}
939
}
940
941
PR_FREEIF(content_disposition);
942
obj = mime_new(clazz, hdrs, content_type);
943
944
FAIL:
945
946
/* If we decided to ignore the content-type in the headers of this object
947
(see above) then make sure that our new content-type is stored in the
948
object itself. (Or free it, if we're in an out-of-memory situation.)
949
*/
950
if (override_content_type) {
951
if (obj) {
952
PR_FREEIF(obj->content_type);
953
obj->content_type = override_content_type;
954
} else {
955
PR_Free(override_content_type);
956
}
957
}
958
959
return obj;
960
}
961
962
static int mime_classinit_1(MimeObjectClass *clazz, MimeObjectClass *target);
963
964
static int mime_classinit(MimeObjectClass *clazz) {
965
int status;
966
if (clazz->class_initialized) return 0;
967
968
NS_ASSERTION(clazz->class_initialize,
969
"1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
970
if (!clazz->class_initialize) return -1;
971
972
/* First initialize the superclass.
973
*/
974
if (clazz->superclass && !clazz->superclass->class_initialized) {
975
status = mime_classinit(clazz->superclass);
976
if (status < 0) return status;
977
}
978
979
/* Now run each of the superclass-init procedures in turn,
980
parentmost-first. */
981
status = mime_classinit_1(clazz, clazz);
982
if (status < 0) return status;
983
984
/* Now we're done. */
985
clazz->class_initialized = true;
986
return 0;
987
}
988
989
static int mime_classinit_1(MimeObjectClass *clazz, MimeObjectClass *target) {
990
int status;
991
if (clazz->superclass) {
992
status = mime_classinit_1(clazz->superclass, target);
993
if (status < 0) return status;
994
}
995
return clazz->class_initialize(target);
996
}
997
998
bool mime_subclass_p(MimeObjectClass *child, MimeObjectClass *parent) {
999
if (child == parent) return true;
1000
if (!child->superclass) return false;
1001
return mime_subclass_p(child->superclass, parent);
1002
}
1003
1004
bool mime_typep(MimeObject *obj, MimeObjectClass *clazz) {
1005
return mime_subclass_p(obj->clazz, clazz);
1006
}
1007
1008
/* URL munging
1009
*/
1010
1011
/* Returns a string describing the location of the part (like "2.5.3").
1012
This is not a full URL, just a part-number.
1013
*/
1014
char *mime_part_address(MimeObject *obj) {
1015
if (!obj->parent) return strdup("0");
1016
1017
/* Find this object in its parent. */
1018
int32_t i, j = -1;
1019
char buf[20];
1020
char *higher = 0;
1021
MimeContainer *cont = (MimeContainer *)obj->parent;
1022
NS_ASSERTION(mime_typep(obj->parent, (MimeObjectClass *)&mimeContainerClass),
1023
"1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
1024
for (i = 0; i < cont->nchildren; i++)
1025
if (cont->children[i] == obj) {
1026
j = i + 1;
1027
break;
1028
}
1029
if (j == -1) {
1030
NS_ERROR("No children under MeimContainer");
1031
return 0;
1032
}
1033
1034
PR_snprintf(buf, sizeof(buf), "%ld", j);
1035
if (obj->parent->parent) {
1036
higher = mime_part_address(obj->parent);
1037
if (!higher) return 0; /* MIME_OUT_OF_MEMORY */
1038
}
1039
1040
if (!higher) return strdup(buf);
1041
1042
uint32_t slen = strlen(higher) + strlen(buf) + 3;
1043
char *s = (char *)PR_MALLOC(slen);
1044
if (!s) {
1045
PR_Free(higher);
1046
return 0; /* MIME_OUT_OF_MEMORY */
1047
}
1048
PL_strncpyz(s, higher, slen);
1049
PL_strcatn(s, slen, ".");
1050
PL_strcatn(s, slen, buf);
1051
PR_Free(higher);
1052
return s;
1053
}
1054
1055
/* Returns a string describing the location of the *IMAP* part (like "2.5.3").
1056
This is not a full URL, just a part-number.
1057
This part is explicitly passed in the X-Mozilla-IMAP-Part header.
1058
Return value must be freed by the caller.
1059
*/
1060
char *mime_imap_part_address(MimeObject *obj) {
1061
if (!obj || !obj->headers) return 0;
1062
return MimeHeaders_get(obj->headers, IMAP_EXTERNAL_CONTENT_HEADER, false,
1063
false);
1064
}
1065
1066
/* Returns a full URL if the current mime object has a
1067
EXTERNAL_ATTACHMENT_URL_HEADER header. Return value must be freed by the
1068
caller.
1069
*/
1070
char *mime_external_attachment_url(MimeObject *obj) {
1071
if (!obj || !obj->headers) return 0;
1072
return MimeHeaders_get(obj->headers, EXTERNAL_ATTACHMENT_URL_HEADER, false,
1073
false);
1074
}
1075
1076
#ifdef ENABLE_SMIME
1077
/* Asks whether the given object is one of the cryptographically signed
1078
or encrypted objects that we know about. (MimeMessageClass uses this
1079
to decide if the headers need to be presented differently.)
1080
*/
1081
bool mime_crypto_object_p(MimeHeaders *hdrs, bool clearsigned_counts,
1082
MimeDisplayOptions *opts) {
1083
char *ct;
1084
MimeObjectClass *clazz;
1085
1086
if (!hdrs) return false;
1087
1088
ct = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, true, false);
1089
if (!ct) return false;
1090
1091
/* Rough cut -- look at the string before doing a more complex comparison. */
1092
if (PL_strcasecmp(ct, MULTIPART_SIGNED) &&
1093
PL_strncasecmp(ct, "application/", 12)) {
1094
PR_Free(ct);
1095
return false;
1096
}
1097
1098
/* It's a candidate for being a crypto object. Let's find out for sure... */
1099
clazz = mime_find_class(ct, hdrs, opts, true);
1100
PR_Free(ct);
1101
1102
if (clazz == ((MimeObjectClass *)&mimeEncryptedCMSClass)) return true;
1103
1104
if (clearsigned_counts &&
1105
clazz == ((MimeObjectClass *)&mimeMultipartSignedCMSClass))
1106
return true;
1107
1108
return false;
1109
}
1110
1111
#endif // ENABLE_SMIME
1112
1113
/* Puts a part-number into a URL. If append_p is true, then the part number
1114
is appended to any existing part-number already in that URL; otherwise,
1115
it replaces it.
1116
*/
1117
char *mime_set_url_part(const char *url, const char *part, bool append_p) {
1118
const char *part_begin = 0;
1119
const char *part_end = 0;
1120
bool got_q = false;
1121
const char *s;
1122
char *result;
1123
1124
if (!url || !part) return 0;
1125
1126
nsAutoCString urlString(url);
1127
int32_t typeIndex = urlString.Find("?type=application/x-message-display");
1128
if (typeIndex != -1) {
1129
urlString.Cut(typeIndex, sizeof("?type=application/x-message-display") - 1);
1130
if (urlString.CharAt(typeIndex) == '&')
1131
urlString.Replace(typeIndex, 1, '?');
1132
url = urlString.get();
1133
}
1134
1135
for (s = url; *s; s++) {
1136
if (*s == '?') {
1137
got_q = true;
1138
if (!PL_strncasecmp(s, "?part=", 6)) part_begin = (s += 6);
1139
} else if (got_q && *s == '&' && !PL_strncasecmp(s, "&part=", 6))
1140
part_begin = (s += 6);
1141
1142
if (part_begin) {
1143
while (*s && *s != '?' && *s != '&') s++;
1144
part_end = s;
1145
break;
1146
}
1147
}
1148
1149
uint32_t resultlen = strlen(url) + strlen(part) + 10;
1150
result = (char *)PR_MALLOC(resultlen);
1151
if (!result) return 0;
1152
1153
if (part_begin) {
1154
if (append_p) {
1155
memcpy(result, url, part_end - url);
1156
result[part_end - url] = '.';
1157
result[part_end - url + 1] = 0;
1158
} else {
1159
memcpy(result, url, part_begin - url);
1160
result[part_begin - url] = 0;
1161
}
1162
} else {
1163
PL_strncpyz(result, url, resultlen);
1164
if (got_q)
1165
PL_strcatn(result, resultlen, "&part=");
1166
else
1167
PL_strcatn(result, resultlen, "?part=");
1168
}
1169
1170
PL_strcatn(result, resultlen, part);
1171
1172
if (part_end && *part_end) PL_strcatn(result, resultlen, part_end);
1173
1174
/* Semi-broken kludge to omit a trailing "?part=0". */
1175
{
1176
int L = strlen(result);
1177
if (L > 6 && (result[L - 7] == '?' || result[L - 7] == '&') &&
1178
!strcmp("part=0", result + L - 6))
1179
result[L - 7] = 0;
1180
}
1181
1182
return result;
1183
}
1184
1185
/* Puts an *IMAP* part-number into a URL.
1186
Strips off any previous *IMAP* part numbers, since they are absolute, not
1187
relative.
1188
*/
1189
char *mime_set_url_imap_part(const char *url, const char *imappart,
1190
const char *libmimepart) {
1191
char *result = 0;
1192
char *whereCurrent = PL_strstr(url, "/;section=");
1193
if (whereCurrent) {
1194
*whereCurrent = 0;
1195
}
1196
1197
uint32_t resultLen =
1198
strlen(url) + strlen(imappart) + strlen(libmimepart) + 17;
1199
result = (char *)PR_MALLOC(resultLen);
1200
if (!result) return 0;
1201
1202
PL_strncpyz(result, url, resultLen);
1203
PL_strcatn(result, resultLen, "/;section=");
1204
PL_strcatn(result, resultLen, imappart);
1205
PL_strcatn(result, resultLen, "?part=");
1206
PL_strcatn(result, resultLen, libmimepart);
1207
1208
if (whereCurrent) *whereCurrent = '/';
1209
1210
return result;
1211
}
1212
1213
/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
1214
number matches, and returns the MimeObject (else NULL.)
1215
(part is not a URL -- it's of the form "1.3.5".)
1216
*/
1217
MimeObject *mime_address_to_part(const char *part, MimeObject *obj) {
1218
/* Note: this is an N^2 operation, but the number of parts in a message
1219
shouldn't ever be large enough that this really matters... */
1220
1221
bool match;
1222
1223
if (!part || !*part) {
1224
match = !obj->parent;
1225
} else {
1226
char *part2 = mime_part_address(obj);
1227
if (!part2) return 0; /* MIME_OUT_OF_MEMORY */
1228
match = !strcmp(part, part2);
1229
PR_Free(part2);
1230
}
1231
1232
if (match) {
1233
/* These are the droids we're looking for. */
1234
return obj;
1235
} else if (!mime_typep(obj, (MimeObjectClass *)&mimeContainerClass)) {
1236
/* Not a container, pull up, pull up! */
1237
return 0;
1238
} else {
1239
int32_t i;
1240
MimeContainer *cont = (MimeContainer *)obj;
1241
for (i = 0; i < cont->nchildren; i++) {
1242
MimeObject *o2 = mime_address_to_part(part, cont->children[i]);
1243
if (o2) return o2;
1244
}
1245
return 0;
1246
}
1247
}
1248
1249
/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
1250
number matches; if one is found, returns the Content-Name of that part.
1251
Else returns NULL. (part is not a URL -- it's of the form "1.3.5".)
1252
*/
1253
char *mime_find_content_type_of_part(const char *part, MimeObject *obj) {
1254
char *result = 0;
1255
1256
obj = mime_address_to_part(part, obj);
1257
if (!obj) return 0;
1258
1259
result = (obj->headers ? MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE,
1260
true, false)
1261
: 0);
1262
1263
return result;
1264
}
1265
1266
/* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
1267
number matches; if one is found, returns the Content-Name of that part.
1268
Else returns NULL. (part is not a URL -- it's of the form "1.3.5".)
1269
*/
1270
char *mime_find_suggested_name_of_part(const char *part, MimeObject *obj) {
1271
char *result = 0;
1272
1273
obj = mime_address_to_part(part, obj);
1274
if (!obj) return 0;
1275
1276
result =
1277
(obj->headers ? MimeHeaders_get_name(obj->headers, obj->options) : 0);
1278
1279
/* If this part doesn't have a name, but this part is one fork of an
1280
AppleDouble, and the AppleDouble itself has a name, then use that. */
1281
if (!result && obj->parent && obj->parent->headers &&
1282
mime_typep(obj->parent,
1283
(MimeObjectClass *)&mimeMultipartAppleDoubleClass))
1284
result = MimeHeaders_get_name(obj->parent->headers, obj->options);
1285
1286
/* Else, if this part is itself an AppleDouble, and one of its children
1287
has a name, then use that (check data fork first, then resource.) */
1288
if (!result &&
1289
mime_typep(obj, (MimeObjectClass *)&mimeMultipartAppleDoubleClass)) {
1290
MimeContainer *cont = (MimeContainer *)obj;
1291
if (cont->nchildren > 1 && cont->children[1] && cont->children[1]->headers)
1292
result = MimeHeaders_get_name(cont->children[1]->headers, obj->options);
1293
1294
if (!result && cont->nchildren > 0 && cont->children[0] &&
1295
cont->children[0]->headers)
1296
result = MimeHeaders_get_name(cont->children[0]->headers, obj->options);
1297
}
1298
1299
/* Ok, now we have the suggested name, if any.
1300
Now we remove any extensions that correspond to the
1301
Content-Transfer-Encoding. For example, if we see the headers
1302
1303
Content-Type: text/plain
1304
Content-Disposition: inline; filename=foo.text.uue
1305
Content-Transfer-Encoding: x-uuencode
1306
1307
then we would look up (in mime.types) the file extensions which are
1308
associated with the x-uuencode encoding, find that "uue" is one of
1309
them, and remove that from the end of the file name, thus returning
1310
"foo.text" as the name. This is because, by the time this file ends
1311
up on disk, its content-transfer-encoding will have been removed;
1312
therefore, we should suggest a file name that indicates that.
1313
*/
1314
if (result && obj->encoding && *obj->encoding) {
1315
int32_t L = strlen(result);
1316
const char **exts = 0;
1317
1318
/*
1319
I'd like to ask the mime.types file, "what extensions correspond
1320
to obj->encoding (which happens to be "x-uuencode") but doing that
1321
in a non-sphagetti way would require brain surgery. So, since
1322
currently uuencode is the only content-transfer-encoding which we
1323
understand which traditionally has an extension, we just special-
1324
case it here! Icepicks in my forehead!
1325
1326
Note that it's special-cased in a similar way in libmsg/compose.c.
1327
*/
1328
if (!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE)) {
1329
static const char *uue_exts[] = {"uu", "uue", 0};
1330
exts = uue_exts;
1331
}
1332
1333
while (exts && *exts) {
1334
const char *ext = *exts;
1335
int32_t L2 = strlen(ext);
1336
if (L > L2 + 1 && /* long enough */
1337
result[L - L2 - 1] == '.' && /* '.' in right place*/
1338
!PL_strcasecmp(ext, result + (L - L2))) /* ext matches */
1339
{
1340
result[L - L2 - 1] = 0; /* truncate at '.' and stop. */
1341
break;
1342
}
1343
exts++;
1344
}
1345
}
1346
1347
return result;
1348
}
1349
1350
/* Parse the various "?" options off the URL and into the options struct.
1351
*/
1352
int mime_parse_url_options(const char *url, MimeDisplayOptions *options) {
1353
const char *q;
1354
1355
if (!url || !*url) return 0;
1356
if (!options) return 0;
1357
1358
MimeHeadersState default_headers = options->headers;
1359
1360
q = PL_strrchr(url, '?');
1361
if (!q) return 0;
1362
q++;
1363
while (*q) {
1364
const char *end, *value, *name_end;
1365
end = q;
1366
while (*end && *end != '&') end++;
1367
value = q;
1368
while (*value != '=' && value < end) value++;
1369
name_end = value;
1370
if (value < end) value++;
1371
if (name_end <= q)
1372
;
1373
else if (!PL_strncasecmp("headers", q, name_end - q)) {
1374
if (end > value && !PL_strncasecmp("only", value, end - value))
1375
options->headers = MimeHeadersOnly;
1376
else if (end > value && !PL_strncasecmp("none", value, end - value))
1377
options->headers = MimeHeadersNone;
1378
else if (end > value && !PL_strncasecmp("all", value, end - value))
1379
options->headers = MimeHeadersAll;
1380
else if (end > value && !PL_strncasecmp("some", value, end - value))
1381
options->headers = MimeHeadersSome;
1382
else if (end > value && !PL_strncasecmp("micro", value, end - value))
1383
options->headers = MimeHeadersMicro;
1384
else if (end > value && !PL_strncasecmp("cite", value, end - value))
1385
options->headers = MimeHeadersCitation;
1386
else if (end > value && !PL_strncasecmp("citation", value, end - value))
1387
options->headers = MimeHeadersCitation;
1388
else
1389
options->headers = default_headers;
1390
} else if (!PL_strncasecmp("part", q, name_end - q) &&
1391
options->format_out != nsMimeOutput::nsMimeMessageBodyQuoting) {
1392
PR_FREEIF(options->part_to_load);
1393
if (end > value) {
1394
options->part_to_load = (char *)PR_MALLOC(end - value + 1);
1395
if (!options->part_to_load) return MIME_OUT_OF_MEMORY;
1396
memcpy(options->part_to_load, value, end - value);
1397
options->part_to_load[end - value] = 0;
1398
}
1399
} else if (!PL_strncasecmp("rot13", q, name_end - q)) {
1400
options->rot13_p =
1401
end <= value || !PL_strncasecmp("true", value, end - value);
1402
} else if (!PL_strncasecmp("emitter", q, name_end - q)) {
1403
if ((end > value) && !PL_strncasecmp("js", value, end - value)) {
1404
// the js emitter needs to hear about nested message bodies
1405
// in order to build a proper representation.
1406
options->notify_nested_bodies = true;
1407
// show_attachment_inline_p has the side-effect of letting the
1408
// emitter see all parts of a multipart/alternative, which it
1409
// really appreciates.
1410
options->show_attachment_inline_p = true;
1411
// however, show_attachment_inline_p also results in a few
1412
// subclasses writing junk into the body for display purposes.
1413
// put a stop to these shenanigans by enabling write_pure_bodies.
1414
// current offenders are:
1415
// - MimeInlineImage
1416
options->write_pure_bodies = true;
1417
// we don't actually care about the data in the attachments, just the
1418
// metadata (i.e. size)
1419
options->metadata_only = true;
1420
}
1421
}
1422
1423
q = end;
1424
if (*q) q++;
1425
}
1426
1427
/* Compatibility with the "?part=" syntax used in the old (Mozilla 2.0)
1428
MIME parser.
1429
1430
Basically, the problem is that the old part-numbering code was totally
1431
busted: here's a comparison of the old and new numberings with a pair
1432
of hypothetical messages (one with a single part, and one with nested
1433
containers).
1434
NEW: OLD: OR:
1435
message/rfc822
1436
image/jpeg 1 0 0
1437
1438
message/rfc822
1439
multipart/mixed 1 0 0
1440
text/plain 1.1 1 1
1441
image/jpeg 1.2 2 2
1442
message/rfc822 1.3 - 3
1443
text/plain 1.3.1 3 -
1444
message/rfc822 1.4 - 4
1445
multipart/mixed 1.4.1 4 -
1446
text/plain 1.4.1.1 4.1 -
1447
image/jpeg 1.4.1.2 4.2 -
1448
text/plain 1.5 5 5
1449
1450
The "NEW" column is how the current code counts. The "OLD" column is
1451
what "?part=" references would do in 3.0b4 and earlier; you'll see that
1452
you couldn't directly refer to the child message/rfc822 objects at all!
1453
But that's when it got really weird, because if you turned on
1454
"Attachments As Links" (or used a URL like "?inline=false&part=...")
1455
then you got a totally different numbering system (seen in the "OR"
1456
column.) Gag!
1457
1458
So, the problem is, ClariNet had been using these part numbers in their
1459
HTML news feeds, as a sleazy way of transmitting both complex HTML layouts
1460
and images using NNTP as transport, without invoking HTTP.
1461
1462
The following clause is to provide some small amount of backward
1463
compatibility. By looking at that table, one can see that in the new
1464
model, "part=0" has no meaning, and neither does "part=2" or "part=3"
1465
and so on.
1466
1467
"part=1" is ambiguous between the old and new way, as is any part
1468
specification that has a "." in it.
1469
1470
So, the compatibility hack we do here is: if the part is "0", then map
1471
that to "1". And if the part is >= "2", then prepend "1." to it (so that
1472
we map "2" to "1.2", and "3" to "1.3".)
1473
1474
This leaves the URLs compatible in the cases of:
1475
1476
= single part messages
1477
= references to elements of a top-level multipart except the first
1478
1479
and leaves them incompatible for:
1480
1481
= the first part of a top-level multipart
1482
= all elements deeper than the outermost part
1483
1484
Life s#$%s when you don't properly think out things that end up turning
1485
into de-facto standards...
1486
*/
1487
1488
if (options->part_to_load &&
1489
!PL_strchr(options->part_to_load, '.')) /* doesn't contain a dot */
1490
{
1491
if (!strcmp(options->part_to_load, "0")) /* 0 */
1492
{
1493
PR_Free(options->part_to_load);
1494
options->part_to_load = strdup("1");
1495
if (!options->part_to_load) return MIME_OUT_OF_MEMORY;
1496
} else if (strcmp(options->part_to_load, "1")) /* not 1 */
1497
{
1498
const char *prefix = "1.";
1499
uint32_t slen = strlen(options->part_to_load) + strlen(prefix) + 1;
1500
char *s = (char *)PR_MALLOC(slen);
1501
if (!s) return MIME_OUT_OF_MEMORY;
1502
PL_strncpyz(s, prefix, slen);
1503
PL_strcatn(s, slen, options->part_to_load);
1504
PR_Free(options->part_to_load);
1505
options->part_to_load = s;
1506
}
1507
}
1508
1509
return 0;
1510
}
1511
1512
/* Some output-generation utility functions...
1513
*/
1514
1515
int MimeOptions_write(MimeHeaders *hdrs, MimeDisplayOptions *opt,
1516
const char *data, int32_t length, bool user_visible_p) {
1517
int status = 0;
1518
void *closure = 0;
1519
if (!opt || !opt->output_fn || !opt->state) return 0;
1520
1521
closure = opt->output_closure;
1522
if (!closure) closure = opt->stream_closure;
1523
1524
// PR_ASSERT(opt->state->first_data_written_p);
1525
1526
if (opt->state->separator_queued_p && user_visible_p) {
1527
opt->state->separator_queued_p = false;
1528
if (opt->state->separator_suppressed_p)
1529
opt->state->separator_suppressed_p = false;
1530
else {
1531
const char *sep = "<BR><FIELDSET CLASS=\"mimeAttachmentHeader\">";
1532
int lstatus = opt->output_fn(sep, strlen(sep), closure);
1533
opt->state->separator_suppressed_p = false;
1534
if (lstatus < 0) return lstatus;
1535
1536
nsCString name;
1537
name.Adopt(MimeHeaders_get_name(hdrs, opt));
1538
MimeHeaders_convert_header_value(opt, name, false);
1539
1540
if (!name.IsEmpty()) {
1541
sep = "<LEGEND CLASS=\"mimeAttachmentHeaderName\">";
1542
lstatus = opt->output_fn(sep, strlen(sep), closure);
1543
opt->state->separator_suppressed_p = false;
1544
if (lstatus < 0) return lstatus;
1545
1546
nsCString escapedName;
1547
nsAppendEscapedHTML(name, escapedName);
1548
1549
lstatus =
1550
opt->output_fn(escapedName.get(), escapedName.Length(), closure);
1551
opt->state->separator_suppressed_p = false;
1552
if (lstatus < 0) return lstatus;
1553
1554
sep = "</LEGEND>";
1555
lstatus = opt->output_fn(sep, strlen(sep), closure);
1556
opt->state->separator_suppressed_p = false;
1557
if (lstatus < 0) return lstatus;
1558
}
1559
1560
sep = "</FIELDSET>";
1561
lstatus = opt->output_fn(sep, strlen(sep), closure);
1562
opt->state->separator_suppressed_p = false;
1563
if (lstatus < 0) return lstatus;
1564
}
1565
}
1566
if (user_visible_p) opt->state->separator_suppressed_p = false;
1567
1568
if (length > 0) {
1569
status = opt->output_fn(data, length, closure);
1570
if (status < 0) return status;
1571
}
1572
1573
return 0;
1574
}
1575
1576
int MimeObject_write(MimeObject *obj, const char *output, int32_t length,
1577
bool user_visible_p) {
1578
if (!obj->output_p) return 0;
1579
1580
// if we're stripping attachments, check if any parent is not being output
1581
if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach) {
1582
// if true, mime generates a separator in html - we don't want that.
1583
user_visible_p = false;
1584
1585
for (MimeObject *parent = obj->parent; parent; parent = parent->parent) {
1586
if (!parent->output_p) return 0;
1587
}
1588
}
1589
if (!obj->options->state->first_data_written_p) {
1590
int status = MimeObject_output_init(obj, 0);
1591
if (status < 0) return status;
1592
NS_ASSERTION(obj->options->state->first_data_written_p,
1593
"1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
1594
}
1595
1596
return MimeOptions_write(obj->headers, obj->options, output, length,
1597
user_visible_p);
1598
}
1599
1600
int MimeObject_write_separator(MimeObject *obj) {
1601
if (obj->options && obj->options->state &&
1602
// we never want separators if we are asking for pure bodies
1603
!obj->options->write_pure_bodies)
1604
obj->options->state->separator_queued_p = true;
1605
return 0;
1606
}
1607
1608
int MimeObject_output_init(MimeObject *obj, const char *content_type) {
1609
if (obj && obj->options && obj->options->state &&
1610
!obj->options->state->first_data_written_p) {
1611
int status;
1612
const char *charset = 0;
1613
char *name = 0, *x_mac_type = 0, *x_mac_creator = 0;
1614
1615
if (!obj->options->output_init_fn) {
1616
obj->options->state->first_data_written_p = true;
1617
return 0;
1618
}
1619
1620
if (obj->headers) {
1621
char *ct;
1622
name = MimeHeaders_get_name(obj->headers, obj->options);
1623
1624
ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, false, false);
1625
if (ct) {
1626
x_mac_type =
1627
MimeHeaders_get_parameter(ct, PARAM_X_MAC_TYPE, nullptr, nullptr);
1628
x_mac_creator = MimeHeaders_get_parameter(ct, PARAM_X_MAC_CREATOR,
1629
nullptr, nullptr);
1630
/* if don't have a x_mac_type and x_mac_creator, we need to try to get
1631
* it from its parent */
1632
if (!x_mac_type && !x_mac_creator && obj->parent &&
1633
obj->parent->headers) {
1634
char *ctp = MimeHeaders_get(obj->parent->headers, HEADER_CONTENT_TYPE,
1635
false, false);
1636
if (ctp) {
1637
x_mac_type = MimeHeaders_get_parameter(ctp, PARAM_X_MAC_TYPE,
1638
nullptr, nullptr);
1639
x_mac_creator = MimeHeaders_get_parameter(ctp, PARAM_X_MAC_CREATOR,
1640
nullptr, nullptr);
1641
PR_Free(ctp);
1642
}
1643
}
1644
1645
if (!(obj->options->override_charset)) {
1646
char *charset =
1647
MimeHeaders_get_parameter(ct, "charset", nullptr, nullptr);
1648
if (charset) {
1649
PR_FREEIF(obj->options->default_charset);
1650
obj->options->default_charset = charset;
1651
}
1652
}
1653
PR_Free(ct);
1654
}
1655
}
1656
1657
if (mime_typep(obj, (MimeObjectClass *)&mimeInlineTextClass))
1658
charset = ((MimeInlineText *)obj)->charset;
1659
1660
if (!content_type) content_type = obj->content_type;
1661
if (!content_type) content_type = TEXT_PLAIN;
1662
1663
//
1664
// Set the charset on the channel we are dealing with so people know
1665
// what the charset is set to. Do this for quoting/Printing ONLY!
1666
//
1667
extern void ResetChannelCharset(MimeObject * obj);
1668
if ((obj->options) &&
1669
(obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
1670
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting ||
1671
obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs ||
1672
obj->options->format_out == nsMimeOutput::nsMimeMessagePrintOutput))
1673
ResetChannelCharset(obj);
1674
1675
status = obj->options->output_init_fn(content_type, charset, name,
1676
x_mac_type, x_mac_creator,
1677
obj->options->stream_closure);
1678
PR_FREEIF(name);
1679
PR_FREEIF(x_mac_type);
1680
PR_FREEIF(x_mac_creator);
1681
obj->options->state->first_data_written_p = true;
1682
return status;
1683
}
1684
return 0;
1685
}
1686
1687
char *mime_get_base_url(const char *url) {
1688
if (!url) return nullptr;
1689
1690
const char *s = strrchr(url, '?');
1691
if (s && !strncmp(s, "?type=application/x-message-display",
1692
sizeof("?type=application/x-message-display") - 1)) {
1693
const char *nextTerm = strchr(s, '&');
1694
s = nextTerm ? nextTerm : s + strlen(s) - 1;
1695
}
1696
// we need to keep the ?number part of the url, or we won't know
1697
// which local message the part belongs to.
1698
if (s && *s && *(s + 1) &&
1699
!strncmp(s + 1, "number=", sizeof("number=") - 1)) {
1700
const char *nextTerm = strchr(++s, '&');
1701
s = nextTerm ? nextTerm : s + strlen(s) - 1;
1702
}
1703
char *result = (char *)PR_MALLOC(strlen(url) + 1);
1704
NS_ASSERTION(result, "out of memory");
1705
if (!result) return nullptr;
1706
1707
memcpy(result, url, s - url);
1708
result[s - url] = 0;
1709
return result;
1710
}