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