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