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 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 *
6 * This library is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
19 * Michael Zucchi <notzed@ximian.com>
20 * Jeffrey Stedfast <fejj@ximian.com>
21 */
22
23 #include "evolution-data-server-config.h"
24
25 #include <ctype.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "camel-charset-map.h"
31 #include "camel-debug.h"
32 #include "camel-iconv.h"
33 #include "camel-filter-output-stream.h"
34 #include "camel-mime-filter-basic.h"
35 #include "camel-mime-filter-charset.h"
36 #include "camel-mime-filter-crlf.h"
37 #include "camel-mime-parser.h"
38 #include "camel-mime-part-utils.h"
39 #include "camel-mime-part.h"
40 #include "camel-mime-utils.h"
41 #include "camel-stream-filter.h"
42 #include "camel-stream-mem.h"
43 #include "camel-stream-null.h"
44 #include "camel-string-utils.h"
45
46 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
47
48 typedef struct _AsyncContext AsyncContext;
49
50 struct _CamelMimePartPrivate {
51 gchar *description;
52 CamelContentDisposition *disposition;
53 gchar *content_id;
54 gchar *content_md5;
55 gchar *content_location;
56 GList *content_languages;
57 CamelTransferEncoding encoding;
58 /* mime headers */
59 CamelNameValueArray *headers;
60 };
61
62 struct _AsyncContext {
63 CamelMimeParser *parser;
64 };
65
66 enum {
67 PROP_0,
68 PROP_CONTENT_ID,
69 PROP_CONTENT_LOCATION,
70 PROP_CONTENT_MD5,
71 PROP_DESCRIPTION,
72 PROP_DISPOSITION,
73 PROP_FILENAME
74 };
75
76 typedef enum {
77 HEADER_UNKNOWN,
78 HEADER_DESCRIPTION,
79 HEADER_DISPOSITION,
80 HEADER_CONTENT_ID,
81 HEADER_ENCODING,
82 HEADER_CONTENT_MD5,
83 HEADER_CONTENT_LOCATION,
84 HEADER_CONTENT_LANGUAGES,
85 HEADER_CONTENT_TYPE
86 } CamelHeaderType;
87
88 static GHashTable *header_name_table;
89 static GHashTable *header_formatted_table;
90
G_DEFINE_TYPE_WITH_PRIVATE(CamelMimePart,camel_mime_part,CAMEL_TYPE_MEDIUM)91 G_DEFINE_TYPE_WITH_PRIVATE (CamelMimePart, camel_mime_part, CAMEL_TYPE_MEDIUM)
92
93 static void
94 async_context_free (AsyncContext *async_context)
95 {
96 if (async_context->parser != NULL)
97 g_object_unref (async_context->parser);
98
99 g_slice_free (AsyncContext, async_context);
100 }
101
102 static gssize
write_header(gpointer stream,const gchar * name,const gchar * value,GCancellable * cancellable,GError ** error)103 write_header (gpointer stream,
104 const gchar *name,
105 const gchar *value,
106 GCancellable *cancellable,
107 GError **error)
108 {
109 GString *buffer;
110 gssize n_written = 0;
111
112 buffer = g_string_new (name);
113 g_string_append_c (buffer, ':');
114 if (!isspace (value[0]))
115 g_string_append_c (buffer, ' ');
116 g_string_append (buffer, value);
117 g_string_append_c (buffer, '\n');
118
119 /* XXX For now we handle both types of streams. */
120
121 if (CAMEL_IS_STREAM (stream)) {
122 n_written = camel_stream_write (
123 CAMEL_STREAM (stream),
124 buffer->str, buffer->len,
125 cancellable, error);
126 } else if (G_IS_OUTPUT_STREAM (stream)) {
127 gboolean success;
128 gsize bytes_written = 0;
129
130 success = g_output_stream_write_all (
131 G_OUTPUT_STREAM (stream),
132 buffer->str, buffer->len,
133 &bytes_written, cancellable, error);
134 if (success)
135 n_written = (gssize) bytes_written;
136 else
137 n_written = -1;
138 } else {
139 g_warn_if_reached ();
140 }
141
142 g_string_free (buffer, TRUE);
143
144 return n_written;
145 }
146
147 static gssize
write_references(gpointer stream,const gchar * name,const gchar * value,GCancellable * cancellable,GError ** error)148 write_references (gpointer stream,
149 const gchar *name,
150 const gchar *value,
151 GCancellable *cancellable,
152 GError **error)
153 {
154 GString *buffer;
155 const gchar *ids, *ide;
156 gssize n_written = 0;
157 gsize len;
158
159 /* this is only approximate, based on the next >, this way it retains
160 * any content from the original which may not be properly formatted,
161 * etc. It also doesn't handle the case where an individual messageid
162 * is too long, however thats a bad mail to start with ... */
163
164 buffer = g_string_new (name);
165 g_string_append_c (buffer, ':');
166 if (!isspace (value[0]))
167 g_string_append_c (buffer, ' ');
168
169 /* Fold only when not folded already */
170 if (!strchr (value, '\n')) {
171 len = buffer->len;
172
173 while (*value) {
174 ids = value;
175 ide = strchr (ids + 1, '>');
176 if (ide)
177 value = ++ide;
178 else
179 ide = value = strlen (ids) + ids;
180
181 if (len > 0 && len + (ide - ids) >= CAMEL_FOLD_SIZE) {
182 g_string_append_len (buffer, "\n\t", 2);
183 len = 0;
184 }
185
186 g_string_append_len (buffer, ids, ide - ids);
187 len += (ide - ids);
188 }
189 } else {
190 g_string_append (buffer, value);
191 }
192
193 if (buffer->len > 0 && buffer->str[buffer->len - 1] != '\n')
194 g_string_append_c (buffer, '\n');
195
196 /* XXX For now we handle both types of streams. */
197
198 if (CAMEL_IS_STREAM (stream)) {
199 n_written = camel_stream_write (
200 CAMEL_STREAM (stream),
201 buffer->str, buffer->len,
202 cancellable, error);
203 } else if (G_IS_OUTPUT_STREAM (stream)) {
204 gboolean success;
205 gsize bytes_written = 0;
206
207 success = g_output_stream_write_all (
208 G_OUTPUT_STREAM (stream),
209 buffer->str, buffer->len,
210 &bytes_written, cancellable, error);
211 if (success)
212 n_written = (gssize) bytes_written;
213 else
214 n_written = -1;
215 } else {
216 g_warn_if_reached ();
217 }
218
219 g_string_free (buffer, TRUE);
220
221 return n_written;
222 }
223
224 /* loads in a hash table the set of header names we */
225 /* recognize and associate them with a unique enum */
226 /* identifier (see CamelHeaderType above) */
227 static void
init_header_name_table(void)228 init_header_name_table (void)
229 {
230 if (header_name_table)
231 return;
232
233 header_name_table = g_hash_table_new (
234 camel_strcase_hash, camel_strcase_equal);
235 g_hash_table_insert (
236 header_name_table,
237 (gpointer) "Content-Description",
238 GINT_TO_POINTER (HEADER_DESCRIPTION));
239 g_hash_table_insert (
240 header_name_table,
241 (gpointer) "Content-Disposition",
242 GINT_TO_POINTER (HEADER_DISPOSITION));
243 g_hash_table_insert (
244 header_name_table,
245 (gpointer) "Content-id",
246 GINT_TO_POINTER (HEADER_CONTENT_ID));
247 g_hash_table_insert (
248 header_name_table,
249 (gpointer) "Content-Transfer-Encoding",
250 GINT_TO_POINTER (HEADER_ENCODING));
251 g_hash_table_insert (
252 header_name_table,
253 (gpointer) "Content-MD5",
254 GINT_TO_POINTER (HEADER_CONTENT_MD5));
255 g_hash_table_insert (
256 header_name_table,
257 (gpointer) "Content-Location",
258 GINT_TO_POINTER (HEADER_CONTENT_LOCATION));
259 g_hash_table_insert (
260 header_name_table,
261 (gpointer) "Content-Type",
262 GINT_TO_POINTER (HEADER_CONTENT_TYPE));
263
264 header_formatted_table = g_hash_table_new (
265 camel_strcase_hash, camel_strcase_equal);
266 g_hash_table_insert (
267 header_formatted_table,
268 (gpointer) "Content-Type", write_header);
269 g_hash_table_insert (
270 header_formatted_table,
271 (gpointer) "Content-Disposition", write_header);
272 g_hash_table_insert (
273 header_formatted_table,
274 (gpointer) "From", write_header);
275 g_hash_table_insert (
276 header_formatted_table,
277 (gpointer) "Reply-To", write_header);
278 g_hash_table_insert (
279 header_formatted_table,
280 (gpointer) "Message-ID", write_header);
281 g_hash_table_insert (
282 header_formatted_table,
283 (gpointer) "In-Reply-To", write_header);
284 g_hash_table_insert (
285 header_formatted_table,
286 (gpointer) "References", write_references);
287 }
288
289 static void
mime_part_set_disposition(CamelMimePart * mime_part,const gchar * disposition)290 mime_part_set_disposition (CamelMimePart *mime_part,
291 const gchar *disposition)
292 {
293 camel_content_disposition_unref (mime_part->priv->disposition);
294 if (disposition)
295 mime_part->priv->disposition =
296 camel_content_disposition_decode (disposition);
297 else
298 mime_part->priv->disposition = NULL;
299 }
300
301 static gboolean
mime_part_process_header(CamelMedium * medium,const gchar * name,const gchar * value)302 mime_part_process_header (CamelMedium *medium,
303 const gchar *name,
304 const gchar *value)
305 {
306 CamelMimePart *mime_part = CAMEL_MIME_PART (medium);
307 CamelHeaderType header_type;
308 CamelContentType *content_type;
309 const gchar *charset;
310 gchar *text;
311
312 /* Try to parse the header pair. If it corresponds to something */
313 /* known, the job is done in the parsing routine. If not, */
314 /* we simply add the header in a raw fashion */
315
316 header_type = (CamelHeaderType) GPOINTER_TO_INT (g_hash_table_lookup (header_name_table, name));
317 switch (header_type) {
318 case HEADER_DESCRIPTION: /* raw header->utf8 conversion */
319 g_free (mime_part->priv->description);
320 if (camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mime_part))) {
321 charset = camel_content_type_param (camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mime_part)), "charset");
322 charset = camel_iconv_charset_name (charset);
323 } else
324 charset = NULL;
325 mime_part->priv->description = g_strstrip (camel_header_decode_string (value, charset));
326 break;
327 case HEADER_DISPOSITION:
328 mime_part_set_disposition (mime_part, value);
329 break;
330 case HEADER_CONTENT_ID:
331 g_free (mime_part->priv->content_id);
332 mime_part->priv->content_id = camel_header_contentid_decode (value);
333 break;
334 case HEADER_ENCODING:
335 text = camel_header_token_decode (value);
336 mime_part->priv->encoding = camel_transfer_encoding_from_string (text);
337 g_free (text);
338 break;
339 case HEADER_CONTENT_MD5:
340 g_free (mime_part->priv->content_md5);
341 mime_part->priv->content_md5 = g_strdup (value);
342 break;
343 case HEADER_CONTENT_LOCATION:
344 g_free (mime_part->priv->content_location);
345 mime_part->priv->content_location = camel_header_location_decode (value);
346 break;
347 case HEADER_CONTENT_TYPE:
348 content_type = camel_content_type_decode (value);
349 if (content_type)
350 camel_data_wrapper_take_mime_type_field (CAMEL_DATA_WRAPPER (mime_part), content_type);
351 break;
352 default:
353 return FALSE;
354 }
355 return TRUE;
356 }
357
358 static void
mime_part_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)359 mime_part_set_property (GObject *object,
360 guint property_id,
361 const GValue *value,
362 GParamSpec *pspec)
363 {
364 switch (property_id) {
365 case PROP_CONTENT_ID:
366 camel_mime_part_set_content_id (
367 CAMEL_MIME_PART (object),
368 g_value_get_string (value));
369 return;
370
371 case PROP_CONTENT_MD5:
372 camel_mime_part_set_content_md5 (
373 CAMEL_MIME_PART (object),
374 g_value_get_string (value));
375 return;
376
377 case PROP_CONTENT_LOCATION:
378 camel_mime_part_set_content_location (
379 CAMEL_MIME_PART (object),
380 g_value_get_string (value));
381 return;
382
383 case PROP_DESCRIPTION:
384 camel_mime_part_set_description (
385 CAMEL_MIME_PART (object),
386 g_value_get_string (value));
387 return;
388
389 case PROP_DISPOSITION:
390 camel_mime_part_set_disposition (
391 CAMEL_MIME_PART (object),
392 g_value_get_string (value));
393 return;
394 }
395
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
397 }
398
399 static void
mime_part_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)400 mime_part_get_property (GObject *object,
401 guint property_id,
402 GValue *value,
403 GParamSpec *pspec)
404 {
405 switch (property_id) {
406 case PROP_CONTENT_ID:
407 g_value_set_string (
408 value, camel_mime_part_get_content_id (
409 CAMEL_MIME_PART (object)));
410 return;
411
412 case PROP_CONTENT_MD5:
413 g_value_set_string (
414 value, camel_mime_part_get_content_md5 (
415 CAMEL_MIME_PART (object)));
416 return;
417
418 case PROP_CONTENT_LOCATION:
419 g_value_set_string (
420 value, camel_mime_part_get_content_location (
421 CAMEL_MIME_PART (object)));
422 return;
423
424 case PROP_DESCRIPTION:
425 g_value_set_string (
426 value, camel_mime_part_get_description (
427 CAMEL_MIME_PART (object)));
428 return;
429
430 case PROP_DISPOSITION:
431 g_value_set_string (
432 value, camel_mime_part_get_disposition (
433 CAMEL_MIME_PART (object)));
434 return;
435 }
436
437 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
438 }
439
440 static void
mime_part_finalize(GObject * object)441 mime_part_finalize (GObject *object)
442 {
443 CamelMimePartPrivate *priv;
444
445 priv = CAMEL_MIME_PART (object)->priv;
446
447 g_free (priv->description);
448 g_free (priv->content_id);
449 g_free (priv->content_md5);
450 g_free (priv->content_location);
451
452 g_list_free_full (priv->content_languages, (GDestroyNotify) g_free);
453 camel_content_disposition_unref (priv->disposition);
454 camel_name_value_array_free (priv->headers);
455
456 /* Chain up to parent's finalize() method. */
457 G_OBJECT_CLASS (camel_mime_part_parent_class)->finalize (object);
458 }
459
460 static void
mime_part_add_header(CamelMedium * medium,const gchar * name,const gchar * value)461 mime_part_add_header (CamelMedium *medium,
462 const gchar *name,
463 const gchar *value)
464 {
465 CamelMimePart *part = CAMEL_MIME_PART (medium);
466
467 /* Try to parse the header pair. If it corresponds to something */
468 /* known, the job is done in the parsing routine. If not, */
469 /* we simply add the header in a raw fashion */
470
471 /* If it was one of the headers we handled, it must be unique, set it instead of add */
472 if (mime_part_process_header (medium, name, value))
473 camel_name_value_array_remove_named (part->priv->headers, CAMEL_COMPARE_CASE_INSENSITIVE, name, TRUE);
474
475 camel_name_value_array_append (part->priv->headers, name, value);
476 }
477
478 static void
mime_part_set_header(CamelMedium * medium,const gchar * name,const gchar * value)479 mime_part_set_header (CamelMedium *medium,
480 const gchar *name,
481 const gchar *value)
482 {
483 CamelMimePart *part = CAMEL_MIME_PART (medium);
484
485 mime_part_process_header (medium, name, value);
486 camel_name_value_array_remove_named (part->priv->headers, CAMEL_COMPARE_CASE_INSENSITIVE, name, TRUE);
487
488 camel_name_value_array_append (part->priv->headers, name, value);
489 }
490
491 static void
mime_part_remove_header(CamelMedium * medium,const gchar * name)492 mime_part_remove_header (CamelMedium *medium,
493 const gchar *name)
494 {
495 CamelMimePart *part = CAMEL_MIME_PART (medium);
496
497 mime_part_process_header (medium, name, NULL);
498 camel_name_value_array_remove_named (part->priv->headers, CAMEL_COMPARE_CASE_INSENSITIVE, name, TRUE);
499 }
500
501 static const gchar *
mime_part_get_header(CamelMedium * medium,const gchar * name)502 mime_part_get_header (CamelMedium *medium,
503 const gchar *name)
504 {
505 CamelMimePart *part = CAMEL_MIME_PART (medium);
506 const gchar *value;
507
508 value = camel_name_value_array_get_named (part->priv->headers, CAMEL_COMPARE_CASE_INSENSITIVE, name);
509
510 /* Skip leading whitespace. */
511 while (value != NULL && g_ascii_isspace (*value))
512 value++;
513
514 return value;
515 }
516
517 static CamelNameValueArray *
mime_part_dup_headers(CamelMedium * medium)518 mime_part_dup_headers (CamelMedium *medium)
519 {
520 CamelMimePart *part = CAMEL_MIME_PART (medium);
521
522 return camel_name_value_array_copy (part->priv->headers);
523 }
524
525 static const CamelNameValueArray *
mime_part_get_headers(CamelMedium * medium)526 mime_part_get_headers (CamelMedium *medium)
527 {
528 CamelMimePart *part = CAMEL_MIME_PART (medium);
529
530 return part->priv->headers;
531 }
532
533 static void
mime_part_set_content(CamelMedium * medium,CamelDataWrapper * content)534 mime_part_set_content (CamelMedium *medium,
535 CamelDataWrapper *content)
536 {
537 CamelDataWrapper *mime_part = CAMEL_DATA_WRAPPER (medium);
538 CamelMediumClass *medium_class;
539 CamelContentType *content_type;
540
541 /* Chain up to parent's set_content() method. */
542 medium_class = CAMEL_MEDIUM_CLASS (camel_mime_part_parent_class);
543 medium_class->set_content (medium, content);
544
545 content_type = camel_data_wrapper_get_mime_type_field (content);
546 if (camel_data_wrapper_get_mime_type_field (mime_part) != content_type) {
547 gchar *txt;
548
549 txt = camel_content_type_format (content_type);
550 camel_medium_set_header (medium, "Content-Type", txt);
551 g_free (txt);
552 }
553 }
554
555 static gssize
mime_part_write_to_stream_sync(CamelDataWrapper * dw,CamelStream * stream,GCancellable * cancellable,GError ** error)556 mime_part_write_to_stream_sync (CamelDataWrapper *dw,
557 CamelStream *stream,
558 GCancellable *cancellable,
559 GError **error)
560 {
561 CamelMimePart *mp = CAMEL_MIME_PART (dw);
562 CamelMedium *medium = CAMEL_MEDIUM (dw);
563 CamelStream *ostream = stream;
564 CamelDataWrapper *content;
565 gssize total = 0;
566 gssize count;
567 gint errnosav;
568 guint ii;
569 const gchar *header_name = NULL, *header_value = NULL;
570
571 d (printf ("mime_part::write_to_stream\n"));
572
573 /* FIXME: something needs to be done about this ... */
574 /* TODO: content-languages header? */
575
576 for (ii = 0; camel_name_value_array_get (mp->priv->headers, ii, &header_name, &header_value); ii++) {
577 gssize (*writefn) (
578 gpointer stream,
579 const gchar *name,
580 const gchar *value,
581 GCancellable *cancellable,
582 GError **error);
583 if (header_value == NULL) {
584 g_warning ("header_value is NULL here for %s", header_name);
585 count = 0;
586 } else if ((writefn = g_hash_table_lookup (header_formatted_table, header_name)) == NULL) {
587 gchar *val = camel_header_fold (header_value, strlen (header_name));
588 count = write_header (
589 stream, header_name, val,
590 cancellable, error);
591 g_free (val);
592 } else {
593 count = writefn (
594 stream, header_name, header_value,
595 cancellable, error);
596 }
597 if (count == -1)
598 return -1;
599 total += count;
600 }
601
602 count = camel_stream_write (stream, "\n", 1, cancellable, error);
603 if (count == -1)
604 return -1;
605 total += count;
606
607 content = camel_medium_get_content (medium);
608 if (content) {
609 CamelMimeFilter *filter = NULL;
610 CamelStream *filter_stream = NULL;
611 CamelMimeFilter *charenc = NULL;
612 const gchar *content_charset = NULL;
613 const gchar *part_charset = NULL;
614 gboolean reencode = FALSE;
615 const gchar *filename;
616
617 if (camel_content_type_is (camel_data_wrapper_get_mime_type_field (dw), "text", "*")) {
618 content_charset = camel_content_type_param (camel_data_wrapper_get_mime_type_field (content), "charset");
619 part_charset = camel_content_type_param (camel_data_wrapper_get_mime_type_field (dw), "charset");
620
621 if (content_charset && part_charset) {
622 content_charset = camel_iconv_charset_name (content_charset);
623 part_charset = camel_iconv_charset_name (part_charset);
624 }
625 }
626
627 if (mp->priv->encoding != camel_data_wrapper_get_encoding (content)) {
628 gchar *content;
629
630 switch (mp->priv->encoding) {
631 case CAMEL_TRANSFER_ENCODING_BASE64:
632 filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
633 break;
634 case CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE:
635 filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_ENC);
636 break;
637 case CAMEL_TRANSFER_ENCODING_UUENCODE:
638 filename = camel_mime_part_get_filename (mp);
639 if (filename == NULL)
640 filename = "untitled";
641
642 content = g_strdup_printf (
643 "begin 644 %s\n", filename);
644 count = camel_stream_write_string (
645 ostream, content, cancellable, error);
646 g_free (content);
647
648 if (count == -1)
649 return -1;
650
651 total += count;
652 filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_UU_ENC);
653 break;
654 default:
655 /* content is encoded but the part doesn't want to be... */
656 reencode = TRUE;
657 break;
658 }
659 }
660
661 if (content_charset && part_charset && part_charset != content_charset)
662 charenc = camel_mime_filter_charset_new (content_charset, part_charset);
663
664 if (filter || charenc) {
665 filter_stream = camel_stream_filter_new (stream);
666
667 /* if we have a character encoder, add that always */
668 if (charenc) {
669 camel_stream_filter_add (
670 CAMEL_STREAM_FILTER (filter_stream), charenc);
671 g_object_unref (charenc);
672 }
673
674 if (filter) {
675 camel_stream_filter_add (
676 CAMEL_STREAM_FILTER (filter_stream), filter);
677 g_object_unref (filter);
678 }
679
680 stream = filter_stream;
681
682 reencode = TRUE;
683 }
684
685 if (reencode)
686 count = camel_data_wrapper_decode_to_stream_sync (
687 content, stream, cancellable, error);
688 else
689 count = camel_data_wrapper_write_to_stream_sync (
690 content, stream, cancellable, error);
691
692 if (filter_stream) {
693 errnosav = errno;
694 camel_stream_flush (stream, NULL, NULL);
695 g_object_unref (filter_stream);
696 errno = errnosav;
697 }
698
699 if (count == -1)
700 return -1;
701
702 total += count;
703
704 if (reencode && mp->priv->encoding == CAMEL_TRANSFER_ENCODING_UUENCODE) {
705 count = camel_stream_write (
706 ostream, "end\n", 4, cancellable, error);
707 if (count == -1)
708 return -1;
709 total += count;
710 }
711 } else {
712 g_warning ("No content for medium, nothing to write");
713 }
714
715 return total;
716 }
717
718 static gboolean
mime_part_construct_from_stream_sync(CamelDataWrapper * dw,CamelStream * stream,GCancellable * cancellable,GError ** error)719 mime_part_construct_from_stream_sync (CamelDataWrapper *dw,
720 CamelStream *stream,
721 GCancellable *cancellable,
722 GError **error)
723 {
724 CamelMimeParser *parser;
725 gboolean success;
726
727 d (printf ("mime_part::construct_from_stream()\n"));
728
729 parser = camel_mime_parser_new ();
730 if (camel_mime_parser_init_with_stream (parser, stream, error) == -1) {
731 success = FALSE;
732 } else {
733 success = camel_mime_part_construct_from_parser_sync (
734 CAMEL_MIME_PART (dw), parser, cancellable, error);
735 }
736 g_object_unref (parser);
737
738 return success;
739 }
740
741 static gssize
mime_part_write_to_output_stream_sync(CamelDataWrapper * dw,GOutputStream * output_stream,GCancellable * cancellable,GError ** error)742 mime_part_write_to_output_stream_sync (CamelDataWrapper *dw,
743 GOutputStream *output_stream,
744 GCancellable *cancellable,
745 GError **error)
746 {
747 CamelMimePart *mp = CAMEL_MIME_PART (dw);
748 CamelMedium *medium = CAMEL_MEDIUM (dw);
749 CamelDataWrapper *content;
750 gsize bytes_written;
751 gssize total = 0;
752 gssize result;
753 gboolean success;
754 guint ii;
755 const gchar *header_name = NULL, *header_value = NULL;
756
757 d (printf ("mime_part::write_to_stream\n"));
758
759 /* FIXME: something needs to be done about this ... */
760 /* TODO: content-languages header? */
761
762 for (ii = 0; camel_name_value_array_get (mp->priv->headers, ii, &header_name, &header_value); ii++) {
763 gssize (*writefn) (
764 gpointer stream,
765 const gchar *name,
766 const gchar *value,
767 GCancellable *cancellable,
768 GError **error);
769 if (header_value == NULL) {
770 g_warning ("header_value is NULL here for %s", header_name);
771 bytes_written = 0;
772 result = 0;
773 } else if ((writefn = g_hash_table_lookup (header_formatted_table, header_name)) == NULL) {
774 gchar *val = camel_header_fold (header_value, strlen (header_name));
775 result = write_header (
776 output_stream, header_name, val,
777 cancellable, error);
778 g_free (val);
779 } else {
780 result = writefn (
781 output_stream, header_name, header_value,
782 cancellable, error);
783 }
784 if (result == -1)
785 return -1;
786 total += result;
787 }
788
789 success = g_output_stream_write_all (
790 output_stream, "\n", 1,
791 &bytes_written, cancellable, error);
792 if (!success)
793 return -1;
794 total += (gssize) bytes_written;
795
796 content = camel_medium_get_content (medium);
797 if (content) {
798 CamelMimeFilter *filter = NULL;
799 GOutputStream *filter_stream;
800 const gchar *content_charset = NULL;
801 const gchar *part_charset = NULL;
802 gboolean content_type_is_text;
803 gboolean uuencoded = FALSE;
804 gboolean reencode = FALSE;
805 const gchar *filename;
806
807 content_type_is_text =
808 camel_content_type_is (camel_data_wrapper_get_mime_type_field (dw), "text", "*");
809
810 if (content_type_is_text) {
811 content_charset = camel_content_type_param (camel_data_wrapper_get_mime_type_field (content), "charset");
812 part_charset = camel_content_type_param (camel_data_wrapper_get_mime_type_field (dw), "charset");
813
814 if (content_charset && part_charset) {
815 content_charset = camel_iconv_charset_name (content_charset);
816 part_charset = camel_iconv_charset_name (part_charset);
817 }
818 }
819
820 if (mp->priv->encoding != camel_data_wrapper_get_encoding (content)) {
821 gchar *content;
822
823 switch (mp->priv->encoding) {
824 case CAMEL_TRANSFER_ENCODING_BASE64:
825 filter = camel_mime_filter_basic_new (
826 CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
827 break;
828 case CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE:
829 filter = camel_mime_filter_basic_new (
830 CAMEL_MIME_FILTER_BASIC_QP_ENC);
831 break;
832 case CAMEL_TRANSFER_ENCODING_UUENCODE:
833 filename = camel_mime_part_get_filename (mp);
834 if (filename == NULL)
835 filename = "untitled";
836
837 content = g_strdup_printf (
838 "begin 644 %s\n", filename);
839 success = g_output_stream_write_all (
840 output_stream,
841 content, strlen (content),
842 &bytes_written, cancellable, error);
843 g_free (content);
844
845 if (!success)
846 return -1;
847
848 uuencoded = TRUE;
849
850 total += bytes_written;
851 filter = camel_mime_filter_basic_new (
852 CAMEL_MIME_FILTER_BASIC_UU_ENC);
853 break;
854 default:
855 /* content is encoded but the part doesn't want to be... */
856 reencode = TRUE;
857 break;
858 }
859 }
860
861 filter_stream = g_object_ref (output_stream);
862
863 if (content_charset && part_charset && part_charset != content_charset) {
864 CamelMimeFilter *charenc;
865 GOutputStream *temp_stream;
866
867 charenc = camel_mime_filter_charset_new (
868 content_charset, part_charset);
869 temp_stream = camel_filter_output_stream_new (
870 filter_stream, charenc);
871 g_filter_output_stream_set_close_base_stream (
872 G_FILTER_OUTPUT_STREAM (temp_stream), FALSE);
873 g_object_unref (charenc);
874
875 g_object_unref (filter_stream);
876 filter_stream = temp_stream;
877
878 reencode = TRUE;
879 }
880
881 if (filter != NULL) {
882 GOutputStream *temp_stream;
883
884 temp_stream = camel_filter_output_stream_new (
885 filter_stream, filter);
886 g_filter_output_stream_set_close_base_stream (
887 G_FILTER_OUTPUT_STREAM (temp_stream), FALSE);
888 g_object_unref (filter);
889
890 g_object_unref (filter_stream);
891 filter_stream = temp_stream;
892
893 reencode = TRUE;
894 }
895
896 if (reencode)
897 result = camel_data_wrapper_decode_to_output_stream_sync (
898 content, filter_stream, cancellable, error);
899 else
900 result = camel_data_wrapper_write_to_output_stream_sync (
901 content, filter_stream, cancellable, error);
902
903 g_object_unref (filter_stream);
904
905 if (result == -1)
906 return -1;
907
908 total += result;
909
910 if (uuencoded) {
911 success = g_output_stream_write_all (
912 output_stream, "end\n", 4,
913 &bytes_written, cancellable, error);
914 if (!success)
915 return -1;
916 total += (gssize) bytes_written;
917 }
918 } else {
919 g_warning ("No content for medium, nothing to write");
920 }
921
922 return total;
923 }
924
925 static gboolean
mime_part_construct_from_input_stream_sync(CamelDataWrapper * dw,GInputStream * input_stream,GCancellable * cancellable,GError ** error)926 mime_part_construct_from_input_stream_sync (CamelDataWrapper *dw,
927 GInputStream *input_stream,
928 GCancellable *cancellable,
929 GError **error)
930 {
931 CamelMimeParser *parser;
932 gboolean success;
933
934 parser = camel_mime_parser_new ();
935 camel_mime_parser_init_with_input_stream (parser, input_stream);
936
937 success = camel_mime_part_construct_from_parser_sync (
938 CAMEL_MIME_PART (dw), parser, cancellable, error);
939
940 g_object_unref (parser);
941
942 return success;
943 }
944
945 static gboolean
mime_part_construct_from_parser_sync(CamelMimePart * mime_part,CamelMimeParser * parser,GCancellable * cancellable,GError ** error)946 mime_part_construct_from_parser_sync (CamelMimePart *mime_part,
947 CamelMimeParser *parser,
948 GCancellable *cancellable,
949 GError **error)
950 {
951 CamelDataWrapper *dw = (CamelDataWrapper *) mime_part;
952 CamelNameValueArray *headers;
953 const gchar *content;
954 gchar *buf;
955 gsize len;
956 gint err;
957 guint ii;
958 gboolean success = TRUE;
959 const gchar *header_name = NULL, *header_value = NULL;
960
961 switch (camel_mime_parser_step (parser, &buf, &len)) {
962 case CAMEL_MIME_PARSER_STATE_MESSAGE:
963 /* set the default type of a message always */
964 camel_data_wrapper_take_mime_type_field (dw, camel_content_type_decode ("message/rfc822"));
965 /* coverity[fallthrough] */
966 /* falls through */
967
968 case CAMEL_MIME_PARSER_STATE_HEADER:
969 case CAMEL_MIME_PARSER_STATE_MULTIPART:
970 /* we have the headers, build them into 'us' */
971 headers = camel_mime_parser_dup_headers (parser);
972
973 /* if content-type exists, process it first, set for fallback charset in headers */
974 content = camel_name_value_array_get_named (headers, CAMEL_COMPARE_CASE_INSENSITIVE, "Content-Type");
975 if (content)
976 mime_part_process_header (CAMEL_MEDIUM (dw), "content-type", content);
977
978 for (ii = 0; camel_name_value_array_get (headers, ii, &header_name, &header_value); ii++) {
979 if (g_ascii_strcasecmp (header_name, "content-type") == 0 && header_value != content)
980 camel_medium_add_header (CAMEL_MEDIUM (dw), "X-Invalid-Content-Type", header_value);
981 else
982 camel_medium_add_header (CAMEL_MEDIUM (dw), header_name, header_value);
983 }
984
985 camel_name_value_array_free (headers);
986
987 success = camel_mime_part_construct_content_from_parser (
988 mime_part, parser, cancellable, error);
989 break;
990 default:
991 g_warning ("Invalid state encountered???: %u", camel_mime_parser_state (parser));
992 }
993
994 err = camel_mime_parser_errno (parser);
995 if (err != 0) {
996 errno = err;
997 g_set_error (
998 error, G_IO_ERROR,
999 g_io_error_from_errno (errno),
1000 "%s", g_strerror (errno));
1001 success = FALSE;
1002 }
1003
1004 return success;
1005 }
1006
1007 static void
camel_mime_part_class_init(CamelMimePartClass * class)1008 camel_mime_part_class_init (CamelMimePartClass *class)
1009 {
1010 GObjectClass *object_class;
1011 CamelMediumClass *medium_class;
1012 CamelDataWrapperClass *data_wrapper_class;
1013
1014 object_class = G_OBJECT_CLASS (class);
1015 object_class->set_property = mime_part_set_property;
1016 object_class->get_property = mime_part_get_property;
1017 object_class->finalize = mime_part_finalize;
1018
1019 medium_class = CAMEL_MEDIUM_CLASS (class);
1020 medium_class->add_header = mime_part_add_header;
1021 medium_class->set_header = mime_part_set_header;
1022 medium_class->remove_header = mime_part_remove_header;
1023 medium_class->get_header = mime_part_get_header;
1024 medium_class->dup_headers = mime_part_dup_headers;
1025 medium_class->get_headers = mime_part_get_headers;
1026 medium_class->set_content = mime_part_set_content;
1027
1028 data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (class);
1029 data_wrapper_class->write_to_stream_sync = mime_part_write_to_stream_sync;
1030 data_wrapper_class->construct_from_stream_sync = mime_part_construct_from_stream_sync;
1031 data_wrapper_class->write_to_output_stream_sync = mime_part_write_to_output_stream_sync;
1032 data_wrapper_class->construct_from_input_stream_sync = mime_part_construct_from_input_stream_sync;
1033
1034 class->construct_from_parser_sync = mime_part_construct_from_parser_sync;
1035
1036 g_object_class_install_property (
1037 object_class,
1038 PROP_CONTENT_ID,
1039 g_param_spec_string (
1040 "content-id",
1041 "Content ID",
1042 NULL,
1043 NULL,
1044 G_PARAM_READWRITE |
1045 G_PARAM_EXPLICIT_NOTIFY |
1046 G_PARAM_STATIC_STRINGS));
1047
1048 g_object_class_install_property (
1049 object_class,
1050 PROP_CONTENT_MD5,
1051 g_param_spec_string (
1052 "content-md5",
1053 "Content MD5",
1054 NULL,
1055 NULL,
1056 G_PARAM_READWRITE |
1057 G_PARAM_EXPLICIT_NOTIFY |
1058 G_PARAM_STATIC_STRINGS));
1059
1060 g_object_class_install_property (
1061 object_class,
1062 PROP_DESCRIPTION,
1063 g_param_spec_string (
1064 "description",
1065 "Description",
1066 NULL,
1067 NULL,
1068 G_PARAM_READWRITE |
1069 G_PARAM_EXPLICIT_NOTIFY |
1070 G_PARAM_STATIC_STRINGS));
1071
1072 g_object_class_install_property (
1073 object_class,
1074 PROP_DISPOSITION,
1075 g_param_spec_string (
1076 "disposition",
1077 "Disposition",
1078 NULL,
1079 NULL,
1080 G_PARAM_READWRITE |
1081 G_PARAM_EXPLICIT_NOTIFY |
1082 G_PARAM_STATIC_STRINGS));
1083
1084 init_header_name_table ();
1085 }
1086
1087 static void
camel_mime_part_init(CamelMimePart * mime_part)1088 camel_mime_part_init (CamelMimePart *mime_part)
1089 {
1090 CamelDataWrapper *data_wrapper;
1091
1092 mime_part->priv = camel_mime_part_get_instance_private (mime_part);
1093 mime_part->priv->encoding = CAMEL_TRANSFER_ENCODING_DEFAULT;
1094 mime_part->priv->headers = camel_name_value_array_new ();
1095
1096 data_wrapper = CAMEL_DATA_WRAPPER (mime_part);
1097
1098 camel_data_wrapper_take_mime_type_field (data_wrapper, camel_content_type_new ("text", "plain"));
1099 }
1100
1101 /**
1102 * camel_mime_part_new:
1103 *
1104 * Create a new MIME part.
1105 *
1106 * Returns: a new #CamelMimePart
1107 **/
1108 CamelMimePart *
camel_mime_part_new(void)1109 camel_mime_part_new (void)
1110 {
1111 return g_object_new (CAMEL_TYPE_MIME_PART, NULL);
1112 }
1113
1114 /**
1115 * camel_mime_part_set_content:
1116 * @mime_part: a #CamelMimePart
1117 * @data: (array length=length) (nullable) (element-type guint8): data to put into the part
1118 * @length: length of @data
1119 * @type: (nullable): Content-Type of the data
1120 *
1121 * Utility function used to set the content of a mime part object to
1122 * be the provided data. If @length is 0, this routine can be used as
1123 * a way to remove old content (in which case @data and @type are
1124 * ignored and may be %NULL).
1125 **/
1126 void
camel_mime_part_set_content(CamelMimePart * mime_part,const gchar * data,gint length,const gchar * type)1127 camel_mime_part_set_content (CamelMimePart *mime_part,
1128 const gchar *data,
1129 gint length,
1130 const gchar *type) /* why on earth is the type last? */
1131 {
1132 CamelMedium *medium = CAMEL_MEDIUM (mime_part);
1133
1134 if (length) {
1135 CamelDataWrapper *dw;
1136 CamelStream *stream;
1137
1138 dw = camel_data_wrapper_new ();
1139 camel_data_wrapper_set_mime_type (dw, type);
1140 stream = camel_stream_mem_new_with_buffer (data, length);
1141 camel_data_wrapper_construct_from_stream_sync (
1142 dw, stream, NULL, NULL);
1143 g_object_unref (stream);
1144 camel_medium_set_content (medium, dw);
1145 g_object_unref (dw);
1146 } else
1147 camel_medium_set_content (medium, NULL);
1148 }
1149
1150 /**
1151 * camel_mime_part_get_content_disposition:
1152 * @mime_part: a #CamelMimePart
1153 *
1154 * Get the disposition of the MIME part as a structure.
1155 * Returned pointer is owned by @mime_part.
1156 *
1157 * Returns: the disposition structure
1158 *
1159 * Since: 2.30
1160 **/
1161 const CamelContentDisposition *
camel_mime_part_get_content_disposition(CamelMimePart * mime_part)1162 camel_mime_part_get_content_disposition (CamelMimePart *mime_part)
1163 {
1164 g_return_val_if_fail (mime_part != NULL, NULL);
1165
1166 return mime_part->priv->disposition;
1167 }
1168
1169 /**
1170 * camel_mime_part_get_content_id:
1171 * @mime_part: a #CamelMimePart
1172 *
1173 * Get the content-id field of a MIME part.
1174 *
1175 * Returns: the content-id field of the MIME part
1176 **/
1177 const gchar *
camel_mime_part_get_content_id(CamelMimePart * mime_part)1178 camel_mime_part_get_content_id (CamelMimePart *mime_part)
1179 {
1180 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL);
1181
1182 return mime_part->priv->content_id;
1183 }
1184
1185 /**
1186 * camel_mime_part_set_content_id:
1187 * @mime_part: a #CamelMimePart
1188 * @contentid: content id
1189 *
1190 * Set the content-id field on a MIME part.
1191 **/
1192 void
camel_mime_part_set_content_id(CamelMimePart * mime_part,const gchar * contentid)1193 camel_mime_part_set_content_id (CamelMimePart *mime_part,
1194 const gchar *contentid)
1195 {
1196 CamelMedium *medium;
1197 gchar *cid, *id;
1198
1199 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1200
1201 medium = CAMEL_MEDIUM (mime_part);
1202
1203 if (contentid)
1204 id = g_strstrip (g_strdup (contentid));
1205 else
1206 id = camel_header_msgid_generate (NULL);
1207
1208 cid = g_strdup_printf ("<%s>", id);
1209 camel_medium_set_header (medium, "Content-ID", cid);
1210 g_free (cid);
1211
1212 g_free (id);
1213
1214 g_object_notify (G_OBJECT (mime_part), "content-id");
1215 }
1216
1217 /**
1218 * camel_mime_part_get_content_location:
1219 * @mime_part: a #CamelMimePart
1220 *
1221 * Get the content-location field of a MIME part.
1222 *
1223 * Returns: the content-location field of a MIME part
1224 **/
1225 const gchar *
camel_mime_part_get_content_location(CamelMimePart * mime_part)1226 camel_mime_part_get_content_location (CamelMimePart *mime_part)
1227 {
1228 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL);
1229
1230 return mime_part->priv->content_location;
1231 }
1232
1233 /**
1234 * camel_mime_part_set_content_location:
1235 * @mime_part: a #CamelMimePart
1236 * @location: the content-location value of the MIME part
1237 *
1238 * Set the content-location field of the MIME part.
1239 **/
1240 void
camel_mime_part_set_content_location(CamelMimePart * mime_part,const gchar * location)1241 camel_mime_part_set_content_location (CamelMimePart *mime_part,
1242 const gchar *location)
1243 {
1244 CamelMedium *medium;
1245
1246 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1247
1248 medium = CAMEL_MEDIUM (mime_part);
1249
1250 /* FIXME: this should perform content-location folding */
1251 camel_medium_set_header (medium, "Content-Location", location);
1252
1253 g_object_notify (G_OBJECT (mime_part), "content-location");
1254 }
1255
1256 /**
1257 * camel_mime_part_get_content_md5:
1258 * @mime_part: a #CamelMimePart
1259 *
1260 * Get the content-md5 field of the MIME part.
1261 *
1262 * Returns: the content-md5 field of the MIME part
1263 **/
1264 const gchar *
camel_mime_part_get_content_md5(CamelMimePart * mime_part)1265 camel_mime_part_get_content_md5 (CamelMimePart *mime_part)
1266 {
1267 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL);
1268
1269 return mime_part->priv->content_md5;
1270 }
1271
1272 /**
1273 * camel_mime_part_set_content_md5:
1274 * @mime_part: a #CamelMimePart
1275 * @md5sum: the md5sum of the MIME part
1276 *
1277 * Set the content-md5 field of the MIME part.
1278 **/
1279 void
camel_mime_part_set_content_md5(CamelMimePart * mime_part,const gchar * content_md5)1280 camel_mime_part_set_content_md5 (CamelMimePart *mime_part,
1281 const gchar *content_md5)
1282 {
1283 CamelMedium *medium;
1284
1285 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1286
1287 medium = CAMEL_MEDIUM (mime_part);
1288
1289 camel_medium_set_header (medium, "Content-MD5", content_md5);
1290 }
1291
1292 /**
1293 * camel_mime_part_get_content_languages:
1294 * @mime_part: a #CamelMimePart
1295 *
1296 * Get the Content-Languages set on the MIME part.
1297 *
1298 * Returns: (element-type utf8) (transfer none): a #GList of languages
1299 **/
1300 const GList *
camel_mime_part_get_content_languages(CamelMimePart * mime_part)1301 camel_mime_part_get_content_languages (CamelMimePart *mime_part)
1302 {
1303 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL);
1304
1305 return mime_part->priv->content_languages;
1306 }
1307
1308 /**
1309 * camel_mime_part_set_content_languages:
1310 * @mime_part: a #CamelMimePart
1311 * @content_languages: (element-type utf8): list of languages
1312 *
1313 * Set the Content-Languages field of a MIME part.
1314 **/
1315 void
camel_mime_part_set_content_languages(CamelMimePart * mime_part,GList * content_languages)1316 camel_mime_part_set_content_languages (CamelMimePart *mime_part,
1317 GList *content_languages)
1318 {
1319 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1320
1321 g_list_free_full (
1322 mime_part->priv->content_languages,
1323 (GDestroyNotify) g_free);
1324
1325 mime_part->priv->content_languages = content_languages;
1326
1327 /* FIXME: translate to a header and set it */
1328 }
1329
1330 /**
1331 * camel_mime_part_get_content_type:
1332 * @mime_part: a #CamelMimePart
1333 *
1334 * Get the Content-Type of a MIME part.
1335 *
1336 * Returns: (transfer none): the parsed #CamelContentType of the MIME part
1337 **/
1338 CamelContentType *
camel_mime_part_get_content_type(CamelMimePart * mime_part)1339 camel_mime_part_get_content_type (CamelMimePart *mime_part)
1340 {
1341 CamelDataWrapper *data_wrapper;
1342
1343 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL);
1344
1345 data_wrapper = CAMEL_DATA_WRAPPER (mime_part);
1346
1347 return camel_data_wrapper_get_mime_type_field (data_wrapper);
1348 }
1349
1350 /**
1351 * camel_mime_part_set_content_type:
1352 * @mime_part: a #CamelMimePart
1353 * @content_type: content-type string
1354 *
1355 * Set the content-type on a MIME part.
1356 **/
1357 void
camel_mime_part_set_content_type(CamelMimePart * mime_part,const gchar * content_type)1358 camel_mime_part_set_content_type (CamelMimePart *mime_part,
1359 const gchar *content_type)
1360 {
1361 CamelMedium *medium;
1362
1363 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1364
1365 medium = CAMEL_MEDIUM (mime_part);
1366
1367 camel_medium_set_header (medium, "Content-Type", content_type);
1368 }
1369
1370 /**
1371 * camel_mime_part_get_description:
1372 * @mime_part: a #CamelMimePart
1373 *
1374 * Get the description of the MIME part.
1375 *
1376 * Returns: the description
1377 **/
1378 const gchar *
camel_mime_part_get_description(CamelMimePart * mime_part)1379 camel_mime_part_get_description (CamelMimePart *mime_part)
1380 {
1381 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL);
1382
1383 return mime_part->priv->description;
1384 }
1385
1386 /**
1387 * camel_mime_part_set_description:
1388 * @mime_part: a #CamelMimePart
1389 * @description: description of the MIME part
1390 *
1391 * Set a description on the MIME part.
1392 **/
1393 void
camel_mime_part_set_description(CamelMimePart * mime_part,const gchar * description)1394 camel_mime_part_set_description (CamelMimePart *mime_part,
1395 const gchar *description)
1396 {
1397 CamelMedium *medium;
1398 gchar *text;
1399
1400 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1401 g_return_if_fail (description != NULL);
1402
1403 medium = CAMEL_MEDIUM (mime_part);
1404
1405 text = camel_header_encode_string ((guchar *) description);
1406 camel_medium_set_header (medium, "Content-Description", text);
1407 g_free (text);
1408
1409 g_object_notify (G_OBJECT (mime_part), "description");
1410 }
1411
1412 /**
1413 * camel_mime_part_get_disposition:
1414 * @mime_part: a #CamelMimePart
1415 *
1416 * Get the disposition of the MIME part.
1417 *
1418 * Returns: the disposition
1419 **/
1420 const gchar *
camel_mime_part_get_disposition(CamelMimePart * mime_part)1421 camel_mime_part_get_disposition (CamelMimePart *mime_part)
1422 {
1423 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL);
1424
1425 if (mime_part->priv->disposition)
1426 return mime_part->priv->disposition->disposition;
1427 else
1428 return NULL;
1429 }
1430
1431 /**
1432 * camel_mime_part_set_disposition:
1433 * @mime_part: a #CamelMimePart
1434 * @disposition: disposition of the MIME part
1435 *
1436 * Set a disposition on the MIME part.
1437 **/
1438 void
camel_mime_part_set_disposition(CamelMimePart * mime_part,const gchar * disposition)1439 camel_mime_part_set_disposition (CamelMimePart *mime_part,
1440 const gchar *disposition)
1441 {
1442 CamelMedium *medium;
1443 gchar *text;
1444
1445 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1446
1447 medium = CAMEL_MEDIUM (mime_part);
1448
1449 /* we poke in a new disposition (so we dont lose 'filename', etc) */
1450 if (mime_part->priv->disposition == NULL)
1451 mime_part_set_disposition (mime_part, disposition);
1452
1453 if (mime_part->priv->disposition != NULL) {
1454 g_free (mime_part->priv->disposition->disposition);
1455 mime_part->priv->disposition->disposition = g_strdup (disposition);
1456 }
1457
1458 text = camel_content_disposition_format (mime_part->priv->disposition);
1459 camel_medium_set_header (medium, "Content-Disposition", text);
1460 g_free (text);
1461
1462 g_object_notify (G_OBJECT (mime_part), "disposition");
1463 }
1464
1465 /**
1466 * camel_mime_part_get_encoding:
1467 * @mime_part: a #CamelMimePart
1468 *
1469 * Get the Content-Transfer-Encoding of a MIME part.
1470 *
1471 * Returns: a #CamelTransferEncoding
1472 **/
1473 CamelTransferEncoding
camel_mime_part_get_encoding(CamelMimePart * mime_part)1474 camel_mime_part_get_encoding (CamelMimePart *mime_part)
1475 {
1476 g_return_val_if_fail (
1477 CAMEL_IS_MIME_PART (mime_part),
1478 CAMEL_TRANSFER_ENCODING_DEFAULT);
1479
1480 return mime_part->priv->encoding;
1481 }
1482
1483 /**
1484 * camel_mime_part_set_encoding:
1485 * @mime_part: a #CamelMimePart
1486 * @encoding: a #CamelTransferEncoding
1487 *
1488 * Set the Content-Transfer-Encoding to use on a MIME part.
1489 **/
1490 void
camel_mime_part_set_encoding(CamelMimePart * mime_part,CamelTransferEncoding encoding)1491 camel_mime_part_set_encoding (CamelMimePart *mime_part,
1492 CamelTransferEncoding encoding)
1493 {
1494 CamelMedium *medium;
1495 const gchar *text;
1496
1497 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1498
1499 medium = CAMEL_MEDIUM (mime_part);
1500
1501 text = camel_transfer_encoding_to_string (encoding);
1502 camel_medium_set_header (medium, "Content-Transfer-Encoding", text);
1503 }
1504
1505 /**
1506 * camel_mime_part_get_filename:
1507 * @mime_part: a #CamelMimePart
1508 *
1509 * Get the filename of a MIME part.
1510 *
1511 * Returns: the filename of the MIME part
1512 **/
1513 const gchar *
camel_mime_part_get_filename(CamelMimePart * mime_part)1514 camel_mime_part_get_filename (CamelMimePart *mime_part)
1515 {
1516 if (mime_part->priv->disposition) {
1517 const gchar *name = camel_header_param (
1518 mime_part->priv->disposition->params, "filename");
1519 if (name)
1520 return name;
1521 }
1522
1523 return camel_content_type_param (camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mime_part)), "name");
1524 }
1525
1526 /**
1527 * camel_mime_part_set_filename:
1528 * @mime_part: a #CamelMimePart
1529 * @filename: filename given to the MIME part
1530 *
1531 * Set the filename on a MIME part.
1532 **/
1533 void
camel_mime_part_set_filename(CamelMimePart * mime_part,const gchar * filename)1534 camel_mime_part_set_filename (CamelMimePart *mime_part,
1535 const gchar *filename)
1536 {
1537 CamelDataWrapper *dw;
1538 CamelMedium *medium;
1539 gchar *str;
1540
1541 medium = CAMEL_MEDIUM (mime_part);
1542
1543 if (mime_part->priv->disposition == NULL)
1544 mime_part->priv->disposition =
1545 camel_content_disposition_decode ("attachment");
1546
1547 camel_header_set_param (
1548 &mime_part->priv->disposition->params, "filename", filename);
1549 str = camel_content_disposition_format (mime_part->priv->disposition);
1550
1551 camel_medium_set_header (medium, "Content-Disposition", str);
1552 g_free (str);
1553
1554 dw = (CamelDataWrapper *) mime_part;
1555 if (!camel_data_wrapper_get_mime_type_field (dw))
1556 camel_data_wrapper_take_mime_type_field (dw, camel_content_type_new ("application", "octet-stream"));
1557 camel_content_type_set_param (camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (dw)), "name", filename);
1558 str = camel_content_type_format (camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (dw)));
1559 camel_medium_set_header (medium, "Content-Type", str);
1560 g_free (str);
1561 }
1562
1563 /**
1564 * camel_mime_part_construct_from_parser_sync:
1565 * @mime_part: a #CamelMimePart
1566 * @parser: a #CamelMimeParser
1567 * @cancellable: optional #GCancellable object, or %NULL
1568 * @error: return location for a #GError, or %NULL
1569 *
1570 * Constructs a MIME part from a parser.
1571 *
1572 * Returns: %TRUE on success, %FALSE on error
1573 *
1574 * Since: 3.0
1575 **/
1576 gboolean
camel_mime_part_construct_from_parser_sync(CamelMimePart * mime_part,CamelMimeParser * parser,GCancellable * cancellable,GError ** error)1577 camel_mime_part_construct_from_parser_sync (CamelMimePart *mime_part,
1578 CamelMimeParser *parser,
1579 GCancellable *cancellable,
1580 GError **error)
1581 {
1582 CamelMimePartClass *class;
1583 gboolean success;
1584
1585 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), FALSE);
1586 g_return_val_if_fail (CAMEL_IS_MIME_PARSER (parser), FALSE);
1587
1588 class = CAMEL_MIME_PART_GET_CLASS (mime_part);
1589 g_return_val_if_fail (class != NULL, FALSE);
1590 g_return_val_if_fail (class->construct_from_parser_sync != NULL, FALSE);
1591
1592 success = class->construct_from_parser_sync (
1593 mime_part, parser, cancellable, error);
1594 CAMEL_CHECK_GERROR (
1595 mime_part, construct_from_parser_sync, success, error);
1596
1597 return success;
1598 }
1599
1600 /* Helper for camel_mime_part_construct_from_parser() */
1601 static void
mime_part_construct_from_parser_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1602 mime_part_construct_from_parser_thread (GTask *task,
1603 gpointer source_object,
1604 gpointer task_data,
1605 GCancellable *cancellable)
1606 {
1607 gboolean success;
1608 AsyncContext *async_context;
1609 GError *local_error = NULL;
1610
1611 async_context = (AsyncContext *) task_data;
1612
1613 success = camel_mime_part_construct_from_parser_sync (
1614 CAMEL_MIME_PART (source_object),
1615 async_context->parser,
1616 cancellable, &local_error);
1617
1618 if (local_error != NULL) {
1619 g_task_return_error (task, local_error);
1620 } else {
1621 g_task_return_boolean (task, success);
1622 }
1623 }
1624
1625 /**
1626 * camel_mime_part_construct_from_parser:
1627 * @mime_part: a #CamelMimePart
1628 * @parser: a #CamelMimeParser
1629 * @io_priority: the I/O priority of the request
1630 * @cancellable: optional #GCancellable object, or %NULL
1631 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1632 * @user_data: data to pass to the callback function
1633 *
1634 * Asynchronously constructs a MIME part from a parser.
1635 *
1636 * When the operation is finished, @callback will be called. You can then
1637 * call camel_mime_part_construct_from_parser_finish() to get the result of
1638 * the operation.
1639 *
1640 * Since: 3.0
1641 **/
1642 void
camel_mime_part_construct_from_parser(CamelMimePart * mime_part,CamelMimeParser * parser,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1643 camel_mime_part_construct_from_parser (CamelMimePart *mime_part,
1644 CamelMimeParser *parser,
1645 gint io_priority,
1646 GCancellable *cancellable,
1647 GAsyncReadyCallback callback,
1648 gpointer user_data)
1649 {
1650 GTask *task;
1651 AsyncContext *async_context;
1652
1653 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1654 g_return_if_fail (CAMEL_IS_MIME_PARSER (parser));
1655
1656 async_context = g_slice_new0 (AsyncContext);
1657 async_context->parser = g_object_ref (parser);
1658
1659 task = g_task_new (mime_part, cancellable, callback, user_data);
1660 g_task_set_source_tag (task, camel_mime_part_construct_from_parser);
1661 g_task_set_priority (task, io_priority);
1662
1663 g_task_set_task_data (
1664 task, async_context,
1665 (GDestroyNotify) async_context_free);
1666
1667 g_task_run_in_thread (task, mime_part_construct_from_parser_thread);
1668
1669 g_object_unref (task);
1670 }
1671
1672 /**
1673 * camel_mime_part_construct_from_parser_finish:
1674 * @mime_part: a #CamelMimePart
1675 * @result: a #GAsyncResult
1676 * @error: return location for a #GError, or %NULL
1677 *
1678 * Finishes the operation started with camel_mime_part_construct_from_parser().
1679 *
1680 * Returns: %TRUE on success, %FALSE on error
1681 *
1682 * Since: 3.0
1683 **/
1684 gboolean
camel_mime_part_construct_from_parser_finish(CamelMimePart * mime_part,GAsyncResult * result,GError ** error)1685 camel_mime_part_construct_from_parser_finish (CamelMimePart *mime_part,
1686 GAsyncResult *result,
1687 GError **error)
1688 {
1689 g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), FALSE);
1690 g_return_val_if_fail (g_task_is_valid (result, mime_part), FALSE);
1691
1692 g_return_val_if_fail (
1693 g_async_result_is_tagged (
1694 result, camel_mime_part_construct_from_parser), FALSE);
1695
1696 return g_task_propagate_boolean (G_TASK (result), error);
1697 }
1698