Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
em-folder-properties.c:210:10 | clang-analyzer | Access to field 'g_class' results in a dereference of a null pointer (loaded from field 'folder') | ||
em-folder-properties.c:210:10 | clang-analyzer | Access to field 'g_class' results in a dereference of a null pointer (loaded from field 'folder') |
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 * Michael Zucchi <notzed@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 "em-folder-properties.h"
28
29 #include <string.h>
30
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33
34 #include <libemail-utils/mail-mt.h>
35 #include <libemail-engine/e-mail-folder-utils.h>
36 #include <libemail-engine/mail-ops.h>
37
38 #include "e-mail-backend.h"
39 #include "e-mail-ui-session.h"
40 #include "em-config.h"
41 #include "mail-vfolder-ui.h"
42
43 typedef struct _AsyncContext AsyncContext;
44
45 struct _AsyncContext {
46 EActivity *activity;
47 CamelFolder *folder;
48 GtkWindow *parent_window;
49 CamelFolderQuotaInfo *quota_info;
50 gint total;
51 gint unread;
52 };
53
54 static void
55 async_context_free (AsyncContext *context)
56 {
57 if (context->activity != NULL)
58 g_object_unref (context->activity);
59
60 if (context->folder != NULL)
61 g_object_unref (context->folder);
62
63 if (context->parent_window != NULL)
64 g_object_unref (context->parent_window);
65
66 if (context->quota_info != NULL)
67 camel_folder_quota_info_free (context->quota_info);
68
69 g_slice_free (AsyncContext, context);
70 }
71
72 static void
73 emfp_free (EConfig *ec,
74 GSList *items,
75 gpointer data)
76 {
77 g_slist_free (items);
78 }
79
80 static gint
81 add_numbered_row (GtkTable *table,
82 gint row,
83 const gchar *description,
84 const gchar *format,
85 gint num)
86 {
87 gchar *str;
88 GtkWidget *label;
89
90 g_return_val_if_fail (table != NULL, row);
91 g_return_val_if_fail (description != NULL, row);
92 g_return_val_if_fail (format != NULL, row);
93
94 label = gtk_label_new (description);
95 gtk_widget_show (label);
96 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
97 gtk_table_attach (
98 table, label, 0, 1, row, row + 1,
99 GTK_FILL, 0, 0, 0);
100
101 str = g_strdup_printf (format, num);
102
103 label = gtk_label_new (str);
104 gtk_widget_show (label);
105 gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
106 gtk_table_attach (
107 table, label, 1, 2, row, row + 1,
108 GTK_FILL | GTK_EXPAND, 0, 0, 0);
109
110 g_free (str);
111
112 return row + 1;
113 }
114
115 static GtkWidget *
116 emfp_get_folder_item (EConfig *ec,
117 EConfigItem *item,
118 GtkWidget *parent,
119 GtkWidget *old,
120 gint position,
121 gpointer data)
122 {
123 GObjectClass *class;
124 GParamSpec **properties;
125 GtkWidget *widget, *table;
126 AsyncContext *context = data;
127 guint ii, n_properties;
128 gint row = 0;
129 gboolean can_apply_filters = FALSE;
130
131 if (old)
132 return old;
133
134 table = gtk_table_new (2, 2, FALSE);
135 gtk_table_set_row_spacings ((GtkTable *) table, 6);
136 gtk_table_set_col_spacings ((GtkTable *) table, 12);
137 gtk_widget_show (table);
138 gtk_box_pack_start ((GtkBox *) parent, table, TRUE, TRUE, 0);
139
140 /* To be on the safe side, ngettext is used here,
141 * see e.g. comment #3 at bug 272567 */
142 row = add_numbered_row (
143 GTK_TABLE (table), row,
144 ngettext (
145 "Unread messages:",
146 "Unread messages:",
147 context->unread),
148 "%d", context->unread);
149
150 /* TODO: can this be done in a loop? */
151 /* To be on the safe side, ngettext is used here,
152 * see e.g. comment #3 at bug 272567 */
153 row = add_numbered_row (
154 GTK_TABLE (table), row,
155 ngettext (
156 "Total messages:",
157 "Total messages:",
158 context->total),
159 "%d", context->total);
160
161 if (context->quota_info) {
162 CamelFolderQuotaInfo *info;
163 CamelFolderQuotaInfo *quota = context->quota_info;
164
165 for (info = quota; info; info = info->next) {
166 gchar *descr;
167 gint procs;
168
169 /* should not happen, but anyway... */
170 if (!info->total)
171 continue;
172
173 /* Show quota name only when available and we
174 * have more than one quota info. */
175 if (info->name && quota->next)
176 descr = g_strdup_printf (
177 _("Quota usage (%s):"), _(info->name));
178 else
179 descr = g_strdup_printf (_("Quota usage"));
180
181 procs = (gint) ((((gdouble) info->used) /
182 ((gdouble) info->total)) * 100.0 + 0.5);
183
184 row = add_numbered_row (
185 GTK_TABLE (table), row,
186 descr, "%d%%", procs);
187
188 g_free (descr);
189 }
190 }
191
192 if (context->folder != NULL) {
193 CamelStore *store;
194 CamelSession *session;
195 CamelFolderInfoFlags fi_flags = 0;
196 MailFolderCache *folder_cache;
197
198 store = camel_folder_get_parent_store (context->folder);
199 session = camel_service_get_session (CAMEL_SERVICE (store));
200 folder_cache = e_mail_session_get_folder_cache (
201 E_MAIL_SESSION (session));
202
203 can_apply_filters =
204 !CAMEL_IS_VEE_FOLDER (context->folder) &&
205 mail_folder_cache_get_folder_info_flags (
206 folder_cache, context->folder, &fi_flags) &&
207 (fi_flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_INBOX;
208 }
209
210 class = G_OBJECT_GET_CLASS (context->folder);
(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)
211 properties = g_object_class_list_properties (class, &n_properties);
212
213 for (ii = 0; ii < n_properties; ii++) {
214 const gchar *blurb;
215
216 if ((properties[ii]->flags & CAMEL_PARAM_PERSISTENT) == 0)
217 continue;
218
219 if (!can_apply_filters &&
220 g_strcmp0 (properties[ii]->name, "apply-filters") == 0)
221 continue;
222
223 blurb = g_param_spec_get_blurb (properties[ii]);
224
225 switch (properties[ii]->value_type) {
226 case G_TYPE_BOOLEAN:
227 widget = gtk_check_button_new_with_mnemonic (blurb);
228 g_object_bind_property (
229 context->folder,
230 properties[ii]->name,
231 widget, "active",
232 G_BINDING_BIDIRECTIONAL |
233 G_BINDING_SYNC_CREATE);
234 gtk_widget_show (widget);
235 gtk_table_attach (
236 GTK_TABLE (table), widget,
237 0, 2, row, row + 1,
238 GTK_FILL | GTK_EXPAND, 0, 0, 0);
239 row++;
240 break;
241 default:
242 g_warn_if_reached ();
243 break;
244 }
245 }
246
247 g_free (properties);
248
249 return table;
250 }
251
252 #define EMFP_FOLDER_SECTION (2)
253
254 static EMConfigItem emfp_items[] = {
255 { E_CONFIG_BOOK, (gchar *) "", NULL },
256 { E_CONFIG_PAGE, (gchar *) "00.general",
257 (gchar *) N_("General") },
258 { E_CONFIG_SECTION, (gchar *) "00.general/00.folder",
259 NULL /* set by code */ },
260 { E_CONFIG_ITEM, (gchar *) "00.general/00.folder/00.info",
261 NULL, emfp_get_folder_item },
262 };
263 static gboolean emfp_items_translated = FALSE;
264
265 static void
266 emfp_dialog_run (AsyncContext *context)
267 {
268 GtkWidget *dialog, *w;
269 GtkWidget *content_area;
270 GSList *l;
271 gint32 i,deleted;
272 EMConfig *ec;
273 EMConfigTargetFolder *target;
274 CamelStore *parent_store;
275 CamelFolderSummary *summary;
276 gboolean store_is_local;
277 gboolean hide_deleted;
278 GSettings *settings;
279 const gchar *name;
280 const gchar *uid;
281
282 parent_store = camel_folder_get_parent_store (context->folder);
283
284 /* Get number of VISIBLE and DELETED messages, instead of TOTAL
285 * messages. VISIBLE+DELETED gives the correct count that matches
286 * the label below the Send & Receive button. */
287 summary = context->folder->summary;
288 context->total = camel_folder_summary_get_visible_count (summary);
289 context->unread = camel_folder_summary_get_unread_count (summary);
290 deleted = camel_folder_summary_get_deleted_count (summary);
291
292 settings = g_settings_new ("org.gnome.evolution.mail");
293 hide_deleted = !g_settings_get_boolean (settings, "show-deleted");
294 g_object_unref (settings);
295
296 /*
297 * Do the calculation only for those accounts that support VTRASHes
298 */
299 if (parent_store->flags & CAMEL_STORE_VTRASH) {
300 if (CAMEL_IS_VTRASH_FOLDER (context->folder))
301 context->total += deleted;
302 else if (!hide_deleted && deleted > 0)
303 context->total += deleted;
304 }
305
306 /*
307 * If the folder is junk folder, get total number of mails.
308 */
309 if (parent_store->flags & CAMEL_STORE_VJUNK)
310 context->total = camel_folder_summary_count (
311 context->folder->summary);
312
313 name = camel_folder_get_display_name (context->folder);
314
315 uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
316 store_is_local = (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0);
317
318 if (store_is_local
319 && (!strcmp (name, "Drafts")
320 || !strcmp (name, "Templates")
321 || !strcmp (name, "Inbox")
322 || !strcmp (name, "Outbox")
323 || !strcmp (name, "Sent"))) {
324 emfp_items[EMFP_FOLDER_SECTION].label = gettext (name);
325 if (!emfp_items_translated) {
326 for (i = 0; i < G_N_ELEMENTS (emfp_items); i++) {
327 if (emfp_items[i].label)
328 emfp_items[i].label = _(emfp_items[i].label);
329 }
330 emfp_items_translated = TRUE;
331 }
332 } else if (!strcmp (name, "INBOX"))
333 emfp_items[EMFP_FOLDER_SECTION].label = _("Inbox");
334 else
335 emfp_items[EMFP_FOLDER_SECTION].label = (gchar *) name;
336
337 dialog = gtk_dialog_new_with_buttons (
338 _("Folder Properties"),
339 context->parent_window,
340 GTK_DIALOG_DESTROY_WITH_PARENT,
341 GTK_STOCK_CLOSE, GTK_RESPONSE_OK, NULL);
342 gtk_window_set_default_size ((GtkWindow *) dialog, 192, 160);
343
344 gtk_widget_ensure_style (dialog);
345
346 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
347 gtk_container_set_border_width (GTK_CONTAINER (content_area), 12);
348
349 /** @HookPoint-EMConfig: Folder Properties Window
350 * @Id: org.gnome.evolution.mail.folderConfig
351 * @Type: E_CONFIG_BOOK
352 * @Class: org.gnome.evolution.mail.config:1.0
353 * @Target: EMConfigTargetFolder
354 *
355 * The folder properties window.
356 */
357 ec = em_config_new (
358 E_CONFIG_BOOK, "org.gnome.evolution.mail.folderConfig");
359 l = NULL;
360 for (i = 0; i < G_N_ELEMENTS (emfp_items); i++)
361 l = g_slist_prepend (l, &emfp_items[i]);
362 e_config_add_items ((EConfig *) ec, l, emfp_free, context);
363
364 target = em_config_target_new_folder (ec, context->folder);
365
366 e_config_set_target ((EConfig *) ec, (EConfigTarget *) target);
367 w = e_config_create_widget ((EConfig *) ec);
368
369 gtk_box_pack_start (GTK_BOX (content_area), w, TRUE, TRUE, 0);
370
371 /* We do 'apply on ok', since instant apply may start some
372 * very long running tasks. */
373
374 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
375 e_config_commit ((EConfig *) ec);
376 camel_object_state_write (CAMEL_OBJECT (context->folder));
377 } else
378 e_config_abort ((EConfig *) ec);
379
380 gtk_widget_destroy (dialog);
381 }
382
383 static void
384 emfp_dialog_got_quota_info (CamelFolder *folder,
385 GAsyncResult *result,
386 AsyncContext *context)
387 {
388 EAlertSink *alert_sink;
389 GError *error = NULL;
390
391 alert_sink = e_activity_get_alert_sink (context->activity);
392
393 context->quota_info =
394 camel_folder_get_quota_info_finish (folder, result, &error);
395
396 /* If the folder does not implement quota info, just continue. */
397 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
398 g_warn_if_fail (context->quota_info == NULL);
399 g_error_free (error);
400
401 } else if (e_activity_handle_cancellation (context->activity, error)) {
402 g_warn_if_fail (context->quota_info == NULL);
403 async_context_free (context);
404 g_error_free (error);
405 return;
406
407 /* FIXME Add an EAlert for failing to get quota info. */
408 } else if (error != NULL) {
409 g_warn_if_fail (context->folder == NULL);
410 e_alert_submit (
411 alert_sink, "mail:folder-open",
412 error->message, NULL);
413 async_context_free (context);
414 g_error_free (error);
415 return;
416 }
417
418 /* Quota info may still be NULL here if not supported. */
419
420 /* Finalize the activity here so we don't leave a message
421 * in the task bar while the properties window is shown. */
422 e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
423 g_object_unref (context->activity);
424 context->activity = NULL;
425
426 emfp_dialog_run (context);
427
428 async_context_free (context);
429 }
430
431 static void
432 emfp_dialog_got_folder (CamelStore *store,
433 GAsyncResult *result,
434 AsyncContext *context)
435 {
436 EAlertSink *alert_sink;
437 GCancellable *cancellable;
438 GError *error = NULL;
439
440 alert_sink = e_activity_get_alert_sink (context->activity);
441 cancellable = e_activity_get_cancellable (context->activity);
442
443 context->folder = camel_store_get_folder_finish (
444 store, result, &error);
445
446 if (e_activity_handle_cancellation (context->activity, error)) {
447 g_warn_if_fail (context->folder == NULL);
448 async_context_free (context);
449 g_error_free (error);
450 return;
451
452 } else if (error != NULL) {
453 g_warn_if_fail (context->folder == NULL);
454 e_alert_submit (
455 alert_sink, "mail:folder-open",
456 error->message, NULL);
457 async_context_free (context);
458 g_error_free (error);
459 return;
460 }
461
462 g_return_if_fail (CAMEL_IS_FOLDER (context->folder));
463
464 camel_folder_get_quota_info (
465 context->folder, G_PRIORITY_DEFAULT, cancellable,
466 (GAsyncReadyCallback) emfp_dialog_got_quota_info, context);
467 }
468
469 /**
470 * em_folder_properties_show:
471 * @store: a #CamelStore
472 * @folder_name: a folder name
473 * @alert_sink: an #EAlertSink
474 * @parent_window: a parent #GtkWindow
475 *
476 * Show folder properties for @folder_name.
477 **/
478 void
479 em_folder_properties_show (CamelStore *store,
480 const gchar *folder_name,
481 EAlertSink *alert_sink,
482 GtkWindow *parent_window)
483 {
484 CamelService *service;
485 CamelSession *session;
486 GCancellable *cancellable;
487 AsyncContext *context;
488 const gchar *uid;
489
490 g_return_if_fail (CAMEL_IS_STORE (store));
491 g_return_if_fail (folder_name != NULL);
492 g_return_if_fail (E_IS_ALERT_SINK (alert_sink));
493 g_return_if_fail (GTK_IS_WINDOW (parent_window));
494
495 service = CAMEL_SERVICE (store);
496 uid = camel_service_get_uid (service);
497 session = camel_service_get_session (service);
498
499 /* Show the Edit Rule dialog for Search Folders, but not "Unmatched".
500 * "Unmatched" is a special Search Folder which can't be modified. */
501 if (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0) {
502 if (g_strcmp0 (folder_name, CAMEL_UNMATCHED_NAME) != 0) {
503 gchar *folder_uri;
504
505 folder_uri = e_mail_folder_uri_build (
506 store, folder_name);
507 vfolder_edit_rule (
508 E_MAIL_SESSION (session),
509 folder_uri, alert_sink);
510 g_free (folder_uri);
511 return;
512 }
513 }
514
515 /* Open the folder asynchronously. */
516
517 context = g_slice_new0 (AsyncContext);
518 context->activity = e_activity_new ();
519 context->parent_window = g_object_ref (parent_window);
520
521 e_activity_set_alert_sink (context->activity, alert_sink);
522
523 cancellable = camel_operation_new ();
524 e_activity_set_cancellable (context->activity, cancellable);
525
526 e_mail_ui_session_add_activity (
527 E_MAIL_UI_SESSION (session), context->activity);
528
529 camel_store_get_folder (
530 store, folder_name, 0, G_PRIORITY_DEFAULT, cancellable,
531 (GAsyncReadyCallback) emfp_dialog_got_folder, context);
532
533 g_object_unref (cancellable);
534 }