1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* GMime
3 * Copyright (C) 2000-2020 Jeffrey Stedfast
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA.
19 */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <ctype.h>
28 #include <locale.h>
29
30 #include "gmime-message.h"
31 #include "gmime-multipart.h"
32 #include "gmime-multipart-signed.h"
33 #include "gmime-multipart-encrypted.h"
34 #include "gmime-part.h"
35 #include "gmime-utils.h"
36 #include "gmime-common.h"
37 #include "gmime-stream-mem.h"
38 #include "gmime-stream-filter.h"
39 #include "gmime-parse-utils.h"
40 #include "gmime-internal.h"
41 #include "gmime-events.h"
42
43
44 /**
45 * SECTION: gmime-message
46 * @title: GMimeMessage
47 * @short_description: Messages
48 * @see_also:
49 *
50 * A #GMimeMessage represents an rfc822 message.
51 **/
52
53 static void g_mime_message_class_init (GMimeMessageClass *klass);
54 static void g_mime_message_init (GMimeMessage *message, GMimeMessageClass *klass);
55 static void g_mime_message_finalize (GObject *object);
56
57 /* GMimeObject class methods */
58 static void message_header_added (GMimeObject *object, GMimeHeader *header);
59 static void message_header_changed (GMimeObject *object, GMimeHeader *header);
60 static void message_header_removed (GMimeObject *object, GMimeHeader *header);
61 static void message_headers_cleared (GMimeObject *object);
62
63 static char *message_get_headers (GMimeObject *object, GMimeFormatOptions *options);
64 static ssize_t message_write_to_stream (GMimeObject *object, GMimeFormatOptions *options,
65 gboolean content_only, GMimeStream *stream);
66 static void message_encode (GMimeObject *object, GMimeEncodingConstraint constraint);
67
68 static void sender_changed (InternetAddressList *list, gpointer args, GMimeMessage *message);
69 static void from_changed (InternetAddressList *list, gpointer args, GMimeMessage *message);
70 static void reply_to_changed (InternetAddressList *list, gpointer args, GMimeMessage *message);
71 static void to_list_changed (InternetAddressList *list, gpointer args, GMimeMessage *message);
72 static void cc_list_changed (InternetAddressList *list, gpointer args, GMimeMessage *message);
73 static void bcc_list_changed (InternetAddressList *list, gpointer args, GMimeMessage *message);
74
75 static GMimeObjectClass *parent_class = NULL;
76
77 static struct {
78 const char *name;
79 GMimeEventCallback changed_cb;
80 } address_types[] = {
81 { "Sender", (GMimeEventCallback) sender_changed },
82 { "From", (GMimeEventCallback) from_changed },
83 { "Reply-To", (GMimeEventCallback) reply_to_changed },
84 { "To", (GMimeEventCallback) to_list_changed },
85 { "Cc", (GMimeEventCallback) cc_list_changed },
86 { "Bcc", (GMimeEventCallback) bcc_list_changed },
87 };
88
89 #define N_ADDRESS_TYPES G_N_ELEMENTS (address_types)
90
91 static char *rfc822_headers[] = {
92 "Return-Path",
93 "Received",
94 "Date",
95 "From",
96 "Reply-To",
97 "Subject",
98 "Sender",
99 "To",
100 "Cc",
101 };
102
103
104 GType
g_mime_message_get_type(void)105 g_mime_message_get_type (void)
106 {
107 static GType type = 0;
108
109 if (!type) {
110 static const GTypeInfo info = {
111 sizeof (GMimeMessageClass),
112 NULL, /* base_class_init */
113 NULL, /* base_class_finalize */
114 (GClassInitFunc) g_mime_message_class_init,
115 NULL, /* class_finalize */
116 NULL, /* class_data */
117 sizeof (GMimeMessage),
118 0, /* n_preallocs */
119 (GInstanceInitFunc) g_mime_message_init,
120 };
121
122 type = g_type_register_static (GMIME_TYPE_OBJECT, "GMimeMessage", &info, 0);
123 }
124
125 return type;
126 }
127
128
129 static void
g_mime_message_class_init(GMimeMessageClass * klass)130 g_mime_message_class_init (GMimeMessageClass *klass)
131 {
132 GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
133 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
134
135 parent_class = g_type_class_ref (GMIME_TYPE_OBJECT);
136
137 gobject_class->finalize = g_mime_message_finalize;
138
139 object_class->header_added = message_header_added;
140 object_class->header_changed = message_header_changed;
141 object_class->header_removed = message_header_removed;
142 object_class->headers_cleared = message_headers_cleared;
143 object_class->get_headers = message_get_headers;
144 object_class->write_to_stream = message_write_to_stream;
145 object_class->encode = message_encode;
146 }
147
148 static void
connect_changed_event(GMimeMessage * message,GMimeAddressType type)149 connect_changed_event (GMimeMessage *message, GMimeAddressType type)
150 {
151 InternetAddressList *addrlist;
152
153 addrlist = message->addrlists[type];
154
155 g_mime_event_add (addrlist->changed, address_types[type].changed_cb, message);
156 }
157
158 static void
disconnect_changed_event(GMimeMessage * message,GMimeAddressType type)159 disconnect_changed_event (GMimeMessage *message, GMimeAddressType type)
160 {
161 InternetAddressList *addrlist;
162
163 addrlist = message->addrlists[type];
164
165 g_mime_event_remove (addrlist->changed, address_types[type].changed_cb, message);
166 }
167
168 static void
block_changed_event(GMimeMessage * message,GMimeAddressType type)169 block_changed_event (GMimeMessage *message, GMimeAddressType type)
170 {
171 InternetAddressList *addrlist;
172
173 addrlist = message->addrlists[type];
174
175 g_mime_event_block (addrlist->changed, address_types[type].changed_cb, message);
176 }
177
178 static void
unblock_changed_event(GMimeMessage * message,GMimeAddressType type)179 unblock_changed_event (GMimeMessage *message, GMimeAddressType type)
180 {
181 InternetAddressList *addrlist;
182
183 addrlist = message->addrlists[type];
184
185 g_mime_event_unblock (addrlist->changed, address_types[type].changed_cb, message);
186 }
187
188 static void
g_mime_message_init(GMimeMessage * message,GMimeMessageClass * klass)189 g_mime_message_init (GMimeMessage *message, GMimeMessageClass *klass)
190 {
191 guint i;
192
193 message->addrlists = g_new (InternetAddressList *, N_ADDRESS_TYPES);
194 ((GMimeObject *) message)->ensure_newline = TRUE;
195 message->message_id = NULL;
196 message->mime_part = NULL;
197 message->subject = NULL;
198 message->date = NULL;
199
200 /* initialize recipient lists */
201 for (i = 0; i < N_ADDRESS_TYPES; i++) {
202 message->addrlists[i] = internet_address_list_new ();
203 connect_changed_event (message, i);
204 }
205 }
206
207 static void
g_mime_message_finalize(GObject * object)208 g_mime_message_finalize (GObject *object)
209 {
210 GMimeMessage *message = (GMimeMessage *) object;
211 guint i;
212
213 /* disconnect changed handlers */
214 for (i = 0; i < N_ADDRESS_TYPES; i++) {
215 disconnect_changed_event (message, i);
216 g_object_unref (message->addrlists[i]);
217 }
218
219 g_free (message->addrlists);
220 g_free (message->message_id);
221 g_free (message->subject);
222 g_free (message->marker);
223
224 if (message->date)
225 g_date_time_unref (message->date);
226
227 /* unref child mime part */
228 if (message->mime_part)
229 g_object_unref (message->mime_part);
230
231 G_OBJECT_CLASS (parent_class)->finalize (object);
232 }
233
234
235 enum {
236 HEADER_SENDER,
237 HEADER_FROM,
238 HEADER_REPLY_TO,
239 HEADER_TO,
240 HEADER_CC,
241 HEADER_BCC,
242 HEADER_SUBJECT,
243 HEADER_DATE,
244 HEADER_MESSAGE_ID,
245 HEADER_MIME_VERSION,
246 HEADER_UNKNOWN
247 };
248
249 static const char *message_headers[] = {
250 "Sender",
251 "From",
252 "Reply-To",
253 "To",
254 "Cc",
255 "Bcc",
256 "Subject",
257 "Date",
258 "Message-Id",
259 "MIME-Version"
260 };
261
262 static void
message_update_addresses(GMimeMessage * message,GMimeParserOptions * options,GMimeAddressType type)263 message_update_addresses (GMimeMessage *message, GMimeParserOptions *options, GMimeAddressType type)
264 {
265 GMimeHeaderList *headers = ((GMimeObject *) message)->headers;
266 InternetAddressList *addrlist, *list;
267 const char *name, *value;
268 GMimeHeader *header;
269 int count, i;
270
271 block_changed_event (message, type);
272
273 addrlist = message->addrlists[type];
274
275 internet_address_list_clear (addrlist);
276
277 count = g_mime_header_list_get_count (headers);
278 for (i = 0; i < count; i++) {
279 header = g_mime_header_list_get_header_at (headers, i);
280 name = g_mime_header_get_name (header);
281
282 if (g_ascii_strcasecmp (address_types[type].name, name) != 0)
283 continue;
284
285 if ((value = g_mime_header_get_raw_value (header))) {
286 if ((list = _internet_address_list_parse (options, value, header->offset))) {
287 internet_address_list_append (addrlist, list);
288 g_object_unref (list);
289 }
290 }
291 }
292
293 unblock_changed_event (message, type);
294 }
295
296 static void
process_header(GMimeObject * object,GMimeHeader * header)297 process_header (GMimeObject *object, GMimeHeader *header)
298 {
299 GMimeParserOptions *options = _g_mime_header_list_get_options (object->headers);
300 GMimeMessage *message = (GMimeMessage *) object;
301 const char *name, *value;
302 guint i;
303
304 name = g_mime_header_get_name (header);
305
306 for (i = 0; i < G_N_ELEMENTS (message_headers); i++) {
307 if (!g_ascii_strcasecmp (message_headers[i], name))
308 break;
309 }
310
311 switch (i) {
312 case HEADER_SENDER:
313 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_SENDER);
314 break;
315 case HEADER_FROM:
316 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_FROM);
317 break;
318 case HEADER_REPLY_TO:
319 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_REPLY_TO);
320 break;
321 case HEADER_TO:
322 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_TO);
323 break;
324 case HEADER_CC:
325 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_CC);
326 break;
327 case HEADER_BCC:
328 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_BCC);
329 break;
330 case HEADER_SUBJECT:
331 g_free (message->subject);
332
333 if ((value = g_mime_header_get_value (header)))
334 message->subject = g_strdup (value);
335 else
336 message->subject = NULL;
337 break;
338 case HEADER_DATE:
339 if ((value = g_mime_header_get_value (header))) {
340 if (message->date)
341 g_date_time_unref (message->date);
342
343 message->date = g_mime_utils_header_decode_date (value);
344 }
345 break;
346 case HEADER_MESSAGE_ID:
347 g_free (message->message_id);
348
349 if ((value = g_mime_header_get_value (header)))
350 message->message_id = g_mime_utils_decode_message_id (value);
351 else
352 message->message_id = NULL;
353 break;
354 }
355 }
356
357 static void
message_header_added(GMimeObject * object,GMimeHeader * header)358 message_header_added (GMimeObject *object, GMimeHeader *header)
359 {
360 process_header (object, header);
361
362 GMIME_OBJECT_CLASS (parent_class)->header_added (object, header);
363 }
364
365 static void
message_header_changed(GMimeObject * object,GMimeHeader * header)366 message_header_changed (GMimeObject *object, GMimeHeader *header)
367 {
368 process_header (object, header);
369
370 GMIME_OBJECT_CLASS (parent_class)->header_changed (object, header);
371 }
372
373 static void
message_header_removed(GMimeObject * object,GMimeHeader * header)374 message_header_removed (GMimeObject *object, GMimeHeader *header)
375 {
376 GMimeParserOptions *options = _g_mime_header_list_get_options (object->headers);
377 GMimeMessage *message = (GMimeMessage *) object;
378 const char *name;
379 guint i;
380
381 name = g_mime_header_get_name (header);
382
383 for (i = 0; i < G_N_ELEMENTS (message_headers); i++) {
384 if (!g_ascii_strcasecmp (message_headers[i], name))
385 break;
386 }
387
388 switch (i) {
389 case HEADER_SENDER:
390 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_SENDER);
391 break;
392 case HEADER_FROM:
393 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_FROM);
394 break;
395 case HEADER_REPLY_TO:
396 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_REPLY_TO);
397 break;
398 case HEADER_TO:
399 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_TO);
400 break;
401 case HEADER_CC:
402 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_CC);
403 break;
404 case HEADER_BCC:
405 message_update_addresses (message, options, GMIME_ADDRESS_TYPE_BCC);
406 break;
407 case HEADER_SUBJECT:
408 g_free (message->subject);
409 message->subject = NULL;
410 break;
411 case HEADER_DATE:
412 if (message->date) {
413 g_date_time_unref (message->date);
414 message->date = NULL;
415 }
416 break;
417 case HEADER_MESSAGE_ID:
418 g_free (message->message_id);
419 message->message_id = NULL;
420 break;
421 }
422
423 GMIME_OBJECT_CLASS (parent_class)->header_removed (object, header);
424 }
425
426 static void
message_headers_cleared(GMimeObject * object)427 message_headers_cleared (GMimeObject *object)
428 {
429 GMimeMessage *message = (GMimeMessage *) object;
430 guint i;
431
432 for (i = 0; i < N_ADDRESS_TYPES; i++) {
433 block_changed_event (message, i);
434 internet_address_list_clear (message->addrlists[i]);
435 unblock_changed_event (message, i);
436 }
437
438 g_free (message->message_id);
439 message->message_id = NULL;
440 g_free (message->subject);
441 message->subject = NULL;
442
443 if (message->date) {
444 g_date_time_unref (message->date);
445 message->date = NULL;
446 }
447
448 GMIME_OBJECT_CLASS (parent_class)->headers_cleared (object);
449 }
450
451
452 static ssize_t
write_headers_to_stream(GMimeObject * object,GMimeFormatOptions * options,GMimeStream * stream)453 write_headers_to_stream (GMimeObject *object, GMimeFormatOptions *options, GMimeStream *stream)
454 {
455 GMimeMessage *message = (GMimeMessage *) object;
456 GMimeObject *mime_part = message->mime_part;
457
458 if (mime_part != NULL) {
459 int body_count = g_mime_header_list_get_count (mime_part->headers);
460 int count = g_mime_header_list_get_count (object->headers);
461 GMimeHeader *header, *body_header;
462 ssize_t nwritten, total = 0;
463 gint64 body_offset, offset;
464 GMimeStream *filtered;
465 GMimeFilter *filter;
466 int body_index = 0;
467 int index = 0;
468
469 filtered = g_mime_stream_filter_new (stream);
470 filter = g_mime_format_options_create_newline_filter (options, FALSE);
471 g_mime_stream_filter_add ((GMimeStreamFilter *) filtered, filter);
472 g_object_unref (filter);
473
474 while (index < count && body_index < body_count) {
475 body_header = g_mime_header_list_get_header_at (mime_part->headers, body_index);
476 if ((body_offset = g_mime_header_get_offset (body_header)) < 0)
477 break;
478
479 header = g_mime_header_list_get_header_at (object->headers, index);
480 offset = g_mime_header_get_offset (header);
481
482 if (offset < body_offset) {
483 if (!g_mime_format_options_is_hidden_header (options, header->name)) {
484 if ((nwritten = g_mime_header_write_to_stream (header, options, filtered)) == -1) {
485 g_object_unref (filtered);
486 return -1;
487 }
488
489 total += nwritten;
490 }
491
492 index++;
493 } else {
494 if (!g_mime_format_options_is_hidden_header (options, header->name)) {
495 if ((nwritten = g_mime_header_write_to_stream (body_header, options, filtered)) == -1) {
496 g_object_unref (filtered);
497 return -1;
498 }
499
500 total += nwritten;
501 }
502
503 body_index++;
504 }
505 }
506
507 while (index < count) {
508 header = g_mime_header_list_get_header_at (object->headers, index);
509
510 if (!g_mime_format_options_is_hidden_header (options, header->name)) {
511 if ((nwritten = g_mime_header_write_to_stream (header, options, filtered)) == -1) {
512 g_object_unref (filtered);
513 return -1;
514 }
515
516 total += nwritten;
517 }
518
519 index++;
520 }
521
522 while (body_index < body_count) {
523 header = g_mime_header_list_get_header_at (mime_part->headers, body_index);
524
525 if (!g_mime_format_options_is_hidden_header (options, header->name)) {
526 if ((nwritten = g_mime_header_write_to_stream (header, options, filtered)) == -1) {
527 g_object_unref (filtered);
528 return -1;
529 }
530
531 total += nwritten;
532 }
533
534 body_index++;
535 }
536
537 g_object_unref (filtered);
538
539 return total;
540 }
541
542 return g_mime_header_list_write_to_stream (object->headers, options, stream);
543 }
544
545
546 static char *
message_get_headers(GMimeObject * object,GMimeFormatOptions * options)547 message_get_headers (GMimeObject *object, GMimeFormatOptions *options)
548 {
549 GMimeStream *stream;
550 GByteArray *ba;
551 char *str;
552
553 ba = g_byte_array_new ();
554 stream = g_mime_stream_mem_new ();
555 g_mime_stream_mem_set_byte_array ((GMimeStreamMem *) stream, ba);
556 write_headers_to_stream (object, options, stream);
557 g_object_unref (stream);
558 g_byte_array_append (ba, (unsigned char *) "", 1);
559 str = (char *) ba->data;
560 g_byte_array_free (ba, FALSE);
561
562 return str;
563 }
564
565 static ssize_t
message_write_to_stream(GMimeObject * object,GMimeFormatOptions * options,gboolean content_only,GMimeStream * stream)566 message_write_to_stream (GMimeObject *object, GMimeFormatOptions *options, gboolean content_only, GMimeStream *stream)
567 {
568 GMimeMessage *message = (GMimeMessage *) object;
569 GMimeObject *mime_part = message->mime_part;
570 ssize_t nwritten, total = 0;
571 const char *newline;
572
573 if (!content_only) {
574 if ((nwritten = write_headers_to_stream (object, options, stream)) == -1)
575 return -1;
576
577 total += nwritten;
578
579 newline = g_mime_format_options_get_newline (options);
580 if ((nwritten = g_mime_stream_write_string (stream, newline)) == -1)
581 return -1;
582
583 total += nwritten;
584 }
585
586 if (mime_part) {
587 GMimeObjectClass *klass = GMIME_OBJECT_GET_CLASS (mime_part);
588
589 mime_part->ensure_newline = ((GMimeObject *) message)->ensure_newline;
590 nwritten = klass->write_to_stream (mime_part, options, TRUE, stream);
591 mime_part->ensure_newline = FALSE;
592
593 if (nwritten == -1)
594 return -1;
595
596 total += nwritten;
597 }
598
599 return total;
600 }
601
602 static void
message_encode(GMimeObject * object,GMimeEncodingConstraint constraint)603 message_encode (GMimeObject *object, GMimeEncodingConstraint constraint)
604 {
605 GMimeMessage *message = (GMimeMessage *) object;
606
607 if (message->mime_part != NULL)
608 g_mime_object_encode (message->mime_part, constraint);
609 }
610
611
612
613 /**
614 * g_mime_message_new:
615 * @pretty_headers: make pretty headers
616 *
617 * If @pretty_headers is %TRUE, then the standard rfc822 headers are
618 * initialized so as to put headers in a nice friendly order. This is
619 * strictly a cosmetic thing, so if you are unsure, it is safe to say
620 * no (%FALSE).
621 *
622 * Returns: an empty #GMimeMessage object.
623 **/
624 GMimeMessage *
g_mime_message_new(gboolean pretty_headers)625 g_mime_message_new (gboolean pretty_headers)
626 {
627 GMimeHeaderList *headers;
628 GMimeMessage *message;
629 guint i;
630
631 message = g_object_new (GMIME_TYPE_MESSAGE, NULL);
632
633 if (pretty_headers) {
634 /* Populate with the "standard" rfc822 headers so we can have a standard order */
635 headers = ((GMimeObject *) message)->headers;
636
637 _g_mime_object_block_header_list_changed ((GMimeObject *) message);
638 for (i = 0; i < G_N_ELEMENTS (rfc822_headers); i++)
639 g_mime_header_list_set (headers, rfc822_headers[i], NULL, NULL);
640 _g_mime_object_unblock_header_list_changed ((GMimeObject *) message);
641 }
642
643 return message;
644 }
645
646
647 /**
648 * g_mime_message_get_sender:
649 * @message: A #GMimeMessage
650 *
651 * Gets the parsed list of addresses in the Sender header.
652 *
653 * Returns: (transfer none): the parsed list of addresses in the Sender header.
654 **/
655 InternetAddressList *
g_mime_message_get_sender(GMimeMessage * message)656 g_mime_message_get_sender (GMimeMessage *message)
657 {
658 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
659
660 return message->addrlists[GMIME_ADDRESS_TYPE_SENDER];
661 }
662
663
664 /**
665 * g_mime_message_get_from:
666 * @message: A #GMimeMessage
667 *
668 * Gets the parsed list of addresses in the From header.
669 *
670 * Returns: (transfer none): the parsed list of addresses in the From header.
671 **/
672 InternetAddressList *
g_mime_message_get_from(GMimeMessage * message)673 g_mime_message_get_from (GMimeMessage *message)
674 {
675 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
676
677 return message->addrlists[GMIME_ADDRESS_TYPE_FROM];
678 }
679
680
681 /**
682 * g_mime_message_get_reply_to:
683 * @message: A #GMimeMessage
684 *
685 * Gets the parsed list of addresses in the Reply-To header.
686 *
687 * Returns: (transfer none): the parsed list of addresses in the Reply-To header.
688 **/
689 InternetAddressList *
g_mime_message_get_reply_to(GMimeMessage * message)690 g_mime_message_get_reply_to (GMimeMessage *message)
691 {
692 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
693
694 return message->addrlists[GMIME_ADDRESS_TYPE_REPLY_TO];
695 }
696
697
698 /**
699 * g_mime_message_get_to:
700 * @message: A #GMimeMessage
701 *
702 * Gets combined list of parsed addresses in the To header(s).
703 *
704 * Returns: (transfer none): the parsed list of addresses in the To header(s).
705 **/
706 InternetAddressList *
g_mime_message_get_to(GMimeMessage * message)707 g_mime_message_get_to (GMimeMessage *message)
708 {
709 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
710
711 return message->addrlists[GMIME_ADDRESS_TYPE_TO];
712 }
713
714
715 /**
716 * g_mime_message_get_cc:
717 * @message: A #GMimeMessage
718 *
719 * Gets combined list of parsed addresses in the Cc header(s).
720 *
721 * Returns: (transfer none): the parsed list of addresses in the Cc header(s).
722 **/
723 InternetAddressList *
g_mime_message_get_cc(GMimeMessage * message)724 g_mime_message_get_cc (GMimeMessage *message)
725 {
726 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
727
728 return message->addrlists[GMIME_ADDRESS_TYPE_CC];
729 }
730
731
732 /**
733 * g_mime_message_get_bcc:
734 * @message: A #GMimeMessage
735 *
736 * Gets combined list of parsed addresses in the Bcc header(s).
737 *
738 * Returns: (transfer none): the parsed list of addresses in the Bcc header(s).
739 **/
740 InternetAddressList *
g_mime_message_get_bcc(GMimeMessage * message)741 g_mime_message_get_bcc (GMimeMessage *message)
742 {
743 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
744
745 return message->addrlists[GMIME_ADDRESS_TYPE_BCC];
746 }
747
748
749 static void
sync_internet_address_list(InternetAddressList * list,GMimeMessage * message,const char * name)750 sync_internet_address_list (InternetAddressList *list, GMimeMessage *message, const char *name)
751 {
752 GMimeObject *object = (GMimeObject *) message;
753 GString *str;
754 guint n;
755
756 str = g_string_new (name);
757 g_string_append_c (str, ':');
758 n = str->len;
759
760 g_string_append_c (str, ' ');
761 internet_address_list_encode (list, NULL, str);
762
763 _g_mime_object_block_header_list_changed (object);
764 _g_mime_header_list_set (object->headers, name, str->str + n);
765 _g_mime_object_unblock_header_list_changed (object);
766
767 g_string_free (str, TRUE);
768 }
769
770 static void
sync_address_header(GMimeMessage * message,GMimeAddressType type)771 sync_address_header (GMimeMessage *message, GMimeAddressType type)
772 {
773 InternetAddressList *list = message->addrlists[type];
774 const char *name = address_types[type].name;
775
776 sync_internet_address_list (list, message, name);
777 }
778
779 static void
sender_changed(InternetAddressList * list,gpointer args,GMimeMessage * message)780 sender_changed (InternetAddressList *list, gpointer args, GMimeMessage *message)
781 {
782 sync_address_header (message, GMIME_ADDRESS_TYPE_SENDER);
783 }
784
785 static void
from_changed(InternetAddressList * list,gpointer args,GMimeMessage * message)786 from_changed (InternetAddressList *list, gpointer args, GMimeMessage *message)
787 {
788 sync_address_header (message, GMIME_ADDRESS_TYPE_FROM);
789 }
790
791 static void
reply_to_changed(InternetAddressList * list,gpointer args,GMimeMessage * message)792 reply_to_changed (InternetAddressList *list, gpointer args, GMimeMessage *message)
793 {
794 sync_address_header (message, GMIME_ADDRESS_TYPE_REPLY_TO);
795 }
796
797 static void
to_list_changed(InternetAddressList * list,gpointer args,GMimeMessage * message)798 to_list_changed (InternetAddressList *list, gpointer args, GMimeMessage *message)
799 {
800 sync_address_header (message, GMIME_ADDRESS_TYPE_TO);
801 }
802
803 static void
cc_list_changed(InternetAddressList * list,gpointer args,GMimeMessage * message)804 cc_list_changed (InternetAddressList *list, gpointer args, GMimeMessage *message)
805 {
806 sync_address_header (message, GMIME_ADDRESS_TYPE_CC);
807 }
808
809 static void
bcc_list_changed(InternetAddressList * list,gpointer args,GMimeMessage * message)810 bcc_list_changed (InternetAddressList *list, gpointer args, GMimeMessage *message)
811 {
812 sync_address_header (message, GMIME_ADDRESS_TYPE_BCC);
813 }
814
815
816 /**
817 * g_mime_message_add_mailbox:
818 * @message: A #GMimeMessage
819 * @type: A #GMimeAddressType
820 * @name: The name of the mailbox (or %NULL)
821 * @addr: The address of the mailbox
822 *
823 * Add a mailbox of a chosen type to the MIME message.
824 *
825 * Note: The @name (and @addr) strings should be in UTF-8.
826 **/
827 void
g_mime_message_add_mailbox(GMimeMessage * message,GMimeAddressType type,const char * name,const char * addr)828 g_mime_message_add_mailbox (GMimeMessage *message, GMimeAddressType type, const char *name, const char *addr)
829 {
830 InternetAddressList *addrlist;
831 InternetAddress *ia;
832
833 g_return_if_fail (GMIME_IS_MESSAGE (message));
834 g_return_if_fail (type < N_ADDRESS_TYPES);
835 g_return_if_fail (addr != NULL);
836
837 addrlist = message->addrlists[type];
838 ia = internet_address_mailbox_new (name, addr);
839 internet_address_list_add (addrlist, ia);
840 g_object_unref (ia);
841 }
842
843
844 /**
845 * g_mime_message_get_addresses:
846 * @message: A #GMimeMessage
847 * @type: A #GMimeAddressType
848 *
849 * Gets a list of addresses of the specified @type from the @message.
850 *
851 * Returns: (transfer none): a list of addresses of the specified
852 * @type from the @message.
853 **/
854 InternetAddressList *
g_mime_message_get_addresses(GMimeMessage * message,GMimeAddressType type)855 g_mime_message_get_addresses (GMimeMessage *message, GMimeAddressType type)
856 {
857 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
858 g_return_val_if_fail (type < N_ADDRESS_TYPES, NULL);
859
860 return message->addrlists[type];
861 }
862
863
864 /**
865 * g_mime_message_get_all_recipients:
866 * @message: A #GMimeMessage
867 *
868 * Gets the complete list of recipients for @message.
869 *
870 * Returns: (transfer full): a newly allocated #InternetAddressList
871 * containing all recipients of the message or %NULL if no recipients
872 * are set.
873 **/
874 InternetAddressList *
g_mime_message_get_all_recipients(GMimeMessage * message)875 g_mime_message_get_all_recipients (GMimeMessage *message)
876 {
877 InternetAddressList *recipients, *list = NULL;
878 guint i;
879
880 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
881
882 for (i = GMIME_ADDRESS_TYPE_TO; i <= GMIME_ADDRESS_TYPE_BCC; i++) {
883 recipients = message->addrlists[i];
884
885 if (internet_address_list_length (recipients) == 0)
886 continue;
887
888 if (list == NULL)
889 list = internet_address_list_new ();
890
891 internet_address_list_append (list, recipients);
892 }
893
894 return list;
895 }
896
897
898 /**
899 * g_mime_message_set_subject:
900 * @message: A #GMimeMessage
901 * @subject: Subject string
902 * @charset: The charset to use for encoding the subject or %NULL to use the default
903 *
904 * Set the subject of a @message.
905 *
906 * Note: The @subject string should be in UTF-8.
907 **/
908 void
g_mime_message_set_subject(GMimeMessage * message,const char * subject,const char * charset)909 g_mime_message_set_subject (GMimeMessage *message, const char *subject, const char *charset)
910 {
911 g_return_if_fail (GMIME_IS_MESSAGE (message));
912 g_return_if_fail (subject != NULL);
913
914 g_mime_object_set_header ((GMimeObject *) message, "Subject", subject, charset);
915 }
916
917
918 /**
919 * g_mime_message_get_subject:
920 * @message: A #GMimeMessage
921 *
922 * Gets the subject of the @message.
923 *
924 * Returns: the subject of the @message in a form suitable for display
925 * or %NULL if no subject is set. If not %NULL, the returned string
926 * will be in UTF-8.
927 **/
928 const char *
g_mime_message_get_subject(GMimeMessage * message)929 g_mime_message_get_subject (GMimeMessage *message)
930 {
931 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
932
933 return message->subject;
934 }
935
936
937 /**
938 * g_mime_message_set_date:
939 * @message: A #GMimeMessage
940 * @date: a date to be used in the Date header
941 *
942 * Sets the Date header on a MIME Message.
943 **/
944 void
g_mime_message_set_date(GMimeMessage * message,GDateTime * date)945 g_mime_message_set_date (GMimeMessage *message, GDateTime *date)
946 {
947 char *str;
948
949 g_return_if_fail (GMIME_IS_MESSAGE (message));
950
951 str = g_mime_utils_header_format_date (date);
952 g_mime_object_set_header ((GMimeObject *) message, "Date", str, NULL);
953 g_free (str);
954 }
955
956
957 /**
958 * g_mime_message_get_date:
959 * @message: A #GMimeMessage
960 *
961 * Gets the parsed date and time value from the Date header.
962 *
963 * Returns: a #GDateTime on success or %NULL if the date could not be parsed.
964 **/
965 GDateTime *
g_mime_message_get_date(GMimeMessage * message)966 g_mime_message_get_date (GMimeMessage *message)
967 {
968 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
969
970 return message->date;
971 }
972
973
974 /**
975 * g_mime_message_set_message_id:
976 * @message: A #GMimeMessage
977 * @message_id: message-id (addr-spec portion)
978 *
979 * Set the Message-Id on a message.
980 **/
981 void
g_mime_message_set_message_id(GMimeMessage * message,const char * message_id)982 g_mime_message_set_message_id (GMimeMessage *message, const char *message_id)
983 {
984 char *msgid;
985
986 g_return_if_fail (GMIME_IS_MESSAGE (message));
987 g_return_if_fail (message_id != NULL);
988
989 msgid = g_strdup_printf ("<%s>", message_id);
990 g_mime_object_set_header ((GMimeObject *) message, "Message-Id", msgid, NULL);
991 g_free (msgid);
992 }
993
994
995 /**
996 * g_mime_message_get_message_id:
997 * @message: A #GMimeMessage
998 *
999 * Gets the Message-Id header of @message.
1000 *
1001 * Returns: the Message-Id of a message.
1002 **/
1003 const char *
g_mime_message_get_message_id(GMimeMessage * message)1004 g_mime_message_get_message_id (GMimeMessage *message)
1005 {
1006 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
1007
1008 return message->message_id;
1009 }
1010
1011
1012 /**
1013 * g_mime_message_get_mime_part:
1014 * @message: A #GMimeMessage
1015 *
1016 * Gets the toplevel MIME part contained within @message.
1017 *
1018 * Returns: (transfer none): the toplevel MIME part of @message.
1019 **/
1020 GMimeObject *
g_mime_message_get_mime_part(GMimeMessage * message)1021 g_mime_message_get_mime_part (GMimeMessage *message)
1022 {
1023 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
1024
1025 if (message->mime_part == NULL)
1026 return NULL;
1027
1028 return message->mime_part;
1029 }
1030
1031
1032 /**
1033 * g_mime_message_set_mime_part:
1034 * @message: A #GMimeMessage
1035 * @mime_part: The root-level MIME Part
1036 *
1037 * Set the root-level MIME part of the message.
1038 **/
1039 void
g_mime_message_set_mime_part(GMimeMessage * message,GMimeObject * mime_part)1040 g_mime_message_set_mime_part (GMimeMessage *message, GMimeObject *mime_part)
1041 {
1042 g_return_if_fail (GMIME_IS_OBJECT (mime_part));
1043 g_return_if_fail (GMIME_IS_MESSAGE (message));
1044
1045 if (message->mime_part == mime_part)
1046 return;
1047
1048 if (message->mime_part)
1049 g_object_unref (message->mime_part);
1050
1051 if (mime_part) {
1052 GMimeHeaderList *headers = ((GMimeObject *) message)->headers;
1053 GMimeHeader *header;
1054 int i;
1055
1056 if (!g_mime_header_list_contains (headers, "MIME-Version"))
1057 g_mime_header_list_append (headers, "MIME-Version", "1.0", NULL);
1058
1059 for (i = 0; i < g_mime_header_list_get_count (mime_part->headers); i++) {
1060 header = g_mime_header_list_get_header_at (mime_part->headers, i);
1061 _g_mime_header_set_offset (header, -1);
1062 }
1063
1064 g_object_ref (mime_part);
1065 }
1066
1067 message->mime_part = mime_part;
1068 }
1069
1070
1071 /**
1072 * g_mime_message_foreach:
1073 * @message: A #GMimeMessage
1074 * @callback: (scope call): function to call on each of the mime parts
1075 * contained by the mime message
1076 * @user_data: user-supplied callback data
1077 *
1078 * Recursively calls @callback on each of the mime parts in the mime message.
1079 **/
1080 void
g_mime_message_foreach(GMimeMessage * message,GMimeObjectForeachFunc callback,gpointer user_data)1081 g_mime_message_foreach (GMimeMessage *message, GMimeObjectForeachFunc callback, gpointer user_data)
1082 {
1083 g_return_if_fail (GMIME_IS_MESSAGE (message));
1084 g_return_if_fail (callback != NULL);
1085
1086 callback ((GMimeObject *) message, message->mime_part, user_data);
1087
1088 if (GMIME_IS_MULTIPART (message->mime_part))
1089 g_mime_multipart_foreach ((GMimeMultipart *) message->mime_part, callback, user_data);
1090 }
1091
1092 static gboolean
part_is_textual(GMimeObject * mime_part)1093 part_is_textual (GMimeObject *mime_part)
1094 {
1095 GMimeContentType *type;
1096
1097 type = g_mime_object_get_content_type (mime_part);
1098
1099 return g_mime_content_type_is_type (type, "text", "*");
1100 }
1101
1102 static GMimeObject *
multipart_guess_body(GMimeMultipart * multipart)1103 multipart_guess_body (GMimeMultipart *multipart)
1104 {
1105 GMimeContentType *type;
1106 GMimeObject *mime_part;
1107 int count, i;
1108
1109 if (GMIME_IS_MULTIPART_ENCRYPTED (multipart)) {
1110 /* nothing more we can do */
1111 return (GMimeObject *) multipart;
1112 }
1113
1114 type = g_mime_object_get_content_type ((GMimeObject *) multipart);
1115 if (g_mime_content_type_is_type (type, "multipart", "alternative")) {
1116 /* very likely that this is the body - leave it up to
1117 * our caller to decide which format of the body it
1118 * wants to use. */
1119 return (GMimeObject *) multipart;
1120 }
1121
1122 count = g_mime_multipart_get_count (multipart);
1123
1124 if (count >= 1 && GMIME_IS_MULTIPART_SIGNED (multipart)) {
1125 /* if the body is in here, it has to be the first part */
1126 count = 1;
1127 }
1128
1129 for (i = 0; i < count; i++) {
1130 mime_part = g_mime_multipart_get_part (multipart, i);
1131
1132 if (GMIME_IS_MULTIPART (mime_part)) {
1133 if ((mime_part = multipart_guess_body ((GMimeMultipart *) mime_part)))
1134 return mime_part;
1135 } else if (GMIME_IS_PART (mime_part)) {
1136 if (part_is_textual (mime_part))
1137 return mime_part;
1138 }
1139 }
1140
1141 return NULL;
1142 }
1143
1144
1145 /**
1146 * g_mime_message_get_body:
1147 * @message: A #GMimeMessage
1148 *
1149 * Attempts to identify the MIME part containing the body of the
1150 * message.
1151 *
1152 * Returns: (transfer none): a #GMimeObject containing the textual
1153 * content that appears to be the main body of the message.
1154 *
1155 * Note: This function is NOT guaranteed to always work as it
1156 * makes some assumptions that are not necessarily true. It is
1157 * recommended that you traverse the MIME structure yourself.
1158 **/
1159 GMimeObject *
g_mime_message_get_body(GMimeMessage * message)1160 g_mime_message_get_body (GMimeMessage *message)
1161 {
1162 GMimeObject *mime_part;
1163
1164 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
1165
1166 if (!(mime_part = message->mime_part))
1167 return NULL;
1168
1169 if (GMIME_IS_MULTIPART (mime_part))
1170 return multipart_guess_body ((GMimeMultipart *) mime_part);
1171 else if (GMIME_IS_PART (mime_part) && part_is_textual (mime_part))
1172 return mime_part;
1173
1174 return NULL;
1175 }
1176
1177
1178 /**
1179 * g_mime_message_get_autocrypt_header:
1180 * @message: a #GMimeMessage object.
1181 * @now: a #GDateTime object, or %NULL
1182 *
1183 * Creates a new #GMimeAutocryptHeader based on the relevant Autocrypt
1184 * header associated with the sender of an e-mail message.
1185 *
1186 * If the message has no sender in the From: field, or has more than
1187 * one sender, then this function will return %NULL. Autocrypt should
1188 * ignore the message entirely.
1189 *
1190 * If there is one sender, but no single Autocrypt header is found
1191 * that matches that e-mail address, a #GMimeAutocryptHeader will be
1192 * returned for the sender, but it will be incomplete (see
1193 * #g_mime_autocrypt_header_is_complete).
1194 *
1195 * Note that the following types of Autocrypt headers will not be
1196 * returned by this function:
1197 *
1198 * - headers that do not match an address in "From:"
1199 * - unparseable headers
1200 * - headers with unknown critical attributes
1201 * - duplicate valid headers for the sender's address
1202 *
1203 * The returned Autocrypt header will have its effective_date set to
1204 * the earliest of either:
1205 *
1206 * - the Date: header of the message or
1207 * - @now (or the current time, if @now is %NULL)
1208 *
1209 * Returns: (transfer full): a new #GMimeAutocryptHeader object,
1210 * or %NULL if the message should be ignored for purposes of
1211 * Autocrypt.
1212 **/
1213 GMimeAutocryptHeader *
g_mime_message_get_autocrypt_header(GMimeMessage * message,GDateTime * now)1214 g_mime_message_get_autocrypt_header (GMimeMessage *message, GDateTime *now)
1215 {
1216 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
1217
1218 GMimeAutocryptHeaderList *retlist = NULL;
1219 GMimeAutocryptHeader *ret = NULL;
1220 GDateTime *newnow = NULL;
1221 GDateTime *effective_date = NULL;
1222 if (now == NULL)
1223 now = newnow = g_date_time_new_now_utc ();
1224 effective_date = now;
1225 if (message->date && g_date_time_compare (message->date, now) < 0)
1226 effective_date = message->date;
1227 retlist = g_mime_object_get_autocrypt_headers (GMIME_OBJECT (message),
1228 effective_date,
1229 "autocrypt",
1230 message->addrlists[GMIME_ADDRESS_TYPE_FROM],
1231 TRUE);
1232 if (newnow)
1233 g_date_time_unref (newnow);
1234 if (retlist) {
1235 if (g_mime_autocrypt_header_list_get_count (retlist) == 1) {
1236 ret = g_mime_autocrypt_header_list_get_header_at (retlist, 0);
1237 g_object_ref (ret);
1238 }
1239 g_object_unref (retlist);
1240 }
1241 return ret;
1242 }
1243
1244
1245 /**
1246 * g_mime_message_get_autocrypt_gossip_headers_from_inner_part:
1247 * @message: a #GMimeMessage object.
1248 * @now: a #GDateTime object, or %NULL
1249 * @inner_part: a #GMimeObject which is the cleartext part of the inner message
1250 *
1251 * Creates a new #GMimeAutocryptHeaderList of relevant headers of the
1252 * given type based on the recipient(s) of an e-mail message.
1253 *
1254 * You must pass the decrypted inner part of the message to this
1255 * function, since Autocrypt-Gossip headers are only stored within the
1256 * encrypted layer.
1257 *
1258 * If you don't already have the decrypted inner part available to
1259 * you, you probably want to use
1260 * #g_mime_message_get_autocrypt_gossip_headers instead.
1261 *
1262 * Each header in the returned list will:
1263 *
1264 * - have a valid address
1265 * - be of the type requested
1266 * - be complete
1267 *
1268 * If no Autocrypt header is found for a recipient, no
1269 * #GMimeAutocryptHeader will be in the list associated with that e-mail address.
1270 *
1271 * Note that the following types of Autocrypt headers will not be
1272 * returned by this function:
1273 *
1274 * - headers of an unrequested type
1275 * - headers that do not match an address in "From:"
1276 * - unparseable headers
1277 * - headers with unknown critical attributes
1278 * - duplicate valid headers for a given address
1279 *
1280 * On error (e.g. if this version of GMime cannot handle the requested
1281 * Autocrypt type, or if a parameter is missing or malformed), returns
1282 * %NULL
1283 *
1284 * The returned Autocrypt headers will have their effective_date set
1285 * to the earliest of either:
1286 *
1287 * - the Date: header of the message or
1288 * - @now (or the current time, if @now is %NULL)
1289 *
1290 * Returns: (transfer full): a new #GMimeAutocryptHeaderList object, or %NULL on error.
1291 **/
1292 GMimeAutocryptHeaderList *
g_mime_message_get_autocrypt_gossip_headers_from_inner_part(GMimeMessage * message,GDateTime * now,GMimeObject * inner_part)1293 g_mime_message_get_autocrypt_gossip_headers_from_inner_part (GMimeMessage *message, GDateTime *now, GMimeObject *inner_part)
1294 {
1295 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
1296 g_return_val_if_fail (GMIME_IS_OBJECT (inner_part), NULL);
1297 InternetAddressList *addresses = g_mime_message_get_all_recipients (message);
1298 GDateTime *newnow = NULL;
1299 GDateTime *effective_date = NULL;
1300 GMimeAutocryptHeaderList *ret = NULL;
1301 if (now == NULL)
1302 now = newnow = g_date_time_new_now_utc ();
1303 effective_date = now;
1304 if (message->date && g_date_time_compare (message->date, now) < 0)
1305 effective_date = message->date;
1306 ret = g_mime_object_get_autocrypt_headers (inner_part,
1307 effective_date,
1308 "autocrypt-gossip",
1309 addresses, FALSE);
1310 g_object_unref (addresses);
1311 if (newnow)
1312 g_date_time_unref (newnow);
1313 return ret;
1314 }
1315
1316
1317 /**
1318 * g_mime_message_get_autocrypt_gossip_headers:
1319 * @message: a #GMimeMessage object, which is expected to be encrypted.
1320 * @now: a #GDateTime object, or %NULL
1321 * @flags: a #GMimeDecryptFlags, to be used during decryption
1322 * @session_key: session key to use or %NULL
1323 * @err: a #GError (can be %NULL)
1324 *
1325 * Creates a new #GMimeAutocryptHeaderList of relevant headers of the
1326 * given type based on the recipient(s) of an e-mail message.
1327 *
1328 * Returns the same object as
1329 * #g_mime_message_get_autocrypt_gossip_headers_with_inner_part , but
1330 * handles decryption and cleanup automatically.
1331 *
1332 * @flags and @session_key are passed through to
1333 * #g_mime_multipart_encrypted_decrypt, as needed.
1334 *
1335 * If the message is not actually an encrypted message, returns %NULL:
1336 * it should be ignored for purposes of evaluating gossip.
1337 *
1338 * If decryption fails, returns %NULL. In this case, an exception
1339 * will be set on @err to provide information about the decryption
1340 * failure.
1341 *
1342 * Returns: (transfer full): a new #GMimeAutocryptHeaderList object,
1343 * or %NULL on error.
1344 **/
g_mime_message_get_autocrypt_gossip_headers(GMimeMessage * message,GDateTime * now,GMimeDecryptFlags flags,const char * session_key,GError ** err)1345 GMimeAutocryptHeaderList *g_mime_message_get_autocrypt_gossip_headers (GMimeMessage *message,
1346 GDateTime *now,
1347 GMimeDecryptFlags flags,
1348 const char *session_key,
1349 GError **err)
1350 {
1351 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
1352 GMimeAutocryptHeaderList *ret = NULL;
1353 GMimeObject *top_level = NULL;
1354 GMimeObject *inner_part = NULL;
1355
1356 top_level = g_mime_message_get_mime_part (message);
1357 if (!GMIME_IS_MULTIPART_ENCRYPTED (top_level))
1358 return NULL;
1359
1360 inner_part = g_mime_multipart_encrypted_decrypt (GMIME_MULTIPART_ENCRYPTED (top_level),
1361 flags,
1362 session_key,
1363 NULL, /* we do not care about decryptresult */
1364 err);
1365 if (inner_part) {
1366 ret = g_mime_message_get_autocrypt_gossip_headers_from_inner_part (message, now, inner_part);
1367 g_object_unref (inner_part);
1368 }
1369
1370 return ret;
1371 }
1372