Updated for string-utils namespace changes.
[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 "camel-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 (camel_strcase_hash, camel_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 (camel_strcase_hash, camel_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         camel_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)
503                 camel_string_list_free (mime_part->content_languages);
504         
505         mime_part->content_languages = content_languages;
506
507         /* FIXME: translate to a header and set it */
508 }
509
510 const GList *
511 camel_mime_part_get_content_languages (CamelMimePart *mime_part)
512 {
513         return mime_part->content_languages;
514 }
515
516
517 /* **** */
518
519 /* **** Content-Type: */
520
521 void 
522 camel_mime_part_set_content_type (CamelMimePart *mime_part, const gchar *content_type)
523 {
524         camel_medium_set_header (CAMEL_MEDIUM (mime_part),
525                                  "Content-Type", content_type);
526 }
527
528 CamelContentType *
529 camel_mime_part_get_content_type (CamelMimePart *mime_part)
530 {
531         return mime_part->content_type;
532 }
533
534 /*********/
535
536
537
538 static void
539 set_content_object (CamelMedium *medium, CamelDataWrapper *content)
540 {
541         CamelMimePart *mime_part = CAMEL_MIME_PART (medium);
542         CamelContentType *object_content_type;
543
544         parent_class->set_content_object (medium, content);
545
546         object_content_type = camel_data_wrapper_get_mime_type_field (content);
547         if (mime_part->content_type != object_content_type) {
548                 char *txt;
549
550                 txt = header_content_type_format (object_content_type);
551                 camel_medium_set_header (CAMEL_MEDIUM (mime_part), "Content-Type", txt);
552                 g_free(txt);
553         }
554 }
555
556 /**********************************************************************/
557
558 static int
559 write_references(CamelStream *stream, struct _header_raw *h)
560 {
561         int len, out, total;
562         char *v, *ids, *ide;
563
564         /* this is only approximate, based on the next >, this way it retains any content
565            from the original which may not be properly formatted, etc.  It also doesn't handle
566            the case where an individual messageid is too long, however thats a bad mail to
567            start with ... */
568
569         v = h->value;
570         len = strlen(h->name)+1;
571         total = camel_stream_printf(stream, "%s%s", h->name, isspace(v[0])?":":": ");
572         if (total == -1)
573                 return -1;
574         while (*v) {
575                 ids = v;
576                 ide = strchr(ids+1, '>');
577                 if (ide)
578                         v = ++ide;
579                 else
580                         ide = v = strlen(ids)+ids;
581
582                 if (len>0 && len + (ide - ids) >= CAMEL_FOLD_SIZE) {
583                         out = camel_stream_printf(stream, "\n\t");
584                         if (out == -1)
585                                 return -1;
586                         total += out;
587                         len = 0;
588                 }
589                 out = camel_stream_write(stream, ids, ide-ids);
590                 if (out == -1)
591                         return -1;
592                 len += out;
593                 total += out;
594         }
595         camel_stream_write(stream, "\n", 1);
596
597         return total;
598 }
599
600 #if 0
601 /* not needed - yet - handled by default case */
602 static int
603 write_fold(CamelStream *stream, struct _header_raw *h)
604 {
605         char *val;
606         int count;
607
608         val = header_fold(h->value, strlen(h->name));
609         count = camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
610         g_free(val);
611
612         return count;
613 }
614 #endif
615
616 static int
617 write_raw(CamelStream *stream, struct _header_raw *h)
618 {
619         char *val = h->value;
620
621         return camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
622 }
623
624 static int
625 write_to_stream(CamelDataWrapper *data_wrapper, CamelStream *stream)
626 {
627         CamelMimePart *mp = CAMEL_MIME_PART(data_wrapper);
628         CamelMedium *medium = CAMEL_MEDIUM(data_wrapper);
629         CamelStream *ostream = stream;
630         CamelDataWrapper *content;
631         int total = 0;
632         int count;
633
634         d(printf("mime_part::write_to_stream\n"));
635
636         /* FIXME: something needs to be done about this ... */
637         /* TODO: content-languages header? */
638
639         if (mp->headers) {
640                 struct _header_raw *h = mp->headers;
641                 char *val;
642                 int (*writefn)(CamelStream *stream, struct _header_raw *);
643                 
644                 /* fold/write the headers.   But dont fold headers that are already formatted
645                    (e.g. ones with parameter-lists, that we know about, and have created) */
646                 while (h) {
647                         val = h->value;
648                         if (val == NULL) {
649                                 g_warning("h->value is NULL here for %s", h->name);
650                                 count = 0;
651                         } else if ((writefn = g_hash_table_lookup(header_formatted_table, h->name)) == NULL) {
652                                 val = header_fold(val, strlen(h->name));
653                                 count = camel_stream_printf(stream, "%s%s%s\n", h->name, isspace(val[0]) ? ":" : ": ", val);
654                                 g_free(val);
655                         } else {
656                                 count = writefn(stream, h);
657                         }
658                         if (count == -1)
659                                 return -1;
660                         total += count;
661                         h = h->next;
662                 }
663         }
664         
665         count = camel_stream_write(stream, "\n", 1);
666         if (count == -1)
667                 return -1;
668         total += count;
669         
670         content = camel_medium_get_content_object(medium);
671         if (content) {
672                 /* I dont really like this here, but i dont know where else it might go ... */
673 #define CAN_THIS_GO_ELSEWHERE
674 #ifdef CAN_THIS_GO_ELSEWHERE
675                 CamelMimeFilter *filter = NULL;
676                 CamelStreamFilter *filter_stream = NULL;
677                 CamelMimeFilter *charenc = NULL;
678                 const char *filename;
679                 const char *charset;
680                 
681                 switch (mp->encoding) {
682                 case CAMEL_MIME_PART_ENCODING_BASE64:
683                         filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
684                         break;
685                 case CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE:
686                         filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_ENC);
687                         break;
688                 case CAMEL_MIME_PART_ENCODING_UUENCODE:
689                         filename = camel_mime_part_get_filename (mp);
690                         count = camel_stream_printf (ostream, "begin 644 %s\n", filename ? filename : "untitled");
691                         if (count == -1)
692                                 return -1;
693                         total += count;
694                         filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_UU_ENC);
695                         break;
696                 default:
697                         break;
698                 }
699                 
700                 if (!content->rawtext && header_content_type_is(mp->content_type, "text", "*")) {
701                         charset = header_content_type_param(mp->content_type, "charset");
702                         if (charset && !(!strcasecmp(charset, "us-ascii") || !strcasecmp(charset, "utf-8"))) {
703                                 charenc = (CamelMimeFilter *)camel_mime_filter_charset_new_convert("UTF-8", charset);
704                         } 
705                 }
706                 
707                 if (filter || charenc) {
708                         filter_stream = camel_stream_filter_new_with_stream(stream);
709                         
710                         /* if we have a character encoder, add that always */
711                         if (charenc) {
712                                 camel_stream_filter_add(filter_stream, charenc);
713                                 camel_object_unref((CamelObject *)charenc);
714                         }
715                         
716                         /* we only re-do crlf on encoded blocks */
717                         if (filter && header_content_type_is(mp->content_type, "text", "*")) {
718                                 CamelMimeFilter *crlf = camel_mime_filter_crlf_new(CAMEL_MIME_FILTER_CRLF_ENCODE,
719                                                                                    CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
720                                 
721                                 camel_stream_filter_add(filter_stream, crlf);
722                                 camel_object_unref((CamelObject *)crlf);
723                         }
724                         
725                         if (filter) {
726                                 camel_stream_filter_add(filter_stream, filter);
727                                 camel_object_unref((CamelObject *)filter);
728                         }
729                         
730                         stream = (CamelStream *)filter_stream;
731                 }
732
733 #endif
734                 
735                 count = camel_data_wrapper_write_to_stream(content, stream);
736                 
737                 if (filter_stream) {
738                         camel_stream_flush((CamelStream *)filter_stream);
739                         camel_object_unref((CamelObject *)filter_stream);
740                 }
741                 if (count == -1)
742                         return -1;
743                 total += count;
744                 
745                 if (mp->encoding == CAMEL_MIME_PART_ENCODING_UUENCODE) {
746                         count = camel_stream_write (ostream, "end\n", 4);
747                         if (count == -1)
748                                 return -1;
749                         total += count;
750                 }
751         } else {
752                 g_warning("No content for medium, nothing to write");
753         }
754         
755         return total;
756 }
757
758 /* mime_part */
759 static int
760 construct_from_parser(CamelMimePart *dw, CamelMimeParser *mp)
761 {
762         struct _header_raw *headers;
763         const char *content;
764         char *buf;
765         size_t len;
766         int err;
767
768         d(printf("mime_part::construct_from_parser()\n"));
769
770         switch (camel_mime_parser_step(mp, &buf, &len)) {
771         case HSCAN_MESSAGE:
772                 /* set the default type of a message always */
773                 if (dw->content_type)
774                         header_content_type_unref (dw->content_type);
775                 dw->content_type = header_content_type_decode ("message/rfc822");
776         case HSCAN_HEADER:
777         case HSCAN_MULTIPART:
778                 /* we have the headers, build them into 'us' */
779                 headers = camel_mime_parser_headers_raw(mp);
780
781                 /* if content-type exists, process it first, set for fallback charset in headers */
782                 content = header_raw_find(&headers, "content-type", NULL);
783                 if (content)
784                         process_header((CamelMedium *)dw, "content-type", content);
785
786                 while (headers) {
787                         if (strcasecmp(headers->name, "content-type") == 0
788                             && headers->value != content)
789                                 camel_medium_add_header((CamelMedium *)dw, "X-Invalid-Content-Type", headers->value);
790                         else
791                                 camel_medium_add_header((CamelMedium *)dw, headers->name, headers->value);
792                         headers = headers->next;
793                 }
794
795                 camel_mime_part_construct_content_from_parser(dw, mp);
796                 break;
797         default:
798                 g_warning("Invalid state encountered???: %d", camel_mime_parser_state(mp));
799         }
800
801         d(printf("mime_part::construct_from_parser() leaving\n"));
802         err = camel_mime_parser_errno(mp);
803         if (err != 0) {
804                 errno = err;
805                 return -1;
806         }
807
808         return 0;
809 }
810
811 /**
812  * camel_mime_part_construct_from_parser:
813  * @mime_part: 
814  * @mp: 
815  * 
816  * 
817  * 
818  * Return value: 
819  **/
820 int
821 camel_mime_part_construct_from_parser(CamelMimePart *mime_part, CamelMimeParser *mp)
822 {
823         return CMP_CLASS (mime_part)->construct_from_parser (mime_part, mp);
824 }
825
826 static int
827 construct_from_stream(CamelDataWrapper *dw, CamelStream *s)
828 {
829         CamelMimeParser *mp;
830         int ret;
831
832         d(printf("mime_part::construct_from_stream()\n"));
833
834         mp = camel_mime_parser_new();
835         if (camel_mime_parser_init_with_stream(mp, s) == -1) {
836                 g_warning("Cannot create parser for stream");
837                 ret = -1;
838         } else {
839                 ret = camel_mime_part_construct_from_parser((CamelMimePart *)dw, mp);
840         }
841         camel_object_unref((CamelObject *)mp);
842         return ret;
843 }
844
845 /* this must be kept in sync with the header */
846 static const char *encodings[] = {
847         "",
848         "7bit",
849         "8bit",
850         "base64",
851         "quoted-printable",
852         "binary",
853         "x-uuencode",
854 };
855
856 const char *
857 camel_mime_part_encoding_to_string (CamelMimePartEncodingType encoding)
858 {
859         if (encoding >= sizeof(encodings)/sizeof(encodings[0]))
860                 encoding = 0;
861
862         return encodings[encoding];
863 }
864
865 /* FIXME I am not sure this is the correct way to do this.  */
866 CamelMimePartEncodingType
867 camel_mime_part_encoding_from_string (const gchar *string)
868 {
869         int i;
870
871         if (string != NULL) {
872                 for (i=0;i<sizeof(encodings)/sizeof(encodings[0]);i++)
873                         if (!strcasecmp(string, encodings[i]))
874                                 return i;
875         }
876
877         return CAMEL_MIME_PART_ENCODING_DEFAULT;
878 }
879
880
881 /******************************/
882 /**  Misc utility functions  **/
883
884 /**
885  * camel_mime_part_new:
886  *
887  * Return value: a new CamelMimePart
888  **/
889 CamelMimePart *
890 camel_mime_part_new (void)
891 {
892         return (CamelMimePart *)camel_object_new (CAMEL_MIME_PART_TYPE);
893 }
894
895 /**
896  * camel_mime_part_set_content:
897  * @camel_mime_part: Mime part
898  * @data: data to put into the part
899  * @length: length of @data
900  * @type: Content-Type of the data
901  * 
902  * Utility function used to set the content of a mime part object to 
903  * be the provided data. If @length is 0, this routine can be used as
904  * a way to remove old content (in which case @data and @type are
905  * ignored and may be %NULL).
906  **/
907 void 
908 camel_mime_part_set_content (CamelMimePart *camel_mime_part,
909                              const char *data, int length,
910                              const char *type) /* why on earth is the type last? */
911 {
912         CamelMedium *medium = CAMEL_MEDIUM (camel_mime_part);
913
914         if (length) {
915                 CamelDataWrapper *dw;
916                 CamelStream *stream;
917
918                 dw = camel_data_wrapper_new ();
919                 camel_data_wrapper_set_mime_type (dw, type);
920                 stream = camel_stream_mem_new_with_buffer (data, length);
921                 camel_data_wrapper_construct_from_stream (dw, stream);
922                 camel_object_unref (CAMEL_OBJECT (stream));
923                 camel_medium_set_content_object (medium, dw);
924                 camel_object_unref (CAMEL_OBJECT (dw));
925         } else {
926                 if (medium->content)
927                         camel_object_unref (CAMEL_OBJECT (medium->content));
928                 medium->content = NULL;
929         }
930 }