Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
em-utils.c:1112:9 | clang-analyzer | Null pointer passed as an argument to a 'nonnull' parameter | ||
em-utils.c:1112:9 | clang-analyzer | Null pointer passed as an argument to a 'nonnull' parameter |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Jeffrey Stedfast <fejj@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <time.h>
33
34 #include <glib/gstdio.h>
35
36 #ifdef G_OS_WIN32
37 /* Work around namespace clobbage in <windows.h> */
38 #define DATADIR windows_DATADIR
39 #include <windows.h>
40 #undef DATADIR
41 #undef interface
42 #endif
43
44 #include "em-filter-editor.h"
45
46 #include <glib/gi18n.h>
47
48 #include <libebook/libebook.h>
49
50 #include <e-util/e-util.h>
51 #include <e-util/e-util-private.h>
52 #include <e-util/e-mktemp.h>
53 #include <e-util/e-dialog-utils.h>
54 #include <libevolution-utils/e-alert-dialog.h>
55
56 #include <shell/e-shell.h>
57 #include <widgets/misc/e-attachment.h>
58
59 #include <em-format/e-mail-parser.h>
60 #include <em-format/e-mail-formatter-quote.h>
61
62 #include <libemail-utils/mail-mt.h>
63
64 #include <libemail-engine/e-mail-folder-utils.h>
65 #include <libemail-engine/e-mail-session.h>
66 #include <libemail-engine/mail-ops.h>
67 #include <libemail-engine/mail-tools.h>
68
69 #include "e-mail-tag-editor.h"
70 #include "em-composer-utils.h"
71 #include "em-utils.h"
72 #include "e-mail-printer.h"
73
74 /* XXX This is a dirty hack on a dirty hack. We really need
75 #include <em-format/e-mail-print-formatter.h>
76 * to rework or get rid of the functions that use this. */
77 extern const gchar *shell_builtin_backend;
78
79 /* How many is too many? */
80 /* Used in em_util_ask_open_many() */
81 #define TOO_MANY 10
82
83 /* drag and drop resulting file naming possibilities */
84 enum {
85 DND_USE_SENT_DATE = 1, /* YYYYMMDDhhmmssms_<title> and use email sent date */
86 DND_USE_DND_DATE = 2, /* YYYYMMDDhhmmssms_<title> and drag'drop date */
87 };
88
89 #define d(x)
90
91 gboolean
92 em_utils_ask_open_many (GtkWindow *parent,
93 gint how_many)
94 {
95 gchar *string;
96 gboolean proceed;
97
98 if (how_many < TOO_MANY)
99 return TRUE;
100
101 string = g_strdup_printf (ngettext (
102 /* Translators: This message is shown only for ten or more
103 * messages to be opened. The %d is replaced with the actual
104 * count of messages. If you need a '%' in your text, then
105 * write it doubled, like '%%'. */
106 "Are you sure you want to open %d message at once?",
107 "Are you sure you want to open %d messages at once?",
108 how_many), how_many);
109 proceed = em_utils_prompt_user (
110 parent, "prompt-on-open-many",
111 "mail:ask-open-many", string, NULL);
112 g_free (string);
113
114 return proceed;
115 }
116
117 /**
118 * em_utils_prompt_user:
119 * @parent: parent window
120 * @promptkey: settings key to check if we should prompt the user or not.
121 * @tag: e_alert tag.
122 *
123 * Convenience function to query the user with a Yes/No dialog and a
124 * "Do not show this dialog again" checkbox. If the user checks that
125 * checkbox, then @promptkey is set to %FALSE, otherwise it is set to
126 * %TRUE.
127 *
128 * Returns %TRUE if the user clicks Yes or %FALSE otherwise.
129 **/
130 gboolean
131 em_utils_prompt_user (GtkWindow *parent,
132 const gchar *promptkey,
133 const gchar *tag,
134 ...)
135 {
136 GtkWidget *dialog;
137 GtkWidget *check = NULL;
138 GtkWidget *container;
139 va_list ap;
140 gint button;
141 GSettings *settings;
142 EAlert *alert = NULL;
143
144 settings = g_settings_new ("org.gnome.evolution.mail");
145
146 if (promptkey && !g_settings_get_boolean (settings, promptkey)) {
147 g_object_unref (settings);
148 return TRUE;
149 }
150
151 va_start (ap, tag);
152 alert = e_alert_new_valist (tag, ap);
153 va_end (ap);
154
155 dialog = e_alert_dialog_new (parent, alert);
156 g_object_unref (alert);
157
158 container = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
159
160 if (promptkey) {
161 check = gtk_check_button_new_with_mnemonic (
162 _("_Do not show this message again"));
163 gtk_box_pack_start (
164 GTK_BOX (container), check, FALSE, FALSE, 0);
165 gtk_widget_show (check);
166 }
167
168 button = gtk_dialog_run (GTK_DIALOG (dialog));
169 if (promptkey)
170 g_settings_set_boolean (
171 settings, promptkey,
172 !gtk_toggle_button_get_active (
173 GTK_TOGGLE_BUTTON (check)));
174
175 gtk_widget_destroy (dialog);
176
177 g_object_unref (settings);
178
179 return button == GTK_RESPONSE_YES;
180 }
181
182 /**
183 * em_utils_uids_copy:
184 * @uids: array of uids
185 *
186 * Duplicates the array of uids held by @uids into a new
187 * GPtrArray. Use em_utils_uids_free() to free the resultant uid
188 * array.
189 *
190 * Returns a duplicate copy of @uids.
191 **/
192 GPtrArray *
193 em_utils_uids_copy (GPtrArray *uids)
194 {
195 GPtrArray *copy;
196 gint i;
197
198 copy = g_ptr_array_new ();
199 g_ptr_array_set_size (copy, uids->len);
200
201 for (i = 0; i < uids->len; i++)
202 copy->pdata[i] = g_strdup (uids->pdata[i]);
203
204 return copy;
205 }
206
207 /**
208 * em_utils_uids_free:
209 * @uids: array of uids
210 *
211 * Frees the array of uids pointed to by @uids back to the system.
212 **/
213 void
214 em_utils_uids_free (GPtrArray *uids)
215 {
216 gint i;
217
218 for (i = 0; i < uids->len; i++)
219 g_free (uids->pdata[i]);
220
221 g_ptr_array_free (uids, TRUE);
222 }
223
224 /* Editing Filters/Search Folders... */
225
226 static GtkWidget *filter_editor = NULL;
227
228 static void
229 em_filter_editor_response (GtkWidget *dialog,
230 gint button,
231 gpointer user_data)
232 {
233 EMFilterContext *fc;
234
235 if (button == GTK_RESPONSE_OK) {
236 const gchar *config_dir;
237 gchar *user;
238
239 config_dir = mail_session_get_config_dir ();
240 fc = g_object_get_data ((GObject *) dialog, "context");
241 user = g_build_filename (config_dir, "filters.xml", NULL);
242 e_rule_context_save ((ERuleContext *) fc, user);
243 g_free (user);
244 }
245
246 gtk_widget_destroy (dialog);
247
248 filter_editor = NULL;
249 }
250
251 static EMFilterSource em_filter_source_element_names[] = {
252 { "incoming", },
253 { "outgoing", },
254 { NULL }
255 };
256
257 /**
258 * em_utils_edit_filters:
259 * @session: an #EMailSession
260 * @alert_sink: an #EAlertSink
261 * @parent_window: a parent #GtkWindow
262 *
263 * Opens or raises the filters editor dialog so that the user may edit
264 * his/her filters. If @parent is non-NULL, then the dialog will be
265 * created as a child window of @parent's toplevel window.
266 **/
267 void
268 em_utils_edit_filters (EMailSession *session,
269 EAlertSink *alert_sink,
270 GtkWindow *parent_window)
271 {
272 const gchar *config_dir;
273 gchar *user, *system;
274 EMFilterContext *fc;
275
276 g_return_if_fail (E_IS_MAIL_SESSION (session));
277 g_return_if_fail (E_IS_ALERT_SINK (alert_sink));
278
279 if (filter_editor) {
280 gtk_window_present (GTK_WINDOW (filter_editor));
281 return;
282 }
283
284 config_dir = mail_session_get_config_dir ();
285
286 fc = em_filter_context_new (session);
287 user = g_build_filename (config_dir, "filters.xml", NULL);
288 system = g_build_filename (EVOLUTION_PRIVDATADIR, "filtertypes.xml", NULL);
289 e_rule_context_load ((ERuleContext *) fc, system, user);
290 g_free (user);
291 g_free (system);
292
293 if (((ERuleContext *) fc)->error) {
294 e_alert_submit (
295 alert_sink,
296 "mail:filter-load-error",
297 ((ERuleContext *) fc)->error, NULL);
298 return;
299 }
300
301 if (em_filter_source_element_names[0].name == NULL) {
302 em_filter_source_element_names[0].name = _("Incoming");
303 em_filter_source_element_names[1].name = _("Outgoing");
304 }
305
306 filter_editor = (GtkWidget *) em_filter_editor_new (
307 fc, em_filter_source_element_names);
308
309 if (GTK_IS_WINDOW (parent_window))
310 gtk_window_set_transient_for (
311 GTK_WINDOW (filter_editor), parent_window);
312
313 gtk_window_set_title (
314 GTK_WINDOW (filter_editor), _("Message Filters"));
315 g_object_set_data_full (
316 G_OBJECT (filter_editor), "context", fc,
317 (GDestroyNotify) g_object_unref);
318 g_signal_connect (
319 filter_editor, "response",
320 G_CALLBACK (em_filter_editor_response), NULL);
321 gtk_widget_show (GTK_WIDGET (filter_editor));
322 }
323
324 /*
325 * Picked this from e-d-s/libedataserver/e-data.
326 * But it allows more characters to occur in filenames, especially
327 * when saving attachment.
328 */
329 void
330 em_filename_make_safe (gchar *string)
331 {
332 gchar *p, *ts;
333 gunichar c;
334 #ifdef G_OS_WIN32
335 const gchar *unsafe_chars = "/\":*?<>|\\#";
336 #else
337 const gchar *unsafe_chars = "/#";
338 #endif
339
340 g_return_if_fail (string != NULL);
341 p = string;
342
343 while (p && *p) {
344 c = g_utf8_get_char (p);
345 ts = p;
346 p = g_utf8_next_char (p);
347 /* I wonder what this code is supposed to actually
348 * achieve, and whether it does that as currently
349 * written?
350 */
351 if (!g_unichar_isprint (c) || (c < 0xff && strchr (unsafe_chars, c&0xff))) {
352 while (ts < p)
353 *ts++ = '_';
354 }
355 }
356 }
357
358 /* ********************************************************************** */
359 /* Flag-for-Followup... */
360
361 /**
362 * em_utils_flag_for_followup:
363 * @reader: an #EMailReader
364 * @folder: folder containing messages to flag
365 * @uids: uids of messages to flag
366 *
367 * Open the Flag-for-Followup editor for the messages specified by
368 * @folder and @uids.
369 **/
370 void
371 em_utils_flag_for_followup (EMailReader *reader,
372 CamelFolder *folder,
373 GPtrArray *uids)
374 {
375 EShell *shell;
376 EMailBackend *backend;
377 EShellSettings *shell_settings;
378 EShellBackend *shell_backend;
379 EMailDisplay *display;
380 GtkWidget *editor;
381 GtkWindow *window;
382 CamelTag *tags;
383 gint i;
384
385 g_return_if_fail (E_IS_MAIL_READER (reader));
386 g_return_if_fail (CAMEL_IS_FOLDER (folder));
387 g_return_if_fail (uids != NULL);
388
389 window = e_mail_reader_get_window (reader);
390 backend = e_mail_reader_get_backend (reader);
391
392 editor = e_mail_tag_editor_new ();
393 gtk_window_set_transient_for (GTK_WINDOW (editor), window);
394
395 shell_backend = E_SHELL_BACKEND (backend);
396 shell = e_shell_backend_get_shell (shell_backend);
397 shell_settings = e_shell_get_shell_settings (shell);
398
399 /* These settings come from the calendar module. */
400 g_object_bind_property (
401 shell_settings, "cal-use-24-hour-format",
402 editor, "use-24-hour-format",
403 G_BINDING_SYNC_CREATE);
404 g_object_bind_property (
405 shell_settings, "cal-week-start-day",
406 editor, "week-start-day",
407 G_BINDING_SYNC_CREATE);
408
409 for (i = 0; i < uids->len; i++) {
410 CamelMessageInfo *info;
411
412 info = camel_folder_get_message_info (folder, uids->pdata[i]);
413
414 if (info == NULL)
415 continue;
416
417 e_mail_tag_editor_add_message (
418 E_MAIL_TAG_EDITOR (editor),
419 camel_message_info_from (info),
420 camel_message_info_subject (info));
421
422 camel_folder_free_message_info (folder, info);
423 }
424
425 /* special-case... */
426 if (uids->len == 1) {
427 CamelMessageInfo *info;
428 const gchar *message_uid;
429
430 message_uid = g_ptr_array_index (uids, 0);
431 info = camel_folder_get_message_info (folder, message_uid);
432 if (info) {
433 tags = (CamelTag *) camel_message_info_user_tags (info);
434
435 if (tags)
436 e_mail_tag_editor_set_tag_list (
437 E_MAIL_TAG_EDITOR (editor), tags);
438 camel_folder_free_message_info (folder, info);
439 }
440 }
441
442 if (gtk_dialog_run (GTK_DIALOG (editor)) != GTK_RESPONSE_OK)
443 goto exit;
444
445 tags = e_mail_tag_editor_get_tag_list (E_MAIL_TAG_EDITOR (editor));
446 if (tags == NULL)
447 goto exit;
448
449 camel_folder_freeze (folder);
450 for (i = 0; i < uids->len; i++) {
451 CamelMessageInfo *info;
452 CamelTag *iter;
453
454 info = camel_folder_get_message_info (folder, uids->pdata[i]);
455
456 if (info == NULL)
457 continue;
458
459 for (iter = tags; iter != NULL; iter = iter->next)
460 camel_message_info_set_user_tag (
461 info, iter->name, iter->value);
462
463 camel_folder_free_message_info (folder, info);
464 }
465
466 camel_folder_thaw (folder);
467 camel_tag_list_free (&tags);
468
469 display = e_mail_reader_get_mail_display (reader);
470 e_mail_display_reload (display);
471
472 exit:
473 /* XXX We shouldn't be freeing this. */
474 em_utils_uids_free (uids);
475
476 gtk_widget_destroy (GTK_WIDGET (editor));
477 }
478
479 /**
480 * em_utils_flag_for_followup_clear:
481 * @parent: parent window
482 * @folder: folder containing messages to unflag
483 * @uids: uids of messages to unflag
484 *
485 * Clears the Flag-for-Followup flag on the messages referenced by
486 * @folder and @uids.
487 **/
488 void
489 em_utils_flag_for_followup_clear (GtkWindow *parent,
490 CamelFolder *folder,
491 GPtrArray *uids)
492 {
493 gint i;
494
495 g_return_if_fail (GTK_IS_WINDOW (parent));
496 g_return_if_fail (CAMEL_IS_FOLDER (folder));
497 g_return_if_fail (uids != NULL);
498
499 camel_folder_freeze (folder);
500 for (i = 0; i < uids->len; i++) {
501 CamelMessageInfo *mi = camel_folder_get_message_info (folder, uids->pdata[i]);
502
503 if (mi) {
504 camel_message_info_set_user_tag (mi, "follow-up", NULL);
505 camel_message_info_set_user_tag (mi, "due-by", NULL);
506 camel_message_info_set_user_tag (mi, "completed-on", NULL);
507 camel_folder_free_message_info (folder, mi);
508 }
509 }
510
511 camel_folder_thaw (folder);
512
513 em_utils_uids_free (uids);
514 }
515
516 /**
517 * em_utils_flag_for_followup_completed:
518 * @parent: parent window
519 * @folder: folder containing messages to 'complete'
520 * @uids: uids of messages to 'complete'
521 *
522 * Sets the completed state (and date/time) for each message
523 * referenced by @folder and @uids that is marked for
524 * Flag-for-Followup.
525 **/
526 void
527 em_utils_flag_for_followup_completed (GtkWindow *parent,
528 CamelFolder *folder,
529 GPtrArray *uids)
530 {
531 gchar *now;
532 gint i;
533
534 g_return_if_fail (GTK_IS_WINDOW (parent));
535 g_return_if_fail (CAMEL_IS_FOLDER (folder));
536 g_return_if_fail (uids != NULL);
537
538 now = camel_header_format_date (time (NULL), 0);
539
540 camel_folder_freeze (folder);
541 for (i = 0; i < uids->len; i++) {
542 const gchar *tag;
543 CamelMessageInfo *mi = camel_folder_get_message_info (folder, uids->pdata[i]);
544
545 if (mi) {
546 tag = camel_message_info_user_tag (mi, "follow-up");
547 if (tag && tag[0])
548 camel_message_info_set_user_tag (mi, "completed-on", now);
549 camel_folder_free_message_info (folder, mi);
550 }
551 }
552
553 camel_folder_thaw (folder);
554
555 g_free (now);
556
557 em_utils_uids_free (uids);
558 }
559
560 /* This kind of sucks, because for various reasons most callers need to run
561 * synchronously in the gui thread, however this could take a long, blocking
562 * time to run. */
563 static gint
564 em_utils_write_messages_to_stream (CamelFolder *folder,
565 GPtrArray *uids,
566 CamelStream *stream)
567 {
568 CamelStream *filtered_stream;
569 CamelMimeFilter *from_filter;
570 gint i, res = 0;
571
572 from_filter = camel_mime_filter_from_new ();
573 filtered_stream = camel_stream_filter_new (stream);
574 camel_stream_filter_add (
575 CAMEL_STREAM_FILTER (filtered_stream), from_filter);
576 g_object_unref (from_filter);
577
578 for (i = 0; i < uids->len; i++) {
579 CamelMimeMessage *message;
580 gchar *from;
581
582 /* FIXME camel_folder_get_message_sync() may block. */
583 message = camel_folder_get_message_sync (
584 folder, uids->pdata[i], NULL, NULL);
585 if (message == NULL) {
586 res = -1;
587 break;
588 }
589
590 /* We need to flush after each stream write since we are
591 * writing to the same stream. */
592 from = camel_mime_message_build_mbox_from (message);
593
594 if (camel_stream_write_string (stream, from, NULL, NULL) == -1
595 || camel_stream_flush (stream, NULL, NULL) == -1
596 || camel_data_wrapper_write_to_stream_sync (
597 (CamelDataWrapper *) message, (CamelStream *)
598 filtered_stream, NULL, NULL) == -1
599 || camel_stream_flush (
600 (CamelStream *) filtered_stream, NULL, NULL) == -1)
601 res = -1;
602
603 g_free (from);
604 g_object_unref (message);
605
606 if (res == -1)
607 break;
608 }
609
610 g_object_unref (filtered_stream);
611
612 return res;
613 }
614
615 static void
616 do_print_msg_to_file (GObject *source,
617 GAsyncResult *result,
618 gpointer user_data)
619 {
620 EMailParser *parser;
621 EMailPartList *parts_list;
622 gchar *filename = user_data;
623 EMailPrinter *printer;
624
625 parser = E_MAIL_PARSER (source);
626 parts_list = e_mail_parser_parse_finish (parser, result, NULL);
627
628 printer = e_mail_printer_new (parts_list);
629 e_mail_printer_set_export_filename (printer, filename);
630 g_signal_connect_swapped (
631 printer, "done",
632 G_CALLBACK (g_object_unref), printer);
633
634 e_mail_printer_print (printer, GTK_PRINT_OPERATION_ACTION_EXPORT, NULL, NULL);
635
636 g_object_unref (parser);
637 }
638
639 static gboolean
640 em_utils_print_messages_to_file (CamelFolder *folder,
641 const gchar *uid,
642 const gchar *filename)
643 {
644 EMailParser *parser;
645 CamelMimeMessage *message;
646 CamelStore *parent_store;
647 CamelSession *session;
648
649 message = camel_folder_get_message_sync (folder, uid, NULL, NULL);
650 if (message == NULL)
651 return FALSE;
652
653 parent_store = camel_folder_get_parent_store (folder);
654 session = camel_service_get_session (CAMEL_SERVICE (parent_store));
655
656 parser = e_mail_parser_new (session);
657
658 e_mail_parser_parse (
659 parser, folder, uid, message,
660 (GAsyncReadyCallback) do_print_msg_to_file,
661 NULL, g_strdup (filename));
662
663 return TRUE;
664 }
665
666 /* This kind of sucks, because for various reasons most callers need to run
667 * synchronously in the gui thread, however this could take a long, blocking
668 * time to run. */
669 static gint
670 em_utils_read_messages_from_stream (CamelFolder *folder,
671 CamelStream *stream)
672 {
673 CamelMimeParser *mp = camel_mime_parser_new ();
674 gboolean success = TRUE;
675
676 camel_mime_parser_scan_from (mp, TRUE);
677 camel_mime_parser_init_with_stream (mp, stream, NULL);
678
679 while (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM) {
680 CamelMimeMessage *msg;
681 gboolean success;
682
683 /* NB: de-from filter, once written */
684 msg = camel_mime_message_new ();
685 if (!camel_mime_part_construct_from_parser_sync (
686 (CamelMimePart *) msg, mp, NULL, NULL)) {
687 g_object_unref (msg);
688 break;
689 }
690
691 /* FIXME camel_folder_append_message_sync() may block. */
692 success = camel_folder_append_message_sync (
693 folder, msg, NULL, NULL, NULL, NULL);
694 g_object_unref (msg);
695
696 if (!success)
697 break;
698
699 camel_mime_parser_step (mp, NULL, NULL);
700 }
701
702 g_object_unref (mp);
703
704 return success ? 0 : -1;
705 }
706
707 /**
708 * em_utils_selection_set_mailbox:
709 * @data: selection data
710 * @folder: folder containign messages to copy into the selection
711 * @uids: uids of the messages to copy into the selection
712 *
713 * Creates a mailbox-format selection.
714 * Warning: Could be BIG!
715 * Warning: This could block the ui for an extended period.
716 **/
717 void
718 em_utils_selection_set_mailbox (GtkSelectionData *data,
719 CamelFolder *folder,
720 GPtrArray *uids)
721 {
722 GByteArray *byte_array;
723 CamelStream *stream;
724 GdkAtom target;
725
726 target = gtk_selection_data_get_target (data);
727
728 byte_array = g_byte_array_new ();
729 stream = camel_stream_mem_new_with_byte_array (byte_array);
730
731 if (em_utils_write_messages_to_stream (folder, uids, stream) == 0)
732 gtk_selection_data_set (
733 data, target, 8,
734 byte_array->data, byte_array->len);
735
736 g_object_unref (stream);
737 }
738
739 /**
740 * em_utils_selection_get_mailbox:
741 * @selection_data: selection data
742 * @folder:
743 *
744 * Receive a mailbox selection/dnd
745 * Warning: Could be BIG!
746 * Warning: This could block the ui for an extended period.
747 * FIXME: Exceptions?
748 **/
749 void
750 em_utils_selection_get_mailbox (GtkSelectionData *selection_data,
751 CamelFolder *folder)
752 {
753 CamelStream *stream;
754 const guchar *data;
755 gint length;
756
757 data = gtk_selection_data_get_data (selection_data);
758 length = gtk_selection_data_get_length (selection_data);
759
760 if (data == NULL || length == -1)
761 return;
762
763 /* TODO: a stream mem with read-only access to existing data? */
764 /* NB: Although copying would let us run this async ... which it should */
765 stream = (CamelStream *)
766 camel_stream_mem_new_with_buffer ((gchar *) data, length);
767 em_utils_read_messages_from_stream (folder, stream);
768 g_object_unref (stream);
769 }
770
771 /**
772 * em_utils_selection_get_message:
773 * @selection_data:
774 * @folder:
775 *
776 * get a message/rfc822 data.
777 **/
778 void
779 em_utils_selection_get_message (GtkSelectionData *selection_data,
780 CamelFolder *folder)
781 {
782 CamelStream *stream;
783 CamelMimeMessage *msg;
784 const guchar *data;
785 gint length;
786
787 data = gtk_selection_data_get_data (selection_data);
788 length = gtk_selection_data_get_length (selection_data);
789
790 if (data == NULL || length == -1)
791 return;
792
793 stream = (CamelStream *)
794 camel_stream_mem_new_with_buffer ((gchar *) data, length);
795 msg = camel_mime_message_new ();
796 if (camel_data_wrapper_construct_from_stream_sync (
797 (CamelDataWrapper *) msg, stream, NULL, NULL))
798 /* FIXME camel_folder_append_message_sync() may block. */
799 camel_folder_append_message_sync (
800 folder, msg, NULL, NULL, NULL, NULL);
801 g_object_unref (msg);
802 g_object_unref (stream);
803 }
804
805 /**
806 * em_utils_selection_set_uidlist:
807 * @selection_data: selection data
808 * @folder:
809 * @uids:
810 *
811 * Sets a "x-uid-list" format selection data.
812 **/
813 void
814 em_utils_selection_set_uidlist (GtkSelectionData *selection_data,
815 CamelFolder *folder,
816 GPtrArray *uids)
817 {
818 GByteArray *array = g_byte_array_new ();
819 GdkAtom target;
820 gchar *folder_uri;
821 gint ii;
822
823 /* format: "uri1\0uid1\0uri2\0uid2\0...\0urin\0uidn\0" */
824
825 if (CAMEL_IS_VEE_FOLDER (folder) &&
826 CAMEL_IS_VEE_STORE (camel_folder_get_parent_store (folder))) {
827 CamelVeeFolder *vfolder = CAMEL_VEE_FOLDER (folder);
828 CamelFolder *real_folder;
829 CamelMessageInfo *info;
830 gchar *real_uid;
831
832 for (ii = 0; ii < uids->len; ii++) {
833 info = camel_folder_get_message_info (folder, uids->pdata[ii]);
834 if (!info) {
835 g_warn_if_reached ();
836 continue;
837 }
838
839 real_folder = camel_vee_folder_get_location (
840 vfolder, (CamelVeeMessageInfo *) info, &real_uid);
841
842 if (real_folder) {
843 folder_uri = e_mail_folder_uri_from_folder (real_folder);
844
845 g_byte_array_append (array, (guchar *) folder_uri, strlen (folder_uri) + 1);
846 g_byte_array_append (array, (guchar *) real_uid, strlen (real_uid) + 1);
847
848 g_free (folder_uri);
849 }
850
851 camel_folder_free_message_info (folder, info);
852 }
853 } else {
854 folder_uri = e_mail_folder_uri_from_folder (folder);
855
856 for (ii = 0; ii < uids->len; ii++) {
857 g_byte_array_append (array, (guchar *) folder_uri, strlen (folder_uri) + 1);
858 g_byte_array_append (array, uids->pdata[ii], strlen (uids->pdata[ii]) + 1);
859 }
860
861 g_free (folder_uri);
862 }
863
864 target = gtk_selection_data_get_target (selection_data);
865 gtk_selection_data_set (
866 selection_data, target, 8, array->data, array->len);
867 g_byte_array_free (array, TRUE);
868 }
869
870 /**
871 * em_utils_selection_get_uidlist:
872 * @data: selection data
873 * @session: an #EMailSession
874 * @move: do we delete the messages.
875 *
876 * Convert a uid list into a copy/move operation.
877 *
878 * Warning: Could take some time to run.
879 **/
880 void
881 em_utils_selection_get_uidlist (GtkSelectionData *selection_data,
882 EMailSession *session,
883 CamelFolder *dest,
884 gint move,
885 GCancellable *cancellable,
886 GError **error)
887 {
888 /* format: "uri1\0uid1\0uri2\0uid2\0...\0urin\0uidn\0" */
889 gchar *inptr, *inend;
890 GPtrArray *items;
891 CamelFolder *folder;
892 const guchar *data;
893 gint length, ii;
894 GHashTable *uids_by_uri;
895 GHashTableIter iter;
896 gpointer key, value;
897 GError *local_error = NULL;
898
899 g_return_if_fail (selection_data != NULL);
900 g_return_if_fail (E_IS_MAIL_SESSION (session));
901
902 data = gtk_selection_data_get_data (selection_data);
903 length = gtk_selection_data_get_length (selection_data);
904
905 if (data == NULL || length == -1)
906 return;
907
908 items = g_ptr_array_new ();
909
910 inptr = (gchar *) data;
911 inend = (gchar *) (data + length);
912 while (inptr < inend) {
913 gchar *start = inptr;
914
915 while (inptr < inend && *inptr)
916 inptr++;
917
918 g_ptr_array_add (items, g_strndup (start, inptr - start));
919
920 inptr++;
921 }
922
923 if (items->len == 0) {
924 g_ptr_array_free (items, TRUE);
925 return;
926 }
927
928 uids_by_uri = g_hash_table_new (g_str_hash, g_str_equal);
929 for (ii = 0; ii < items->len - 1; ii += 2) {
930 gchar *uri, *uid;
931 GPtrArray *uids;
932
933 uri = items->pdata[ii];
934 uid = items->pdata[ii + 1];
935
936 uids = g_hash_table_lookup (uids_by_uri, uri);
937 if (!uids) {
938 uids = g_ptr_array_new ();
939 g_hash_table_insert (uids_by_uri, uri, uids);
940 }
941
942 /* reuse uid pointer from uids, do not strdup it */
943 g_ptr_array_add (uids, uid);
944 }
945
946 g_hash_table_iter_init (&iter, uids_by_uri);
947 while (g_hash_table_iter_next (&iter, &key, &value)) {
948 const gchar *uri = key;
949 GPtrArray *uids = value;
950
951 if (!local_error) {
952 /* FIXME e_mail_session_uri_to_folder_sync() may block. */
953 folder = e_mail_session_uri_to_folder_sync (
954 session, uri, 0, cancellable, &local_error);
955 if (folder) {
956 /* FIXME camel_folder_transfer_messages_to_sync() may block. */
957 camel_folder_transfer_messages_to_sync (
958 folder, uids, dest, move, NULL, cancellable, &local_error);
959 g_object_unref (folder);
960 }
961 }
962
963 g_ptr_array_free (uids, TRUE);
964 }
965
966 g_hash_table_destroy (uids_by_uri);
967 em_utils_uids_free (items);
968
969 if (local_error)
970 g_propagate_error (error, local_error);
971 }
972
973 static gchar *
974 em_utils_build_export_filename (CamelFolder *folder,
975 const gchar *uid,
976 const gchar *exporttype,
977 gint exportname,
978 const gchar *tmpdir)
979 {
980 CamelMessageInfo *info;
981 gchar *file = NULL;
982 gchar *tmpfile = NULL;
983 struct tm *ts;
984 gchar datetmp[15];
985
986 /* Try to get the drop filename from the message or folder */
987 info = camel_folder_get_message_info (folder, uid);
988 if (info) {
989 if (camel_message_info_subject (info)) {
990 time_t reftime;
991 reftime = camel_message_info_date_sent (info);
992 if (exportname == DND_USE_DND_DATE) {
993 reftime = time (NULL);
994 }
995
996 ts = localtime (&reftime);
997 strftime (datetmp, 15, "%Y%m%d%H%M%S", ts);
998
999 if (g_ascii_strcasecmp (exporttype, "pdf") == 0)
1000 file = g_strdup_printf ("%s_%s.pdf", datetmp, camel_message_info_subject (info));
1001 else
1002 file = g_strdup_printf ("%s_%s", datetmp, camel_message_info_subject (info));
1003
1004 }
1005 camel_folder_free_message_info (folder, info);
1006 } else {
1007 time_t reftime;
1008 reftime = time (NULL);
1009 ts = localtime (&reftime);
1010 strftime (datetmp, 15, "%Y%m%d%H%M%S", ts);
1011 if (g_ascii_strcasecmp (exporttype, "pdf") == 0)
1012 file = g_strdup_printf ("%s_Untitled Message.pdf", datetmp);
1013 else
1014 file = g_strdup_printf ("%s_Untitled Message", datetmp);
1015
1016 }
1017
1018 if (file != NULL) {
1019 e_filename_make_safe (file);
1020 tmpfile = g_build_filename (tmpdir, file, NULL);
1021 g_free (file);
1022 }
1023
1024 return tmpfile;
1025 }
1026
1027 /**
1028 * em_utils_selection_set_urilist:
1029 * @data:
1030 * @folder:
1031 * @uids:
1032 *
1033 * Set the selection data @data to a uri which points to a file, which is
1034 * a berkely mailbox format mailbox. The file is automatically cleaned
1035 * up when the application quits.
1036 **/
1037 void
1038 em_utils_selection_set_urilist (GtkSelectionData *data,
1039 CamelFolder *folder,
1040 GPtrArray *uids)
1041 {
1042 gchar *tmpdir;
1043 gchar *uri;
1044 gint fd;
1045 GSettings *settings;
1046 gchar *exporttype;
1047 gint exportname;
1048
1049 tmpdir = e_mkdtemp ("drag-n-drop-XXXXXX");
1050 if (tmpdir == NULL)
1051 return;
1052
1053 settings = g_settings_new ("org.gnome.evolution.mail");
1054 exporttype = g_settings_get_string (settings, "drag-and-drop-save-file-format");
1055 if (g_strcmp0 (exporttype, "mbox") != 0 && g_strcmp0 (exporttype, "pdf") != 0) {
1056 g_free (exporttype);
1057 exporttype = g_strdup ("mbox");
1058 }
1059 exportname = g_settings_get_int (settings, "drag-and-drop-save-name-format");
1060 g_object_unref (settings);
1061
1062 if (g_ascii_strcasecmp (exporttype, "mbox") == 0) {
1063 gchar * file = NULL;
1064 CamelStream *fstream;
1065
1066 if (uids->len > 1) {
1067 gchar * tmp = g_strdup_printf (_("Messages from %s"), camel_folder_get_display_name (folder));
1068 e_filename_make_safe (tmp);
1069 file = g_build_filename (tmpdir, tmp, NULL);
1070 g_free (tmp);
1071 } else {
1072 file = em_utils_build_export_filename (folder, uids->pdata[0], exporttype, exportname, tmpdir);
1073 }
1074
1075 fd = g_open (file, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0666);
1076 if (fd == -1) {
1077 g_free (file);
1078 g_free (exporttype);
1079 g_free (tmpdir);
1080 return;
1081 }
1082
1083 uri = g_filename_to_uri (file, NULL, NULL);
1084 fstream = camel_stream_fs_new_with_fd (fd);
1085 if (fstream) {
1086 if (em_utils_write_messages_to_stream (folder, uids, fstream) == 0) {
1087 GdkAtom type;
1088 /* terminate with \r\n to be compliant with the spec */
1089 gchar *uri_crlf = g_strconcat (uri, "\r\n", NULL);
1090
1091 type = gtk_selection_data_get_target (data);
1092 gtk_selection_data_set (data, type, 8, (guchar *) uri_crlf, strlen (uri_crlf));
1093 g_free (uri_crlf);
1094 }
1095 g_object_unref (fstream);
1096 } else
1097 close (fd);
1098
1099 g_free (exporttype);
1100 g_free (tmpdir);
1101 g_free (file);
1102 g_free (uri);
1103 } else if (g_ascii_strcasecmp (exporttype, "pdf") == 0) {
1104 gchar ** filenames, **uris;
1105 gint i, uris_count = 0;
1106
1107 filenames = g_new (gchar *, uids->len);
1108 uris = g_new (gchar *, uids->len + 1);
1109 for (i = 0; i < uids->len; i++) {
1110 filenames[i] = em_utils_build_export_filename (folder, uids->pdata[i], exporttype, exportname, tmpdir);
1111 /* validity test */
1112 fd = g_open (filenames[i], O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0666);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1113 if (fd == -1) {
1114 gint j;
1115 for (j = 0; j <= i; j++) {
1116 g_free (filenames[j]);
1117 }
1118 g_free (filenames);
1119 g_free (uris);
1120 g_free (tmpdir);
1121 g_free (exporttype);
1122 return;
1123 }
1124 close (fd);
1125
1126 /* export */
1127 if (em_utils_print_messages_to_file (folder, uids->pdata[i], filenames[i])) {
1128 /* terminate with \r\n to be compliant with the spec */
1129 uri = g_filename_to_uri (filenames[i], NULL, NULL);
1130 uris[uris_count++] = g_strconcat (uri, "\r\n", NULL);
1131 g_free (uri);
1132 }
1133 }
1134
1135 uris[uris_count] = NULL;
1136 gtk_selection_data_set_uris (data, uris);
1137
1138 for (i = 0; i < uids->len; i++) {
1139 g_free (filenames[i]);
1140 }
1141 g_free (filenames);
1142 for (i = 0; i < uris_count; i++) {
1143 g_free (uris[i]);
1144 }
1145 g_free (uris);
1146 g_free (exporttype);
1147 g_free (tmpdir);
1148 } else {
1149 g_free (exporttype);
1150 g_free (tmpdir);
1151 }
1152 }
1153
1154 /**
1155 * em_utils_selection_get_urilist:
1156 * @data:
1157 * @folder:
1158 * @uids:
1159 *
1160 * Get the selection data @data from a uri list which points to a
1161 * file, which is a berkely mailbox format mailbox. The file is
1162 * automatically cleaned up when the application quits.
1163 **/
1164 void
1165 em_utils_selection_get_urilist (GtkSelectionData *selection_data,
1166 CamelFolder *folder)
1167 {
1168 CamelStream *stream;
1169 CamelURL *url;
1170 gint fd, i, res = 0;
1171 gchar **uris;
1172
1173 d (printf (" * drop uri list\n"));
1174
1175 uris = gtk_selection_data_get_uris (selection_data);
1176
1177 for (i = 0; res == 0 && uris[i]; i++) {
1178 g_strstrip (uris[i]);
1179 if (uris[i][0] == '#')
1180 continue;
1181
1182 url = camel_url_new (uris[i], NULL);
1183 if (url == NULL)
1184 continue;
1185
1186 if (strcmp (url->protocol, "file") == 0
1187 && (fd = g_open (url->path, O_RDONLY | O_BINARY, 0)) != -1) {
1188 stream = camel_stream_fs_new_with_fd (fd);
1189 if (stream) {
1190 res = em_utils_read_messages_from_stream (folder, stream);
1191 g_object_unref (stream);
1192 } else
1193 close (fd);
1194 }
1195 camel_url_free (url);
1196 }
1197
1198 g_strfreev (uris);
1199 }
1200
1201 /* ********************************************************************** */
1202 static EProxy *emu_proxy = NULL;
1203 static GStaticMutex emu_proxy_lock = G_STATIC_MUTEX_INIT;
1204
1205 static gpointer
1206 emu_proxy_setup (gpointer data)
1207 {
1208 if (!emu_proxy) {
1209 emu_proxy = e_proxy_new ();
1210 e_proxy_setup_proxy (emu_proxy);
1211 /* not necessary to listen for changes here */
1212 }
1213
1214 return NULL;
1215 }
1216
1217 EProxy *
1218 em_utils_get_proxy (void)
1219 {
1220 g_static_mutex_lock (&emu_proxy_lock);
1221
1222 if (!emu_proxy) {
1223 mail_call_main (MAIL_CALL_p_p, (MailMainFunc) emu_proxy_setup, NULL);
1224 }
1225
1226 g_static_mutex_unlock (&emu_proxy_lock);
1227
1228 return emu_proxy;
1229 }
1230
1231 static gboolean
1232 is_only_text_part_in_this_level (GSList *parts,
1233 EMailPart *text_html_part)
1234 {
1235 const gchar *dot;
1236 gint level_len;
1237 GSList *iter;
1238
1239 g_return_val_if_fail (parts != NULL, FALSE);
1240 g_return_val_if_fail (text_html_part != NULL, FALSE);
1241
1242 dot = strrchr (text_html_part->id, '.');
1243 if (!dot)
1244 return FALSE;
1245
1246 level_len = dot - text_html_part->id;
1247 for (iter = parts; iter; iter = iter->next) {
1248 EMailPart *part = iter->data;
1249
1250 if (!part || !part->mime_type || part == text_html_part ||
1251 part->is_hidden || part->is_attachment)
1252 continue;
1253
1254 dot = strrchr (part->id, '.');
1255 if (dot - part->id != level_len ||
1256 strncmp (text_html_part->id, part->id, level_len) != 0)
1257 continue;
1258
1259 if (g_ascii_strncasecmp (part->mime_type, "text/", 5) == 0)
1260 return FALSE;
1261 }
1262
1263 return TRUE;
1264 }
1265
1266 /**
1267 * em_utils_message_to_html:
1268 * @session: a #CamelSession
1269 * @message:
1270 * @credits:
1271 * @flags: EMFormatQuote flags
1272 * @source:
1273 * @append: Text to append, can be NULL.
1274 * @validity_found: if not NULL, then here will be set what validities
1275 * had been found during message conversion. Value is a bit OR
1276 * of EM_FORMAT_VALIDITY_FOUND_* constants.
1277 *
1278 * Convert a message to html, quoting if the @credits attribution
1279 * string is given.
1280 *
1281 * Return value: The html version as a NULL terminated string.
1282 **/
1283 gchar *
1284 em_utils_message_to_html (CamelSession *session,
1285 CamelMimeMessage *message,
1286 const gchar *credits,
1287 guint32 flags,
1288 EMailPartList *parts_list,
1289 const gchar *append,
1290 guint32 *validity_found)
1291 {
1292 EMailFormatter *formatter;
1293 EMailParser *parser = NULL;
1294 CamelStream *mem;
1295 GByteArray *buf;
1296 EShell *shell;
1297 GtkWindow *window;
1298 EMailPart *hidden_text_html_part = NULL;
1299 guint32 is_validity_found = 0;
1300 GSList *iter;
1301
1302 shell = e_shell_get_default ();
1303 window = e_shell_get_active_window (shell);
1304
1305 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1306
1307 buf = g_byte_array_new ();
1308 mem = camel_stream_mem_new ();
1309 camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (mem), buf);
1310
1311 formatter = e_mail_formatter_quote_new (credits, flags);
1312 e_mail_formatter_set_style (formatter,
1313 gtk_widget_get_style (GTK_WIDGET (window)),
1314 gtk_widget_get_state (GTK_WIDGET (window)));
1315
1316 if (!parts_list) {
1317 GSettings *settings;
1318 gchar *charset;
1319
1320 /* FIXME We should be getting this from the
1321 * current view, not the global setting. */
1322 settings = g_settings_new ("org.gnome.evolution.mail");
1323 charset = g_settings_get_string (settings, "charset");
1324 if (charset && *charset)
1325 e_mail_formatter_set_default_charset (formatter, charset);
1326 g_object_unref (settings);
1327 g_free (charset);
1328
1329 parser = e_mail_parser_new (session);
1330 parts_list = e_mail_parser_parse_sync (parser, NULL, NULL, message, NULL);
1331 } else {
1332 g_object_ref (parts_list);
1333 }
1334
1335 /* Return all found validities and possibly show hidden prefer-plain part */
1336 for (iter = parts_list->list; iter; iter = iter->next) {
1337
1338 EMailPart *part = iter->data;
1339 if (!part)
1340 continue;
1341
1342 /* prefer-plain can hide HTML parts, even when it's the only
1343 * text part in the email, thus show it (and hide again later) */
1344 if (part->is_hidden && !hidden_text_html_part &&
1345 part->mime_type && !part->is_attachment &&
1346 g_ascii_strcasecmp (part->mime_type, "text/html") == 0 &&
1347 is_only_text_part_in_this_level (parts_list->list, part)) {
1348 part->is_hidden = FALSE;
1349 hidden_text_html_part = part;
1350 }
1351
1352 if (part->validity_type)
1353 is_validity_found |= part->validity_type;
1354 }
1355
1356 if (validity_found)
1357 *validity_found = is_validity_found;
1358
1359 e_mail_formatter_format_sync (
1360 formatter, parts_list, mem, 0,
1361 E_MAIL_FORMATTER_MODE_PRINTING, NULL);
1362 g_object_unref (formatter);
1363
1364 if (hidden_text_html_part)
1365 hidden_text_html_part->is_hidden = TRUE;
1366
1367 g_object_unref (parts_list);
1368 if (parser)
1369 g_object_unref (parser);
1370
1371 if (append && *append)
1372 camel_stream_write_string (mem, append, NULL, NULL);
1373
1374 camel_stream_write (mem, "", 1, NULL, NULL);
1375 g_object_unref (mem);
1376
1377 return (gchar *) g_byte_array_free (buf, FALSE);
1378 }
1379
1380 /* ********************************************************************** */
1381
1382 /**
1383 * em_utils_empty_trash:
1384 * @parent: parent window
1385 * @session: an #EMailSession
1386 *
1387 * Empties all Trash folders.
1388 **/
1389 void
1390 em_utils_empty_trash (GtkWidget *parent,
1391 EMailSession *session)
1392 {
1393 ESourceRegistry *registry;
1394 GList *list, *link;
1395
1396 g_return_if_fail (E_IS_MAIL_SESSION (session));
1397
1398 registry = e_mail_session_get_registry (session);
1399
1400 if (!em_utils_prompt_user ((GtkWindow *) parent,
1401 "prompt-on-empty-trash",
1402 "mail:ask-empty-trash", NULL))
1403 return;
1404
1405 list = camel_session_list_services (CAMEL_SESSION (session));
1406
1407 for (link = list; link != NULL; link = g_list_next (link)) {
1408 CamelProvider *provider;
1409 CamelService *service;
1410 ESource *source;
1411 const gchar *uid;
1412 gboolean enabled = TRUE;
1413
1414 service = CAMEL_SERVICE (link->data);
1415 provider = camel_service_get_provider (service);
1416 uid = camel_service_get_uid (service);
1417
1418 if (!CAMEL_IS_STORE (service))
1419 continue;
1420
1421 if ((provider->flags & CAMEL_PROVIDER_IS_STORAGE) == 0)
1422 continue;
1423
1424 source = e_source_registry_ref_source (registry, uid);
1425
1426 if (source != NULL) {
1427 enabled = e_source_get_enabled (source);
1428 g_object_unref (source);
1429 }
1430
1431 if (enabled)
1432 mail_empty_trash (CAMEL_STORE (service));
1433 }
1434
1435 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1436 }
1437
1438 /* ********************************************************************** */
1439
1440 gchar *
1441 em_utils_url_unescape_amp (const gchar *url)
1442 {
1443 gchar *buff;
1444 gint i, j, amps;
1445
1446 if (!url)
1447 return NULL;
1448
1449 amps = 0;
1450 for (i = 0; url[i]; i++) {
1451 if (url[i] == '&' && strncmp (url + i, "&", 5) == 0)
1452 amps++;
1453 }
1454
1455 buff = g_strdup (url);
1456
1457 if (!amps)
1458 return buff;
1459
1460 for (i = 0, j = 0; url[i]; i++, j++) {
1461 buff[j] = url[i];
1462
1463 if (url[i] == '&' && strncmp (url + i, "&", 5) == 0)
1464 i += 4;
1465 }
1466 buff[j] = 0;
1467
1468 return buff;
1469 }
1470
1471 void
1472 emu_restore_folder_tree_state (EMFolderTree *folder_tree)
1473 {
1474 EShell *shell;
1475 EShellBackend *backend;
1476 GKeyFile *key_file;
1477 const gchar *config_dir;
1478 gchar *filename;
1479
1480 g_return_if_fail (folder_tree != NULL);
1481 g_return_if_fail (EM_IS_FOLDER_TREE (folder_tree));
1482
1483 shell = e_shell_get_default ();
1484 backend = e_shell_get_backend_by_name (shell, "mail");
1485 g_return_if_fail (backend != NULL);
1486
1487 config_dir = e_shell_backend_get_config_dir (backend);
1488 g_return_if_fail (config_dir != NULL);
1489
1490 filename = g_build_filename (config_dir, "state.ini", NULL);
1491
1492 key_file = g_key_file_new ();
1493 g_key_file_load_from_file (key_file, filename, 0, NULL);
1494 g_free (filename);
1495
1496 em_folder_tree_restore_state (folder_tree, key_file);
1497
1498 g_key_file_free (key_file);
1499 }
1500
1501 static gboolean
1502 check_prefix (const gchar *subject,
1503 const gchar *prefix,
1504 gint *skip_len)
1505 {
1506 gint plen;
1507
1508 g_return_val_if_fail (subject != NULL, FALSE);
1509 g_return_val_if_fail (prefix != NULL, FALSE);
1510 g_return_val_if_fail (*prefix, FALSE);
1511 g_return_val_if_fail (skip_len != NULL, FALSE);
1512
1513 plen = strlen (prefix);
1514 if (g_ascii_strncasecmp (subject, prefix, plen) != 0)
1515 return FALSE;
1516
1517 if (g_ascii_strncasecmp (subject + plen, ": ", 2) == 0) {
1518 *skip_len = plen + 2;
1519 return TRUE;
1520 }
1521
1522 if (g_ascii_strncasecmp (subject + plen, " : ", 3) == 0) {
1523 *skip_len = plen + 3;
1524 return TRUE;
1525 }
1526
1527 return FALSE;
1528 }
1529
1530 gboolean
1531 em_utils_is_re_in_subject (EShell *shell,
1532 const gchar *subject,
1533 gint *skip_len)
1534 {
1535 EShellSettings *shell_settings;
1536 gchar *prefixes, **prefixes_strv;
1537 gboolean res;
1538 gint ii;
1539
1540 g_return_val_if_fail (shell != NULL, FALSE);
1541 g_return_val_if_fail (subject != NULL, FALSE);
1542 g_return_val_if_fail (skip_len != NULL, FALSE);
1543
1544 *skip_len = -1;
1545
1546 if (strlen (subject) < 3)
1547 return FALSE;
1548
1549 if (check_prefix (subject, "Re", skip_len))
1550 return TRUE;
1551
1552 shell_settings = e_shell_get_shell_settings (shell);
1553 prefixes = e_shell_settings_get_string (shell_settings, "composer-localized-re");
1554 if (!prefixes || !*prefixes) {
1555 g_free (prefixes);
1556 return FALSE;
1557 }
1558
1559 prefixes_strv = g_strsplit (prefixes, ",", -1);
1560 g_free (prefixes);
1561
1562 if (!prefixes_strv)
1563 return FALSE;
1564
1565 res = FALSE;
1566
1567 for (ii = 0; !res && prefixes_strv[ii]; ii++) {
1568 const gchar *prefix = prefixes_strv[ii];
1569
1570 if (*prefix)
1571 res = check_prefix (subject, prefix, skip_len);
1572 }
1573
1574 g_strfreev (prefixes_strv);
1575
1576 return res;
1577 }