evolution-3.6.4/em-format/e-mail-part-utils.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-part-utils.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-part-utils.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * e-mail-part-utils.h
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  */
 18 
 19 #ifdef HAVE_CONFIG_H
 20 #include <config.h>
 21 #endif
 22 
 23 #include <glib/gi18n-lib.h>
 24 
 25 #include "e-mail-part-utils.h"
 26 #include "e-mail-parser-extension.h"
 27 
 28 #include <camel/camel.h>
 29 #include <e-util/e-util.h>
 30 #include <gdk/gdk.h>
 31 
 32 #include <libsoup/soup.h>
 33 
 34 #include <string.h>
 35 
 36 #define d(x)
 37 
 38 /**
 39  * e_mail_parst_is_secured:
 40  * @part: a #CamelMimePart
 41  *
 42  * Whether @part is signed or encrypted or not.
 43  *
 44  * Return Value: TRUE/FALSE
 45  */
 46 gboolean
 47 e_mail_part_is_secured (CamelMimePart *part)
 48 {
 49 	CamelContentType *ct = camel_mime_part_get_content_type (part);
 50 
 51 	return (camel_content_type_is (ct, "multipart", "signed") ||
 52 		camel_content_type_is (ct, "multipart", "encrypted") ||
 53 		camel_content_type_is (ct, "application", "x-inlinepgp-signed") ||
 54 		camel_content_type_is (ct, "application", "x-inlinepgp-encrypted") ||
 55 		camel_content_type_is (ct, "application", "x-pkcs7-mime") ||
 56 		camel_content_type_is (ct, "application", "pkcs7-mime"));
 57 }
 58 
 59 /**
 60  * e_mail_partr_snoop_type:
 61  * @part: a #CamelMimePart
 62  *
 63  * Tries to snoop the mime type of a part.
 64  *
 65  * Return value: %NULL if unknown (more likely application/octet-stream).
 66  **/
 67 const gchar *
 68 e_mail_part_snoop_type (CamelMimePart *part)
 69 {
 70 	/* cache is here only to be able still return const gchar * */
 71 	static GHashTable *types_cache = NULL;
 72 
 73 	const gchar *filename;
 74 	gchar *name_type = NULL, *magic_type = NULL, *res, *tmp;
 75 	CamelDataWrapper *dw;
 76 
 77 	filename = camel_mime_part_get_filename (part);
 78 	if (filename != NULL)
 79 		name_type = e_util_guess_mime_type (filename, FALSE);
 80 
 81 	dw = camel_medium_get_content ((CamelMedium *) part);
 82 	if (!camel_data_wrapper_is_offline (dw)) {
 83 		GByteArray *byte_array;
 84 		CamelStream *stream;
 85 
 86 		byte_array = g_byte_array_new ();
 87 		stream = camel_stream_mem_new_with_byte_array (byte_array);
 88 
 89 		if (camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL) > 0) {
 90 			gchar *content_type;
 91 
 92 			content_type = g_content_type_guess (
 93 				filename, byte_array->data,
 94 				byte_array->len, NULL);
 95 
 96 			if (content_type != NULL)
 97 				magic_type = g_content_type_get_mime_type (content_type);
 98 
 99 			g_free (content_type);
100 		}
101 
102 		g_object_unref (stream);
103 	}
104 
105 	/* If gvfs doesn't recognize the data by magic, but it
106 	 * contains English words, it will call it text/plain. If the
107 	 * filename-based check came up with something different, use
108 	 * that instead and if it returns "application/octet-stream"
109 	 * try to do better with the filename check.
110 	 */
111 
112 	if (magic_type) {
113 		if (name_type
114 		    && (!strcmp (magic_type, "text/plain")
115 			|| !strcmp (magic_type, "application/octet-stream")))
116 			res = name_type;
117 		else
118 			res = magic_type;
119 	} else
120 		res = name_type;
121 
122 	if (res != name_type)
123 		g_free (name_type);
124 
125 	if (res != magic_type)
126 		g_free (magic_type);
127 
128 	if (!types_cache)
129 		types_cache = g_hash_table_new_full (
130 			g_str_hash, g_str_equal,
131 			(GDestroyNotify) g_free,
132 			(GDestroyNotify) NULL);
133 
134 	if (res) {
135 		tmp = g_hash_table_lookup (types_cache, res);
136 		if (tmp) {
137 			g_free (res);
138 			res = tmp;
139 		} else {
140 			g_hash_table_insert (types_cache, res, res);
141 		}
142 	}
143 
144 	d (printf ("Snooped mime type %s\n", res));
145 	return res;
146 
147 	/* We used to load parts to check their type, we don't anymore,
148 	 * see bug #211778 for some discussion */
149 }
150 
151 /**
152  * e_mail_part_is_attachment
153  * @part: Part to check.
154  *
155  * Returns true if the part is an attachment.
156  *
157  * A part is not considered an attachment if it is a
158  * multipart, or a text part with no filename.  It is used
159  * to determine if an attachment header should be displayed for
160  * the part.
161  *
162  * Content-Disposition is not checked.
163  *
164  * Return value: TRUE/FALSE
165  **/
166 gboolean
167 e_mail_part_is_attachment (CamelMimePart *part)
168 {
169 	/*CamelContentType *ct = camel_mime_part_get_content_type(part);*/
170 	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
171 
172 	if (!dw)
173 		return 0;
174 
175 	d (printf ("checking is attachment %s/%s\n", dw->mime_type->type, dw->mime_type->subtype));
176 	return !(camel_content_type_is (dw->mime_type, "multipart", "*")
177 		 || camel_content_type_is (
178 			dw->mime_type, "application", "x-pkcs7-mime")
179 		 || camel_content_type_is (
180 			dw->mime_type, "application", "pkcs7-mime")
181 		 || camel_content_type_is (
182 			dw->mime_type, "application", "x-inlinepgp-signed")
183 		 || camel_content_type_is (
184 			dw->mime_type, "application", "x-inlinepgp-encrypted")
185 		 || camel_content_type_is (
186 			dw->mime_type, "x-evolution", "evolution-rss-feed")
187 		 || camel_content_type_is (dw->mime_type, "text", "calendar")
188 		 || camel_content_type_is (dw->mime_type, "text", "x-calendar")
189 		 || (camel_content_type_is (dw->mime_type, "text", "*")
190 		     && camel_mime_part_get_filename (part) == NULL));
191 }
192 
193 /**
194  * e_mail_part_preserve_charset_in_content_type:
195  * @ipart: Source #CamelMimePart
196  * @opart: Target #CamelMimePart
197  *
198  * Copies 'charset' part of content-type header from @ipart to @opart.
199  */
200 void
201 e_mail_part_preserve_charset_in_content_type (CamelMimePart *ipart,
202                                               CamelMimePart *opart)
203 {
204 	CamelDataWrapper *data_wrapper;
205 	CamelContentType *content_type;
206 	const gchar *charset;
207 
208 	g_return_if_fail (ipart != NULL);
209 	g_return_if_fail (opart != NULL);
210 
211 	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart));
212 	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
213 
214 	if (content_type == NULL)
215 		return;
216 
217 	charset = camel_content_type_param (content_type, "charset");
218 
219 	if (charset == NULL || *charset == '\0')
220 		return;
221 
222 	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart));
223 	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
224 
225 	if (content_type)
226 		camel_content_type_set_param (content_type, "charset", charset);
227 
228 	/* update charset also on the part itself */
229 	data_wrapper = CAMEL_DATA_WRAPPER (opart);
230 	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
231 	if (content_type)
232 		camel_content_type_set_param (content_type, "charset", charset);
233 }
234 
235 /**
236  * e_mail_part_get_related_display_part:
237  * @part: a multipart/related or multipart/alternative #CamelMimePart
238  * @out_displayid: (out) returns index of the returned part
239  *
240  * Goes through all subparts of given @part and tries to determine which
241  * part should be displayed and which parts are just attachments to the
242  * part.
243  *
244  * Return Value: A #CamelMimePart that should be displayed
245  */
246 CamelMimePart *
247 e_mail_part_get_related_display_part (CamelMimePart *part,
248                                       gint *out_displayid)
249 {
250 	CamelMultipart *mp;
251 	CamelMimePart *body_part, *display_part = NULL;
252 	CamelContentType *content_type;
253 	const gchar *start;
254 	gint i, nparts, displayid = 0;
255 
256 	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
257 
258 	if (!CAMEL_IS_MULTIPART (mp))
259 		return NULL;
260 
261 	nparts = camel_multipart_get_number (mp);
262 	content_type = camel_mime_part_get_content_type (part);
263 	start = camel_content_type_param (content_type, "start");
264 	if (start && strlen (start) > 2) {
265 		gint len;
266 		const gchar *cid;
267 
268 		/* strip <>'s from CID */
269 		len = strlen (start) - 2;
270 		start++;
271 
272 		for (i = 0; i < nparts; i++) {
273 			body_part = camel_multipart_get_part (mp, i);
274 			cid = camel_mime_part_get_content_id (body_part);
275 
276 			if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
277 				display_part = body_part;
278 				displayid = i;
279 				break;
280 			}
281 		}
282 	} else {
283 		display_part = camel_multipart_get_part (mp, 0);
284 	}
285 
286 	if (out_displayid)
287 		*out_displayid = displayid;
288 
289 	return display_part;
290 }
291 
292 void
293 e_mail_part_animation_extract_frame (const GByteArray *anim,
294                                      gchar **frame,
295                                      gsize *len)
296 {
297 	GdkPixbufLoader *loader;
298 	GdkPixbufAnimation *animation;
299 	GdkPixbuf *frame_buf;
300 
301         /* GIF89a (GIF image signature) */
302 	const gchar GIF_HEADER[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
303 	const gint   GIF_HEADER_LEN = sizeof (GIF_HEADER);
304 
305         /* NETSCAPE2.0 (extension describing animated GIF, starts on 0x310) */
306 	const gchar GIF_APPEXT[] = { 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41,
307 				     0x50, 0x45, 0x32, 0x2E, 0x30 };
308 	const gint   GIF_APPEXT_LEN = sizeof (GIF_APPEXT);
309 
310 	if ((anim == NULL) || (anim->data == NULL)) {
311 		*frame = NULL;
312 		*len = 0;
313 		return;
314 	}
315 
316         /* Check if the image is an animated GIF. We don't care about any
317          * other animated formats (APNG or MNG) as WebKit does not support them
318          * and displays only the first frame. */
319 	if ((anim->len < 0x331)
320 	    || (memcmp (anim->data, GIF_HEADER, GIF_HEADER_LEN) != 0)
321 	    || (memcmp (&anim->data[0x310], GIF_APPEXT, GIF_APPEXT_LEN) != 0)) {
322 
323                 *frame = g_memdup (anim->data, anim->len);
324                 *len = anim->len;
325 		return;
326 	}
327 
328 	loader = gdk_pixbuf_loader_new ();
329 	gdk_pixbuf_loader_write (loader, (guchar *) anim->data, anim->len, NULL);
330 	gdk_pixbuf_loader_close (loader, NULL);
331 	animation = gdk_pixbuf_loader_get_animation (loader);
332 	if (!animation) {
333 
334                 *frame = g_memdup (anim->data, anim->len);
335                 *len = anim->len;
336 		g_object_unref (loader);
337 		return;
338 	}
339 
340         /* Extract first frame */
341 	frame_buf = gdk_pixbuf_animation_get_static_image (animation);
342 	if (!frame_buf) {
343                 *frame = g_memdup (anim->data, anim->len);
344                 *len = anim->len;
345 		g_object_unref (loader);
346 		g_object_unref (animation);
347 		return;
348 	}
349 
350         /* Unforunatelly, GdkPixbuf cannot save to GIF, but WebKit does not
351          * have any trouble displaying PNG image despite the part having
352          * image/gif mime-type */
353 	gdk_pixbuf_save_to_buffer (frame_buf, frame, len, "png", NULL, NULL);
354 
355 	g_object_unref (loader);
356 }
357 
358 /**
359  * e_mail_part_build_url:
360  * @folder: (allow-none) a #CamelFolder with the message or %NULL
361  * @message_uid: uid of the message within the @folder
362  * @first_param_name: Name of first query parameter followed by GType of it's value and value
363  * terminated by %NULL.
364  *
365  * Construct a URI for message.
366  *
367  * The URI can contain multiple query parameters. The list of parameters must be
368  * NULL-terminated. Each query must contain name, GType of value and value.
369  *
370  * Return Value: a URL of a message or part
371  */
372 gchar *
373 e_mail_part_build_uri (CamelFolder *folder,
374                        const gchar *message_uid,
375                        const gchar *first_param_name,
376                        ...)
377 {
378 	CamelStore *store;
379 	gchar *uri, *tmp;
380 	va_list ap;
381 	const gchar *name;
382 	const gchar *service_uid, *folder_name;
383 	gchar *encoded_message_uid;
384 	gchar separator;
385 
386 	g_return_val_if_fail (message_uid && *message_uid, NULL);
387 
388 	if (!folder) {
389 		folder_name = "generic";
390 		service_uid = "generic";
391 	} else {
392 		tmp = (gchar *) camel_folder_get_full_name (folder);
393 		folder_name = (const gchar *) soup_uri_encode (tmp, NULL);
394 		store = camel_folder_get_parent_store (folder);
395 		if (store)
396 			service_uid = camel_service_get_uid (CAMEL_SERVICE (store));
397 		else
398 			service_uid = "generic";
399 	}
400 
401 	encoded_message_uid = soup_uri_encode (message_uid, NULL);
402 	tmp = g_strdup_printf (
403 		"mail://%s/%s/%s",
404 		service_uid,
405 		folder_name,
406 		encoded_message_uid);
407 	g_free (encoded_message_uid);
408 
409 	if (folder) {
410 		g_free ((gchar *) folder_name);
411 	}
412 
413 	va_start (ap, first_param_name);
414 	name = first_param_name;
415 	separator = '?';
416 	while (name) {
417 		gchar *tmp2;
418 		gint type = va_arg (ap, gint);
419 		switch (type) {
420 			case G_TYPE_INT:
421 			case G_TYPE_BOOLEAN: {
422 				gint val = va_arg (ap, gint);
423 				tmp2 = g_strdup_printf (
424 					"%s%c%s=%d", tmp,
425 						separator, name, val);
426 				break;
427 			}
428 			case G_TYPE_FLOAT:
429 			case G_TYPE_DOUBLE: {
430 				gdouble val = va_arg (ap, double);
431 				tmp2 = g_strdup_printf (
432 					"%s%c%s=%f", tmp,
433 						separator, name, val);
434 				break;
435 			}
436 			case G_TYPE_STRING: {
437 				gchar *val = va_arg (ap, gchar *);
438 				gchar *escaped = soup_uri_encode (val, NULL);
439 				tmp2 = g_strdup_printf (
440 					"%s%c%s=%s", tmp,
441 						separator, name, escaped);
442 				g_free (escaped);
443 				break;
444 			}
445 			default:
446 				g_warning ("Invalid param type %s", g_type_name (type));
447 				return NULL;
448 		}
449 
450 		g_free (tmp);
451 		tmp = tmp2;
452 
453 		if (separator == '?')
454 			separator = '&';
455 
456 		name = va_arg (ap, gchar *);
457 	}
458 	va_end (ap);
459 
460 	uri = tmp;
461 	if (uri == NULL)
462 		return NULL;
463 
464 	/* For some reason, webkit won't accept URL with username, but
465 	 * without password (mail://store@host/folder/mail), so we
466 	 * will replace the '@' symbol by '/' to get URL like
467 	 * mail://store/host/folder/mail which is OK
468 	 */
469 	while ((tmp = strchr (uri, '@')) != NULL) {
470 		tmp[0] = '/';
471 	}
472 
473 	return uri;
474 }
475 
476 /**
477  * e_mail_part_describe:
478  * @part: a #CamelMimePart
479  * @mimetype: mimetype of the content
480  *
481  * Generate a simple textual description of a part, @mime_type represents
482  * the content.
483  *
484  * Return value:
485  **/
486 gchar *
487 e_mail_part_describe (CamelMimePart *part,
488                       const gchar *mime_type)
489 {
490 	GString *stext;
491 	const gchar *filename, *description;
492 	gchar *content_type, *desc;
493 
494 	stext = g_string_new ("");
495 	content_type = g_content_type_from_mime_type (mime_type);
496 	desc = g_content_type_get_description (
497 		content_type != NULL ? content_type : mime_type);
498 	g_free (content_type);
499 	g_string_append_printf (
500 		stext, _("%s attachment"), desc ? desc : mime_type);
501 	g_free (desc);
502 
503 	filename = camel_mime_part_get_filename (part);
504 	description = camel_mime_part_get_description (part);
505 
506 	if (!filename || !*filename) {
507 		CamelDataWrapper *content;
508 
509 		content = camel_medium_get_content (CAMEL_MEDIUM (part));
510 
511 		if (CAMEL_IS_MIME_MESSAGE (content))
512 			filename = camel_mime_message_get_subject (
513 				CAMEL_MIME_MESSAGE (content));
514 	}
515 
516 	if (filename != NULL && *filename != '\0') {
517 		gchar *basename = g_path_get_basename (filename);
518 		g_string_append_printf (stext, " (%s)", basename);
519 		g_free (basename);
520 	}
521 
522 	if (description != NULL && *description != '\0' &&
523 		g_strcmp0 (filename, description) != 0)
524 		g_string_append_printf (stext, ", \"%s\"", description);
525 
526 	return g_string_free (stext, FALSE);
527 }
528 
529 gboolean
530 e_mail_part_is_inline (CamelMimePart *mime_part,
531                        GQueue *extensions)
532 {
533 	const gchar *disposition;
534 	EMailParserExtension *extension;
535 
536 	if ((extensions == NULL) || g_queue_is_empty (extensions))
537 		return FALSE;
538 
539 	extension = g_queue_peek_head (extensions);
540 	/* Some types need to override the disposition.
541 	 * e.g. application/x-pkcs7-mime */
542 	if (e_mail_parser_extension_get_flags (extension) &
543 		E_MAIL_PARSER_EXTENSION_INLINE_DISPOSITION)
544 		return TRUE;
545 
546 	disposition = camel_mime_part_get_disposition (mime_part);
547 	if (disposition != NULL)
548 		return g_ascii_strcasecmp (disposition, "inline") == 0;
549 
550 	/* Otherwise, use the default for this handler type. */
551 	return (e_mail_parser_extension_get_flags (extension) &
552 			E_MAIL_PARSER_EXTENSION_INLINE) != 0;
553 }
554 
555 /**
556  * e_mail_part_utils_body_refers:
557  * @body: text body to search for references in; can be %NULL, then returns %FALSE
558  * @cid: a Content-ID to search for; if found in body, it should be of form "cid:xxxxx"; can be %NULL
559  *
560  * Returns whether @body contains a reference to @cid enclosed in quotes;
561  *    returns %FALSE if any of the arguments is %NULL.
562  **/
563 gboolean
564 e_mail_part_utils_body_refers (const gchar *body,
565 			       const gchar *cid)
566 {
567 	const gchar *ptr;
568 
569 	if (!body || !cid || !*cid)
570 		return FALSE;
571 
572 	ptr = body;
573 	while (ptr = strstr (ptr, cid), ptr != NULL) {
574 		if (ptr - body > 1 && ptr[-1] == '\"' && ptr[strlen (cid)] == '\"')
575 			return TRUE;
576 
577 		ptr++;
578 	}
579 
580 	return FALSE;
581 }