No issues found
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 |
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 }