1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /* camel-mime-message.c : class for a mime_message */
5 * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
6 * Michael Zucchi <notzed@ximian.com>
7 * Jeffrey Stedfast <fejj@ximian.com>
9 * Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
35 #include <gal/util/e-iconv.h>
37 #include <e-util/e-time-utils.h>
39 #include "camel-mime-message.h"
40 #include "camel-multipart.h"
41 #include "camel-stream-mem.h"
42 #include "camel-string-utils.h"
43 #include "camel-url.h"
45 #include "camel-stream-filter.h"
46 #include "camel-stream-null.h"
47 #include "camel-mime-filter-charset.h"
48 #include "camel-mime-filter-bestenc.h"
52 /* these 2 below should be kept in sync */
68 static char *header_names[] = {
69 /* dont include HEADER_UNKNOWN string */
70 "From", "Reply-To", "Subject", "To", "Resent-To", "Cc", "Resent-Cc",
71 "Bcc", "Resent-Bcc", "Date", "Message-Id", NULL
74 static GHashTable *header_name_table;
76 static CamelMimePartClass *parent_class = NULL;
78 static char *recipient_names[] = {
79 "To", "Cc", "Bcc", "Resent-To", "Resent-Cc", "Resent-Bcc", NULL
82 static int write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
83 static void add_header (CamelMedium *medium, const char *header_name, const void *header_value);
84 static void set_header (CamelMedium *medium, const char *header_name, const void *header_value);
85 static void remove_header (CamelMedium *medium, const char *header_name);
86 static int construct_from_parser (CamelMimePart *, CamelMimeParser *);
87 static void unref_recipient (gpointer key, gpointer value, gpointer user_data);
89 /* Returns the class for a CamelMimeMessage */
90 #define CMM_CLASS(so) CAMEL_MIME_MESSAGE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
91 #define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
92 #define CMD_CLASS(so) CAMEL_MEDIUM_CLASS (CAMEL_OBJECT_GET_CLASS(so))
95 camel_mime_message_class_init (CamelMimeMessageClass *camel_mime_message_class)
97 CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (camel_mime_message_class);
98 CamelMimePartClass *camel_mime_part_class = CAMEL_MIME_PART_CLASS (camel_mime_message_class);
99 CamelMediumClass *camel_medium_class = CAMEL_MEDIUM_CLASS (camel_mime_message_class);
102 parent_class = CAMEL_MIME_PART_CLASS(camel_type_get_global_classfuncs (camel_mime_part_get_type ()));
104 header_name_table = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
105 for (i=0;header_names[i];i++)
106 g_hash_table_insert (header_name_table, header_names[i], GINT_TO_POINTER(i+1));
108 /* virtual method overload */
109 camel_data_wrapper_class->write_to_stream = write_to_stream;
111 camel_medium_class->add_header = add_header;
112 camel_medium_class->set_header = set_header;
113 camel_medium_class->remove_header = remove_header;
115 camel_mime_part_class->construct_from_parser = construct_from_parser;
120 camel_mime_message_init (gpointer object, gpointer klass)
122 CamelMimeMessage *mime_message = (CamelMimeMessage *)object;
125 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (object), "message/rfc822");
127 mime_message->recipients = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
128 for (i=0;recipient_names[i];i++) {
129 g_hash_table_insert(mime_message->recipients, recipient_names[i], camel_internet_address_new());
132 mime_message->subject = NULL;
133 mime_message->reply_to = NULL;
134 mime_message->from = NULL;
135 mime_message->date = CAMEL_MESSAGE_DATE_CURRENT;
136 mime_message->date_offset = 0;
137 mime_message->date_received = CAMEL_MESSAGE_DATE_CURRENT;
138 mime_message->date_received_offset = 0;
139 mime_message->message_id = NULL;
143 camel_mime_message_finalize (CamelObject *object)
145 CamelMimeMessage *message = CAMEL_MIME_MESSAGE (object);
147 g_free (message->subject);
149 g_free (message->message_id);
151 if (message->reply_to)
152 camel_object_unref ((CamelObject *)message->reply_to);
155 camel_object_unref ((CamelObject *)message->from);
157 g_hash_table_foreach (message->recipients, unref_recipient, NULL);
158 g_hash_table_destroy (message->recipients);
163 camel_mime_message_get_type (void)
165 static CamelType camel_mime_message_type = CAMEL_INVALID_TYPE;
167 if (camel_mime_message_type == CAMEL_INVALID_TYPE) {
168 camel_mime_message_type = camel_type_register (camel_mime_part_get_type(), "CamelMimeMessage",
169 sizeof (CamelMimeMessage),
170 sizeof (CamelMimeMessageClass),
171 (CamelObjectClassInitFunc) camel_mime_message_class_init,
173 (CamelObjectInitFunc) camel_mime_message_init,
174 (CamelObjectFinalizeFunc) camel_mime_message_finalize);
177 return camel_mime_message_type;
181 unref_recipient (gpointer key, gpointer value, gpointer user_data)
183 camel_object_unref (CAMEL_OBJECT (value));
187 camel_mime_message_new (void)
189 CamelMimeMessage *mime_message;
190 mime_message = CAMEL_MIME_MESSAGE (camel_object_new (CAMEL_MIME_MESSAGE_TYPE));
198 camel_mime_message_set_date (CamelMimeMessage *message, time_t date, int offset)
204 if (date == CAMEL_MESSAGE_DATE_CURRENT) {
209 e_localtime_with_offset(date, &local, &tz);
210 offset = (((tz/60/60) * 100) + (tz/60 % 60));
212 message->date = date;
213 message->date_offset = offset;
215 datestr = header_format_date (date, offset);
216 CAMEL_MEDIUM_CLASS (parent_class)->set_header ((CamelMedium *)message, "Date", datestr);
221 camel_mime_message_get_date (CamelMimeMessage *msg, int *offset)
224 *offset = msg->date_offset;
230 camel_mime_message_get_date_received (CamelMimeMessage *msg, int *offset)
232 if (msg->date_received == CAMEL_MESSAGE_DATE_CURRENT) {
233 const char *received;
235 received = camel_medium_get_header ((CamelMedium *)msg, "received");
237 received = strrchr (received, ';');
239 msg->date_received = header_decode_date (received + 1, &msg->date_received_offset);
243 *offset = msg->date_received_offset;
245 return msg->date_received;
248 /* **** Message-Id: */
251 camel_mime_message_set_message_id (CamelMimeMessage *mime_message, const char *message_id)
255 g_assert (mime_message);
257 g_free (mime_message->message_id);
260 id = g_strstrip (g_strdup (message_id));
262 id = header_msgid_generate ();
265 mime_message->message_id = id;
266 id = g_strdup_printf ("<%s>", mime_message->message_id);
267 CAMEL_MEDIUM_CLASS (parent_class)->set_header (CAMEL_MEDIUM (mime_message), "Message-Id", id);
272 camel_mime_message_get_message_id (CamelMimeMessage *mime_message)
274 g_assert (mime_message);
276 return mime_message->message_id;
282 camel_mime_message_set_reply_to (CamelMimeMessage *msg, const CamelInternetAddress *reply_to)
289 camel_object_unref ((CamelObject *)msg->reply_to);
290 msg->reply_to = NULL;
293 if (reply_to == NULL) {
294 CAMEL_MEDIUM_CLASS (parent_class)->remove_header (CAMEL_MEDIUM (msg), "Reply-To");
298 msg->reply_to = (CamelInternetAddress *)camel_address_new_clone ((CamelAddress *)reply_to);
299 addr = camel_address_encode ((CamelAddress *)msg->reply_to);
300 CAMEL_MEDIUM_CLASS (parent_class)->set_header (CAMEL_MEDIUM (msg), "Reply-To", addr);
304 const CamelInternetAddress *
305 camel_mime_message_get_reply_to (CamelMimeMessage *mime_message)
307 g_assert (mime_message);
309 /* TODO: ref for threading? */
311 return mime_message->reply_to;
317 camel_mime_message_set_subject (CamelMimeMessage *mime_message, const char *subject)
321 g_assert(mime_message);
323 g_free (mime_message->subject);
324 mime_message->subject = g_strstrip (g_strdup (subject));
325 text = header_encode_string((unsigned char *)mime_message->subject);
326 CAMEL_MEDIUM_CLASS(parent_class)->set_header(CAMEL_MEDIUM (mime_message), "Subject", text);
331 camel_mime_message_get_subject (CamelMimeMessage *mime_message)
333 g_assert(mime_message);
335 return mime_message->subject;
340 /* Thought: Since get_from/set_from are so rarely called, it is probably not useful
341 to cache the from (and reply_to) addresses as InternetAddresses internally, we
342 could just get it from the headers and reprocess every time. */
344 camel_mime_message_set_from (CamelMimeMessage *msg, const CamelInternetAddress *from)
351 camel_object_unref((CamelObject *)msg->from);
355 if (from == NULL || camel_address_length((CamelAddress *)from) == 0) {
356 CAMEL_MEDIUM_CLASS(parent_class)->remove_header(CAMEL_MEDIUM(msg), "From");
360 msg->from = (CamelInternetAddress *)camel_address_new_clone((CamelAddress *)from);
361 addr = camel_address_encode((CamelAddress *)msg->from);
362 CAMEL_MEDIUM_CLASS (parent_class)->set_header(CAMEL_MEDIUM(msg), "From", addr);
366 const CamelInternetAddress *
367 camel_mime_message_get_from (CamelMimeMessage *mime_message)
369 g_assert (mime_message);
371 /* TODO: we should really ref this for multi-threading to work */
373 return mime_message->from;
376 /* **** To: Cc: Bcc: */
379 camel_mime_message_set_recipients(CamelMimeMessage *mime_message, const char *type, const CamelInternetAddress *r)
382 CamelInternetAddress *addr;
384 g_assert(mime_message);
386 addr = g_hash_table_lookup (mime_message->recipients, type);
388 g_warning ("trying to set a non-valid receipient type: %s", type);
392 if (r == NULL || camel_address_length ((CamelAddress *)r) == 0) {
393 camel_address_remove ((CamelAddress *)addr, -1);
394 CAMEL_MEDIUM_CLASS (parent_class)->remove_header (CAMEL_MEDIUM (mime_message), type);
398 /* note this does copy, and not append (cat) */
399 camel_address_copy ((CamelAddress *)addr, (const CamelAddress *)r);
401 /* and sync our headers */
402 text = camel_address_encode (CAMEL_ADDRESS (addr));
403 CAMEL_MEDIUM_CLASS (parent_class)->set_header (CAMEL_MEDIUM (mime_message), type, text);
408 camel_mime_message_set_source (CamelMimeMessage *mime_message, const char *src)
413 g_assert (mime_message);
415 url = camel_url_new (src, NULL);
417 uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
418 camel_medium_add_header (CAMEL_MEDIUM (mime_message), "X-Evolution-Source", uri);
420 camel_url_free (url);
425 camel_mime_message_get_source (CamelMimeMessage *mime_message)
429 g_assert(mime_message);
431 src = camel_medium_get_header (CAMEL_MEDIUM (mime_message), "X-Evolution-Source");
433 while (*src && isspace ((unsigned) *src))
439 const CamelInternetAddress *
440 camel_mime_message_get_recipients (CamelMimeMessage *mime_message, const char *type)
442 g_assert(mime_message);
444 return g_hash_table_lookup (mime_message->recipients, type);
449 construct_from_parser (CamelMimePart *dw, CamelMimeParser *mp)
457 d(printf("constructing mime-message\n"));
459 d(printf("mime_message::construct_from_parser()\n"));
461 /* let the mime-part construct the guts ... */
462 ret = ((CamelMimePartClass *)parent_class)->construct_from_parser(dw, mp);
467 /* ... then clean up the follow-on state */
468 state = camel_mime_parser_step (mp, &buf, &len);
470 case HSCAN_EOF: case HSCAN_FROM_END: /* these doesn't belong to us */
471 camel_mime_parser_unstep (mp);
472 case HSCAN_MESSAGE_END:
475 g_error ("Bad parser state: Expecing MESSAGE_END or EOF or EOM, got: %d", camel_mime_parser_state (mp));
476 camel_mime_parser_unstep (mp);
480 d(printf("mime_message::construct_from_parser() leaving\n"));
481 err = camel_mime_parser_errno(mp);
491 write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
493 CamelMimeMessage *mm = CAMEL_MIME_MESSAGE (data_wrapper);
495 /* force mandatory headers ... */
496 if (mm->from == NULL) {
497 /* FIXME: should we just abort? Should we make one up? */
498 g_warning ("No from set for message");
499 camel_medium_set_header ((CamelMedium *)mm, "From", "");
501 if (!camel_medium_get_header ((CamelMedium *)mm, "Date"))
502 camel_mime_message_set_date (mm, CAMEL_MESSAGE_DATE_CURRENT, 0);
504 if (mm->subject == NULL)
505 camel_mime_message_set_subject (mm, "No Subject");
507 if (mm->message_id == NULL)
508 camel_mime_message_set_message_id (mm, NULL);
510 /* FIXME: "To" header needs to be set explicitly as well ... */
512 if (!camel_medium_get_header ((CamelMedium *)mm, "Mime-Version"))
513 camel_medium_set_header ((CamelMedium *)mm, "Mime-Version", "1.0");
515 return CAMEL_DATA_WRAPPER_CLASS (parent_class)->write_to_stream (data_wrapper, stream);
518 /* FIXME: check format of fields. */
520 process_header (CamelMedium *medium, const char *header_name, const char *header_value)
522 CamelHeaderType header_type;
523 CamelMimeMessage *message = CAMEL_MIME_MESSAGE (medium);
524 CamelInternetAddress *addr;
527 header_type = (CamelHeaderType)g_hash_table_lookup (header_name_table, header_name);
528 switch (header_type) {
531 camel_object_unref (CAMEL_OBJECT (message->from));
532 message->from = camel_internet_address_new ();
533 camel_address_decode (CAMEL_ADDRESS (message->from), header_value);
535 case HEADER_REPLY_TO:
536 if (message->reply_to)
537 camel_object_unref (CAMEL_OBJECT (message->reply_to));
538 message->reply_to = camel_internet_address_new ();
539 camel_address_decode (CAMEL_ADDRESS (message->reply_to), header_value);
542 g_free (message->subject);
543 if (((CamelMimePart *) message)->content_type) {
544 charset = header_content_type_param (((CamelMimePart *) message)->content_type, "charset");
545 charset = e_iconv_charset_name (charset);
548 message->subject = g_strstrip (header_decode_string (header_value, charset));
553 case HEADER_RESENT_TO:
554 case HEADER_RESENT_CC:
555 case HEADER_RESENT_BCC:
556 addr = g_hash_table_lookup (message->recipients, header_name);
558 camel_address_decode (CAMEL_ADDRESS (addr), header_value);
560 camel_address_remove (CAMEL_ADDRESS (addr), -1);
564 message->date = header_decode_date (header_value, &message->date_offset);
566 message->date = CAMEL_MESSAGE_DATE_CURRENT;
567 message->date_offset = 0;
570 case HEADER_MESSAGE_ID:
571 g_free (message->message_id);
573 message->message_id = header_msgid_decode (header_value);
575 message->message_id = NULL;
585 set_header (CamelMedium *medium, const char *header_name, const void *header_value)
587 process_header (medium, header_name, header_value);
588 parent_class->parent_class.set_header (medium, header_name, header_value);
592 add_header (CamelMedium *medium, const char *header_name, const void *header_value)
594 /* if we process it, then it must be forced unique as well ... */
595 if (process_header (medium, header_name, header_value))
596 parent_class->parent_class.set_header (medium, header_name, header_value);
598 parent_class->parent_class.add_header (medium, header_name, header_value);
602 remove_header (CamelMedium *medium, const char *header_name)
604 process_header (medium, header_name, NULL);
605 parent_class->parent_class.remove_header (medium, header_name);
608 typedef gboolean (*CamelPartFunc)(CamelMimeMessage *, CamelMimePart *, void *data);
611 message_foreach_part_rec (CamelMimeMessage *msg, CamelMimePart *part, CamelPartFunc callback, void *data)
613 CamelDataWrapper *containee;
617 if (callback (msg, part, data) == FALSE)
620 containee = camel_medium_get_content_object (CAMEL_MEDIUM (part));
622 if (containee == NULL)
625 /* using the object types is more accurate than using the mime/types */
626 if (CAMEL_IS_MULTIPART (containee)) {
627 parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
628 for (i = 0; go && i < parts; i++) {
629 CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
631 go = message_foreach_part_rec (msg, part, callback, data);
633 } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
634 go = message_foreach_part_rec (msg, (CamelMimePart *)containee, callback, data);
640 /* dont make this public yet, it might need some more thinking ... */
643 camel_mime_message_foreach_part (CamelMimeMessage *msg, CamelPartFunc callback, void *data)
645 message_foreach_part_rec (msg, (CamelMimePart *)msg, callback, data);
649 check_8bit (CamelMimeMessage *msg, CamelMimePart *part, void *data)
651 CamelMimePartEncodingType encoding;
654 /* check this part, and stop as soon as we are done */
655 encoding = camel_mime_part_get_encoding (part);
657 *has8bit = encoding == CAMEL_MIME_PART_ENCODING_8BIT || encoding == CAMEL_MIME_PART_ENCODING_BINARY;
663 camel_mime_message_has_8bit_parts (CamelMimeMessage *msg)
667 camel_mime_message_foreach_part (msg, check_8bit, &has8bit);
672 /* finds the best charset and transfer encoding for a given part */
673 static CamelMimePartEncodingType
674 find_best_encoding (CamelMimePart *part, CamelBestencRequired required, CamelBestencEncoding enctype, char **charsetp)
676 const char *charsetin = NULL;
677 char *charset = NULL;
679 CamelStreamFilter *filter;
680 CamelMimeFilterCharset *charenc = NULL;
681 CamelMimeFilterBestenc *bestenc;
684 unsigned int flags, callerflags;
685 CamelMimePartEncodingType encoding;
686 CamelDataWrapper *content;
688 /* we use all these weird stream things so we can do it with streams, and
689 not have to read the whole lot into memory - although i have a feeling
690 it would make things a fair bit simpler to do so ... */
692 d(printf("starting to check part\n"));
694 content = camel_medium_get_content_object ((CamelMedium *)part);
695 if (content == NULL) {
696 /* charset might not be right here, but it'll get the right stuff
699 return CAMEL_MIME_PART_ENCODING_DEFAULT;
702 istext = header_content_type_is (part->content_type, "text", "*");
704 flags = CAMEL_BESTENC_GET_CHARSET | CAMEL_BESTENC_GET_ENCODING;
705 enctype |= CAMEL_BESTENC_TEXT;
707 flags = CAMEL_BESTENC_GET_ENCODING;
710 /* when building the message, any encoded parts are translated already */
711 flags |= CAMEL_BESTENC_LF_IS_CRLF;
712 /* and get any flags the caller passed in */
713 callerflags = (required & CAMEL_BESTENC_NO_FROM);
714 flags |= callerflags;
716 /* first a null stream, so any filtering is thrown away; we only want the sideeffects */
717 null = (CamelStream *)camel_stream_null_new ();
718 filter = camel_stream_filter_new_with_stream (null);
720 /* if we're not looking for the best charset, then use the one we have */
721 if (istext && (required & CAMEL_BESTENC_GET_CHARSET) == 0
722 && (charsetin = header_content_type_param (part->content_type, "charset"))) {
723 /* if libunicode doesn't support it, we dont really have utf8 anyway, so
724 we dont need a converter */
725 charenc = camel_mime_filter_charset_new_convert ("UTF-8", charsetin);
727 idc = camel_stream_filter_add (filter, (CamelMimeFilter *)charenc);
731 bestenc = camel_mime_filter_bestenc_new (flags);
732 idb = camel_stream_filter_add (filter, (CamelMimeFilter *)bestenc);
733 d(printf("writing to checking stream\n"));
734 camel_data_wrapper_write_to_stream (content, (CamelStream *)filter);
735 camel_stream_filter_remove (filter, idb);
737 camel_stream_filter_remove (filter, idc);
738 camel_object_unref ((CamelObject *)charenc);
743 charsetin = camel_mime_filter_bestenc_get_best_charset (bestenc);
745 d(printf("charsetin = %s\n", charsetin ? charsetin : "(null)"));
747 /* if we have US-ASCII, or we're not doing text, we dont need to bother with the rest */
748 if (charsetin != NULL && (required & CAMEL_BESTENC_GET_CHARSET) != 0) {
749 charset = g_strdup (charsetin);
751 d(printf("have charset, trying conversion/etc\n"));
753 /* now the 'bestenc' can has told us what the best encoding is, we can use that to create
754 a charset conversion filter as well, and then re-add the bestenc to filter the
755 result to find the best encoding to use as well */
757 charenc = camel_mime_filter_charset_new_convert ("UTF-8", charset);
759 /* eek, libunicode doesn't undertand this charset anyway, then the 'utf8' we
760 thought we had is really the native format, in which case, we just treat
761 it as binary data (and take the result we have so far) */
763 if (charenc != NULL) {
764 /* otherwise, try another pass, converting to the real charset */
766 camel_mime_filter_reset ((CamelMimeFilter *)bestenc);
767 camel_mime_filter_bestenc_set_flags (bestenc, CAMEL_BESTENC_GET_ENCODING |
768 CAMEL_BESTENC_LF_IS_CRLF | callerflags);
770 camel_stream_filter_add (filter, (CamelMimeFilter *)charenc);
771 camel_stream_filter_add (filter, (CamelMimeFilter *)bestenc);
773 /* and write it to the new stream */
774 camel_data_wrapper_write_to_stream (content, (CamelStream *)filter);
776 camel_object_unref ((CamelObject *)charenc);
780 encoding = camel_mime_filter_bestenc_get_best_encoding (bestenc, enctype);
782 camel_object_unref ((CamelObject *)filter);
783 camel_object_unref ((CamelObject *)bestenc);
784 camel_object_unref ((CamelObject *)null);
786 d(printf("done, best encoding = %d\n", encoding));
797 CamelBestencRequired required;
798 CamelBestencEncoding enctype;
802 best_encoding (CamelMimeMessage *msg, CamelMimePart *part, void *datap)
804 struct _enc_data *data = datap;
805 CamelMimePartEncodingType encoding;
806 CamelDataWrapper *wrapper;
809 wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
813 /* we only care about actual content objects */
814 if (!CAMEL_IS_MULTIPART (wrapper) && !CAMEL_IS_MIME_MESSAGE (wrapper)) {
815 encoding = find_best_encoding (part, data->required, data->enctype, &charset);
816 /* we always set the encoding, if we got this far. GET_CHARSET implies
818 camel_mime_part_set_encoding (part, encoding);
820 if ((data->required & CAMEL_BESTENC_GET_CHARSET) != 0) {
821 if (header_content_type_is (part->content_type, "text", "*")) {
824 /* FIXME: ick, the part content_type interface needs fixing bigtime */
825 header_content_type_set_param (part->content_type, "charset",
826 charset ? charset : "us-ascii");
827 newct = header_content_type_format (part->content_type);
829 d(printf("Setting content-type to %s\n", newct));
831 camel_mime_part_set_content_type (part, newct);
842 camel_mime_message_set_best_encoding (CamelMimeMessage *msg, CamelBestencRequired required, CamelBestencEncoding enctype)
844 struct _enc_data data;
846 if ((required & (CAMEL_BESTENC_GET_ENCODING|CAMEL_BESTENC_GET_CHARSET)) == 0)
849 data.required = required;
850 data.enctype = enctype;
852 camel_mime_message_foreach_part (msg, best_encoding, &data);
856 camel_mime_message_encode_8bit_parts (CamelMimeMessage *mime_message)
858 camel_mime_message_set_best_encoding (mime_message, CAMEL_BESTENC_GET_ENCODING, CAMEL_BESTENC_7BIT);
862 struct _check_content_id {
864 const char *content_id;
868 check_content_id (CamelMimeMessage *message, CamelMimePart *part, void *data)
870 struct _check_content_id *check = (struct _check_content_id *) data;
871 const char *content_id;
874 content_id = camel_mime_part_get_content_id (part);
876 found = content_id && !strcmp (content_id, check->content_id) ? TRUE : FALSE;
879 camel_object_ref (CAMEL_OBJECT (part));
886 camel_mime_message_get_part_by_content_id (CamelMimeMessage *message, const char *id)
888 struct _check_content_id check;
890 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
895 check.content_id = id;
898 camel_mime_message_foreach_part (message, check_content_id, &check);
903 static char *tz_months[] = {
904 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
905 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
908 static char *tz_days[] = {
909 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
913 camel_mime_message_build_mbox_from (CamelMimeMessage *message)
915 struct _header_raw *header = ((CamelMimePart *)message)->headers;
916 GString *out = g_string_new("From ");
923 tmp = header_raw_find (&header, "Sender", NULL);
925 tmp = header_raw_find (&header, "From", NULL);
927 struct _header_address *addr = header_address_decode (tmp, NULL);
931 if (addr->type == HEADER_ADDRESS_NAME) {
932 g_string_append (out, addr->v.addr);
935 header_address_unref (addr);
940 g_string_append (out, "unknown@nodomain.now.au");
942 /* try use the received header to get the date */
943 tmp = header_raw_find (&header, "Received", NULL);
945 tmp = strrchr(tmp, ';');
950 /* if there isn't one, try the Date field */
952 tmp = header_raw_find (&header, "Date", NULL);
954 thetime = header_decode_date (tmp, &offset);
955 thetime += ((offset / 100) * (60 * 60)) + (offset % 100) * 60;
956 gmtime_r (&thetime, &tm);
957 g_string_append_printf (out, " %s %s %2d %02d:%02d:%02d %4d\n",
958 tz_days[tm.tm_wday], tz_months[tm.tm_mon],
959 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
963 g_string_free (out, FALSE);