Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
em-folder-utils.c:642:7 | clang-analyzer | Dereference of null pointer (loaded from variable 'folder_name') | ||
em-folder-utils.c:642:7 | clang-analyzer | Dereference of null pointer (loaded from variable 'folder_name') |
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 <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35
36 #include <libxml/tree.h>
37
38 #include <gtk/gtk.h>
39 #include <gdk-pixbuf/gdk-pixbuf.h>
40 #include <glib/gi18n.h>
41
42 #include "e-util/e-mktemp.h"
43
44 #include "libevolution-utils/e-alert-dialog.h"
45 #include "e-util/e-dialog-utils.h"
46
47 #include "em-vfolder-editor-rule.h"
48
49 #include "libemail-utils/mail-mt.h"
50 #include "libemail-engine/e-mail-folder-utils.h"
51 #include "libemail-engine/e-mail-session.h"
52 #include "libemail-engine/e-mail-store-utils.h"
53 #include "libemail-engine/e-mail-utils.h"
54 #include "libemail-engine/mail-ops.h"
55 #include "libemail-engine/mail-tools.h"
56 #include "libemail-engine/mail-folder-cache.h"
57
58 #include "e-mail-ui-session.h"
59 #include "em-utils.h"
60 #include "em-folder-tree.h"
61 #include "em-folder-tree-model.h"
62 #include "em-folder-utils.h"
63 #include "em-folder-selector.h"
64 #include "em-folder-properties.h"
65 #include "mail-vfolder-ui.h"
66
67 #define d(x)
68
69 typedef struct _AsyncContext AsyncContext;
70
71 struct _AsyncContext {
72 EMFolderTree *folder_tree;
73 gchar *folder_uri;
74 };
75
76 static void
77 async_context_free (AsyncContext *context)
78 {
79 if (context->folder_tree != NULL)
80 g_object_unref (context->folder_tree);
81
82 g_free (context->folder_uri);
83
84 g_slice_free (AsyncContext, context);
85 }
86
87 static gboolean
88 emfu_is_special_local_folder (const gchar *name)
89 {
90 return (!strcmp (name, "Drafts") ||
91 !strcmp (name, "Inbox") ||
92 !strcmp (name, "Outbox") ||
93 !strcmp (name, "Sent") ||
94 !strcmp (name, "Templates"));
95 }
96
97 struct _EMCopyFolders {
98 MailMsg base;
99
100 /* input data */
101 CamelStore *fromstore;
102 CamelStore *tostore;
103
104 gchar *frombase;
105 gchar *tobase;
106
107 gint delete;
108 };
109
110 static gchar *
111 emft_copy_folders__desc (struct _EMCopyFolders *m,
112 gint complete)
113 {
114 if (m->delete)
115 return g_strdup_printf (_("Moving folder %s"), m->frombase);
116 else
117 return g_strdup_printf (_("Copying folder %s"), m->frombase);
118 }
119
120 static void
121 emft_copy_folders__exec (struct _EMCopyFolders *m,
122 GCancellable *cancellable,
123 GError **error)
124 {
125 guint32 flags;
126 GList *pending = NULL, *deleting = NULL, *l;
127 GString *fromname, *toname;
128 CamelFolderInfo *fi;
129 const gchar *tmp;
130 gint fromlen;
131
132 flags = CAMEL_STORE_FOLDER_INFO_FAST |
133 CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
134
135 /* If we're copying, then we need to copy every subfolder. If we're
136 * *moving*, though, then we only need to rename the top-level folder */
137 if (!m->delete)
138 flags |= CAMEL_STORE_FOLDER_INFO_RECURSIVE;
139
140 fi = camel_store_get_folder_info_sync (
141 m->fromstore, m->frombase, flags, cancellable, error);
142 if (fi == NULL)
143 return;
144
145 pending = g_list_append (pending, fi);
146
147 toname = g_string_new ("");
148 fromname = g_string_new ("");
149
150 tmp = strrchr (m->frombase, '/');
151 if (tmp == NULL)
152 fromlen = 0;
153 else
154 fromlen = tmp - m->frombase + 1;
155
156 d (printf ("top name is '%s'\n", fi->full_name));
157
158 while (pending) {
159 CamelFolderInfo *info = pending->data;
160
161 pending = g_list_remove_link (pending, pending);
162 while (info) {
163 CamelFolder *fromfolder, *tofolder;
164 GPtrArray *uids;
165 gint deleted = 0;
166
167 /* We still get immediate children even without the
168 * CAMEL_STORE_FOLDER_INFO_RECURSIVE flag. But we only
169 * want to process the children too if we're *copying * */
170 if (info->child && !m->delete)
171 pending = g_list_append (pending, info->child);
172
173 if (m->tobase[0])
174 g_string_printf (
175 toname, "%s/%s", m->tobase,
176 info->full_name + fromlen);
177 else
178 g_string_printf (
179 toname, "%s",
180 info->full_name + fromlen);
181
182 d (printf (
183 "Copying from '%s' to '%s'\n",
184 info->full_name, toname->str));
185
186 /* This makes sure we create the same tree,
187 * e.g. from a nonselectable source. */
188 /* Not sure if this is really the 'right thing',
189 * e.g. for spool stores, but it makes the ui work. */
190 if ((info->flags & CAMEL_FOLDER_NOSELECT) == 0) {
191 d (printf ("this folder is selectable\n"));
192 if (m->tostore == m->fromstore && m->delete) {
193 camel_store_rename_folder_sync (
194 m->fromstore,
195 info->full_name,
196 toname->str,
197 cancellable, error);
198 if (error && *error)
199 goto exception;
200
201 /* this folder no longer exists, unsubscribe it */
202 if (CAMEL_IS_SUBSCRIBABLE (m->fromstore))
203 camel_subscribable_unsubscribe_folder_sync (
204 CAMEL_SUBSCRIBABLE (m->fromstore),
205 info->full_name, NULL, NULL);
206
207 deleted = 1;
208 } else {
209 fromfolder = camel_store_get_folder_sync (
210 m->fromstore, info->full_name, 0,
211 cancellable, error);
212 if (fromfolder == NULL)
213 goto exception;
214
215 tofolder = camel_store_get_folder_sync (
216 m->tostore, toname->str,
217 CAMEL_STORE_FOLDER_CREATE,
218 cancellable, error);
219 if (tofolder == NULL) {
220 g_object_unref (fromfolder);
221 goto exception;
222 }
223
224 uids = camel_folder_get_uids (fromfolder);
225 camel_folder_transfer_messages_to_sync (
226 fromfolder, uids, tofolder,
227 m->delete, NULL,
228 cancellable, error);
229 camel_folder_free_uids (fromfolder, uids);
230
231 if (m->delete && (!error || !*error))
232 camel_folder_synchronize_sync (
233 fromfolder, TRUE,
234 NULL, NULL);
235
236 g_object_unref (fromfolder);
237 g_object_unref (tofolder);
238 }
239 }
240
241 if (error && *error)
242 goto exception;
243 else if (m->delete && !deleted)
244 deleting = g_list_prepend (deleting, info);
245
246 /* subscribe to the new folder if appropriate */
247 if (CAMEL_IS_SUBSCRIBABLE (m->tostore)
248 && !camel_subscribable_folder_is_subscribed (
249 CAMEL_SUBSCRIBABLE (m->tostore),
250 toname->str))
251 camel_subscribable_subscribe_folder_sync (
252 CAMEL_SUBSCRIBABLE (m->tostore),
253 toname->str, NULL, NULL);
254
255 info = info->next;
256 }
257 }
258
259 /* Delete the folders in reverse order from how we copied them,
260 * if we are deleting any. */
261 l = deleting;
262 while (l) {
263 CamelFolderInfo *info = l->data;
264
265 d (printf ("deleting folder '%s'\n", info->full_name));
266
267 /* FIXME: we need to do something with the exception
268 * since otherwise the users sees a failed operation
269 * with no error message or even any warnings */
270 if (CAMEL_IS_SUBSCRIBABLE (m->fromstore))
271 camel_subscribable_unsubscribe_folder_sync (
272 CAMEL_SUBSCRIBABLE (m->fromstore),
273 info->full_name, NULL, NULL);
274
275 camel_store_delete_folder_sync (
276 m->fromstore, info->full_name, NULL, NULL);
277 l = l->next;
278 }
279
280 exception:
281
282 camel_store_free_folder_info (m->fromstore, fi);
283 g_list_free (deleting);
284
285 g_string_free (toname, TRUE);
286 g_string_free (fromname, TRUE);
287 }
288
289 static void
290 emft_copy_folders__free (struct _EMCopyFolders *m)
291 {
292 g_object_unref (m->fromstore);
293 g_object_unref (m->tostore);
294
295 g_free (m->frombase);
296 g_free (m->tobase);
297 }
298
299 static MailMsgInfo copy_folders_info = {
300 sizeof (struct _EMCopyFolders),
301 (MailMsgDescFunc) emft_copy_folders__desc,
302 (MailMsgExecFunc) emft_copy_folders__exec,
303 (MailMsgDoneFunc) NULL,
304 (MailMsgFreeFunc) emft_copy_folders__free
305 };
306
307 gint
308 em_folder_utils_copy_folders (CamelStore *fromstore,
309 const gchar *frombase,
310 CamelStore *tostore,
311 const gchar *tobase,
312 gint delete)
313 {
314 struct _EMCopyFolders *m;
315 gint seq;
316
317 m = mail_msg_new (©_folders_info);
318 g_object_ref (fromstore);
319 m->fromstore = fromstore;
320 g_object_ref (tostore);
321 m->tostore = tostore;
322 m->frombase = g_strdup (frombase);
323 m->tobase = g_strdup (tobase);
324 m->delete = delete;
325 seq = m->base.seq;
326
327 mail_msg_unordered_push (m);
328
329 return seq;
330 }
331
332 struct _copy_folder_data {
333 CamelStore *source_store;
334 gchar *source_folder_name;
335 gboolean delete;
336 };
337
338 static void
339 emfu_copy_folder_selected (EMailSession *session,
340 EAlertSink *alert_sink,
341 const gchar *uri,
342 gpointer data)
343 {
344 struct _copy_folder_data *cfd = data;
345 CamelStore *tostore = NULL;
346 CamelService *service;
347 gboolean store_is_local;
348 const gchar *uid;
349 gchar *tobase = NULL;
350 GError *local_error = NULL;
351
352 if (uri == NULL)
353 goto fail;
354
355 service = CAMEL_SERVICE (cfd->source_store);
356 camel_service_connect_sync (service, NULL, &local_error);
357
358 if (local_error != NULL) {
359 e_alert_submit (
360 alert_sink, cfd->delete ?
361 "mail:no-move-folder-nostore" :
362 "mail:no-copy-folder-nostore",
363 cfd->source_folder_name, uri,
364 local_error->message, NULL);
365 goto fail;
366 }
367
368 g_return_if_fail (CAMEL_IS_STORE (service));
369
370 uid = camel_service_get_uid (CAMEL_SERVICE (cfd->source_store));
371 store_is_local = (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0);
372
373 if (cfd->delete && store_is_local &&
374 emfu_is_special_local_folder (cfd->source_folder_name)) {
375 e_alert_submit (
376 alert_sink,
377 "mail:no-rename-special-folder",
378 cfd->source_folder_name, NULL);
379 goto fail;
380 }
381
382 if (!e_mail_folder_uri_parse (
383 CAMEL_SESSION (session), uri,
384 &tostore, &tobase, &local_error))
385 tostore = NULL;
386
387 if (tostore != NULL)
388 camel_service_connect_sync (
389 CAMEL_SERVICE (tostore), NULL, &local_error);
390
391 if (local_error != NULL) {
392 e_alert_submit (
393 alert_sink, cfd->delete ?
394 "mail:no-move-folder-to-nostore" :
395 "mail:no-copy-folder-to-nostore",
396 cfd->source_folder_name, uri,
397 local_error->message, NULL);
398 goto fail;
399 }
400
401 g_return_if_fail (CAMEL_IS_STORE (tostore));
402
403 em_folder_utils_copy_folders (
404 cfd->source_store, cfd->source_folder_name,
405 tostore, tobase ? tobase : "", cfd->delete);
406
407 fail:
408 g_clear_error (&local_error);
409
410 g_object_unref (cfd->source_store);
411 g_free (cfd->source_folder_name);
412 g_free (cfd);
413
414 if (tostore)
415 g_object_unref (tostore);
416 g_free (tobase);
417 }
418
419 /* tree here is the 'destination' selector, not 'self' */
420 static gboolean
421 emfu_copy_folder_exclude (EMFolderTree *tree,
422 GtkTreeModel *model,
423 GtkTreeIter *iter,
424 gpointer data)
425 {
426 struct _copy_folder_data *cfd = data;
427 CamelStore *store;
428 const gchar *uid;
429 gint fromvfolder, tovfolder;
430 guint flags;
431
432 /* handles moving to/from vfolders */
433
434 uid = camel_service_get_uid (CAMEL_SERVICE (cfd->source_store));
435 fromvfolder = (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0);
436
437 gtk_tree_model_get (
438 model, iter,
439 COL_UINT_FLAGS, &flags,
440 COL_POINTER_CAMEL_STORE, &store, -1);
441
442 uid = camel_service_get_uid (CAMEL_SERVICE (store));
443 tovfolder = (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0);
444
445 /* moving from vfolder to normal- not allowed */
446 if (fromvfolder && !tovfolder && cfd->delete)
447 return FALSE;
448 /* copy/move from normal folder to vfolder - not allowed */
449 if (!fromvfolder && tovfolder)
450 return FALSE;
451 /* copying to vfolder - not allowed */
452 if (tovfolder && !cfd->delete)
453 return FALSE;
454
455 return (flags & EMFT_EXCLUDE_NOINFERIORS) == 0;
456 }
457
458 void
459 em_folder_utils_copy_folder (GtkWindow *parent,
460 EMailSession *session,
461 EAlertSink *alert_sink,
462 const gchar *folder_uri,
463 gint delete)
464 {
465 GtkWidget *dialog;
466 EMFolderSelector *selector;
467 EMFolderTree *folder_tree;
468 EMFolderTreeModel *model;
469 const gchar *label;
470 const gchar *title;
471 struct _copy_folder_data *cfd;
472 GError *error = NULL;
473
474 g_return_if_fail (E_IS_MAIL_SESSION (session));
475 g_return_if_fail (E_IS_ALERT_SINK (alert_sink));
476 g_return_if_fail (folder_uri != NULL);
477
478 cfd = g_malloc (sizeof (*cfd));
479 cfd->delete = delete;
480
481 e_mail_folder_uri_parse (
482 CAMEL_SESSION (session), folder_uri,
483 &cfd->source_store, &cfd->source_folder_name, &error);
484
485 if (error != NULL) {
486 e_notice (parent, GTK_MESSAGE_ERROR, "%s", error->message);
487 g_error_free (error);
488 g_free (cfd);
489 return;
490 }
491
492 label = delete ? _("_Move") : _("C_opy");
493 title = delete ? _("Move Folder To") : _("Copy Folder To");
494
495 model = em_folder_tree_model_get_default ();
496
497 dialog = em_folder_selector_new (
498 parent, model,
499 EM_FOLDER_SELECTOR_CAN_CREATE,
500 title, NULL, label);
501
502 selector = EM_FOLDER_SELECTOR (dialog);
503 folder_tree = em_folder_selector_get_folder_tree (selector);
504
505 em_folder_tree_set_excluded_func (
506 folder_tree, emfu_copy_folder_exclude, cfd);
507
508 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
509 const gchar *uri;
510
511 uri = em_folder_selector_get_selected_uri (selector);
512 emfu_copy_folder_selected (session, alert_sink, uri, cfd);
513 }
514
515 gtk_widget_destroy (dialog);
516 }
517
518 static void
519 new_folder_created_cb (CamelStore *store,
520 GAsyncResult *result,
521 AsyncContext *context)
522 {
523 GError *error = NULL;
524
525 e_mail_store_create_folder_finish (store, result, &error);
526
527 /* FIXME Use an EActivity here. */
528 if (error != NULL) {
529 e_notice (NULL, GTK_MESSAGE_ERROR, "%s", error->message);
530 g_error_free (error);
531
532 } else if (context->folder_tree != NULL) {
533 gpointer data;
534 gboolean expand_only;
535
536 /* XXX What in the hell kind of lazy hack is this? */
537 data = g_object_get_data (
538 G_OBJECT (context->folder_tree), "select");
539 expand_only = GPOINTER_TO_INT (data) ? FALSE : TRUE;
540
541 em_folder_tree_set_selected (
542 context->folder_tree,
543 context->folder_uri, expand_only);
544 }
545
546 async_context_free (context);
547 }
548
549 void
550 em_folder_utils_create_folder (GtkWindow *parent,
551 EMailSession *session,
552 EMFolderTree *emft,
553 const gchar *initial_uri)
554 {
555 EMFolderSelector *selector;
556 EMFolderTree *folder_tree;
557 EMFolderTreeModel *model;
558 EMailAccountStore *account_store;
559 CamelStore *store = NULL;
560 gchar *folder_name = NULL;
561 GtkWidget *dialog;
562 GQueue queue = G_QUEUE_INIT;
563 GError *error = NULL;
564
565 g_return_if_fail (GTK_IS_WINDOW (parent));
566 g_return_if_fail (E_IS_MAIL_SESSION (session));
567
568 model = em_folder_tree_model_new ();
569 em_folder_tree_model_set_session (model, session);
570
571 account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session));
572 e_mail_account_store_queue_enabled_services (account_store, &queue);
573
574 while (!g_queue_is_empty (&queue)) {
575 CamelService *service;
576 CamelStoreFlags flags;
577
578 service = g_queue_pop_head (&queue);
579 g_warn_if_fail (CAMEL_IS_STORE (service));
580
581 flags = CAMEL_STORE (service)->flags;
582 if ((flags & CAMEL_STORE_CAN_EDIT_FOLDERS) == 0)
583 continue;
584
585 em_folder_tree_model_add_store (model, CAMEL_STORE (service));
586 }
587
588 dialog = em_folder_selector_create_new (
589 parent, model, 0,
590 _("Create Folder"),
591 _("Specify where to create the folder:"));
592
593 g_object_unref (model);
594
595 selector = EM_FOLDER_SELECTOR (dialog);
596 folder_tree = em_folder_selector_get_folder_tree (selector);
597
598 if (initial_uri != NULL)
599 em_folder_tree_set_selected (folder_tree, initial_uri, FALSE);
600
601 if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
602 goto exit;
603
604 if (em_folder_tree_store_root_selected (folder_tree, &store)) {
605 const gchar *folder_uri;
606
607 folder_uri = em_folder_selector_get_selected_uri (selector);
608
609 if (!folder_uri || !strrchr (folder_uri, '/'))
610 g_set_error (
611 &error, CAMEL_FOLDER_ERROR,
612 CAMEL_FOLDER_ERROR_INVALID,
613 _("Invalid folder URI '%s'"),
614 folder_uri ? folder_uri : "null");
615 else
616 folder_name = g_strdup (strrchr (folder_uri, '/'));
617 } else {
618 const gchar *folder_uri;
619
620 folder_uri = em_folder_selector_get_selected_uri (selector);
621
622 e_mail_folder_uri_parse (
623 CAMEL_SESSION (session), folder_uri,
624 &store, &folder_name, &error);
625 }
626
627 /* XXX This is unlikely to fail since the URI comes straight from
628 * EMFolderSelector, but leave a breadcrumb if it does fail. */
629 if (error != NULL) {
630 g_warn_if_fail (store == NULL);
631 g_warn_if_fail (folder_name == NULL);
632 e_notice (parent, GTK_MESSAGE_ERROR, "%s", error->message);
633 g_error_free (error);
634 goto exit;
635 }
636
637 /* HACK: we need to create vfolders using the vfolder editor */
638 if (CAMEL_IS_VEE_STORE (store)) {
639 EFilterRule *rule;
640 const gchar *skip_slash;
641
642 if (*folder_name == '/')
(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)
643 skip_slash = folder_name + 1;
644 else
645 skip_slash = folder_name;
646
647 rule = em_vfolder_editor_rule_new (session);
648 e_filter_rule_set_name (rule, skip_slash);
649 vfolder_gui_add_rule (EM_VFOLDER_RULE (rule));
650 } else {
651 AsyncContext *context;
652
653 context = g_slice_new0 (AsyncContext);
654 context->folder_uri = e_mail_folder_uri_build (store, folder_name);
655
656 if (EM_IS_FOLDER_TREE (emft))
657 context->folder_tree = g_object_ref (emft);
658
659 /* FIXME Not passing a GCancellable. */
660 e_mail_store_create_folder (
661 store, folder_name, G_PRIORITY_DEFAULT, NULL,
662 (GAsyncReadyCallback) new_folder_created_cb,
663 context);
664 }
665
666 g_free (folder_name);
667 g_object_unref (store);
668
669 exit:
670 gtk_widget_destroy (dialog);
671 }
672
673 const gchar *
674 em_folder_utils_get_icon_name (guint32 flags)
675 {
676 const gchar *icon_name;
677
678 switch (flags & CAMEL_FOLDER_TYPE_MASK) {
679 case CAMEL_FOLDER_TYPE_INBOX:
680 icon_name = "mail-inbox";
681 break;
682 case CAMEL_FOLDER_TYPE_OUTBOX:
683 icon_name = "mail-outbox";
684 break;
685 case CAMEL_FOLDER_TYPE_TRASH:
686 icon_name = "user-trash";
687 break;
688 case CAMEL_FOLDER_TYPE_JUNK:
689 icon_name = "mail-mark-junk";
690 break;
691 case CAMEL_FOLDER_TYPE_SENT:
692 icon_name = "mail-sent";
693 break;
694 case CAMEL_FOLDER_TYPE_CONTACTS:
695 icon_name = "x-office-address-book";
696 break;
697 case CAMEL_FOLDER_TYPE_EVENTS:
698 icon_name = "x-office-calendar";
699 break;
700 case CAMEL_FOLDER_TYPE_MEMOS:
701 icon_name = "evolution-memos";
702 break;
703 case CAMEL_FOLDER_TYPE_TASKS:
704 icon_name = "evolution-tasks";
705 break;
706 default:
707 if (flags & CAMEL_FOLDER_SHARED_TO_ME)
708 icon_name = "stock_shared-to-me";
709 else if (flags & CAMEL_FOLDER_SHARED_BY_ME)
710 icon_name = "stock_shared-by-me";
711 else if (flags & CAMEL_FOLDER_VIRTUAL)
712 icon_name = "folder-saved-search";
713 else
714 icon_name = "folder";
715 }
716
717 return icon_name;
718 }