1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /* Balsa E-Mail Client
3 *
4 * Copyright (C) 1997-2013 Stuart Parmenter and others,
5 * See the file AUTHORS for a list.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
24 # include "config.h"
25 #endif /* HAVE_CONFIG_H */
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <glib.h>
32 #include <fcntl.h>
33
34 #include "libbalsa.h"
35 #include "libbalsa-vfs.h"
36 #include "misc.h"
37 #include <glib/gi18n.h>
38
39 LibBalsaMessageBody *
libbalsa_message_body_new(LibBalsaMessage * message)40 libbalsa_message_body_new(LibBalsaMessage * message)
41 {
42 LibBalsaMessageBody *body;
43
44 body = g_new0(LibBalsaMessageBody, 1);
45
46 body->message = message;
47 body->buffer = NULL;
48 body->html_buffer = NULL;
49 body->embhdrs = NULL;
50 body->content_type = NULL;
51 body->filename = NULL;
52 body->file_uri = NULL;
53 body->temp_filename = NULL;
54 body->charset = NULL;
55
56 #ifdef HAVE_GPGME
57 body->was_encrypted = FALSE;
58 body->sig_info = NULL;
59 #endif
60
61 body->next = NULL;
62 body->parts = NULL;
63
64 body->mime_part = NULL;
65
66 return body;
67 }
68
69
70 void
libbalsa_message_body_free(LibBalsaMessageBody * body)71 libbalsa_message_body_free(LibBalsaMessageBody * body)
72 {
73 if (body == NULL)
74 return;
75
76 g_free(body->buffer);
77 g_free(body->html_buffer);
78 libbalsa_message_headers_destroy(body->embhdrs);
79 g_free(body->content_type);
80 g_free(body->filename);
81 if (body->file_uri)
82 g_object_unref(body->file_uri);
83 if (body->temp_filename) {
84 unlink(body->temp_filename);
85 g_free(body->temp_filename);
86 }
87
88 g_free(body->charset);
89
90 #ifdef HAVE_GPGME
91 if (body->sig_info)
92 g_object_unref(G_OBJECT(body->sig_info));
93 #endif
94
95 libbalsa_message_body_free(body->next);
96 libbalsa_message_body_free(body->parts);
97
98 if (body->mime_part)
99 g_object_unref(body->mime_part);
100
101 g_free(body);
102 }
103
104
105 static LibBalsaMessageHeaders *
libbalsa_message_body_extract_embedded_headers(GMimeMessage * msg)106 libbalsa_message_body_extract_embedded_headers(GMimeMessage* msg)
107 {
108 LibBalsaMessageHeaders *ehdr;
109 const char *subj;
110 int offset;
111
112 ehdr = g_new0(LibBalsaMessageHeaders, 1);
113
114 libbalsa_message_headers_from_gmime(ehdr, msg);
115 ehdr->user_hdrs = libbalsa_message_user_hdrs_from_gmime(msg);
116
117 subj = g_mime_message_get_subject(msg);
118 if (subj) {
119 ehdr->subject =
120 g_mime_utils_header_decode_text(subj);
121 libbalsa_utf8_sanitize(&ehdr->subject, TRUE, NULL);
122 } else
123 ehdr->subject = g_strdup(_("(No subject)"));
124 g_mime_message_get_date(msg, &ehdr->date, &offset);
125
126 return ehdr;
127 }
128
129 /* Create a LibBalsaMessageBody with structure matching the GMimeObject;
130 * the body may already have the necessary parts, so we check before
131 * allocating new ones; it may have too many, so we free any that are no
132 * longer needed.
133 */
134
135 /* First some helpers. */
136 static void
libbalsa_message_body_set_filename(LibBalsaMessageBody * body)137 libbalsa_message_body_set_filename(LibBalsaMessageBody * body)
138 {
139 if (GMIME_IS_PART(body->mime_part)) {
140 g_free(body->filename);
141 body->filename =
142 g_strdup(g_mime_part_get_filename(GMIME_PART(body->mime_part)));
143 }
144 }
145
146 static void
libbalsa_message_body_set_types(LibBalsaMessageBody * body)147 libbalsa_message_body_set_types(LibBalsaMessageBody * body)
148 {
149 GMimeContentType *type;
150
151 type = g_mime_object_get_content_type(body->mime_part);
152 if (g_mime_content_type_is_type(type, "audio", "*"))
153 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_AUDIO;
154 else if (g_mime_content_type_is_type(type, "application", "*"))
155 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_APPLICATION;
156 else if (g_mime_content_type_is_type(type, "image", "*"))
157 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_IMAGE;
158 else if (g_mime_content_type_is_type(type, "message", "*"))
159 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_MESSAGE;
160 else if (g_mime_content_type_is_type(type, "model", "*"))
161 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_MODEL;
162 else if (g_mime_content_type_is_type(type, "multipart", "*"))
163 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_MULTIPART;
164 else if (g_mime_content_type_is_type(type, "text", "*"))
165 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_TEXT;
166 else if (g_mime_content_type_is_type(type, "video", "*"))
167 body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_VIDEO;
168 else body->body_type = LIBBALSA_MESSAGE_BODY_TYPE_OTHER;
169
170 g_free(body->content_type);
171 body->content_type = g_mime_content_type_to_string(type);
172 }
173
174 static LibBalsaMessageBody **
libbalsa_message_body_set_message_part(LibBalsaMessageBody * body,LibBalsaMessageBody ** next_part)175 libbalsa_message_body_set_message_part(LibBalsaMessageBody * body,
176 LibBalsaMessageBody ** next_part)
177 {
178 GMimeMessagePart *message_part;
179 GMimeMessage *embedded_message;
180
181 message_part = GMIME_MESSAGE_PART(body->mime_part);
182 embedded_message = g_mime_message_part_get_message(message_part);
183
184 if (embedded_message) {
185 libbalsa_message_headers_destroy(body->embhdrs);
186 body->embhdrs =
187 libbalsa_message_body_extract_embedded_headers
188 (embedded_message);
189 if (!*next_part)
190 *next_part = libbalsa_message_body_new(body->message);
191 libbalsa_message_body_set_mime_body(*next_part,
192 embedded_message->mime_part);
193 }
194
195 return *next_part ? &(*next_part)->next : next_part;
196 }
197
198 static LibBalsaMessageBody **
libbalsa_message_body_set_multipart(LibBalsaMessageBody * body,LibBalsaMessageBody ** next_part)199 libbalsa_message_body_set_multipart(LibBalsaMessageBody * body,
200 LibBalsaMessageBody ** next_part)
201 {
202 GMimeMultipart *multipart = GMIME_MULTIPART(body->mime_part);
203 GMimeObject *part;
204 int count, i;
205
206 count = g_mime_multipart_get_count (multipart);
207 for (i = 0; i < count; i++) {
208 part = g_mime_multipart_get_part (multipart, i);
209 if (!*next_part)
210 *next_part = libbalsa_message_body_new(body->message);
211 libbalsa_message_body_set_mime_body(*next_part, part);
212 next_part = &(*next_part)->next;
213 }
214
215 return next_part;
216 }
217
218 static void
libbalsa_message_body_set_parts(LibBalsaMessageBody * body)219 libbalsa_message_body_set_parts(LibBalsaMessageBody * body)
220 {
221 LibBalsaMessageBody **next_part = &body->parts;
222
223 if (GMIME_IS_MESSAGE_PART(body->mime_part))
224 next_part = libbalsa_message_body_set_message_part(body, next_part);
225 else if (GMIME_IS_MULTIPART(body->mime_part))
226 next_part = libbalsa_message_body_set_multipart(body, next_part);
227
228 /* Free any parts that weren't used; the test isn't strictly
229 * necessary, but it should fail unless something really strange has
230 * happened, so it's worth including. */
231 if (*next_part) {
232 libbalsa_message_body_free(*next_part);
233 *next_part = NULL;
234 }
235 }
236
237 void
libbalsa_message_body_set_mime_body(LibBalsaMessageBody * body,GMimeObject * mime_part)238 libbalsa_message_body_set_mime_body(LibBalsaMessageBody * body,
239 GMimeObject * mime_part)
240 {
241 g_return_if_fail(body != NULL);
242 g_return_if_fail(GMIME_IS_OBJECT(mime_part));
243
244 g_object_ref(mime_part);
245 if (body->mime_part)
246 g_object_unref(body->mime_part);
247 body->mime_part = mime_part;
248
249 libbalsa_message_body_set_filename(body);
250 libbalsa_message_body_set_types(body);
251 libbalsa_message_body_set_parts(body);
252 }
253
254 LibBalsaMessageBodyType
libbalsa_message_body_type(LibBalsaMessageBody * body)255 libbalsa_message_body_type(LibBalsaMessageBody * body)
256 {
257 /* FIXME: this could be a virtual function... OR not? */
258 return body->body_type;
259 }
260
261 gchar *
libbalsa_message_body_get_parameter(LibBalsaMessageBody * body,const gchar * param)262 libbalsa_message_body_get_parameter(LibBalsaMessageBody * body,
263 const gchar * param)
264 {
265 GMimeContentType *type;
266 gchar *res = NULL;
267
268 g_return_val_if_fail(body != NULL, NULL);
269
270 if (body->mime_part) {
271 type = g_mime_object_get_content_type(body->mime_part);
272 res = g_strdup(g_mime_content_type_get_parameter(type, param));
273 } else if (body->content_type) {
274 type = g_mime_content_type_new_from_string(body->content_type);
275 res = g_strdup(g_mime_content_type_get_parameter(type, param));
276 g_object_unref(type);
277 }
278
279 return res;
280 }
281
282 static gchar *
libbalsa_message_body_get_cid(LibBalsaMessageBody * body)283 libbalsa_message_body_get_cid(LibBalsaMessageBody * body)
284 {
285 const gchar *content_id = body->mime_part ?
286 g_mime_object_get_content_id(body->mime_part) : body->content_id;
287
288 if (content_id) {
289 if (content_id[0] == '<'
290 && content_id[strlen(content_id) - 1] == '>')
291 return g_strndup(content_id + 1, strlen(content_id) - 2);
292 return g_strdup(content_id);
293 }
294
295 return NULL;
296 }
297
298 /* libbalsa_message_body_save_temporary:
299 check if body has already its copy in temporary file and if not,
300 allocates a temporary file name and saves the body there.
301 */
302 gboolean
libbalsa_message_body_save_temporary(LibBalsaMessageBody * body,GError ** err)303 libbalsa_message_body_save_temporary(LibBalsaMessageBody * body, GError **err)
304 {
305 if (!body) {
306 g_set_error(err, LIBBALSA_MAILBOX_ERROR, LIBBALSA_MAILBOX_ACCESS_ERROR,
307 "NULL message body");
308 return FALSE;
309 }
310
311 if (body->temp_filename == NULL) {
312 gchar *filename;
313 gint fd = -1;
314 GMimeStream *tmp_stream;
315
316 filename = body->filename ?
317 g_strdup(body->filename) : libbalsa_message_body_get_cid(body);
318
319 if (!filename)
320 fd = g_file_open_tmp("balsa-body-XXXXXX",
321 &body->temp_filename, err);
322 else {
323 const gchar *tempdir =
324 libbalsa_message_get_tempdir(body->message);
325
326 if (!tempdir) {
327 g_set_error(err, LIBBALSA_MAILBOX_ERROR,
328 LIBBALSA_MAILBOX_TEMPDIR_ERROR,
329 "Failed to create temporary directory");
330 } else {
331 body->temp_filename =
332 g_build_filename(tempdir, filename, NULL);
333 fd = open(body->temp_filename,
334 O_WRONLY | O_EXCL | O_CREAT,
335 S_IRUSR);
336 }
337 g_free(filename);
338 }
339
340 if (fd < 0) {
341 if (err && !*err)
342 g_set_error(err, LIBBALSA_ERROR_QUARK, 1,
343 "Failed to create temporary file");
344 return FALSE;
345 }
346
347 if ((tmp_stream = g_mime_stream_fs_new(fd)) != NULL)
348 return libbalsa_message_body_save_stream(body, tmp_stream,
349 FALSE, err);
350 else {
351 g_set_error(err, LIBBALSA_ERROR_QUARK, 1,
352 _("Failed to create output stream"));
353 close(fd);
354 return FALSE;
355 }
356 } else {
357 /* the temporary name has been already allocated on previous
358 save_temporary action. We just check if the file is still there.
359 */
360 struct stat s;
361 if (stat(body->temp_filename, &s) == 0 &&
362 S_ISREG(s.st_mode) &&
363 s.st_uid == getuid())
364 return TRUE;
365 else
366 return libbalsa_message_body_save(body, body->temp_filename,
367 S_IRUSR,
368 FALSE, err);
369 }
370 }
371
372 /* libbalsa_message_body_save:
373 NOTE: has to use libbalsa_safe_open to set the file access privileges
374 to safe.
375 */
376 gboolean
libbalsa_message_body_save(LibBalsaMessageBody * body,const gchar * filename,mode_t mode,gboolean filter_crlf,GError ** err)377 libbalsa_message_body_save(LibBalsaMessageBody * body,
378 const gchar * filename, mode_t mode,
379 gboolean filter_crlf,
380 GError **err)
381 {
382 int fd;
383 int flags = O_CREAT | O_EXCL | O_WRONLY;
384 GMimeStream *out_stream;
385
386 #ifdef O_NOFOLLOW
387 flags |= O_NOFOLLOW;
388 #endif
389
390 if ((fd = libbalsa_safe_open(filename, flags, mode, err)) < 0)
391 return FALSE;
392
393 if ((out_stream = g_mime_stream_fs_new(fd)) != NULL)
394 return libbalsa_message_body_save_stream(body, out_stream,
395 filter_crlf, err);
396
397 /* could not create stream */
398 g_set_error(err, LIBBALSA_ERROR_QUARK, 1,
399 _("Failed to create output stream"));
400 close(fd);
401 return FALSE;
402 }
403
404
405 gboolean
libbalsa_message_body_save_vfs(LibBalsaMessageBody * body,LibbalsaVfs * dest,mode_t mode,gboolean filter_crlf,GError ** err)406 libbalsa_message_body_save_vfs(LibBalsaMessageBody * body,
407 LibbalsaVfs * dest, mode_t mode,
408 gboolean filter_crlf,
409 GError **err)
410 {
411 GMimeStream * out_stream;
412
413 if (!(out_stream = libbalsa_vfs_create_stream(dest, mode, TRUE, err)))
414 return FALSE;
415
416 return libbalsa_message_body_save_stream(body, out_stream, filter_crlf, err);
417 }
418
419 static GMimeStream *
libbalsa_message_body_stream_add_filter(GMimeStream * stream,GMimeFilter * filter)420 libbalsa_message_body_stream_add_filter(GMimeStream * stream,
421 GMimeFilter * filter)
422 {
423 if (!GMIME_IS_STREAM_FILTER(stream)) {
424 GMimeStream *filtered_stream =
425 g_mime_stream_filter_new(stream);
426 g_object_unref(stream);
427 stream = filtered_stream;
428 }
429
430 g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream), filter);
431 g_object_unref(filter);
432
433 return stream;
434 }
435
436 static GMimeStream *
libbalsa_message_body_get_part_stream(LibBalsaMessageBody * body,GError ** err)437 libbalsa_message_body_get_part_stream(LibBalsaMessageBody * body,
438 GError ** err)
439 {
440 GMimeStream *stream;
441 GMimeDataWrapper *wrapper;
442 GMimeContentEncoding encoding;
443 GMimeFilter *filter;
444 gchar *mime_type = NULL;
445 const gchar *charset;
446
447 wrapper = g_mime_part_get_content_object(GMIME_PART(body->mime_part));
448 if (!wrapper) {
449 /* part is incomplete. */
450 g_set_error(err, LIBBALSA_MAILBOX_ERROR,
451 LIBBALSA_MAILBOX_ACCESS_ERROR,
452 "Internal error in get_stream");
453 return NULL;
454 }
455
456 stream = g_object_ref(g_mime_data_wrapper_get_stream(wrapper));
457 encoding = g_mime_data_wrapper_get_encoding(wrapper);
458
459 switch (encoding) {
460 case GMIME_CONTENT_ENCODING_BASE64:
461 case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
462 case GMIME_CONTENT_ENCODING_UUENCODE:
463 filter = g_mime_filter_basic_new(encoding, FALSE);
464 stream = libbalsa_message_body_stream_add_filter(stream, filter);
465 break;
466 default:
467 break;
468 }
469
470 /* convert text bodies but HTML - gtkhtml does conversion on its own. */
471 if (libbalsa_message_body_type(body) == LIBBALSA_MESSAGE_BODY_TYPE_TEXT
472 && strcmp(mime_type = libbalsa_message_body_get_mime_type(body),
473 "text/html") != 0
474 && (charset = libbalsa_message_body_charset(body)) != NULL
475 && g_ascii_strcasecmp(charset, "unknown-8bit") != 0) {
476 GMimeStream *stream_null;
477 GMimeStream *stream_filter;
478 GMimeFilter *filter_windows;
479
480 stream_null = g_mime_stream_null_new();
481 stream_filter = g_mime_stream_filter_new(stream_null);
482 g_object_unref(stream_null);
483
484 filter_windows = g_mime_filter_windows_new(charset);
485 g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
486 filter_windows);
487
488 libbalsa_mailbox_lock_store(body->message->mailbox);
489 g_mime_stream_reset(stream);
490 g_mime_stream_write_to_stream(stream, stream_filter);
491 libbalsa_mailbox_unlock_store(body->message->mailbox);
492 g_object_unref(stream_filter);
493
494 charset = g_mime_filter_windows_real_charset(GMIME_FILTER_WINDOWS
495 (filter_windows));
496 if ((filter = g_mime_filter_charset_new(charset, "UTF-8"))) {
497 stream =
498 libbalsa_message_body_stream_add_filter(stream, filter);
499 g_free(body->charset);
500 body->charset = g_strdup(charset);
501 }
502 g_object_unref(filter_windows);
503 }
504
505 g_free(mime_type);
506
507 return stream;
508 }
509
510 static GMimeStream *
libbalsa_message_body_get_message_part_stream(LibBalsaMessageBody * body,GError ** err)511 libbalsa_message_body_get_message_part_stream(LibBalsaMessageBody * body,
512 GError ** err)
513 {
514 GMimeStream *stream;
515 ssize_t bytes_written;
516 GMimeMessage *msg = g_mime_message_part_get_message
517 (GMIME_MESSAGE_PART(body->mime_part));
518
519 stream = g_mime_stream_mem_new();
520 libbalsa_mailbox_lock_store(body->message->mailbox);
521 bytes_written =
522 g_mime_object_write_to_stream(GMIME_OBJECT(msg), stream);
523 libbalsa_mailbox_unlock_store(body->message->mailbox);
524 printf("Written %ld bytes of embedded message\n",
525 (long) bytes_written);
526
527 if (bytes_written < 0) {
528 g_object_unref(stream);
529 g_set_error(err, LIBBALSA_MAILBOX_ERROR,
530 LIBBALSA_MAILBOX_ACCESS_ERROR,
531 _("Could not read embedded message"));
532 return NULL;
533 }
534
535 g_mime_stream_reset(stream);
536 return stream;
537 }
538
539 GMimeStream *
libbalsa_message_body_get_stream(LibBalsaMessageBody * body,GError ** err)540 libbalsa_message_body_get_stream(LibBalsaMessageBody * body, GError **err)
541 {
542 g_return_val_if_fail(body != NULL, NULL);
543 g_return_val_if_fail(body->message != NULL, NULL);
544
545 if (!body->message->mailbox) {
546 g_set_error(err, LIBBALSA_MAILBOX_ERROR,
547 LIBBALSA_MAILBOX_ACCESS_ERROR,
548 "Internal error in get_stream");
549 return NULL;
550 }
551
552 if (!libbalsa_mailbox_get_message_part(body->message, body, err)
553 || !(GMIME_IS_PART(body->mime_part)
554 || GMIME_IS_MESSAGE_PART(body->mime_part))) {
555 if (err && !*err)
556 g_set_error(err, LIBBALSA_MAILBOX_ERROR,
557 LIBBALSA_MAILBOX_ACCESS_ERROR,
558 "Cannot get stream for part of type %s",
559 g_type_name(G_TYPE_FROM_INSTANCE
560 (body->mime_part)));
561 return NULL;
562 }
563
564 /* We handle "real" parts and embedded rfc822 messages
565 differently. There is probably a way to unify if we use
566 GMimeObject common denominator. */
567 if (GMIME_IS_MESSAGE_PART(body->mime_part))
568 return libbalsa_message_body_get_message_part_stream(body, err);
569
570 return libbalsa_message_body_get_part_stream(body, err);
571 }
572
573 gssize
libbalsa_message_body_get_content(LibBalsaMessageBody * body,gchar ** buf,GError ** err)574 libbalsa_message_body_get_content(LibBalsaMessageBody * body, gchar ** buf,
575 GError **err)
576 {
577 GMimeStream *stream, *stream_mem;
578 GByteArray *array;
579 gssize len;
580
581 g_return_val_if_fail(body != NULL, -1);
582 g_return_val_if_fail(body->message != NULL, -1);
583 g_return_val_if_fail(buf != NULL, -1);
584
585 *buf = NULL;
586 stream = libbalsa_message_body_get_stream(body, err);
587 if (!stream)
588 return -1;
589
590 array = g_byte_array_new();
591 stream_mem = g_mime_stream_mem_new_with_byte_array(array);
592 g_mime_stream_mem_set_owner(GMIME_STREAM_MEM(stream_mem), FALSE);
593
594 libbalsa_mailbox_lock_store(body->message->mailbox);
595 g_mime_stream_reset(stream);
596 len = g_mime_stream_write_to_stream(stream, stream_mem);
597 libbalsa_mailbox_unlock_store(body->message->mailbox);
598 g_object_unref(stream);
599 g_object_unref(stream_mem);
600
601 if (len >= 0) {
602 guint8 zero = 0;
603 len = array->len;
604 /* NULL-terminate, in case it is used as a string. */
605 g_byte_array_append(array, &zero, 1);
606 *buf = (gchar *) g_byte_array_free(array, FALSE);
607 } else {
608 g_byte_array_free(array, TRUE);
609 g_set_error(err, LIBBALSA_MAILBOX_ERROR,
610 LIBBALSA_MAILBOX_ACCESS_ERROR,
611 "Write error in get_content");
612 }
613
614 return len;
615 }
616
617 GdkPixbuf *
libbalsa_message_body_get_pixbuf(LibBalsaMessageBody * body,GError ** err)618 libbalsa_message_body_get_pixbuf(LibBalsaMessageBody * body, GError ** err)
619 {
620 GMimeStream *stream;
621 gchar *mime_type;
622 GdkPixbufLoader *loader;
623 GdkPixbuf *pixbuf = NULL;
624
625 stream = libbalsa_message_body_get_stream(body, err);
626 if (!stream)
627 return pixbuf;
628
629 libbalsa_mailbox_lock_store(body->message->mailbox);
630 g_mime_stream_reset(stream);
631
632 mime_type = libbalsa_message_body_get_mime_type(body);
633 loader = gdk_pixbuf_loader_new_with_mime_type(mime_type, err);
634
635 #define ENABLE_WORKAROUND_FOR_IE_NON_IANA_MIME_TYPE TRUE
636 #if ENABLE_WORKAROUND_FOR_IE_NON_IANA_MIME_TYPE
637 if (!loader
638 && (!g_ascii_strcasecmp(mime_type, "image/pjpeg")
639 || !g_ascii_strcasecmp(mime_type, "image/jpg"))) {
640 g_clear_error(err);
641 loader = gdk_pixbuf_loader_new_with_mime_type("image/jpeg", err);
642 }
643 #endif /* ENABLE_WORKAROUND_FOR_IE_NON_IANA_MIME_TYPE */
644
645 g_free(mime_type);
646
647 if (loader) {
648 gssize count;
649 gchar buf[4096];
650
651 while ((count = g_mime_stream_read(stream, buf, sizeof(buf))) > 0)
652 if (!gdk_pixbuf_loader_write(loader, (guchar *) buf, count, err))
653 break;
654
655 if (!*err && gdk_pixbuf_loader_close(loader, err)) {
656 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
657 pixbuf = gdk_pixbuf_apply_embedded_orientation(pixbuf);
658 }
659
660 g_object_unref(loader);
661 }
662
663 libbalsa_mailbox_unlock_store(body->message->mailbox);
664 g_object_unref(stream);
665
666 return pixbuf;
667 }
668
669 gboolean
libbalsa_message_body_save_stream(LibBalsaMessageBody * body,GMimeStream * dest,gboolean filter_crlf,GError ** err)670 libbalsa_message_body_save_stream(LibBalsaMessageBody * body,
671 GMimeStream * dest, gboolean filter_crlf,
672 GError ** err)
673 {
674 GMimeStream *stream;
675 ssize_t len;
676
677 stream = libbalsa_message_body_get_stream(body, err);
678 if (!body->mime_part)
679 return FALSE;
680 g_clear_error(err);
681
682 libbalsa_mailbox_lock_store(body->message->mailbox);
683
684 if (stream) {
685 g_mime_stream_reset(stream);
686
687 if (filter_crlf) {
688 GMimeFilter *filter = g_mime_filter_crlf_new(FALSE, FALSE);
689 stream =
690 libbalsa_message_body_stream_add_filter(stream, filter);
691 }
692
693 len = g_mime_stream_write_to_stream(stream, dest);
694 g_object_unref(stream);
695 } else
696 /* body->mime_part is neither a GMimePart nor a GMimeMessagePart. */
697 len = g_mime_object_write_to_stream(body->mime_part, dest);
698
699 libbalsa_mailbox_unlock_store(body->message->mailbox);
700 g_object_unref(dest);
701
702 if (len < 0)
703 g_set_error(err, LIBBALSA_MAILBOX_ERROR, LIBBALSA_MAILBOX_ACCESS_ERROR,
704 "Write error in save_stream");
705
706 return len >= 0;
707 }
708
709 gchar *
libbalsa_message_body_get_mime_type(LibBalsaMessageBody * body)710 libbalsa_message_body_get_mime_type(LibBalsaMessageBody * body)
711 {
712 gchar *res, *tmp;
713
714 g_return_val_if_fail(body != NULL, NULL);
715
716 if (!body->content_type)
717 return g_strdup("text/plain");
718
719 tmp = strchr(body->content_type, ';');
720 res = g_ascii_strdown(body->content_type,
721 tmp ? tmp-body->content_type : -1);
722 return res;
723 }
724
725 gboolean
libbalsa_message_body_is_multipart(LibBalsaMessageBody * body)726 libbalsa_message_body_is_multipart(LibBalsaMessageBody * body)
727 {
728 return body->mime_part ?
729 GMIME_IS_MULTIPART(body->mime_part) :
730 body->body_type == LIBBALSA_MESSAGE_BODY_TYPE_MULTIPART;
731 }
732
733 gboolean
libbalsa_message_body_is_inline(LibBalsaMessageBody * body)734 libbalsa_message_body_is_inline(LibBalsaMessageBody * body)
735 {
736 const gchar *disposition;
737
738 g_return_val_if_fail(body->mime_part == NULL ||
739 GMIME_IS_OBJECT(body->mime_part), FALSE);
740
741 if (body->mime_part)
742 disposition = g_mime_object_get_header(body->mime_part,
743 "Content-Disposition");
744 else
745 disposition = body->content_dsp;
746
747 if (!disposition)
748 /* Default disposition is in-line for text/plain, and generally
749 * attachment for other content types. Default content type is
750 * text/plain except in multipart/digest, where it is
751 * message/rfc822; in either case, we want to in-line the part.
752 */
753 return (body->content_type == NULL
754 || g_ascii_strcasecmp(body->content_type,
755 "message/rfc822") == 0
756 || g_ascii_strcasecmp(body->content_type,
757 "text/plain") == 0);
758
759 return g_ascii_strncasecmp(disposition,
760 GMIME_DISPOSITION_INLINE,
761 strlen(GMIME_DISPOSITION_INLINE)) == 0;
762 }
763
764 /* libbalsa_message_body_is_flowed:
765 * test whether a message body is format=flowed */
766 gboolean
libbalsa_message_body_is_flowed(LibBalsaMessageBody * body)767 libbalsa_message_body_is_flowed(LibBalsaMessageBody * body)
768 {
769 gchar *content_type;
770 gboolean flowed = FALSE;
771
772 content_type = libbalsa_message_body_get_mime_type(body);
773 if (g_ascii_strcasecmp(content_type, "text/plain") == 0) {
774 gchar *format =
775 libbalsa_message_body_get_parameter(body, "format");
776
777 if (format) {
778 flowed = (g_ascii_strcasecmp(format, "flowed") == 0);
779 g_free(format);
780 }
781 }
782 g_free(content_type);
783
784 return flowed;
785 }
786
787 gboolean
libbalsa_message_body_is_delsp(LibBalsaMessageBody * body)788 libbalsa_message_body_is_delsp(LibBalsaMessageBody * body)
789 {
790 gboolean delsp = FALSE;
791
792 if (libbalsa_message_body_is_flowed(body)) {
793 gchar *delsp_param =
794 libbalsa_message_body_get_parameter(body, "delsp");
795
796 if (delsp_param) {
797 delsp = (g_ascii_strcasecmp(delsp_param, "yes") == 0);
798 g_free(delsp_param);
799 }
800 }
801
802 return delsp;
803 }
804
805 LibBalsaMessageBody *
libbalsa_message_body_get_by_id(LibBalsaMessageBody * body,const gchar * id)806 libbalsa_message_body_get_by_id(LibBalsaMessageBody * body,
807 const gchar * id)
808 {
809 LibBalsaMessageBody *res;
810 gchar *cid;
811
812 g_return_val_if_fail(id != NULL, NULL);
813
814 if (!body)
815 return NULL;
816
817 if ((cid = libbalsa_message_body_get_cid(body))) {
818 gboolean matches = !strcmp(id, cid);
819 g_free(cid);
820 if (matches)
821 return body;
822 }
823
824 if ((res = libbalsa_message_body_get_by_id(body->parts, id)) != NULL)
825 return res;
826
827 return libbalsa_message_body_get_by_id(body->next, id);
828 }
829
830 #ifdef HAVE_GPGME
831 LibBalsaMsgProtectState
libbalsa_message_body_protect_state(LibBalsaMessageBody * body)832 libbalsa_message_body_protect_state(LibBalsaMessageBody *body)
833 {
834 if (!body || !body->sig_info ||
835 body->sig_info->status == GPG_ERR_NOT_SIGNED ||
836 body->sig_info->status == GPG_ERR_CANCELED)
837 return LIBBALSA_MSG_PROTECT_NONE;
838
839 if (body->sig_info->status == GPG_ERR_NO_ERROR) {
840 /* good signature, check if the validity and trust (OpenPGP only) are
841 at least marginal */
842 if (body->sig_info->validity >= GPGME_VALIDITY_MARGINAL &&
843 (body->sig_info->protocol == GPGME_PROTOCOL_CMS ||
844 body->sig_info->key->owner_trust >= GPGME_VALIDITY_MARGINAL))
845 return LIBBALSA_MSG_PROTECT_SIGN_GOOD;
846 else
847 return LIBBALSA_MSG_PROTECT_SIGN_NOTRUST;
848 }
849
850 return LIBBALSA_MSG_PROTECT_SIGN_BAD;
851 }
852 #endif
853