f049eb92bb8cbf922e9aebb98f8464ccf5668e2d
[platform/upstream/evolution-data-server.git] / camel / camel-mime-part.c
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 */
3
4 /* 
5  * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
6  *          Michael Zucchi <notzed@ximian.com>
7  *
8  * Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
9  *
10  * This program is free software; you can redistribute it and/or 
11  * modify it under the terms of version 2 of the GNU General Public 
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <errno.h>
33
34 #include <gal/util/e-iconv.h>
35
36 #include "camel-mime-parser.h"
37 #include "camel-stream-mem.h"
38 #include "camel-stream-filter.h"
39 #include "camel-mime-filter-basic.h"
40 #include "camel-mime-filter-crlf.h"
41 #include "camel-mime-filter-charset.h"
42 #include "camel-mime-part.h"
43 #include "camel-mime-part-utils.h"
44 #include "camel-mime-utils.h"
45 #include "camel-exception.h"
46 #include "camel-charset-map.h"
47 #include "string-utils.h"
48
49 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
50
51 typedef enum {
52         HEADER_UNKNOWN,
53         HEADER_DESCRIPTION,
54         HEADER_DISPOSITION,
55         HEADER_CONTENT_ID,
56         HEADER_ENCODING,
57         HEADER_CONTENT_MD5,
58         HEADER_CONTENT_LOCATION,
59         HEADER_CONTENT_LANGUAGES,
60         HEADER_CONTENT_TYPE
61 } CamelHeaderType;
62
63
64 static GHashTable *header_name_table;
65 static GHashTable *header_formatted_table;
66
67 static CamelMediumClass *parent_class=NULL;
68
69 /* Returns the class for a CamelMimePart */
70 #define CMP_CLASS(so) CAMEL_MIME_PART_CLASS (CAMEL_OBJECT_GET_CLASS(so))
71 #define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
72 #define CMD_CLASS(so) CAMEL_MEDIUM_CLASS (CAMEL_OBJECT_GET_CLASS(so))
73
74 /* from CamelDataWrapper */
75 static int             write_to_stream                 (CamelDataWrapper *data_wrapper, CamelStream *stream);
76 static int             construct_from_stream           (CamelDataWrapper *dw, CamelStream *s);
77
78 /* from CamelMedia */ 
79 static void            add_header                      (CamelMedium *medium, const char *header_name, const void *header_value);
80 static void            set_header                      (CamelMedium *medium, const char *header_name, const void *header_value);
81 static void            remove_header                   (CamelMedium *medium, const char *header_name);
82 static const void     *get_header                      (CamelMedium *medium, const char *header_name);
83 static GArray         *get_headers                     (CamelMedium *medium);
84 static void            free_headers                    (CamelMedium *medium, GArray *headers);
85
86 static void            set_content_object              (CamelMedium *medium, CamelDataWrapper *content);
87
88 /* from camel mime parser */
89 static int             construct_from_parser           (CamelMimePart *, CamelMimeParser *);
90
91 /* forward references */
92 static void set_disposition (CamelMimePart *mime_part, const gchar *disposition);
93
94 /* format output of headers */
95 static int write_references(CamelStream *stream, struct _header_raw *h);
96 /*static int write_fold(CamelStream *stream, struct _header_raw *h);*/
97 static int write_raw(CamelStream *stream, struct _header_raw *h);
98
99
100 /* loads in a hash table the set of header names we */
101 /* recognize and associate them with a unique enum  */
102 /* identifier (see CamelHeaderType above)           */
103 static void
104 init_header_name_table()
105 {
106         header_name_table = g_hash_table_new (g_strcase_hash, g_strcase_equal);
107         g_hash_table_insert (header_name_table, "Content-Description", (gpointer)HEADER_DESCRIPTION);
108         g_hash_table_insert (header_name_table, "Content-Disposition", (gpointer)HEADER_DISPOSITION);
109         g_hash_table_insert (header_name_table, "Content-id", (gpointer)HEADER_CONTENT_ID);
110         g_hash_table_insert (header_name_table, "Content-Transfer-Encoding", (gpointer)HEADER_ENCODING);
111         g_hash_table_insert (header_name_table, "Content-MD5", (gpointer)HEADER_CONTENT_MD5);
112         g_hash_table_insert (header_name_table, "Content-Location", (gpointer)HEADER_CONTENT_LOCATION);
113         g_hash_table_insert (header_name_table, "Content-Type", (gpointer)HEADER_CONTENT_TYPE);
114
115         header_formatted_table = g_hash_table_new(g_strcase_hash, g_strcase_equal);
116         g_hash_table_insert(header_formatted_table, "Content-Type", write_raw);
117         g_hash_table_insert(header_formatted_table, "Content-Disposition", write_raw);
118         g_hash_table_insert(header_formatted_table, "To", write_raw);
119         g_hash_table_insert(header_formatted_table, "From", write_raw);
120         g_hash_table_insert(header_formatted_table, "Reply-To", write_raw);
121         g_hash_table_insert(header_formatted_table, "Cc", write_raw);
122         g_hash_table_insert(header_formatted_table, "Bcc", write_raw);
123         g_hash_table_insert(header_formatted_table, "Message-ID", write_raw);
124         g_hash_table_insert(header_formatted_table, "In-Reply-To", write_raw);
125         g_hash_table_insert(header_formatted_table, "References", write_references);
126 }
127
128 static void
129 camel_mime_part_class_init (CamelMimePartClass *camel_mime_part_class)
130 {
131         CamelMediumClass *camel_medium_class = CAMEL_MEDIUM_CLASS (camel_mime_part_class);
132         CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (camel_mime_part_class);
133
134         parent_class = CAMEL_MEDIUM_CLASS (camel_type_get_global_classfuncs (camel_medium_get_type ()));
135         init_header_name_table();
136
137         camel_mime_part_class->construct_from_parser = construct_from_parser;
138         
139         /* virtual method overload */   
140         camel_medium_class->add_header                = add_header;
141         camel_medium_class->set_header                = set_header;
142         camel_medium_class->get_header                = get_header;
143         camel_medium_class->remove_header             = remove_header;
144         camel_medium_class->get_headers               = get_headers;
145         camel_medium_class->free_headers              = free_headers;
146         camel_medium_class->set_content_object        = set_content_object;
147
148         camel_data_wrapper_class->write_to_stream     = write_to_stream;
149         camel_data_wrapper_class->construct_from_stream= construct_from_stream;
150 }
151
152 static void
153 camel_mime_part_init (gpointer   object,  gpointer   klass)
154 {
155         CamelMimePart *camel_mime_part = CAMEL_MIME_PART (object);
156         
157         camel_mime_part->content_type         = header_content_type_new ("text", "plain");
158         camel_mime_part->description          = NULL;
159         camel_mime_part->disposition          = NULL;
160         camel_mime_part->content_id           = NULL;
161         camel_mime_part->content_MD5          = NULL;
162         camel_mime_part->content_location     = NULL;
163         camel_mime_part->content_languages    = NULL;
164         camel_mime_part->encoding             = CAMEL_MIME_PART_ENCODING_DEFAULT;
165 }
166
167
168 static void           
169 camel_mime_part_finalize (CamelObject *object)
170 {
171         CamelMimePart *mime_part = CAMEL_MIME_PART (object);
172         
173         g_free (mime_part->description);
174         g_free (mime_part->content_id);
175         g_free (mime_part->content_MD5);
176         g_free (mime_part->content_location);
177         string_list_free (mime_part->content_languages);
178         header_disposition_unref(mime_part->disposition);
179         
180         if (mime_part->content_type)
181                 header_content_type_unref (mime_part->content_type);
182
183         header_raw_clear(&mime_part->headers);
184 }
185
186
187
188 CamelType
189 camel_mime_part_get_type (void)
190 {
191         static CamelType camel_mime_part_type = CAMEL_INVALID_TYPE;
192         
193         if (camel_mime_part_type == CAMEL_INVALID_TYPE) {
194                 camel_mime_part_type = camel_type_register (CAMEL_MEDIUM_TYPE, "CamelMimePart",
195                                                             sizeof (CamelMimePart),
196                                                             sizeof (CamelMimePartClass),
197                                                             (CamelObjectClassInitFunc) camel_mime_part_class_init,
198                                                             NULL,
199                                                             (CamelObjectInitFunc) camel_mime_part_init,
200                                                             (CamelObjectFinalizeFunc) camel_mime_part_finalize);
201         }
202         
203         return camel_mime_part_type;
204 }
205
206
207 /* **** */
208
209 static gboolean
210 process_header(CamelMedium *medium, const char *header_name, const char *header_value)
211 {
212         CamelMimePart *mime_part = CAMEL_MIME_PART (medium);
213         CamelHeaderType header_type;
214         const char *charset;
215         char *text;
216
217         /* Try to parse the header pair. If it corresponds to something   */
218         /* known, the job is done in the parsing routine. If not,         */
219         /* we simply add the header in a raw fashion                      */
220
221         header_type = (CamelHeaderType) g_hash_table_lookup (header_name_table, header_name);
222         switch (header_type) {
223         case HEADER_DESCRIPTION: /* raw header->utf8 conversion */
224                 g_free (mime_part->description);
225                 if (mime_part->content_type) {
226                         charset = header_content_type_param (mime_part->content_type, "charset");
227                         charset = e_iconv_charset_name (charset);
228                 } else
229                         charset = NULL;
230                 mime_part->description = g_strstrip (header_decode_string (header_value, charset));
231                 break;
232         case HEADER_DISPOSITION:
233                 set_disposition (mime_part, header_value);
234                 break;
235         case HEADER_CONTENT_ID:
236                 g_free (mime_part->content_id);
237                 mime_part->content_id = header_contentid_decode (header_value);
238                 break;
239         case HEADER_ENCODING:
240                 text = header_token_decode (header_value);
241                 mime_part->encoding = camel_mime_part_encoding_from_string (text);
242                 g_free (text);
243                 break;
244         case HEADER_CONTENT_MD5:
245                 g_free (mime_part->content_MD5);
246                 mime_part->content_MD5 = g_strdup (header_value);
247                 break;
248         case HEADER_CONTENT_LOCATION:
249                 g_free (mime_part->content_location);
250                 mime_part->content_location = header_location_decode (header_value);
251                 break;
252         case HEADER_CONTENT_TYPE:
253                 if (mime_part->content_type)
254                         header_content_type_unref (mime_part->content_type);
255                 mime_part->content_type = header_content_type_decode (header_value);
256                 break;
257         default:
258                 return FALSE;
259         }
260         return TRUE;
261 }
262
263 static void
264 set_header (CamelMedium *medium, const char *header_name, const void *header_value)
265 {
266         CamelMimePart *part = CAMEL_MIME_PART (medium);
267         
268         process_header(medium, header_name, header_value);
269         header_raw_replace(&part->headers, header_name, header_value, -1);
270 }
271
272 static void
273 add_header (CamelMedium *medium, const char *header_name, const void *header_value)
274 {
275         CamelMimePart *part = CAMEL_MIME_PART (medium);
276         
277         /* Try to parse the header pair. If it corresponds to something   */
278         /* known, the job is done in the parsing routine. If not,         */
279         /* we simply add the header in a raw fashion                      */
280
281         /* If it was one of the headers we handled, it must be unique, set it instead of add */
282         if (process_header(medium, header_name, header_value))
283                 header_raw_replace(&part->headers, header_name, header_value, -1);
284         else
285                 header_raw_append(&part->headers, header_name, header_value, -1);
286 }
287
288 static void
289 remove_header (CamelMedium *medium, const char *header_name)
290 {
291         CamelMimePart *part = (CamelMimePart *)medium;
292         
293         process_header(medium, header_name, NULL);
294         header_raw_remove(&part->headers, header_name);
295 }
296
297 static const void *
298 get_header (CamelMedium *medium, const char *header_name)
299 {
300         CamelMimePart *part = (CamelMimePart *)medium;
301
302         return header_raw_find(&part->headers, header_name, NULL);
303 }
304
305 static GArray *
306 get_headers (CamelMedium *medium)
307 {
308         CamelMimePart *part = (CamelMimePart *)medium;
309         GArray *headers;
310         CamelMediumHeader header;
311         struct _header_raw *h;
312
313         headers = g_array_new (FALSE, FALSE, sizeof (CamelMediumHeader));
314         for (h = part->headers; h; h = h->next) {
315                 header.name = h->name;
316                 header.value = h->value;
317                 g_array_append_val (headers, header);
318         }
319
320         return headers;
321 }
322
323 static void
324 free_headers (CamelMedium *medium, GArray *gheaders)
325 {
326         g_array_free (gheaders, TRUE);
327 }
328
329 /* **** Content-Description */
330 void
331 camel_mime_part_set_description (CamelMimePart *mime_part, const gchar *description)
332 {
333         char *text = header_encode_string (description);
334         
335         camel_medium_set_header (CAMEL_MEDIUM (mime_part),
336                                  "Content-Description", text);
337         g_free (text);
338 }
339
340 const gchar *
341 camel_mime_part_get_description (CamelMimePart *mime_part)
342 {
343         return mime_part->description;
344 }
345
346 /* **** Content-Disposition */
347
348 static void
349 set_disposition (CamelMimePart *mime_part, const gchar *disposition)
350 {
351         header_disposition_unref(mime_part->disposition);
352         if (disposition)
353                 mime_part->disposition = header_disposition_decode(disposition);
354         else
355                 mime_part->disposition = NULL;
356 }
357
358
359 void
360 camel_mime_part_set_disposition (CamelMimePart *mime_part, const gchar *disposition)
361 {
362         char *text;
363
364         /* we poke in a new disposition (so we dont lose 'filename', etc) */
365         if (mime_part->disposition == NULL) {
366                 set_disposition(mime_part, disposition);
367         }
368         if (mime_part->disposition != NULL) {
369                 g_free(mime_part->disposition->disposition);
370                 mime_part->disposition->disposition = g_strdup(disposition);
371         }
372         text = header_disposition_format(mime_part->disposition);
373
374         camel_medium_set_header (CAMEL_MEDIUM (mime_part),
375                                  "Content-Disposition", text);
376
377         g_free(text);
378 }
379
380 const gchar *
381 camel_mime_part_get_disposition (CamelMimePart *mime_part)
382 {
383         if (mime_part->disposition)
384                 return (mime_part->disposition)->disposition;
385         else
386                 return NULL;
387 }
388
389
390 /* **** Content-Disposition: filename="xxx" */
391
392 void
393 camel_mime_part_set_filename (CamelMimePart *mime_part, const gchar *filename)
394 {
395         char *str;
396         
397         if (mime_part->disposition == NULL)
398                 mime_part->disposition = header_disposition_decode("attachment");
399
400         header_set_param(&mime_part->disposition->params, "filename", filename);
401         str = header_disposition_format(mime_part->disposition);
402
403         camel_medium_set_header (CAMEL_MEDIUM (mime_part),
404                                  "Content-Disposition", str);
405         g_free(str);
406         
407         header_content_type_set_param (mime_part->content_type, "name", filename);
408         str = header_content_type_format (mime_part->content_type);
409         camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-Type", str);
410         g_free (str);
411 }
412
413 const gchar *
414 camel_mime_part_get_filename (CamelMimePart *mime_part)
415 {
416         if (mime_part->disposition) {
417                 const gchar *name = header_param (mime_part->disposition->params, "filename");
418                 if (name)
419                         return name;
420         }
421
422         return header_content_type_param (mime_part->content_type, "name");
423 }
424
425
426 /* **** Content-ID: */
427
428 void
429 camel_mime_part_set_content_id (CamelMimePart *mime_part, const char *contentid)
430 {
431         char *cid, *id;
432         
433         if (contentid)
434                 id = g_strstrip (g_strdup (contentid));
435         else
436                 id = header_msgid_generate ();
437         
438         cid = g_strdup_printf ("<%s>", id);
439         g_free (id);
440         camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-ID", cid);
441         g_free (cid);
442 }
443
444 const gchar *
445 camel_mime_part_get_content_id (CamelMimePart *mime_part)
446 {
447         return mime_part->content_id;
448 }
449
450 /* **** Content-MD5: */
451
452 void
453 camel_mime_part_set_content_MD5 (CamelMimePart *mime_part, const char *md5)
454 {
455         camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-MD5", md5);
456 }
457
458 const gchar *
459 camel_mime_part_get_content_MD5 (CamelMimePart *mime_part)
460 {
461         return mime_part->content_MD5;
462 }
463
464 /* **** Content-MD5: */
465
466 void
467 camel_mime_part_set_content_location (CamelMimePart *mime_part, const char *location)
468 {
469         camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-Location", location);
470 }
471
472 const gchar *
473 camel_mime_part_get_content_location (CamelMimePart *mime_part)
474 {
475         return mime_part->content_location;
476 }
477
478 /* **** Content-Transfer-Encoding: */
479
480 void
481 camel_mime_part_set_encoding (CamelMimePart *mime_part,
482                               CamelMimePartEncodingType encoding)
483 {
484         const char *text;
485
486         text = camel_mime_part_encoding_to_string (encoding);
487         camel_medium_set_header (CAMEL_MEDIUM (mime_part),
488                                  "Content-Transfer-Encoding", text);
489 }
490
491 CamelMimePartEncodingType
492 camel_mime_part_get_encoding (CamelMimePart *mime_part)
493 {
494         return mime_part->encoding;
495 }
496
497 /* FIXME: do something with this stuff ... */
498
499 void
500 camel_mime_part_set_content_languages (CamelMimePart *mime_part, GList *content_languages)
501 {
502         if (mime_part->content_languages) string_list_free (mime_part->content_languages);
503         mime_part->content_languages = content_languages;
504
505         /* FIXME: translate to a header and set it */
506 }
507
508 const GList *
509 camel_mime_part_get_content_languages (CamelMimePart *mime_part)
510 {
511         return mime_part->content_languages;
512 }
513
514
515 /* **** */
516
517 /* **** Content-Type: */
518
519 void 
520 camel_mime_part_set_content_type (CamelMimePart *mime_part, const gchar *content_type)
521 {
522         camel_medium_set_header (CAMEL_MEDIUM (mime_part),
523                                  "Content-Type", content_type);
524 }
525
526 CamelContentType *
527 camel_mime_part_get_content_type (CamelMimePart *mime_part)
528 {
529         return mime_part->content_type;
530 }
531
532 /*********/
533
534
535
536 static void
537 set_content_object (CamelMedium *medium, CamelDataWrapper *content)
538 {
539         CamelMimePart *mime_part = CAMEL_MIME_PART (medium);
540         CamelContentType *object_content_type;
541
542         parent_class->set_content_object (medium, content);
543
544         object_content_type = camel_data_wrapper_get_mime_type_field (content);
545         if (mime_part->content_type != object_content_type) {
546                 char *txt;
547
548                 txt = header_content_type_format (object_content_type);
549                 camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-Type", txt);
550                 g_free(txt);
551         }
552 }
553
554 /**********************************************************************/
555
556 static int
557 write_references(CamelStream *stream, struct _header_raw *h)
558 {
559         int len, out, total;
560         char *v, *ids, *ide;
561
562         /* this is only approximate, based on the next >, this way it retains any content
563            from the original which may not be properly formatted, etc.  It also doesn't handle
564            the case where an individual messageid is too long, however thats a bad mail to
565            start with ... */
566
567         v = h->value;
568         len = strlen(h->name)+1;
569         total = camel_stream_printf(stream, "%s%s", h->name, isspace(v[0])?":":": ");
570         if (total == -1)
571                 return -1;
572         while (*v) {
573                 ids = v;
574                 ide = strchr(ids+1, '>');
575                 if (ide)
576                         v = ++ide;
577                 else
578                         ide = v = strlen(ids)+ids;
579
580                 if (len>0 && len + (ide - ids) >= CAMEL_FOLD_SIZE) {
581                         out = camel_stream_printf(stream, "\n\t");
582                         if (out == -1)
583                                 return -1;
584                         total += out;
585                         len = 0;
586                 }
587                 out = camel_stream_write(stream, ids, ide-ids);
588                 if (out == -1)
589                         return -1;
590                 len += out;
591                 total += out;
592         }
593         camel_stream_write(stream, "\n", 1);
594
595         return total;
596 }
597
598 #if 0
599 /* not needed - yet - handled by default case */
600 static int
601 write_fold(CamelStream *stream, struct _header_raw *h)
602 {
603         char *val;
604         int count;
605
606         val = header_fold(h->value, strlen(h->name));
607         count = camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
608         g_free(val);
609
610         return count;
611 }
612 #endif
613
614 static int
615 write_raw(CamelStream *stream, struct _header_raw *h)
616 {
617         char *val = h->value;
618
619         return camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
620 }
621
622 static int
623 write_to_stream(CamelDataWrapper *data_wrapper, CamelStream *stream)
624 {
625         CamelMimePart *mp = CAMEL_MIME_PART(data_wrapper);
626         CamelMedium *medium = CAMEL_MEDIUM(data_wrapper);
627         CamelStream *ostream = stream;
628         CamelDataWrapper *content;
629         int total = 0;
630         int count;
631
632         d(printf("mime_part::write_to_stream\n"));
633
634         /* FIXME: something needs to be done about this ... */
635         /* TODO: content-languages header? */
636
637         if (mp->headers) {
638                 struct _header_raw *h = mp->headers;
639                 char *val;
640                 int (*writefn)(CamelStream *stream, struct _header_raw *);
641                 
642                 /* fold/write the headers.   But dont fold headers that are already formatted
643                    (e.g. ones with parameter-lists, that we know about, and have created) */
644                 while (h) {
645                         val = h->value;
646                         if (val == NULL) {
647                                 g_warning("h->value is NULL here for %s", h->name);
648                                 count = 0;
649                         } else if ((writefn = g_hash_table_lookup(header_formatted_table, h->name)) == NULL) {
650                                 val = header_fold(val, strlen(h->name));
651                                 count = camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
652                                 g_free(val);
653                         } else {
654                                 count = writefn(stream, h);
655                         }
656                         if (count == -1)
657                                 return -1;
658                         total += count;
659                         h = h->next;
660                 }
661         }
662         
663         count = camel_stream_write(stream, "\n", 1);
664         if (count == -1)
665                 return -1;
666         total += count;
667         
668         content = camel_medium_get_content_object(medium);
669         if (content) {
670                 /* I dont really like this here, but i dont know where else it might go ... */
671 #define CAN_THIS_GO_ELSEWHERE
672 #ifdef CAN_THIS_GO_ELSEWHERE
673                 CamelMimeFilter *filter = NULL;
674                 CamelStreamFilter *filter_stream = NULL;
675                 CamelMimeFilter *charenc = NULL;
676                 const char *filename;
677                 const char *charset;
678                 
679                 switch (mp->encoding) {
680                 case CAMEL_MIME_PART_ENCODING_BASE64:
681                         filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
682                         break;
683                 case CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE:
684                         filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_ENC);
685                         break;
686                 case CAMEL_MIME_PART_ENCODING_UUENCODE:
687                         filename = camel_mime_part_get_filename (mp);
688                         count = camel_stream_printf (ostream, "begin 644 %s\n", filename ? filename : "untitled");
689                         if (count == -1)
690                                 return -1;
691                         total += count;
692                         filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_UU_ENC);
693                         break;
694                 default:
695                         break;
696                 }
697                 
698                 if (!content->rawtext && header_content_type_is(mp->content_type, "text", "*")) {
699                         charset = header_content_type_param(mp->content_type, "charset");
700                         if (charset && !(!strcasecmp(charset, "us-ascii") || !strcasecmp(charset, "utf-8"))) {
701                                 charenc = (CamelMimeFilter *)camel_mime_filter_charset_new_convert("UTF-8", charset);
702                         } 
703                 }
704                 
705                 if (filter || charenc) {
706                         filter_stream = camel_stream_filter_new_with_stream(stream);
707                         
708                         /* if we have a character encoder, add that always */
709                         if (charenc) {
710                                 camel_stream_filter_add(filter_stream, charenc);
711                                 camel_object_unref((CamelObject *)charenc);
712                         }
713                         
714                         /* we only re-do crlf on encoded blocks */
715                         if (filter && header_content_type_is(mp->content_type, "text", "*")) {
716                                 CamelMimeFilter *crlf = camel_mime_filter_crlf_new(CAMEL_MIME_FILTER_CRLF_ENCODE,
717                                                                                    CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
718                                 
719                                 camel_stream_filter_add(filter_stream, crlf);
720                                 camel_object_unref((CamelObject *)crlf);
721                         }
722                         
723                         if (filter) {
724                                 camel_stream_filter_add(filter_stream, filter);
725                                 camel_object_unref((CamelObject *)filter);
726                         }
727                         
728                         stream = (CamelStream *)filter_stream;
729                 }
730
731 #endif
732                 
733                 count = camel_data_wrapper_write_to_stream(content, stream);
734                 
735                 if (filter_stream) {
736                         camel_stream_flush((CamelStream *)filter_stream);
737                         camel_object_unref((CamelObject *)filter_stream);
738                 }
739                 if (count == -1)
740                         return -1;
741                 total += count;
742                 
743                 if (mp->encoding == CAMEL_MIME_PART_ENCODING_UUENCODE) {
744                         count = camel_stream_write (ostream, "end\n", 4);
745                         if (count == -1)
746                                 return -1;
747                         total += count;
748                 }
749         } else {
750                 g_warning("No content for medium, nothing to write");
751         }
752         
753         return total;
754 }
755
756 /* mime_part */
757 static int
758 construct_from_parser(CamelMimePart *dw, CamelMimeParser *mp)
759 {
760         struct _header_raw *headers;
761         const char *content;
762         char *buf;
763         size_t len;
764         int err;
765
766         d(printf("mime_part::construct_from_parser()\n"));
767
768         switch (camel_mime_parser_step(mp, &buf, &len)) {
769         case HSCAN_MESSAGE:
770                 /* set the default type of a message always */
771                 if (dw->content_type)
772                         header_content_type_unref (dw->content_type);
773                 dw->content_type = header_content_type_decode ("message/rfc822");
774         case HSCAN_HEADER:
775         case HSCAN_MULTIPART:
776                 /* we have the headers, build them into 'us' */
777                 headers = camel_mime_parser_headers_raw(mp);
778
779                 /* if content-type exists, process it first, set for fallback charset in headers */
780                 content = header_raw_find(&headers, "content-type", NULL);
781                 if (content)
782                         process_header((CamelMedium *)dw, "content-type", content);
783
784                 while (headers) {
785                         if (strcasecmp(headers->name, "content-type") == 0
786                             && headers->value != content)
787                                 camel_medium_add_header((CamelMedium *)dw, "X-Invalid-Content-Type", headers->value);
788                         else
789                                 camel_medium_add_header((CamelMedium *)dw, headers->name, headers->value);
790                         headers = headers->next;
791                 }
792
793                 camel_mime_part_construct_content_from_parser(dw, mp);
794                 break;
795         default:
796                 g_warning("Invalid state encountered???: %d", camel_mime_parser_state(mp));
797         }
798
799         d(printf("mime_part::construct_from_parser() leaving\n"));
800         err = camel_mime_parser_errno(mp);
801         if (err != 0) {
802                 errno = err;
803                 return -1;
804         }
805
806         return 0;
807 }
808
809 /**
810  * camel_mime_part_construct_from_parser:
811  * @mime_part: 
812  * @mp: 
813  * 
814  * 
815  * 
816  * Return value: 
817  **/
818 int
819 camel_mime_part_construct_from_parser(CamelMimePart *mime_part, CamelMimeParser *mp)
820 {
821         return CMP_CLASS (mime_part)->construct_from_parser (mime_part, mp);
822 }
823
824 static int
825 construct_from_stream(CamelDataWrapper *dw, CamelStream *s)
826 {
827         CamelMimeParser *mp;
828         int ret;
829
830         d(printf("mime_part::construct_from_stream()\n"));
831
832         mp = camel_mime_parser_new();
833         if (camel_mime_parser_init_with_stream(mp, s) == -1) {
834                 g_warning("Cannot create parser for stream");
835                 ret = -1;
836         } else {
837                 ret = camel_mime_part_construct_from_parser((CamelMimePart *)dw, mp);
838         }
839         camel_object_unref((CamelObject *)mp);
840         return ret;
841 }
842
843 /* this must be kept in sync with the header */
844 static const char *encodings[] = {
845         "",
846         "7bit",
847         "8bit",
848         "base64",
849         "quoted-printable",
850         "binary",
851         "x-uuencode",
852 };
853
854 const char *
855 camel_mime_part_encoding_to_string (CamelMimePartEncodingType encoding)
856 {
857         if (encoding >= sizeof(encodings)/sizeof(encodings[0]))
858                 encoding = 0;
859
860         return encodings[encoding];
861 }
862
863 /* FIXME I am not sure this is the correct way to do this.  */
864 CamelMimePartEncodingType
865 camel_mime_part_encoding_from_string (const gchar *string)
866 {
867         int i;
868
869         if (string != NULL) {
870                 for (i=0;i<sizeof(encodings)/sizeof(encodings[0]);i++)
871                         if (!strcasecmp(string, encodings[i]))
872                                 return i;
873         }
874
875         return CAMEL_MIME_PART_ENCODING_DEFAULT;
876 }
877
878
879 /******************************/
880 /**  Misc utility functions  **/
881
882 /**
883  * camel_mime_part_new:
884  *
885  * Return value: a new CamelMimePart
886  **/
887 CamelMimePart *
888 camel_mime_part_new (void)
889 {
890         return (CamelMimePart *)camel_object_new (CAMEL_MIME_PART_TYPE);
891 }
892
893 /**
894  * camel_mime_part_set_content:
895  * @camel_mime_part: Mime part
896  * @data: data to put into the part
897  * @length: length of @data
898  * @type: Content-Type of the data
899  * 
900  * Utility function used to set the content of a mime part object to 
901  * be the provided data. If @length is 0, this routine can be used as
902  * a way to remove old content (in which case @data and @type are
903  * ignored and may be %NULL).
904  **/
905 void 
906 camel_mime_part_set_content (CamelMimePart *camel_mime_part,
907                              const char *data, int length,
908                              const char *type) /* why on earth is the type last? */
909 {
910         CamelMedium *medium = CAMEL_MEDIUM (camel_mime_part);
911
912         if (length) {
913                 CamelDataWrapper *dw;
914                 CamelStream *stream;
915
916                 dw = camel_data_wrapper_new ();
917                 camel_data_wrapper_set_mime_type (dw, type);
918                 stream = camel_stream_mem_new_with_buffer (data, length);
919                 camel_data_wrapper_construct_from_stream (dw, stream);
920                 camel_object_unref (CAMEL_OBJECT (stream));
921                 camel_medium_set_content_object (medium, dw);
922                 camel_object_unref (CAMEL_OBJECT (dw));
923         } else {
924                 if (medium->content)
925                         camel_object_unref (CAMEL_OBJECT (medium->content));
926                 medium->content = NULL;
927         }
928 }