1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /* camelMimePart.c : Abstract class for a mime_part */
5 * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
6 * Michael Zucchi <notzed@ximian.com>
7 * Jeffrey Stedfast <fejj@ximian.com>
9 * Copyright 1999-2003 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 Lesser 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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
36 #include <libedataserver/e-iconv.h>
38 #include "camel-charset-map.h"
39 #include "camel-exception.h"
40 #include "camel-mime-filter-basic.h"
41 #include "camel-mime-filter-charset.h"
42 #include "camel-mime-filter-crlf.h"
43 #include "camel-mime-parser.h"
44 #include "camel-mime-part-utils.h"
45 #include "camel-mime-part.h"
46 #include "camel-mime-utils.h"
47 #include "camel-stream-filter.h"
48 #include "camel-stream-mem.h"
49 #include "camel-string-utils.h"
51 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
60 HEADER_CONTENT_LOCATION,
61 HEADER_CONTENT_LANGUAGES,
66 static GHashTable *header_name_table;
67 static GHashTable *header_formatted_table;
69 static CamelMediumClass *parent_class=NULL;
71 /* Returns the class for a CamelMimePart */
72 #define CMP_CLASS(so) CAMEL_MIME_PART_CLASS (CAMEL_OBJECT_GET_CLASS(so))
73 #define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
74 #define CMD_CLASS(so) CAMEL_MEDIUM_CLASS (CAMEL_OBJECT_GET_CLASS(so))
76 /* from CamelDataWrapper */
77 static ssize_t write_to_stream (CamelDataWrapper *dw, CamelStream *stream);
78 static int construct_from_stream (CamelDataWrapper *dw, CamelStream *stream);
80 /* from CamelMedium */
81 static void add_header (CamelMedium *medium, const char *name, const void *value);
82 static void set_header (CamelMedium *medium, const char *name, const void *value);
83 static void remove_header (CamelMedium *medium, const char *name);
84 static const void *get_header (CamelMedium *medium, const char *name);
85 static GArray *get_headers (CamelMedium *medium);
86 static void free_headers (CamelMedium *medium, GArray *headers);
88 static void set_content_object (CamelMedium *medium, CamelDataWrapper *content);
90 /* from camel mime parser */
91 static int construct_from_parser (CamelMimePart *mime_part, CamelMimeParser *mp);
93 /* forward references */
94 static void set_disposition (CamelMimePart *mime_part, const char *disposition);
96 /* format output of headers */
97 static ssize_t write_references(CamelStream *stream, struct _camel_header_raw *h);
98 /*static int write_fold(CamelStream *stream, struct _camel_header_raw *h);*/
99 static ssize_t write_raw(CamelStream *stream, struct _camel_header_raw *h);
102 /* loads in a hash table the set of header names we */
103 /* recognize and associate them with a unique enum */
104 /* identifier (see CamelHeaderType above) */
106 init_header_name_table()
108 header_name_table = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
109 g_hash_table_insert (header_name_table, "Content-Description", (gpointer)HEADER_DESCRIPTION);
110 g_hash_table_insert (header_name_table, "Content-Disposition", (gpointer)HEADER_DISPOSITION);
111 g_hash_table_insert (header_name_table, "Content-id", (gpointer)HEADER_CONTENT_ID);
112 g_hash_table_insert (header_name_table, "Content-Transfer-Encoding", (gpointer)HEADER_ENCODING);
113 g_hash_table_insert (header_name_table, "Content-MD5", (gpointer)HEADER_CONTENT_MD5);
114 g_hash_table_insert (header_name_table, "Content-Location", (gpointer)HEADER_CONTENT_LOCATION);
115 g_hash_table_insert (header_name_table, "Content-Type", (gpointer)HEADER_CONTENT_TYPE);
117 header_formatted_table = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
118 g_hash_table_insert(header_formatted_table, "Content-Type", write_raw);
119 g_hash_table_insert(header_formatted_table, "Content-Disposition", write_raw);
120 g_hash_table_insert(header_formatted_table, "To", write_raw);
121 g_hash_table_insert(header_formatted_table, "From", write_raw);
122 g_hash_table_insert(header_formatted_table, "Reply-To", write_raw);
123 g_hash_table_insert(header_formatted_table, "Cc", write_raw);
124 g_hash_table_insert(header_formatted_table, "Bcc", write_raw);
125 g_hash_table_insert(header_formatted_table, "Message-ID", write_raw);
126 g_hash_table_insert(header_formatted_table, "In-Reply-To", write_raw);
127 g_hash_table_insert(header_formatted_table, "References", write_references);
131 camel_mime_part_class_init (CamelMimePartClass *camel_mime_part_class)
133 CamelMediumClass *camel_medium_class = CAMEL_MEDIUM_CLASS (camel_mime_part_class);
134 CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (camel_mime_part_class);
136 parent_class = CAMEL_MEDIUM_CLASS (camel_type_get_global_classfuncs (camel_medium_get_type ()));
137 init_header_name_table();
139 camel_mime_part_class->construct_from_parser = construct_from_parser;
141 /* virtual method overload */
142 camel_medium_class->add_header = add_header;
143 camel_medium_class->set_header = set_header;
144 camel_medium_class->get_header = get_header;
145 camel_medium_class->remove_header = remove_header;
146 camel_medium_class->get_headers = get_headers;
147 camel_medium_class->free_headers = free_headers;
148 camel_medium_class->set_content_object = set_content_object;
150 camel_data_wrapper_class->write_to_stream = write_to_stream;
151 camel_data_wrapper_class->construct_from_stream= construct_from_stream;
155 camel_mime_part_init (gpointer object, gpointer klass)
157 CamelMimePart *mime_part = CAMEL_MIME_PART (object);
159 if (((CamelDataWrapper *) mime_part)->mime_type)
160 camel_content_type_unref (((CamelDataWrapper *) mime_part)->mime_type);
161 ((CamelDataWrapper *) mime_part)->mime_type = camel_content_type_new ("text", "plain");
163 mime_part->description = NULL;
164 mime_part->disposition = NULL;
165 mime_part->content_id = NULL;
166 mime_part->content_MD5 = NULL;
167 mime_part->content_location = NULL;
168 mime_part->content_languages = NULL;
169 mime_part->encoding = CAMEL_TRANSFER_ENCODING_DEFAULT;
174 camel_mime_part_finalize (CamelObject *object)
176 CamelMimePart *mime_part = CAMEL_MIME_PART (object);
178 g_free (mime_part->description);
179 g_free (mime_part->content_id);
180 g_free (mime_part->content_MD5);
181 g_free (mime_part->content_location);
182 camel_string_list_free (mime_part->content_languages);
183 camel_content_disposition_unref(mime_part->disposition);
185 camel_header_raw_clear(&mime_part->headers);
191 camel_mime_part_get_type (void)
193 static CamelType type = CAMEL_INVALID_TYPE;
195 if (type == CAMEL_INVALID_TYPE) {
196 type = camel_type_register (CAMEL_MEDIUM_TYPE,
198 sizeof (CamelMimePart),
199 sizeof (CamelMimePartClass),
200 (CamelObjectClassInitFunc) camel_mime_part_class_init,
202 (CamelObjectInitFunc) camel_mime_part_init,
203 (CamelObjectFinalizeFunc) camel_mime_part_finalize);
213 process_header(CamelMedium *medium, const char *name, const char *value)
215 CamelMimePart *mime_part = CAMEL_MIME_PART (medium);
216 CamelHeaderType header_type;
220 /* Try to parse the header pair. If it corresponds to something */
221 /* known, the job is done in the parsing routine. If not, */
222 /* we simply add the header in a raw fashion */
224 header_type = (CamelHeaderType) g_hash_table_lookup (header_name_table, name);
225 switch (header_type) {
226 case HEADER_DESCRIPTION: /* raw header->utf8 conversion */
227 g_free (mime_part->description);
228 if (((CamelDataWrapper *) mime_part)->mime_type) {
229 charset = camel_content_type_param (((CamelDataWrapper *) mime_part)->mime_type, "charset");
230 charset = e_iconv_charset_name (charset);
233 mime_part->description = g_strstrip (camel_header_decode_string (value, charset));
235 case HEADER_DISPOSITION:
236 set_disposition (mime_part, value);
238 case HEADER_CONTENT_ID:
239 g_free (mime_part->content_id);
240 mime_part->content_id = camel_header_contentid_decode (value);
242 case HEADER_ENCODING:
243 text = camel_header_token_decode (value);
244 mime_part->encoding = camel_transfer_encoding_from_string (text);
247 case HEADER_CONTENT_MD5:
248 g_free (mime_part->content_MD5);
249 mime_part->content_MD5 = g_strdup (value);
251 case HEADER_CONTENT_LOCATION:
252 g_free (mime_part->content_location);
253 mime_part->content_location = camel_header_location_decode (value);
255 case HEADER_CONTENT_TYPE:
256 if (((CamelDataWrapper *) mime_part)->mime_type)
257 camel_content_type_unref (((CamelDataWrapper *) mime_part)->mime_type);
258 ((CamelDataWrapper *) mime_part)->mime_type = camel_content_type_decode (value);
267 set_header (CamelMedium *medium, const char *name, const void *value)
269 CamelMimePart *part = CAMEL_MIME_PART (medium);
271 process_header(medium, name, value);
272 camel_header_raw_replace(&part->headers, name, value, -1);
276 add_header (CamelMedium *medium, const char *name, const void *value)
278 CamelMimePart *part = CAMEL_MIME_PART (medium);
280 /* Try to parse the header pair. If it corresponds to something */
281 /* known, the job is done in the parsing routine. If not, */
282 /* we simply add the header in a raw fashion */
284 /* If it was one of the headers we handled, it must be unique, set it instead of add */
285 if (process_header(medium, name, value))
286 camel_header_raw_replace(&part->headers, name, value, -1);
288 camel_header_raw_append(&part->headers, name, value, -1);
292 remove_header (CamelMedium *medium, const char *name)
294 CamelMimePart *part = (CamelMimePart *)medium;
296 process_header(medium, name, NULL);
297 camel_header_raw_remove(&part->headers, name);
301 get_header (CamelMedium *medium, const char *name)
303 CamelMimePart *part = (CamelMimePart *)medium;
305 return camel_header_raw_find(&part->headers, name, NULL);
309 get_headers (CamelMedium *medium)
311 CamelMimePart *part = (CamelMimePart *)medium;
313 CamelMediumHeader header;
314 struct _camel_header_raw *h;
316 headers = g_array_new (FALSE, FALSE, sizeof (CamelMediumHeader));
317 for (h = part->headers; h; h = h->next) {
318 header.name = h->name;
319 header.value = h->value;
320 g_array_append_val (headers, header);
327 free_headers (CamelMedium *medium, GArray *gheaders)
329 g_array_free (gheaders, TRUE);
332 /* **** Content-Description */
335 * camel_mime_part_set_description:
336 * @mime_part: a #CamelMimePart object
337 * @description: description of the MIME part
339 * Set a description on the MIME part.
342 camel_mime_part_set_description (CamelMimePart *mime_part, const char *description)
344 char *text = camel_header_encode_string (description);
346 camel_medium_set_header (CAMEL_MEDIUM (mime_part),
347 "Content-Description", text);
353 * camel_mime_part_get_description:
354 * @mime_part: a #CamelMimePart object
356 * Get the description of the MIME part.
358 * Returns the description
361 camel_mime_part_get_description (CamelMimePart *mime_part)
363 return mime_part->description;
366 /* **** Content-Disposition */
369 set_disposition (CamelMimePart *mime_part, const char *disposition)
371 camel_content_disposition_unref(mime_part->disposition);
373 mime_part->disposition = camel_content_disposition_decode(disposition);
375 mime_part->disposition = NULL;
380 * camel_mime_part_set_disposition:
381 * @mime_part: a #CamelMimePart object
382 * @disposition: disposition of the MIME part
384 * Set a disposition on the MIME part.
387 camel_mime_part_set_disposition (CamelMimePart *mime_part, const char *disposition)
391 /* we poke in a new disposition (so we dont lose 'filename', etc) */
392 if (mime_part->disposition == NULL) {
393 set_disposition(mime_part, disposition);
395 if (mime_part->disposition != NULL) {
396 g_free(mime_part->disposition->disposition);
397 mime_part->disposition->disposition = g_strdup(disposition);
399 text = camel_content_disposition_format(mime_part->disposition);
401 camel_medium_set_header (CAMEL_MEDIUM (mime_part),
402 "Content-Disposition", text);
409 * camel_mime_part_get_disposition:
410 * @mime_part: a #CamelMimePart object
412 * Get the disposition of the MIME part.
414 * Returns the dispisition
417 camel_mime_part_get_disposition (CamelMimePart *mime_part)
419 if (mime_part->disposition)
420 return mime_part->disposition->disposition;
426 /* **** Content-Disposition: filename="xxx" */
429 * camel_mime_part_set_filename:
430 * @mime_part: a #CamelMimePart object
431 * @filename: filename given to the MIME part
433 * Set the filename on a MIME part.
436 camel_mime_part_set_filename (CamelMimePart *mime_part, const char *filename)
440 if (mime_part->disposition == NULL)
441 mime_part->disposition = camel_content_disposition_decode("attachment");
443 camel_header_set_param(&mime_part->disposition->params, "filename", filename);
444 str = camel_content_disposition_format(mime_part->disposition);
446 camel_medium_set_header (CAMEL_MEDIUM (mime_part),
447 "Content-Disposition", str);
450 camel_content_type_set_param (((CamelDataWrapper *) mime_part)->mime_type, "name", filename);
451 str = camel_content_type_format (((CamelDataWrapper *) mime_part)->mime_type);
452 camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-Type", str);
458 * camel_mime_part_get_filename:
459 * @mime_part: a #CamelMimePart object
461 * Get the filename of a MIME part.
463 * Returns the filename of the MIME part
466 camel_mime_part_get_filename (CamelMimePart *mime_part)
468 if (mime_part->disposition) {
469 const char *name = camel_header_param (mime_part->disposition->params, "filename");
474 return camel_content_type_param (((CamelDataWrapper *) mime_part)->mime_type, "name");
478 /* **** Content-ID: */
481 * camel_mime_part_set_content_id:
482 * @mime_part: a #CamelMimePart object
483 * @contentid: content id
485 * Set the content-id field on a MIME part.
488 camel_mime_part_set_content_id (CamelMimePart *mime_part, const char *contentid)
493 id = g_strstrip (g_strdup (contentid));
495 id = camel_header_msgid_generate ();
497 cid = g_strdup_printf ("<%s>", id);
499 camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-ID", cid);
505 * camel_mime_part_get_content_id:
506 * @mime_part: a #CamelMimePart object
508 * Get the content-id field of a MIME part.
510 * Returns the content-id field of the MIME part
513 camel_mime_part_get_content_id (CamelMimePart *mime_part)
515 return mime_part->content_id;
518 /* **** Content-MD5: */
521 * camel_mime_part_set_content_MD5:
522 * @mime_part: a #CamelMimePart object
523 * @md5sum: the md5sum of the MIME part
525 * Set the content-md5 field of the MIME part.
528 camel_mime_part_set_content_MD5 (CamelMimePart *mime_part, const char *md5)
530 camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-MD5", md5);
535 * camel_mime_part_get_content_MD5:
536 * @mime_part: a #CamelMimePart object
538 * Get the content-md5 field of the MIME part.
540 * Returns the content-md5 field of the MIME part
543 camel_mime_part_get_content_MD5 (CamelMimePart *mime_part)
545 return mime_part->content_MD5;
548 /* **** Content-Location: */
552 * camel_mime_part_set_content_location:
553 * @mime_part: a #CamelMimePart object
554 * @location: the content-location value of the MIME part
556 * Set the content-location field of the MIME part.
559 camel_mime_part_set_content_location (CamelMimePart *mime_part, const char *location)
561 /* FIXME: this should perform content-location folding */
562 camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-Location", location);
567 * camel_mime_part_get_content_location:
568 * @mime_part: a #CamelMimePart object
570 * Get the content-location field of a MIME part.
572 * Returns the content-location field of a MIME part
575 camel_mime_part_get_content_location (CamelMimePart *mime_part)
577 return mime_part->content_location;
580 /* **** Content-Transfer-Encoding: */
584 * camel_mime_part_set_encoding:
585 * @mime_part: a #CamelMimePart object
586 * @encoding: a #CamelTransferEncoding
588 * Set the Content-Transfer-Encoding to use on a MIME part.
591 camel_mime_part_set_encoding (CamelMimePart *mime_part,
592 CamelTransferEncoding encoding)
596 text = camel_transfer_encoding_to_string (encoding);
597 camel_medium_set_header (CAMEL_MEDIUM (mime_part),
598 "Content-Transfer-Encoding", text);
603 * camel_mime_part_get_encoding:
604 * @mime_part: a #CamelMimePart object
606 * Get the Content-Transfer-Encoding of a MIME part.
608 * Returns a #CamelTransferEncoding
610 CamelTransferEncoding
611 camel_mime_part_get_encoding (CamelMimePart *mime_part)
613 return mime_part->encoding;
616 /* FIXME: do something with this stuff ... */
620 * camel_mime_part_set_content_languages:
621 * @mime_part: a #CamelMimePart object
622 * @content_languages: list of languages
624 * Set the Content-Languages field of a MIME part.
627 camel_mime_part_set_content_languages (CamelMimePart *mime_part, GList *content_languages)
629 if (mime_part->content_languages)
630 camel_string_list_free (mime_part->content_languages);
632 mime_part->content_languages = content_languages;
634 /* FIXME: translate to a header and set it */
639 * camel_mime_part_get_content_languages:
640 * @mime_part: a #CamelMimePart object
642 * Get the Content-Languages set on the MIME part.
644 * Returns a #GList of languages
647 camel_mime_part_get_content_languages (CamelMimePart *mime_part)
649 return mime_part->content_languages;
655 /* **** Content-Type: */
658 * camel_mime_part_set_content_type:
659 * @mime_part: a #CamelMimePart object
660 * @content_type: content-type string
662 * Set the content-type on a MIME part.
665 camel_mime_part_set_content_type (CamelMimePart *mime_part, const char *content_type)
667 camel_medium_set_header (CAMEL_MEDIUM (mime_part),
668 "Content-Type", content_type);
673 * camel_mime_part_get_content_type:
674 * @mime_part: a #CamelMimePart object
676 * Get the Content-Type of a MIME part.
678 * Returns the parsed #CamelContentType of the MIME part
681 camel_mime_part_get_content_type (CamelMimePart *mime_part)
683 return ((CamelDataWrapper *) mime_part)->mime_type;
687 set_content_object (CamelMedium *medium, CamelDataWrapper *content)
689 CamelDataWrapper *mime_part = CAMEL_DATA_WRAPPER (medium);
690 CamelContentType *content_type;
692 parent_class->set_content_object (medium, content);
694 content_type = camel_data_wrapper_get_mime_type_field (content);
695 if (mime_part->mime_type != content_type) {
698 txt = camel_content_type_format (content_type);
699 camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-Type", txt);
704 /**********************************************************************/
707 write_references(CamelStream *stream, struct _camel_header_raw *h)
709 ssize_t len, out, total;
712 /* this is only approximate, based on the next >, this way it retains any content
713 from the original which may not be properly formatted, etc. It also doesn't handle
714 the case where an individual messageid is too long, however thats a bad mail to
718 len = strlen(h->name)+1;
719 total = camel_stream_printf(stream, "%s%s", h->name, isspace(v[0])?":":": ");
724 ide = strchr(ids+1, '>');
728 ide = v = strlen(ids)+ids;
730 if (len>0 && len + (ide - ids) >= CAMEL_FOLD_SIZE) {
731 out = camel_stream_printf(stream, "\n\t");
737 out = camel_stream_write(stream, ids, ide-ids);
743 camel_stream_write(stream, "\n", 1);
749 /* not needed - yet - handled by default case */
751 write_fold(CamelStream *stream, struct _camel_header_raw *h)
756 val = camel_header_fold(h->value, strlen(h->name));
757 count = camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
765 write_raw(CamelStream *stream, struct _camel_header_raw *h)
767 char *val = h->value;
769 return camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
773 write_to_stream (CamelDataWrapper *dw, CamelStream *stream)
775 CamelMimePart *mp = CAMEL_MIME_PART (dw);
776 CamelMedium *medium = CAMEL_MEDIUM (dw);
777 CamelStream *ostream = stream;
778 CamelDataWrapper *content;
783 d(printf("mime_part::write_to_stream\n"));
785 /* FIXME: something needs to be done about this ... */
786 /* TODO: content-languages header? */
789 struct _camel_header_raw *h = mp->headers;
791 ssize_t (*writefn)(CamelStream *stream, struct _camel_header_raw *);
793 /* fold/write the headers. But dont fold headers that are already formatted
794 (e.g. ones with parameter-lists, that we know about, and have created) */
798 g_warning("h->value is NULL here for %s", h->name);
800 } else if ((writefn = g_hash_table_lookup(header_formatted_table, h->name)) == NULL) {
801 val = camel_header_fold(val, strlen(h->name));
802 count = camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
805 count = writefn(stream, h);
814 count = camel_stream_write(stream, "\n", 1);
819 content = camel_medium_get_content_object(medium);
821 CamelMimeFilter *filter = NULL;
822 CamelStreamFilter *filter_stream = NULL;
823 CamelMimeFilter *charenc = NULL;
824 const char *content_charset = NULL;
825 const char *part_charset = NULL;
826 gboolean reencode = FALSE;
827 const char *filename;
829 if (camel_content_type_is (dw->mime_type, "text", "*")) {
830 content_charset = camel_content_type_param (content->mime_type, "charset");
831 part_charset = camel_content_type_param (dw->mime_type, "charset");
833 if (content_charset && part_charset) {
834 content_charset = e_iconv_charset_name (content_charset);
835 part_charset = e_iconv_charset_name (part_charset);
839 if (mp->encoding != content->encoding) {
840 switch (mp->encoding) {
841 case CAMEL_TRANSFER_ENCODING_BASE64:
842 filter = (CamelMimeFilter *) camel_mime_filter_basic_new_type (CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
844 case CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE:
845 filter = (CamelMimeFilter *) camel_mime_filter_basic_new_type (CAMEL_MIME_FILTER_BASIC_QP_ENC);
847 case CAMEL_TRANSFER_ENCODING_UUENCODE:
848 filename = camel_mime_part_get_filename (mp);
849 count = camel_stream_printf (ostream, "begin 644 %s\n", filename ? filename : "untitled");
853 filter = (CamelMimeFilter *) camel_mime_filter_basic_new_type (CAMEL_MIME_FILTER_BASIC_UU_ENC);
856 /* content is encoded but the part doesn't want to be... */
862 if (content_charset && part_charset && part_charset != content_charset)
863 charenc = (CamelMimeFilter *) camel_mime_filter_charset_new_convert (content_charset, part_charset);
865 if (filter || charenc) {
866 filter_stream = camel_stream_filter_new_with_stream(stream);
868 /* if we have a character encoder, add that always */
870 camel_stream_filter_add(filter_stream, charenc);
871 camel_object_unref (charenc);
874 /* we only re-do crlf on encoded blocks */
875 if (filter && camel_content_type_is (dw->mime_type, "text", "*")) {
876 CamelMimeFilter *crlf = camel_mime_filter_crlf_new(CAMEL_MIME_FILTER_CRLF_ENCODE,
877 CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
879 camel_stream_filter_add(filter_stream, crlf);
880 camel_object_unref (crlf);
884 camel_stream_filter_add(filter_stream, filter);
885 camel_object_unref (filter);
888 stream = (CamelStream *)filter_stream;
894 count = camel_data_wrapper_decode_to_stream (content, stream);
896 count = camel_data_wrapper_write_to_stream (content, stream);
900 camel_stream_flush (stream);
901 camel_object_unref (filter_stream);
910 if (reencode && mp->encoding == CAMEL_TRANSFER_ENCODING_UUENCODE) {
911 count = camel_stream_write (ostream, "end\n", 4);
917 g_warning("No content for medium, nothing to write");
925 construct_from_parser (CamelMimePart *mime_part, CamelMimeParser *mp)
927 CamelDataWrapper *dw = (CamelDataWrapper *) mime_part;
928 struct _camel_header_raw *headers;
934 d(printf("mime_part::construct_from_parser()\n"));
936 switch (camel_mime_parser_step(mp, &buf, &len)) {
937 case CAMEL_MIME_PARSER_STATE_MESSAGE:
938 /* set the default type of a message always */
940 camel_content_type_unref (dw->mime_type);
941 dw->mime_type = camel_content_type_decode ("message/rfc822");
942 case CAMEL_MIME_PARSER_STATE_HEADER:
943 case CAMEL_MIME_PARSER_STATE_MULTIPART:
944 /* we have the headers, build them into 'us' */
945 headers = camel_mime_parser_headers_raw(mp);
947 /* if content-type exists, process it first, set for fallback charset in headers */
948 content = camel_header_raw_find(&headers, "content-type", NULL);
950 process_header((CamelMedium *)dw, "content-type", content);
953 if (g_ascii_strcasecmp(headers->name, "content-type") == 0
954 && headers->value != content)
955 camel_medium_add_header((CamelMedium *)dw, "X-Invalid-Content-Type", headers->value);
957 camel_medium_add_header((CamelMedium *)dw, headers->name, headers->value);
958 headers = headers->next;
961 camel_mime_part_construct_content_from_parser (mime_part, mp);
964 g_warning("Invalid state encountered???: %u", camel_mime_parser_state(mp));
967 d(printf("mime_part::construct_from_parser() leaving\n"));
968 err = camel_mime_parser_errno(mp);
978 * camel_mime_part_construct_from_parser:
979 * @mime_part: a #CamelMimePart object
980 * @parser: a #CamelMimeParser object
982 * Constructs a MIME part from a parser.
984 * Returns %0 on success or %-1 on fail
987 camel_mime_part_construct_from_parser(CamelMimePart *mime_part, CamelMimeParser *mp)
989 return CMP_CLASS (mime_part)->construct_from_parser (mime_part, mp);
993 construct_from_stream(CamelDataWrapper *dw, CamelStream *s)
998 d(printf("mime_part::construct_from_stream()\n"));
1000 mp = camel_mime_parser_new();
1001 if (camel_mime_parser_init_with_stream(mp, s) == -1) {
1002 g_warning("Cannot create parser for stream");
1005 ret = camel_mime_part_construct_from_parser((CamelMimePart *)dw, mp);
1007 camel_object_unref((CamelObject *)mp);
1011 /******************************/
1012 /** Misc utility functions **/
1015 * camel_mime_part_new:
1017 * Create a new MIME part.
1019 * Returns a new #CamelMimePart object
1022 camel_mime_part_new (void)
1024 return (CamelMimePart *)camel_object_new (CAMEL_MIME_PART_TYPE);
1028 * camel_mime_part_set_content:
1029 * @mime_part: a #CamelMimePart object
1030 * @data: data to put into the part
1031 * @length: length of @data
1032 * @type: Content-Type of the data
1034 * Utility function used to set the content of a mime part object to
1035 * be the provided data. If @length is 0, this routine can be used as
1036 * a way to remove old content (in which case @data and @type are
1037 * ignored and may be %NULL).
1040 camel_mime_part_set_content (CamelMimePart *mime_part,
1041 const char *data, int length,
1042 const char *type) /* why on earth is the type last? */
1044 CamelMedium *medium = CAMEL_MEDIUM (mime_part);
1047 CamelDataWrapper *dw;
1048 CamelStream *stream;
1050 dw = camel_data_wrapper_new ();
1051 camel_data_wrapper_set_mime_type (dw, type);
1052 stream = camel_stream_mem_new_with_buffer (data, length);
1053 camel_data_wrapper_construct_from_stream (dw, stream);
1054 camel_object_unref (stream);
1055 camel_medium_set_content_object (medium, dw);
1056 camel_object_unref (dw);
1058 if (medium->content)
1059 camel_object_unref (medium->content);
1060 medium->content = NULL;