1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /* Balsa E-Mail Client
3 * Copyright (C) 1997-2013 Stuart Parmenter and others,
4 * See the file AUTHORS for a list.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 /* DESIGN NOTES.
23 MESSAGE_COPY_CONTENT define is an attempt to reduce memory usage of balsa.
24 When it is defined, The message date is stored in one place only (in
25 libmutt structures). This should reduce memory usage to some extent.
26 However, it is not implemented very extensively at the present moment
27 and the memory usage reduction is hardly noticeable.
28 - Lack of inline functions in C increases program complexity. This cost
29 can be accepted.
30 - thorough analysis of memory usage is needed.
31 */
32
33 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
34 # include "config.h"
35 #endif /* HAVE_CONFIG_H */
36
37 #include <ctype.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <glib.h>
42
43 #include "libbalsa.h"
44 #include "libbalsa_private.h"
45
46 /* needed for truncate_string */
47 #include "misc.h"
48
49 #include "mime-stream-shared.h"
50 #include <glib/gi18n.h>
51
52 #include <gmime/gmime.h>
53
54 static void libbalsa_message_class_init(LibBalsaMessageClass * klass);
55 static void libbalsa_message_init(LibBalsaMessage * message);
56
57 static void libbalsa_message_finalize(GObject * object);
58
59
60 static GObjectClass *parent_class = NULL;
61
62 GType
libbalsa_message_get_type()63 libbalsa_message_get_type()
64 {
65 static GType libbalsa_message_type = 0;
66
67 if (!libbalsa_message_type) {
68 static const GTypeInfo libbalsa_message_info = {
69 sizeof(LibBalsaMessageClass),
70 NULL, /* base_init */
71 NULL, /* base_finalize */
72 (GClassInitFunc) libbalsa_message_class_init,
73 NULL, /* class_finalize */
74 NULL, /* class_data */
75 sizeof(LibBalsaMessage),
76 0, /* n_preallocs */
77 (GInstanceInitFunc) libbalsa_message_init
78 };
79
80 libbalsa_message_type =
81 g_type_register_static(G_TYPE_OBJECT, "LibBalsaMessage",
82 &libbalsa_message_info, 0);
83 }
84
85 return libbalsa_message_type;
86 }
87
88 static void
libbalsa_message_init(LibBalsaMessage * message)89 libbalsa_message_init(LibBalsaMessage * message)
90 {
91 message->headers = g_new0(LibBalsaMessageHeaders, 1);
92 message->flags = 0;
93 message->mailbox = NULL;
94 message->sender = NULL;
95 message->subj = NULL;
96 message->references = NULL;
97 message->in_reply_to = NULL;
98 message->message_id = NULL;
99 message->subtype = 0;
100 message->parameters = NULL;
101 message->body_ref = 0;
102 message->body_list = NULL;
103 message->has_all_headers = 0;
104 #ifdef HAVE_GPGME
105 message->prot_state = LIBBALSA_MSG_PROTECT_NONE;
106 message->force_key_id = NULL;
107 #endif
108 }
109
110
111 static void
libbalsa_message_class_init(LibBalsaMessageClass * klass)112 libbalsa_message_class_init(LibBalsaMessageClass * klass)
113 {
114 GObjectClass *object_class = G_OBJECT_CLASS(klass);
115
116 parent_class = g_type_class_peek_parent(klass);
117
118 object_class->finalize = libbalsa_message_finalize;
119 }
120
121 LibBalsaMessage *
libbalsa_message_new(void)122 libbalsa_message_new(void)
123 {
124 LibBalsaMessage *message;
125
126 message = g_object_new(LIBBALSA_TYPE_MESSAGE, NULL);
127
128 return message;
129 }
130
131 /* libbalsa_message_finalize:
132 finalize methods must leave object in 'sane' state.
133 This means NULLifing released pointers.
134 */
135 static void
libbalsa_message_finalize(GObject * object)136 libbalsa_message_finalize(GObject * object)
137 {
138 LibBalsaMessage *message;
139
140 g_return_if_fail(object != NULL);
141 g_return_if_fail(LIBBALSA_IS_MESSAGE(object));
142
143 message = LIBBALSA_MESSAGE(object);
144
145 libbalsa_message_headers_destroy(message->headers);
146 message->headers = NULL;
147
148 if (message->sender) {
149 g_object_unref(message->sender);
150 message->sender = NULL;
151 }
152
153 #if MESSAGE_COPY_CONTENT
154 g_free(message->subj);
155 message->subj = NULL;
156 #endif
157 g_list_foreach(message->references, (GFunc) g_free, NULL);
158 g_list_free(message->references);
159 message->references = NULL;
160
161 g_list_foreach(message->in_reply_to, (GFunc) g_free, NULL);
162 g_list_free(message->in_reply_to);
163 message->in_reply_to = NULL;
164
165 g_free(message->message_id);
166 message->message_id = NULL;
167
168 g_free(message->subtype);
169 message->subtype = NULL;
170
171 g_list_foreach(message->parameters, (GFunc) g_strfreev, NULL);
172 g_list_free(message->parameters);
173 message->parameters = NULL;
174
175
176 libbalsa_message_body_free(message->body_list);
177 message->body_list = NULL;
178
179 if (message->mime_msg) {
180 g_object_unref(message->mime_msg);
181 message->mime_msg = NULL;
182 }
183
184 #ifdef HAVE_GPGME
185 g_free(message->force_key_id);
186 #endif
187
188 if (message->tempdir) {
189 if (rmdir(message->tempdir))
190 g_warning("Could not remove %s", message->tempdir);
191 g_free(message->tempdir);
192 message->tempdir = NULL;
193 }
194
195 G_OBJECT_CLASS(parent_class)->finalize(object);
196 }
197
198 static void
lb_message_headers_extra_destroy(LibBalsaMessageHeaders * headers)199 lb_message_headers_extra_destroy(LibBalsaMessageHeaders * headers)
200 {
201 if (!headers)
202 return;
203
204 FREE_HEADER_LIST(headers->user_hdrs);
205 headers->user_hdrs = NULL;
206
207 g_free(headers->fcc_url);
208 headers->fcc_url = NULL;
209 }
210
211 void
libbalsa_message_headers_destroy(LibBalsaMessageHeaders * headers)212 libbalsa_message_headers_destroy(LibBalsaMessageHeaders * headers)
213 {
214 if (!headers)
215 return;
216
217 g_free(headers->subject);
218 headers->subject = NULL;
219
220 if (headers->from) {
221 g_object_unref(headers->from);
222 headers->from = NULL;
223 }
224
225 if (headers->to_list) {
226 g_object_unref(headers->to_list);
227 headers->to_list = NULL;
228 }
229
230 if (headers->content_type) {
231 g_object_unref(headers->content_type);
232 headers->content_type = NULL;
233 }
234
235 if (headers->cc_list) {
236 g_object_unref(headers->cc_list);
237 headers->cc_list = NULL;
238 }
239
240 if (headers->bcc_list) {
241 g_object_unref(headers->bcc_list);
242 headers->bcc_list = NULL;
243 }
244
245 if (headers->reply_to) {
246 g_object_unref(headers->reply_to);
247 headers->reply_to = NULL;
248 }
249
250 if(headers->dispnotify_to) {
251 g_object_unref(headers->dispnotify_to);
252 headers->dispnotify_to = NULL;
253 }
254
255 lb_message_headers_extra_destroy(headers);
256
257 g_free(headers);
258 }
259
260 const gchar *
libbalsa_message_body_charset(LibBalsaMessageBody * body)261 libbalsa_message_body_charset(LibBalsaMessageBody * body)
262 {
263 const gchar *charset;
264
265 if (!body)
266 return NULL;
267
268 if (body->charset) /* This overrides all! Important for non
269 * us-ascii messages over IMAP. */
270 return body->charset;
271
272 if (GMIME_IS_PART(body->mime_part)) {
273 GMimeContentType *type;
274
275 type = g_mime_object_get_content_type(body->mime_part);
276 return g_mime_content_type_get_parameter(type, "charset");
277 }
278
279 charset = libbalsa_message_body_charset(body->parts);
280 if (charset)
281 return charset;
282
283 return libbalsa_message_body_charset(body->next);
284 }
285
286 /* UTF-8-aware header cleaning by Albrecht */
287 static void
canonize_header_value(gchar * value)288 canonize_header_value(gchar *value)
289 {
290 gchar *dptr = value;
291
292 while (*value) {
293 if (g_unichar_isspace(g_utf8_get_char(value))) {
294 do {
295 value = g_utf8_next_char(value);
296 } while (g_unichar_isspace(g_utf8_get_char(value)));
297 *dptr++ = ' ';
298 } else {
299 gint bytes = g_utf8_next_char(value) - value;
300
301 do {
302 *dptr++ = *value++;
303 } while (--bytes > 0);
304 }
305 }
306
307 *dptr = '\0';
308 }
309
310 /* message_user_hdrs:
311 returns allocated GList containing (header=>value) ALL headers pairs
312 as generated by g_strsplit.
313 The list has to be freed by the following chunk of code:
314 FREE_HEADER_LIST(list);
315 */
316 static gchar **
libbalsa_create_hdr_pair(const gchar * name,gchar * value)317 libbalsa_create_hdr_pair(const gchar * name, gchar * value)
318 {
319 gchar **item = g_new(gchar *, 3);
320
321 canonize_header_value(value);
322 item[0] = g_strdup(name);
323 item[1] = value;
324 item[2] = NULL;
325 return item;
326 }
327
328 static GList*
libbalsa_message_header_get_helper(LibBalsaMessageHeaders * headers,const gchar * find)329 libbalsa_message_header_get_helper(LibBalsaMessageHeaders* headers,
330 const gchar *find)
331 {
332 GList *list;
333 for (list = headers->user_hdrs; list; list = list->next) {
334 const gchar * const *tmp = list->data;
335
336 if (g_ascii_strcasecmp(tmp[0], find) == 0)
337 return list;
338 }
339 return NULL;
340 }
341
342 /** libbalsa_message_find_user_hdr:
343 returns.... list element matching given header.
344 */
345 static GList *
libbalsa_message_find_user_hdr(LibBalsaMessage * message,const gchar * find)346 libbalsa_message_find_user_hdr(LibBalsaMessage * message, const gchar * find)
347 {
348 LibBalsaMessageHeaders *headers = message->headers;
349
350 g_return_val_if_fail(headers, NULL);
351 if (!headers->user_hdrs && message->mailbox)
352 libbalsa_mailbox_set_msg_headers(message->mailbox, message);
353
354 return libbalsa_message_header_get_helper(headers, find);
355 }
356
357 /*
358 * Public user header methods
359 */
360 const gchar*
libbalsa_message_header_get_one(LibBalsaMessageHeaders * headers,const gchar * find)361 libbalsa_message_header_get_one(LibBalsaMessageHeaders* headers,
362 const gchar *find)
363 {
364 GList *header;
365 const gchar *const *pair;
366
367 if (!(header = libbalsa_message_header_get_helper(headers, find)))
368 return NULL;
369
370 pair = header->data;
371 return pair[1];
372 }
373
374 GList*
libbalsa_message_header_get_all(LibBalsaMessageHeaders * headers,const gchar * find)375 libbalsa_message_header_get_all(LibBalsaMessageHeaders* headers,
376 const gchar *find)
377 {
378 GList *header;
379 const gchar *const *pair;
380 GList *res = NULL;
381
382 if (!(header = libbalsa_message_header_get_helper(headers, find)))
383 return NULL;
384 pair = header->data;
385 for(pair++; *pair; pair++)
386 res = g_list_append(res, g_strdup(*pair));
387
388 return res;
389 }
390
391 const gchar *
libbalsa_message_get_user_header(LibBalsaMessage * message,const gchar * name)392 libbalsa_message_get_user_header(LibBalsaMessage * message,
393 const gchar * name)
394 {
395 GList *header;
396 const gchar *const *pair;
397
398 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), NULL);
399 g_return_val_if_fail(name != NULL, NULL);
400
401 if (!(header = libbalsa_message_find_user_hdr(message, name)))
402 return NULL;
403
404 pair = header->data;
405 return pair[1];
406 }
407
408 void
libbalsa_message_set_user_header(LibBalsaMessage * message,const gchar * name,const gchar * value)409 libbalsa_message_set_user_header(LibBalsaMessage * message,
410 const gchar * name, const gchar * value)
411 {
412 LibBalsaMessageHeaders *headers;
413 GList *header;
414
415 g_return_if_fail(LIBBALSA_IS_MESSAGE(message));
416 g_return_if_fail(name != NULL);
417
418 headers = message->headers;
419 g_return_if_fail(headers != NULL);
420
421 if ((header = libbalsa_message_find_user_hdr(message, name))) {
422 headers->user_hdrs =
423 g_list_remove_link(headers->user_hdrs, header);
424 FREE_HEADER_LIST(header);
425 }
426
427 if (value && *value)
428 headers->user_hdrs =
429 g_list_prepend(headers->user_hdrs,
430 libbalsa_create_hdr_pair(name,
431 g_strdup(value)));
432 }
433
434 static void
prepend_header_misc(const char * name,const char * value,gpointer user_data)435 prepend_header_misc(const char *name, const char *value,
436 gpointer user_data)
437 {
438 char lcname[28]; /* one byte longer than the longest ignored header */
439 static const char ignored_headers[] =
440 "subject date from to cc bcc "
441 "message-id references in-reply-to status lines"
442 "disposition-notification-to";
443 unsigned i;
444 GList *res = *(GList **)user_data;
445 if (!*value)
446 /* Empty header */
447 return;
448 /* Standard Headers*/
449 for(i=0; i<sizeof(lcname)-1 && name[i]; i++)
450 lcname[i] = tolower(name[i]);
451 if (i < sizeof(lcname)) {
452 /* short enough to be on the ignored-headers list */
453 lcname[i] = '\0';
454 if(strstr(ignored_headers, lcname))
455 return;
456 }
457
458 res = g_list_prepend(res, libbalsa_create_hdr_pair(name, g_strdup(value)));
459 *(GList **)user_data = res;
460 }
461
462 /*
463 * libbalsa_message_user_hdrs_from_gmime:
464 *
465 * returns allocated GList containing (header=>value) ALL headers
466 * pairs as generated by g_strsplit. The list has to be freed by the
467 * following chunk of code (or something functionally similar):
468 *
469 * g_list_foreach(list, (GFunc) g_strfreev, NULL);
470 * g_list_free(list);
471 */
472
473
474 GList *
libbalsa_message_user_hdrs_from_gmime(GMimeMessage * message)475 libbalsa_message_user_hdrs_from_gmime(GMimeMessage * message)
476 {
477 GMimeHeaderList *hdrlist;
478 GMimeHeaderIter iter;
479 GList *res = NULL;
480 const char *value;
481
482 g_return_val_if_fail(message != NULL, NULL);
483
484 value = g_mime_message_get_message_id(message);
485 if (value)
486 res = g_list_prepend(res, libbalsa_create_hdr_pair("Message-ID",
487 g_strdup_printf("<%s>", value)));
488
489 /* FIXME: This duplicates References headers since they are
490 already present in LibBalsaMessage::references field. FWIW,
491 mailbox driver does not copy references to user_headers.
492 */
493 value = g_mime_object_get_header(GMIME_OBJECT(message), "References");
494 if (value) {
495 #if BALSA_NEEDS_SEPARATE_USER_HEADERS
496 GMimeReferences *references, *reference;
497 reference = references = g_mime_references_decode(value);
498 while (reference) {
499 res =
500 g_list_prepend(res,
501 libbalsa_create_hdr_pair("References",
502 g_strdup_printf
503 ("<%s>",
504 reference->
505 msgid)));
506 reference = reference->next;
507 }
508 g_mime_references_clear(&references);
509 #else
510 res = g_list_prepend(res,
511 libbalsa_create_hdr_pair("References",
512 g_strdup(value)));
513 #endif
514 }
515
516 value = g_mime_object_get_header(GMIME_OBJECT(message), "In-Reply-To");
517 if (value) {
518 res =
519 g_list_prepend(res,
520 libbalsa_create_hdr_pair
521 ("In-Reply-To",
522 g_mime_utils_header_decode_text(value)));
523 }
524
525 hdrlist = g_mime_object_get_header_list (GMIME_OBJECT(message));
526 if (g_mime_header_list_get_iter (hdrlist, &iter)) {
527 do {
528 prepend_header_misc (g_mime_header_iter_get_name (&iter),
529 g_mime_header_iter_get_value (&iter),
530 &res);
531 } while (g_mime_header_iter_next (&iter));
532 }
533
534 return g_list_reverse(res);
535 }
536
537 /* libbalsa_message_get_part_by_id:
538 return a message part identified by Content-ID=id
539 message must be referenced. (FIXME?)
540 */
541 LibBalsaMessageBody *
libbalsa_message_get_part_by_id(LibBalsaMessage * msg,const gchar * id)542 libbalsa_message_get_part_by_id(LibBalsaMessage* msg, const gchar* id)
543 {
544 return libbalsa_message_body_get_by_id(msg->body_list, id);
545 }
546
547 /* libbalsa_message_save:
548 return TRUE on success and FALSE on failure.
549 */
550 gboolean
libbalsa_message_save(LibBalsaMessage * message,const gchar * filename)551 libbalsa_message_save(LibBalsaMessage * message, const gchar *filename)
552 {
553 FILE *outfile;
554 int res;
555 GMimeStream *msg_stream;
556 GMimeStream *out_stream;
557
558 g_return_val_if_fail(message->mailbox, FALSE);
559
560 if( (outfile = fopen(filename, "w")) == NULL) return FALSE;
561 g_return_val_if_fail(outfile, FALSE);
562
563 msg_stream = libbalsa_message_stream(message);
564 if (msg_stream == NULL)
565 return FALSE;
566 out_stream = g_mime_stream_file_new(outfile);
567 libbalsa_mailbox_lock_store(message->mailbox);
568 res = g_mime_stream_write_to_stream(msg_stream, out_stream);
569 libbalsa_mailbox_unlock_store(message->mailbox);
570
571 g_object_unref(msg_stream);
572 g_object_unref(out_stream);
573
574 return res >= 0;
575 }
576
577 LibBalsaMessageAttach
libbalsa_message_get_attach_icon(LibBalsaMessage * message)578 libbalsa_message_get_attach_icon(LibBalsaMessage * message)
579 {
580 #ifdef HAVE_GPGME
581 if (libbalsa_message_is_pgp_encrypted(message))
582 return LIBBALSA_MESSAGE_ATTACH_ENCR;
583 else if (message->prot_state != LIBBALSA_MSG_PROTECT_NONE ||
584 libbalsa_message_is_pgp_signed(message)) {
585 switch (message->prot_state) {
586 case LIBBALSA_MSG_PROTECT_SIGN_GOOD:
587 return LIBBALSA_MESSAGE_ATTACH_GOOD;
588 case LIBBALSA_MSG_PROTECT_SIGN_NOTRUST:
589 return LIBBALSA_MESSAGE_ATTACH_NOTRUST;
590 case LIBBALSA_MSG_PROTECT_SIGN_BAD:
591 return LIBBALSA_MESSAGE_ATTACH_BAD;
592 case LIBBALSA_MSG_PROTECT_CRYPT:
593 return LIBBALSA_MESSAGE_ATTACH_ENCR;
594 default:
595 return LIBBALSA_MESSAGE_ATTACH_SIGN;
596 }
597 } else
598 #endif
599 if (libbalsa_message_has_attachment(message))
600 return LIBBALSA_MESSAGE_ATTACH_ATTACH;
601 else
602 return LIBBALSA_MESSAGE_ATTACH_ICONS_NUM;
603 }
604
605 /* Tell the mailbox driver to change flags. */
606 void
libbalsa_message_change_flags(LibBalsaMessage * message,LibBalsaMessageFlag set,LibBalsaMessageFlag clear)607 libbalsa_message_change_flags(LibBalsaMessage * message,
608 LibBalsaMessageFlag set,
609 LibBalsaMessageFlag clear)
610 {
611 g_return_if_fail(LIBBALSA_IS_MESSAGE(message));
612 g_return_if_fail(LIBBALSA_IS_MAILBOX(message->mailbox));
613 g_return_if_fail(message->msgno > 0);
614
615 if (message->mailbox->readonly) {
616 libbalsa_information(LIBBALSA_INFORMATION_WARNING,
617 _("Mailbox (%s) is readonly: "
618 "cannot change flags."),
619 message->mailbox->name);
620 return;
621 }
622
623 libbalsa_mailbox_msgno_change_flags(message->mailbox, message->msgno,
624 set, clear);
625 }
626
627 void
libbalsa_message_reply(LibBalsaMessage * message)628 libbalsa_message_reply(LibBalsaMessage * message)
629 {
630 g_return_if_fail(message->mailbox);
631 libbalsa_lock_mailbox(message->mailbox);
632 libbalsa_message_change_flags(message, LIBBALSA_MESSAGE_FLAG_REPLIED, 0);
633 libbalsa_unlock_mailbox(message->mailbox);
634 }
635
636
637 /* libbalsa_message_body_ref:
638 references the structure of given message possibly fetching also all
639 headers.
640 message parts can be fetched later on.
641 */
642 gboolean
libbalsa_message_body_ref(LibBalsaMessage * message,gboolean read,gboolean fetch_all_headers)643 libbalsa_message_body_ref(LibBalsaMessage * message, gboolean read,
644 gboolean fetch_all_headers)
645 {
646 LibBalsaFetchFlag flags = 0;
647 gboolean retval = TRUE;
648
649 g_return_val_if_fail(message, FALSE);
650 if (!message->mailbox) return FALSE;
651 g_return_val_if_fail(MAILBOX_OPEN(message->mailbox), FALSE);
652
653 libbalsa_lock_mailbox(message->mailbox);
654
655 if(fetch_all_headers && !message->has_all_headers)
656 flags |= LB_FETCH_RFC822_HEADERS;
657
658 if (message->body_ref == 0 && !message->body_list)
659 /* not fetched yet */
660 flags |= LB_FETCH_STRUCTURE;
661
662 if (flags)
663 retval =
664 libbalsa_mailbox_fetch_message_structure(message->mailbox,
665 message, flags);
666 if (retval)
667 message->body_ref++;
668 libbalsa_unlock_mailbox(message->mailbox);
669
670 return retval;
671 }
672
673
674 void
libbalsa_message_body_unref(LibBalsaMessage * message)675 libbalsa_message_body_unref(LibBalsaMessage * message)
676 {
677 g_return_if_fail(LIBBALSA_IS_MESSAGE(message));
678
679 if (message->body_ref == 0)
680 return;
681
682 if(message->mailbox) { libbalsa_lock_mailbox(message->mailbox); }
683 if (--message->body_ref == 0) {
684 libbalsa_message_body_free(message->body_list);
685 message->body_list = NULL;
686 if (message->mailbox)
687 libbalsa_mailbox_release_message(message->mailbox, message);
688
689 /* Free headers that we no longer need. */
690 lb_message_headers_extra_destroy(message->headers);
691 message->has_all_headers = 0;
692 }
693 if(message->mailbox) { libbalsa_unlock_mailbox(message->mailbox); }
694 }
695
696 gboolean
libbalsa_message_is_multipart(LibBalsaMessage * message)697 libbalsa_message_is_multipart(LibBalsaMessage * message)
698 {
699 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), FALSE);
700
701 return message->headers->content_type ?
702 g_mime_content_type_is_type(message->headers->content_type,
703 "multipart", "*") : FALSE;
704 }
705
706 gboolean
libbalsa_message_is_partial(LibBalsaMessage * message,gchar ** id)707 libbalsa_message_is_partial(LibBalsaMessage * message, gchar ** id)
708 {
709 GMimeContentType *content_type;
710
711 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), FALSE);
712
713 content_type = message->headers->content_type;
714 if (!content_type
715 || !g_mime_content_type_is_type(content_type,
716 "message", "partial"))
717 return FALSE;
718
719 if (id)
720 *id = g_strdup(g_mime_content_type_get_parameter(content_type,
721 "id"));
722
723 return TRUE;
724 }
725
726 /* Go through all parts and try to figure out whether it is a message
727 with attachments or not. It still yields insatsfactory
728 results... */
729 static gboolean
has_attached_part(LibBalsaMessageBody * body)730 has_attached_part(LibBalsaMessageBody *body)
731 {
732 LibBalsaMessageBody *lbbody;
733 /* the condition matches the one used in add_multipart_mixed() */
734 for(lbbody=body; lbbody; lbbody = lbbody->next) {
735 /* printf("part %s has disposition %s\n",
736 lbbody->content_type, lbbody->content_dsp); */
737 if(!libbalsa_message_body_is_multipart(lbbody) &&
738 !libbalsa_message_body_is_inline(lbbody) ) {
739 /* puts("Attachment found!"); */
740 return TRUE;
741 }
742 if(lbbody->parts && has_attached_part(lbbody->parts))
743 return TRUE;
744 }
745 /* no part was an attachment */
746 return FALSE;
747 }
748
749 gboolean
libbalsa_message_has_attachment(LibBalsaMessage * message)750 libbalsa_message_has_attachment(LibBalsaMessage * message)
751 {
752 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), FALSE);
753
754 /* A message has attachments if main message or one of the parts
755 has Content-type: multipart/mixed AND members with
756 Content-disposition: attachment. Unfortunately, part list may
757 not be available at this stage. */
758 if(!message->body_list) {
759 return message->headers->content_type &&
760 g_mime_content_type_is_type(message->headers->content_type,
761 "multipart", "mixed");
762 } else {
763 /* use "exact" algorithm */
764 return (has_attached_part(message->body_list->next) ||
765 has_attached_part(message->body_list->parts));
766 }
767 }
768
769 #ifdef HAVE_GPGME
770 gboolean
libbalsa_message_is_pgp_signed(LibBalsaMessage * message)771 libbalsa_message_is_pgp_signed(LibBalsaMessage * message)
772 {
773 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), FALSE);
774
775 return message->headers->content_type ?
776 g_mime_content_type_is_type(message->headers->content_type,
777 "multipart", "signed") : FALSE;
778 }
779
780 gboolean
libbalsa_message_is_pgp_encrypted(LibBalsaMessage * message)781 libbalsa_message_is_pgp_encrypted(LibBalsaMessage * message)
782 {
783 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), FALSE);
784
785 return message->headers->content_type ?
786 g_mime_content_type_is_type(message->headers->content_type,
787 "multipart", "encrypted") : FALSE;
788 }
789 #endif
790
791 void
libbalsa_message_append_part(LibBalsaMessage * message,LibBalsaMessageBody * body)792 libbalsa_message_append_part(LibBalsaMessage * message,
793 LibBalsaMessageBody * body)
794 {
795 LibBalsaMessageBody *part;
796
797 g_return_if_fail(message != NULL);
798 g_return_if_fail(LIBBALSA_IS_MESSAGE(message));
799
800 if (message->body_list == NULL) {
801 message->body_list = body;
802 } else {
803 part = message->body_list;
804 while (part->next != NULL)
805 part = part->next;
806 part->next = body;
807 }
808 }
809
810 /* libbalsa_message_set_dispnotify:
811 sets a disposition notify to a given address
812 address can be NULL.
813 */
814 void
libbalsa_message_set_dispnotify(LibBalsaMessage * message,InternetAddress * ia)815 libbalsa_message_set_dispnotify(LibBalsaMessage * message,
816 InternetAddress * ia)
817 {
818 g_return_if_fail(message);
819
820 g_object_unref(message->headers->dispnotify_to);
821 if (ia) {
822 message->headers->dispnotify_to = internet_address_list_new ();
823 internet_address_list_add (message->headers->dispnotify_to, ia);
824 } else {
825 message->headers->dispnotify_to = NULL;
826 }
827 }
828
829 #ifndef MESSAGE_COPY_CONTENT
830 /* libbalsa_message_get_subject:
831 get constant pointer to the subject of the message;
832 */
833 const gchar *
libbalsa_message_get_subject(LibBalsaMessage * msg)834 libbalsa_message_get_subject(LibBalsaMessage* msg)
835 {
836 const gchar *ret;
837 if(!msg->subj &&
838 msg->mime_msg && msg->mailbox) { /* a message in a mailbox... */
839 g_return_val_if_fail(MAILBOX_OPEN(msg->mailbox), NULL);
840 ret = g_mime_message_get_subject(msg->mime_msg);
841 libbalsa_message_set_subject_from_header(msg, ret);
842 } else
843 ret = msg->subj;
844
845 return ret ? ret : _("(No subject)");
846 }
847
848
849 guint
libbalsa_message_get_lines(LibBalsaMessage * msg)850 libbalsa_message_get_lines(LibBalsaMessage* msg)
851 {
852 /* set the line count */
853 const char *value;
854 if (!msg->mime_msg)
855 return 0;
856 value = g_mime_object_get_header(msg->mime_msg, "Lines");
857 if (!value)
858 return 0;
859 return atoi(value);
860 }
861 glong
libbalsa_message_get_length(LibBalsaMessage * msg)862 libbalsa_message_get_length(LibBalsaMessage* msg)
863 {
864 /* set the length */
865 const char *value;
866 if (!msg->mime_msg)
867 return 0;
868 value = g_mime_object_get_header(msg->mime_msg, "Content-Length");
869 if (!value)
870 return 0;
871 return atoi(value);
872 }
873
874 glong
libbalsa_message_get_no(LibBalsaMessage * msg)875 libbalsa_message_get_no(LibBalsaMessage* msg)
876 {
877 return msg->msgno;
878 }
879
880
881 #endif
882
883 /* Populate headers from mime_msg, but only the members that are needed
884 * all the time. */
885 static InternetAddressList *
lb_message_recipients(GMimeMessage * message,GMimeRecipientType type)886 lb_message_recipients(GMimeMessage * message, GMimeRecipientType type)
887 {
888 const InternetAddressList *list;
889 InternetAddressList *copy = NULL;
890
891 if ((list = g_mime_message_get_recipients (message, type))) {
892 copy = internet_address_list_new ();
893 internet_address_list_append (copy, (InternetAddressList *) list);
894 }
895
896 return copy;
897 }
898
899 static void
lb_message_headers_basic_from_gmime(LibBalsaMessageHeaders * headers,GMimeMessage * mime_msg)900 lb_message_headers_basic_from_gmime(LibBalsaMessageHeaders *headers,
901 GMimeMessage *mime_msg)
902 {
903 g_return_if_fail(headers);
904 g_return_if_fail(mime_msg != NULL);
905
906 if (!headers->from)
907 headers->from = internet_address_list_parse_string(mime_msg->from);
908
909 if (!headers->date)
910 g_mime_message_get_date(mime_msg, &headers->date, NULL);
911
912 if (!headers->to_list)
913 headers->to_list =
914 lb_message_recipients(mime_msg, GMIME_RECIPIENT_TYPE_TO);
915
916 if (!headers->content_type) {
917 /* If we could:
918 * headers->content_type =
919 * g_mime_content_type_copy
920 * (g_mime_object_get_content_type(mime_msg->mime_part));
921 */
922 GMimeContentType *content_type;
923 gchar *str;
924 g_return_if_fail(headers->content_type == NULL);
925 content_type = g_mime_object_get_content_type(mime_msg->mime_part);
926 str = g_mime_content_type_to_string(content_type);
927 headers->content_type = g_mime_content_type_new_from_string(str);
928 g_free(str);
929 }
930 }
931
932 /* Populate headers from mime_msg, but only the members not handled in
933 * lb_message_headers_basic_from_gmime. */
934 static void
lb_message_headers_extra_from_gmime(LibBalsaMessageHeaders * headers,GMimeMessage * mime_msg)935 lb_message_headers_extra_from_gmime(LibBalsaMessageHeaders *headers,
936 GMimeMessage *mime_msg)
937 {
938 g_return_if_fail(headers);
939 g_return_if_fail(mime_msg != NULL);
940
941 if (!headers->reply_to)
942 headers->reply_to =
943 internet_address_list_parse_string(mime_msg->reply_to);
944
945 if (!headers->dispnotify_to)
946 headers->dispnotify_to =
947 internet_address_list_parse_string(g_mime_object_get_header
948 (GMIME_OBJECT(mime_msg),
949 "Disposition-Notification-To"));
950
951 if (!headers->cc_list)
952 headers->cc_list =
953 lb_message_recipients(mime_msg, GMIME_RECIPIENT_TYPE_CC);
954
955 if (!headers->bcc_list)
956 headers->bcc_list =
957 lb_message_recipients(mime_msg, GMIME_RECIPIENT_TYPE_BCC);
958
959 /* Get fcc from message */
960 if (!headers->fcc_url)
961 headers->fcc_url =
962 g_strdup(g_mime_object_get_header(GMIME_OBJECT(mime_msg), "X-Balsa-Fcc"));
963 }
964
965 /* Populate headers from the info in mime_msg. */
966 void
libbalsa_message_headers_from_gmime(LibBalsaMessageHeaders * headers,GMimeMessage * mime_msg)967 libbalsa_message_headers_from_gmime(LibBalsaMessageHeaders *headers,
968 GMimeMessage *mime_msg)
969 {
970 lb_message_headers_basic_from_gmime(headers, mime_msg);
971 lb_message_headers_extra_from_gmime(headers, mime_msg);
972 }
973
974 /* Populate message and message->headers from the info in mime_msg,
975 * but only the members that are needed all the time. */
976 void
libbalsa_message_init_from_gmime(LibBalsaMessage * message,GMimeMessage * mime_msg)977 libbalsa_message_init_from_gmime(LibBalsaMessage * message,
978 GMimeMessage *mime_msg)
979 {
980 const gchar *header;
981
982 g_return_if_fail(LIBBALSA_IS_MESSAGE(message));
983 g_return_if_fail(GMIME_IS_MESSAGE(mime_msg));
984
985 #ifdef MESSAGE_COPY_CONTENT
986 header = g_mime_message_get_subject(mime_msg);
987 libbalsa_message_set_subject_from_header(message, header);
988
989 header = g_mime_object_get_header(GMIME_OBJECT(mime_msg), "Content-Length");
990 if (header)
991 message->length = atoi(header);
992 #endif
993 header = g_mime_message_get_message_id(mime_msg);
994 if (header)
995 message->message_id = g_strdup(header);
996
997 header = g_mime_object_get_header(GMIME_OBJECT(mime_msg), "References");
998 if (header)
999 libbalsa_message_set_references_from_string(message, header);
1000
1001 header = g_mime_object_get_header(GMIME_OBJECT(mime_msg), "In-Reply-To");
1002 if (header)
1003 libbalsa_message_set_in_reply_to_from_string(message, header);
1004
1005 lb_message_headers_basic_from_gmime(message->headers, mime_msg);
1006 }
1007
1008 /* Create a newly allocated list of references for threading.
1009 * This is a deep copy, with its own strings: deallocate with
1010 * g_free and g_list_free. */
1011 GList *
libbalsa_message_refs_for_threading(LibBalsaMessage * message)1012 libbalsa_message_refs_for_threading(LibBalsaMessage * message)
1013 {
1014 GList *tmp;
1015 GList *foo;
1016
1017 g_return_val_if_fail(message != NULL, NULL);
1018
1019 if (message->in_reply_to && message->in_reply_to->next)
1020 return NULL;
1021
1022 tmp = g_list_copy(message->references);
1023
1024 if (message->in_reply_to) {
1025 /* some mailers provide in_reply_to but no references, and
1026 * some apparently provide both but with the references in
1027 * the wrong order; we'll just make sure it's the last item
1028 * of this list */
1029 foo = g_list_find_custom(tmp, message->in_reply_to->data,
1030 (GCompareFunc) strcmp);
1031
1032 if (foo) {
1033 tmp = g_list_remove_link(tmp, foo);
1034 g_list_free_1(foo);
1035 }
1036 tmp = g_list_append(tmp, message->in_reply_to->data);
1037 }
1038
1039 for (foo = tmp; foo; foo = foo->next)
1040 foo->data = g_strdup((gchar *) foo->data);
1041
1042 return tmp;
1043 }
1044
1045 static GList *
references_decode(const gchar * str)1046 references_decode(const gchar * str)
1047 {
1048 GMimeReferences *references, *reference;
1049 GList *list = NULL;
1050
1051 reference = references = g_mime_references_decode(str);
1052 while (reference) {
1053 list = g_list_prepend(list, g_strdup(reference->msgid));
1054 reference = reference->next;
1055 }
1056 g_mime_references_clear(&references);
1057
1058 return g_list_reverse(list);
1059 }
1060
1061 void
libbalsa_message_set_references_from_string(LibBalsaMessage * message,const gchar * str)1062 libbalsa_message_set_references_from_string(LibBalsaMessage * message,
1063 const gchar *str)
1064 {
1065 /* Empty references are acceptable but require no action. Similarly,
1066 if references were set already, there is not reason to set them
1067 again - they are immutable anyway. */
1068 if(!message->references && str)
1069 message->references = references_decode(str);
1070 }
1071
1072 void
libbalsa_message_set_in_reply_to_from_string(LibBalsaMessage * message,const gchar * str)1073 libbalsa_message_set_in_reply_to_from_string(LibBalsaMessage * message,
1074 const gchar * str)
1075 {
1076 if (!message->in_reply_to && str) {
1077 /* FIXME for Balsa's old non-compliant header */
1078 gchar *p = strrchr(str, ';');
1079 p = p ? g_strndup(str, p - str) : g_strdup(str);
1080 message->in_reply_to = references_decode(p);
1081 g_free(p);
1082 }
1083 }
1084
1085 /* set a header, if (all) or if it's needed all the time:
1086 * headers->from
1087 * headers->date
1088 * headers->to_list
1089 * headers->content_type
1090 * subj
1091 * length
1092 *
1093 * needed for threading local mailboxes:
1094 * message_id
1095 * references
1096 * in_reply_to
1097 */
1098 static gboolean
lbmsg_set_header(LibBalsaMessage * message,const gchar * name,const gchar * value,gboolean all)1099 lbmsg_set_header(LibBalsaMessage *message, const gchar *name,
1100 const gchar* value, gboolean all)
1101 {
1102 gchar *val = NULL;
1103
1104 if (libbalsa_text_attr_string(value)) {
1105 /* Broken header: force it to utf8 using Balsa's fallback
1106 * charset, then rfc2047-encode it for passing to the
1107 * appropriate GMime decoder. */
1108 gchar *tmp = g_strdup(value);
1109 libbalsa_utf8_sanitize(&tmp, TRUE, NULL);
1110 val = g_mime_utils_header_encode_text(tmp);
1111 g_free(tmp);
1112 #ifdef DEBUG
1113 g_print("%s: non-ascii \"%s\" header \"%s\" encoded as \"%s\"\n",
1114 __func__, name, value, val);
1115 #endif /* DEBUG */
1116 value = val;
1117 }
1118
1119 if (g_ascii_strcasecmp(name, "Subject") == 0) {
1120 if (!strcmp(value,
1121 "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA")) {
1122 g_free(val);
1123 return FALSE;
1124 }
1125 #if MESSAGE_COPY_CONTENT
1126 libbalsa_message_set_subject_from_header(message, value);
1127 #endif
1128 } else
1129 if (g_ascii_strcasecmp(name, "Date") == 0) {
1130 message->headers->date = g_mime_utils_header_decode_date(value, NULL);
1131 } else
1132 if (message->headers->from == NULL &&
1133 g_ascii_strcasecmp(name, "From") == 0) {
1134 message->headers->from = internet_address_list_parse_string(value);
1135 } else
1136 if (message->headers->to_list == NULL &&
1137 g_ascii_strcasecmp(name, "To") == 0) {
1138 message->headers->to_list = internet_address_list_parse_string(value);
1139 } else
1140 if (g_ascii_strcasecmp(name, "In-Reply-To") == 0) {
1141 libbalsa_message_set_in_reply_to_from_string(message, value);
1142 } else
1143 if (message->message_id == NULL &&
1144 g_ascii_strcasecmp(name, "Message-ID") == 0) {
1145 message->message_id = g_mime_utils_decode_message_id(value);
1146 } else
1147 if (g_ascii_strcasecmp(name, "References") == 0) {
1148 libbalsa_message_set_references_from_string(message, value);
1149 } else
1150 if (message->headers->content_type == NULL &&
1151 g_ascii_strcasecmp(name, "Content-Type") == 0) {
1152 message->headers->content_type =
1153 g_mime_content_type_new_from_string(value);
1154 } else
1155 if (message->headers->dispnotify_to == NULL &&
1156 g_ascii_strcasecmp(name, "Disposition-Notification-To") == 0) {
1157 message->headers->dispnotify_to =
1158 internet_address_list_parse_string(value);
1159 } else
1160 #ifdef MESSAGE_COPY_CONTENT
1161 if (g_ascii_strcasecmp(name, "Content-Length") == 0) {
1162 message->length = atoi(value);
1163 } else
1164 #endif
1165 if (all)
1166 message->headers->user_hdrs =
1167 g_list_prepend(message->headers->user_hdrs,
1168 libbalsa_create_hdr_pair(name,
1169 g_strdup(value)));
1170
1171 g_free(val);
1172
1173 return TRUE;
1174 }
1175
1176 static gboolean
lb_message_set_headers_from_string(LibBalsaMessage * message,const gchar * lines,gboolean all)1177 lb_message_set_headers_from_string(LibBalsaMessage *message,
1178 const gchar *lines, gboolean all)
1179 {
1180 gchar *header, *value;
1181 const gchar *val, *eoh;
1182 do {
1183 for(val = lines; *val && *val >32 && *val<127 && *val != ':'; val++)
1184 ;
1185 if(*val != ':') /* parsing error */
1186 return FALSE;
1187 for(eoh = val+1; *eoh && (eoh[0] != '\n' || isspace(eoh[1])); eoh++)
1188 ;
1189 header = g_strndup(lines, val-lines);
1190 lines = eoh;
1191 for(val=val+1; *val && isspace(*val); val++)
1192 ; /* strip spaces at front... */
1193 while(eoh>val && isspace(*eoh)) eoh--; /* .. and at the end */
1194 value = g_strndup(val, eoh-val+1);
1195
1196 lbmsg_set_header(message, header, value, all);
1197 g_free(header); g_free(value);
1198 if(!*lines || !*++lines) break;
1199 } while(1);
1200 return TRUE;
1201 }
1202
1203 gboolean
libbalsa_message_set_headers_from_string(LibBalsaMessage * message,const gchar * lines)1204 libbalsa_message_set_headers_from_string(LibBalsaMessage *message,
1205 const gchar *lines)
1206 {
1207 return lb_message_set_headers_from_string(message, lines, TRUE);
1208 }
1209
1210 void
libbalsa_message_load_envelope_from_stream(LibBalsaMessage * message,GMimeStream * gmime_stream)1211 libbalsa_message_load_envelope_from_stream(LibBalsaMessage * message,
1212 GMimeStream *gmime_stream)
1213 {
1214 GMimeStream *gmime_stream_filter;
1215 GMimeFilter *gmime_filter_crlf;
1216 GMimeStream *gmime_stream_buffer;
1217 GByteArray *line;
1218 guchar lookahead;
1219
1220 libbalsa_mime_stream_shared_lock(gmime_stream);
1221
1222 /* CRLF-filter the message stream; we do not want '\r' in header
1223 * fields, and finding the empty line that separates the body from
1224 * the header is simpler if it has no '\r' in it. */
1225 gmime_stream_filter =
1226 g_mime_stream_filter_new(gmime_stream);
1227
1228 gmime_filter_crlf =
1229 g_mime_filter_crlf_new(FALSE,
1230 FALSE);
1231 g_mime_stream_filter_add(GMIME_STREAM_FILTER(gmime_stream_filter),
1232 gmime_filter_crlf);
1233 g_object_unref(gmime_filter_crlf);
1234
1235 /* Buffer the message stream, so we can read it line by line. */
1236 gmime_stream_buffer =
1237 g_mime_stream_buffer_new(gmime_stream_filter,
1238 GMIME_STREAM_BUFFER_BLOCK_READ);
1239 g_object_unref(gmime_stream_filter);
1240
1241 /* Read header fields until either:
1242 * - we find an empty line, or
1243 * - end of file.
1244 */
1245 line = g_byte_array_new();
1246 do {
1247 g_mime_stream_buffer_readln(gmime_stream_buffer, line);
1248 while (g_mime_stream_read(gmime_stream_buffer,
1249 (char *) &lookahead, 1) == 1
1250 && (lookahead == ' ' || lookahead == '\t')) {
1251 g_byte_array_append(line, &lookahead, 1);
1252 g_mime_stream_buffer_readln(gmime_stream_buffer, line);
1253 }
1254 if (line->len == 0 || line->data[line->len-1]!='\n') {
1255 /* EOF or read error; in either case, message has no body. */
1256 break;
1257 }
1258 line->data[line->len-1]='\0'; /* terminate line by overwriting '\n' */
1259 if (!lb_message_set_headers_from_string(message,
1260 (gchar *) line->data,
1261 FALSE)) {
1262 /* Ignore error return caused by malformed header. */
1263 }
1264 if (lookahead == '\n') {/* end of header */
1265 /* Message looks valid--set its length. */
1266 message->length = g_mime_stream_length(gmime_stream);
1267 break;
1268 }
1269 line->len = 0;
1270 g_byte_array_append(line, &lookahead, 1);
1271 } while (TRUE);
1272 g_byte_array_free(line, TRUE);
1273
1274 g_object_unref(gmime_stream_buffer);
1275 g_mime_stream_reset(gmime_stream);
1276 libbalsa_mime_stream_shared_unlock(gmime_stream);
1277 }
1278
1279 void
libbalsa_message_load_envelope(LibBalsaMessage * message)1280 libbalsa_message_load_envelope(LibBalsaMessage *message)
1281 {
1282 GMimeStream *gmime_stream;
1283
1284 gmime_stream = libbalsa_message_stream(message);
1285 if (!gmime_stream)
1286 return;
1287
1288 libbalsa_message_load_envelope_from_stream(message, gmime_stream);
1289 g_object_unref(gmime_stream);
1290 }
1291
1292 GMimeStream *
libbalsa_message_stream(LibBalsaMessage * message)1293 libbalsa_message_stream(LibBalsaMessage * message)
1294 {
1295 LibBalsaMailbox *mailbox;
1296 GMimeStream *mime_stream;
1297
1298 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), NULL);
1299 mailbox = message->mailbox;
1300 g_return_val_if_fail(mailbox != NULL || message->mime_msg != NULL,
1301 NULL);
1302
1303 if (mailbox)
1304 return libbalsa_mailbox_get_message_stream(mailbox,
1305 message->msgno, FALSE);
1306
1307 mime_stream = g_mime_stream_mem_new();
1308 g_mime_object_write_to_stream(GMIME_OBJECT(message->mime_msg),
1309 mime_stream);
1310 g_mime_stream_reset(mime_stream);
1311
1312 return mime_stream;
1313 }
1314
1315 gboolean
libbalsa_message_copy(LibBalsaMessage * message,LibBalsaMailbox * dest,GError ** err)1316 libbalsa_message_copy(LibBalsaMessage * message, LibBalsaMailbox * dest,
1317 GError ** err)
1318 {
1319 LibBalsaMailbox *mailbox;
1320 gboolean retval;
1321
1322 g_return_val_if_fail(LIBBALSA_IS_MESSAGE(message), FALSE);
1323 g_return_val_if_fail(LIBBALSA_IS_MAILBOX(dest), FALSE);
1324 mailbox = message->mailbox;
1325 g_return_val_if_fail(mailbox != NULL || message->mime_msg != NULL,
1326 FALSE);
1327
1328 if (mailbox) {
1329 GArray *msgnos = g_array_sized_new(FALSE, FALSE, sizeof(guint), 1);
1330 g_array_append_val(msgnos, message->msgno);
1331 retval =
1332 libbalsa_mailbox_messages_copy(mailbox, msgnos, dest, err);
1333 g_array_free(msgnos, TRUE);
1334 } else {
1335 GMimeStream *mime_stream = libbalsa_message_stream(message);
1336 retval = libbalsa_mailbox_add_message(dest, mime_stream,
1337 message->flags, err);
1338 g_object_unref(mime_stream);
1339 }
1340
1341 return retval;
1342 }
1343
1344 void
libbalsa_message_set_subject(LibBalsaMessage * message,const gchar * subject)1345 libbalsa_message_set_subject(LibBalsaMessage * message,
1346 const gchar * subject)
1347 {
1348 g_free(message->subj);
1349 message->subj = g_strdup(subject);
1350 libbalsa_utf8_sanitize(&message->subj, TRUE, NULL);
1351 canonize_header_value(message->subj);
1352 }
1353
1354 void
libbalsa_message_set_subject_from_header(LibBalsaMessage * message,const gchar * header)1355 libbalsa_message_set_subject_from_header(LibBalsaMessage * message,
1356 const gchar * header)
1357 {
1358 if (header) {
1359 gchar *subject =
1360 g_mime_utils_header_decode_text(header);
1361 libbalsa_message_set_subject(message, subject);
1362 g_free(subject);
1363 }
1364 }
1365
1366 const gchar *
libbalsa_message_get_tempdir(LibBalsaMessage * message)1367 libbalsa_message_get_tempdir(LibBalsaMessage * message)
1368 {
1369 if (!message->tempdir) {
1370 if (!libbalsa_mktempdir(&message->tempdir))
1371 g_warning("Could not make tempdir");
1372 }
1373
1374 return message->tempdir;
1375 }
1376