1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * SPDX-FileCopyrightText: (C) 2012 Red Hat, Inc. (www.redhat.com)
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  */
6 
7 #include "evolution-ews-config.h"
8 
9 #include <string.h>
10 #include <unistd.h>
11 #include <glib/gi18n-lib.h>
12 
13 #include <gtk/gtk.h>
14 #include <libedataserver/libedataserver.h>
15 #include <libedataserverui/libedataserverui.h>
16 
17 #include <e-util/e-util.h>
18 #include <mail/em-folder-tree.h>
19 #include <mail/em-folder-utils.h>
20 #include <libemail-engine/libemail-engine.h>
21 #include <shell/e-shell.h>
22 #include <shell/e-shell-sidebar.h>
23 #include <shell/e-shell-view.h>
24 #include <shell/e-shell-window.h>
25 
26 #include "common/e-ews-connection.h"
27 #include "common/e-ews-connection-utils.h"
28 #include "common/e-source-ews-folder.h"
29 
30 #include "e-ews-edit-folder-permissions.h"
31 
32 #include "camel/camel-ews-store.h"
33 #include "camel/camel-ews-store-summary.h"
34 
35 #include "e-ews-config-utils.h"
36 #include "e-ews-search-user.h"
37 #include "e-ews-subscribe-foreign-folder.h"
38 
39 struct RunWithFeedbackData
40 {
41 	GtkWindow *parent;
42 	GtkWidget *dialog;
43 	GCancellable *cancellable;
44 	GObject *with_object;
45 	EEwsSetupFunc thread_func;
46 	EEwsSetupFunc idle_func;
47 	EEwsSetupFunc finish_idle_func;
48 	gpointer user_data;
49 	GDestroyNotify free_user_data;
50 	GError *error;
51 	gboolean run_modal;
52 };
53 
54 static void
free_run_with_feedback_data(gpointer ptr)55 free_run_with_feedback_data (gpointer ptr)
56 {
57 	struct RunWithFeedbackData *rfd = ptr;
58 
59 	if (!rfd)
60 		return;
61 
62 	if (rfd->dialog)
63 		gtk_widget_destroy (rfd->dialog);
64 
65 	g_object_unref (rfd->cancellable);
66 	g_object_unref (rfd->with_object);
67 
68 	if (rfd->free_user_data)
69 		rfd->free_user_data (rfd->user_data);
70 
71 	g_clear_error (&rfd->error);
72 
73 	g_slice_free (struct RunWithFeedbackData, rfd);
74 }
75 
76 static gboolean
run_with_feedback_idle(gpointer user_data)77 run_with_feedback_idle (gpointer user_data)
78 {
79 	struct RunWithFeedbackData *rfd = user_data;
80 	gboolean was_cancelled = FALSE;
81 
82 	g_return_val_if_fail (rfd != NULL, FALSE);
83 
84 	if (!g_cancellable_is_cancelled (rfd->cancellable)) {
85 		if (rfd->idle_func && !rfd->error)
86 			rfd->idle_func (rfd->with_object, rfd->user_data, rfd->cancellable, &rfd->error);
87 
88 		was_cancelled = g_cancellable_is_cancelled (rfd->cancellable);
89 
90 		if (rfd->dialog) {
91 			gtk_widget_destroy (rfd->dialog);
92 			rfd->dialog = NULL;
93 		}
94 	} else {
95 		was_cancelled = TRUE;
96 	}
97 
98 	if (rfd->finish_idle_func)
99 		rfd->finish_idle_func (rfd->with_object, rfd->user_data, rfd->cancellable, &rfd->error);
100 
101 	if (!was_cancelled) {
102 		if (rfd->error) {
103 			g_dbus_error_strip_remote_error (rfd->error);
104 
105 			e_notice (rfd->parent, GTK_MESSAGE_ERROR, "%s", rfd->error->message);
106 		}
107 	}
108 
109 	free_run_with_feedback_data (rfd);
110 
111 	return FALSE;
112 }
113 
114 static gpointer
run_with_feedback_thread(gpointer user_data)115 run_with_feedback_thread (gpointer user_data)
116 {
117 	struct RunWithFeedbackData *rfd = user_data;
118 
119 	g_return_val_if_fail (rfd != NULL, NULL);
120 	g_return_val_if_fail (rfd->thread_func != NULL, NULL);
121 
122 	if (!g_cancellable_is_cancelled (rfd->cancellable))
123 		rfd->thread_func (rfd->with_object, rfd->user_data, rfd->cancellable, &rfd->error);
124 
125 	g_idle_add (run_with_feedback_idle, rfd);
126 
127 	return NULL;
128 }
129 
130 static void
run_with_feedback_response_cb(GtkWidget * dialog,gint resonse_id,struct RunWithFeedbackData * rfd)131 run_with_feedback_response_cb (GtkWidget *dialog,
132                                gint resonse_id,
133                                struct RunWithFeedbackData *rfd)
134 {
135 	g_return_if_fail (rfd != NULL);
136 
137 	rfd->dialog = NULL;
138 
139 	g_cancellable_cancel (rfd->cancellable);
140 
141 	gtk_widget_destroy (dialog);
142 }
143 
144 static void
e_ews_config_utils_run_in_thread_with_feedback_general(GtkWindow * parent,GObject * with_object,const gchar * description,EEwsSetupFunc thread_func,EEwsSetupFunc idle_func,gpointer user_data,GDestroyNotify free_user_data,gboolean run_modal)145 e_ews_config_utils_run_in_thread_with_feedback_general (GtkWindow *parent,
146                                                         GObject *with_object,
147                                                         const gchar *description,
148                                                         EEwsSetupFunc thread_func,
149                                                         EEwsSetupFunc idle_func,
150                                                         gpointer user_data,
151                                                         GDestroyNotify free_user_data,
152                                                         gboolean run_modal)
153 {
154 	GtkWidget *dialog, *label, *content, *spinner, *box;
155 	struct RunWithFeedbackData *rfd;
156 
157 	g_return_if_fail (with_object != NULL);
158 	g_return_if_fail (description != NULL);
159 	g_return_if_fail (thread_func != NULL);
160 
161 	dialog = gtk_dialog_new_with_buttons (
162 		"",
163 		parent,
164 		GTK_DIALOG_MODAL,
165 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
166 		NULL);
167 
168 	box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
169 
170 	spinner = e_spinner_new ();
171 	e_spinner_start (E_SPINNER (spinner));
172 	gtk_box_pack_start (GTK_BOX (box), spinner, FALSE, FALSE, 0);
173 
174 	label = gtk_label_new (description);
175 	gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
176 
177 	gtk_widget_show_all (box);
178 
179 	content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
180 
181 	gtk_container_add (GTK_CONTAINER (content), box);
182 	gtk_container_set_border_width (GTK_CONTAINER (content), 12);
183 
184 	rfd = g_slice_new0 (struct RunWithFeedbackData);
185 	rfd->parent = parent;
186 	rfd->dialog = dialog;
187 	rfd->cancellable = g_cancellable_new ();
188 	rfd->with_object = g_object_ref (with_object);
189 	rfd->thread_func = thread_func;
190 	rfd->idle_func = idle_func;
191 	rfd->finish_idle_func = NULL;
192 	rfd->user_data = user_data;
193 	rfd->free_user_data = free_user_data;
194 	rfd->error = NULL;
195 	rfd->run_modal = run_modal;
196 
197 	g_signal_connect (dialog, "response", G_CALLBACK (run_with_feedback_response_cb), rfd);
198 
199 	if (run_modal) {
200 		GThread *thread;
201 		GCancellable *cancellable;
202 
203 		cancellable = g_object_ref (rfd->cancellable);
204 
205 		thread = g_thread_new (NULL, run_with_feedback_thread, rfd);
206 		g_thread_unref (thread);
207 
208 		gtk_dialog_run (GTK_DIALOG (dialog));
209 
210 		g_cancellable_cancel (cancellable);
211 		g_object_unref (cancellable);
212 	} else {
213 		GThread *thread;
214 
215 		gtk_widget_show (dialog);
216 
217 		thread = g_thread_new (NULL, run_with_feedback_thread, rfd);
218 		g_thread_unref (thread);
219 	}
220 }
221 
222 void
e_ews_config_utils_run_in_thread_with_feedback(GtkWindow * parent,GObject * with_object,const gchar * description,EEwsSetupFunc thread_func,EEwsSetupFunc idle_func,gpointer user_data,GDestroyNotify free_user_data)223 e_ews_config_utils_run_in_thread_with_feedback (GtkWindow *parent,
224                                                 GObject *with_object,
225                                                 const gchar *description,
226                                                 EEwsSetupFunc thread_func,
227                                                 EEwsSetupFunc idle_func,
228                                                 gpointer user_data,
229                                                 GDestroyNotify free_user_data)
230 {
231 	e_ews_config_utils_run_in_thread_with_feedback_general (parent, with_object, description, thread_func, idle_func, user_data, free_user_data, FALSE);
232 }
233 
234 void
e_ews_config_utils_run_in_thread_with_feedback_modal(GtkWindow * parent,GObject * with_object,const gchar * description,EEwsSetupFunc thread_func,EEwsSetupFunc idle_func,gpointer user_data,GDestroyNotify free_user_data)235 e_ews_config_utils_run_in_thread_with_feedback_modal (GtkWindow *parent,
236                                                       GObject *with_object,
237                                                       const gchar *description,
238                                                       EEwsSetupFunc thread_func,
239                                                       EEwsSetupFunc idle_func,
240                                                       gpointer user_data,
241                                                       GDestroyNotify free_user_data)
242 {
243 	e_ews_config_utils_run_in_thread_with_feedback_general (parent, with_object, description, thread_func, idle_func, user_data, free_user_data, TRUE);
244 }
245 
246 void
e_ews_config_utils_run_in_thread(GObject * with_object,EEwsSetupFunc thread_func,EEwsSetupFunc idle_func,gpointer user_data,GDestroyNotify free_user_data,GCancellable * cancellable)247 e_ews_config_utils_run_in_thread (GObject *with_object,
248 				  EEwsSetupFunc thread_func,
249 				  EEwsSetupFunc idle_func,
250 				  gpointer user_data,
251 				  GDestroyNotify free_user_data,
252 				  GCancellable *cancellable)
253 {
254 	struct RunWithFeedbackData *rfd;
255 	GThread *thread;
256 
257 	g_return_if_fail (with_object != NULL);
258 	g_return_if_fail (thread_func != NULL);
259 
260 	rfd = g_slice_new0 (struct RunWithFeedbackData);
261 	rfd->parent = NULL;
262 	rfd->dialog = NULL;
263 	rfd->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
264 	rfd->with_object = g_object_ref (with_object);
265 	rfd->thread_func = thread_func;
266 	rfd->idle_func = NULL;
267 	rfd->finish_idle_func = idle_func;
268 	rfd->user_data = user_data;
269 	rfd->free_user_data = free_user_data;
270 	rfd->error = NULL;
271 	rfd->run_modal = FALSE;
272 
273 	thread = g_thread_new (NULL, run_with_feedback_thread, rfd);
274 	g_thread_unref (thread);
275 }
276 
277 typedef struct _TryCredentialsData {
278 	CamelEwsSettings *ews_settings;
279 	const gchar *connect_url;
280 	EEwsConfigUtilTryCredentialsFunc try_credentials_func;
281 	gpointer user_data;
282 	EEwsConnection *conn;
283 } TryCredentialsData;
284 
285 static gboolean
ews_config_utils_try_credentials_sync(ECredentialsPrompter * prompter,ESource * source,const ENamedParameters * credentials,gboolean * out_authenticated,gpointer user_data,GCancellable * cancellable,GError ** error)286 ews_config_utils_try_credentials_sync (ECredentialsPrompter *prompter,
287 				       ESource *source,
288 				       const ENamedParameters *credentials,
289 				       gboolean *out_authenticated,
290 				       gpointer user_data,
291 				       GCancellable *cancellable,
292 				       GError **error)
293 {
294 	TryCredentialsData *data = user_data;
295 	ESourceAuthenticationResult auth_result;
296 	gchar *hosturl;
297 	gboolean res = TRUE;
298 
299 	hosturl = camel_ews_settings_dup_hosturl (data->ews_settings);
300 	data->conn = e_ews_connection_new (source, data->connect_url ? data->connect_url : hosturl, data->ews_settings);
301 	g_free (hosturl);
302 
303 	e_ews_connection_update_credentials (data->conn, credentials);
304 
305 	if (data->try_credentials_func)
306 		auth_result = data->try_credentials_func (data->conn, credentials, data->user_data, cancellable, error);
307 	else
308 		auth_result = e_ews_connection_try_credentials_sync (data->conn, credentials, NULL, NULL, NULL, cancellable, error);
309 
310 	if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
311 		*out_authenticated = TRUE;
312 	} else if (auth_result == E_SOURCE_AUTHENTICATION_REJECTED) {
313 		*out_authenticated = FALSE;
314 		g_clear_object (&data->conn);
315 		g_clear_error (error);
316 	} else {
317 		res = FALSE;
318 		g_clear_object (&data->conn);
319 	}
320 
321 	return res;
322 }
323 
324 EEwsConnection	*
e_ews_config_utils_open_connection_for(ESource * source,CamelEwsSettings * ews_settings,const gchar * connect_url,EEwsConfigUtilTryCredentialsFunc try_credentials_func,gpointer user_data,GCancellable * cancellable,GError ** perror)325 e_ews_config_utils_open_connection_for (ESource *source,
326                                         CamelEwsSettings *ews_settings,
327 					const gchar *connect_url,
328 					EEwsConfigUtilTryCredentialsFunc try_credentials_func,
329 					gpointer user_data,
330                                         GCancellable *cancellable,
331                                         GError **perror)
332 {
333 	EEwsConnection *conn = NULL;
334 	CamelNetworkSettings *network_settings;
335 	GError *local_error = NULL;
336 
337 	g_return_val_if_fail (source != NULL, NULL);
338 	g_return_val_if_fail (ews_settings != NULL, NULL);
339 
340 	network_settings = CAMEL_NETWORK_SETTINGS (ews_settings);
341 
342 	/* use the one from mailer, if there, otherwise open new */
343 	conn = e_ews_connection_find (
344 		connect_url && *connect_url ? connect_url : camel_ews_settings_get_hosturl (ews_settings),
345 		camel_network_settings_get_user (network_settings));
346 	if (conn) {
347 		if (try_credentials_func &&
348 		    try_credentials_func (conn, NULL, user_data, cancellable, perror) != E_SOURCE_AUTHENTICATION_ACCEPTED) {
349 			g_clear_object (&conn);
350 		}
351 		return conn;
352 	}
353 
354 	while (!conn && !g_cancellable_is_cancelled (cancellable) && !local_error) {
355 		if (e_ews_connection_utils_get_without_password (ews_settings)) {
356 			ESourceAuthenticationResult result;
357 			gchar *hosturl;
358 
359 			hosturl = camel_ews_settings_dup_hosturl (ews_settings);
360 			conn = e_ews_connection_new (source, connect_url && *connect_url ? connect_url : hosturl, ews_settings);
361 			g_free (hosturl);
362 
363 			e_ews_connection_update_credentials (conn, NULL);
364 
365 			if (try_credentials_func)
366 				result = try_credentials_func (conn, NULL, user_data, cancellable, &local_error);
367 			else
368 				result = e_ews_connection_try_credentials_sync (conn, NULL, NULL, NULL, NULL, cancellable, &local_error);
369 
370 			if (result != E_SOURCE_AUTHENTICATION_ACCEPTED) {
371 				g_clear_object (&conn);
372 				if (result != E_SOURCE_AUTHENTICATION_REJECTED || local_error)
373 					break;
374 			}
375 		}
376 
377 		if (!conn) {
378 			EShell *shell;
379 			TryCredentialsData data;
380 
381 			e_ews_connection_utils_force_off_ntlm_auth_check ();
382 			g_clear_error (&local_error);
383 
384 			shell = e_shell_get_default ();
385 
386 			data.ews_settings = g_object_ref (ews_settings);
387 			data.connect_url = connect_url && *connect_url ? connect_url : NULL;
388 			data.try_credentials_func = try_credentials_func;
389 			data.user_data = user_data;
390 			data.conn = NULL;
391 
392 			e_credentials_prompter_loop_prompt_sync (e_shell_get_credentials_prompter (shell),
393 				source, E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE,
394 				ews_config_utils_try_credentials_sync, &data, cancellable, &local_error);
395 
396 			if (data.conn)
397 				conn = g_object_ref (data.conn);
398 
399 			g_clear_object (&data.ews_settings);
400 			g_clear_object (&data.conn);
401 		}
402 	}
403 
404 	if (local_error)
405 		g_propagate_error (perror, local_error);
406 
407 	return conn;
408 }
409 
410 enum {
411 	COL_FOLDER_ICON = 0,	/* G_TYPE_STRING */
412 	COL_FOLDER_NAME,	/* G_TYPE_STRING */
413 	COL_FOLDER_SIZE,	/* G_TYPE_STRING */
414 	COL_FOLDER_FLAGS,	/* G_TYPE_UINT */
415 	N_COLUMNS
416 };
417 
418 typedef struct
419 {
420 	GtkDialog *dialog;
421 	GtkGrid *spinner_grid;
422 
423 	ESourceRegistry *registry;
424 	ESource *source;
425 	CamelEwsSettings *ews_settings;
426 	CamelEwsStore *ews_store;
427 
428 	GHashTable *folder_sizes;
429 	GCancellable *cancellable;
430 	GError *error;
431 } FolderSizeDialogData;
432 
433 static gint
folder_tree_model_sort(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer unused)434 folder_tree_model_sort (GtkTreeModel *model,
435 			GtkTreeIter *a,
436 			GtkTreeIter *b,
437 			gpointer unused)
438 {
439 	gchar *aname, *bname;
440 	guint32 aflags, bflags;
441 	gint ret = -2;
442 
443 	gtk_tree_model_get (
444 		model, a,
445 		COL_FOLDER_NAME, &aname,
446 		COL_FOLDER_FLAGS, &aflags,
447 		-1);
448 
449 	gtk_tree_model_get (
450 		model, b,
451 		COL_FOLDER_NAME, &bname,
452 		COL_FOLDER_FLAGS, &bflags,
453 		-1);
454 
455 	/* Inbox is always first. */
456 	if ((aflags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX)
457 		ret = -1;
458 	else if ((bflags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX)
459 		ret = 1;
460 	else {
461 		if (aname != NULL && bname != NULL)
462 			ret = g_utf8_collate (aname, bname);
463 		else if (aname == bname)
464 			ret = 0;
465 		else if (aname == NULL)
466 			ret = -1;
467 		else
468 			ret = 1;
469 	}
470 
471 	g_free (aname);
472 	g_free (bname);
473 
474 	return ret;
475 }
476 
477 static void
folder_sizes_tree_populate(GtkTreeStore * store,CamelFolderInfo * folder_info,GtkTreeIter * parent,FolderSizeDialogData * fsd)478 folder_sizes_tree_populate (GtkTreeStore *store,
479 			    CamelFolderInfo *folder_info,
480 			    GtkTreeIter *parent,
481 			    FolderSizeDialogData *fsd)
482 {
483 	while (folder_info != NULL) {
484 		GtkTreeIter iter;
485 		const gchar *icon_name;
486 		const gchar *folder_size;
487 
488 		icon_name = em_folder_utils_get_icon_name (folder_info->flags);
489 		if (g_strcmp0 (icon_name, "folder") == 0) {
490 			CamelFolder *folder;
491 
492 			folder = camel_store_get_folder_sync (
493 				CAMEL_STORE (fsd->ews_store), folder_info->full_name, 0, NULL, NULL);
494 
495 			if (folder != NULL) {
496 				if (em_utils_folder_is_drafts (fsd->registry, folder))
497 					icon_name = "accessories-text-editor";
498 
499 				g_object_unref (folder);
500 			}
501 		}
502 
503 		folder_size = g_hash_table_lookup (fsd->folder_sizes, folder_info->full_name);
504 
505 		gtk_tree_store_append (store, &iter, parent);
506 		gtk_tree_store_set (store, &iter,
507 				COL_FOLDER_ICON, icon_name,
508 				COL_FOLDER_NAME, folder_info->display_name,
509 				COL_FOLDER_SIZE, folder_size,
510 				COL_FOLDER_FLAGS, folder_info->flags,
511 				-1);
512 
513 		if (folder_info->child != NULL)
514 			folder_sizes_tree_populate (store, folder_info->child, &iter, fsd);
515 
516 		folder_info = folder_info->next;
517 	}
518 }
519 
520 static gboolean
ews_settings_get_folder_sizes_idle(gpointer user_data)521 ews_settings_get_folder_sizes_idle (gpointer user_data)
522 {
523 	GtkWidget *widget;
524 	GtkCellRenderer *renderer;
525 	GtkTreeStore *tree_store;
526 	GtkBox *content_area;
527 	FolderSizeDialogData *fsd = user_data;
528 	CamelFolderInfo *root;
529 
530 	g_return_val_if_fail (fsd != NULL, FALSE);
531 
532 	if (g_cancellable_is_cancelled (fsd->cancellable))
533 		goto cleanup;
534 
535 	/* Hide progress bar. Set status */
536 	gtk_widget_destroy (GTK_WIDGET (fsd->spinner_grid));
537 
538 	if (fsd->folder_sizes != NULL) {
539 		GtkWidget *scrolledwindow, *tree_view;
540 
541 		scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
542 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
543 				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
544 		gtk_widget_show (scrolledwindow);
545 
546 		/* Tree View */
547 		tree_view =  gtk_tree_view_new ();
548 		renderer = gtk_cell_renderer_pixbuf_new ();
549 		gtk_tree_view_insert_column_with_attributes (
550 			GTK_TREE_VIEW (tree_view),
551 			-1,
552 			NULL,
553 			renderer,
554 			"icon-name",
555 			COL_FOLDER_ICON,
556 			NULL);
557 
558 		renderer = gtk_cell_renderer_text_new ();
559 		gtk_tree_view_insert_column_with_attributes (
560 			GTK_TREE_VIEW (tree_view),
561 			-1,
562 			_("Folder"),
563 			renderer,
564 			"text",
565 			COL_FOLDER_NAME,
566 			NULL);
567 
568 		renderer = gtk_cell_renderer_text_new ();
569 		gtk_tree_view_insert_column_with_attributes (
570 			GTK_TREE_VIEW (tree_view),
571 			-1,
572 			_("Size"),
573 			renderer,
574 			"text",
575 			COL_FOLDER_SIZE,
576 			NULL);
577 
578 		/* Model for TreeView */
579 		tree_store = gtk_tree_store_new (
580 			N_COLUMNS,
581 			/* COL_FOLDER_ICON */ G_TYPE_STRING,
582 			/* COL_FOLDER_NAME */ G_TYPE_STRING,
583 			/* COL_FOLDER_SIZE */ G_TYPE_STRING,
584 			/* COL_FOLDER_FLAGS */ G_TYPE_UINT);
585 
586 		gtk_tree_sortable_set_default_sort_func (
587 			GTK_TREE_SORTABLE (tree_store),
588 			folder_tree_model_sort, NULL, NULL);
589 
590 		gtk_tree_sortable_set_sort_column_id (
591 			GTK_TREE_SORTABLE (tree_store),
592 			GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
593 			GTK_SORT_ASCENDING);
594 
595 		gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (tree_store));
596 
597 		root = camel_store_get_folder_info_sync (
598 			CAMEL_STORE (fsd->ews_store), NULL,
599 			CAMEL_STORE_FOLDER_INFO_RECURSIVE,
600 			NULL, NULL);
601 
602 		folder_sizes_tree_populate (tree_store, root, NULL, fsd);
603 
604 		camel_folder_info_free (root);
605 
606 		gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
607 		gtk_container_add (GTK_CONTAINER (scrolledwindow), tree_view);
608 		widget = scrolledwindow;
609 	} else if (fsd->error) {
610 		gchar *msg = g_strconcat (_("Unable to retrieve folder size information"), "\n",
611 				fsd->error->message, NULL);
612 		widget = gtk_label_new (msg);
613 		g_free (msg);
614 	} else {
615 		widget = gtk_label_new (_("Unable to retrieve folder size information"));
616 	}
617 
618 	gtk_widget_show_all (widget);
619 
620 	/* Pack into content_area */
621 	content_area = GTK_BOX (gtk_dialog_get_content_area (fsd->dialog));
622 	gtk_box_pack_start (content_area, widget, TRUE, TRUE, 6);
623 
624 cleanup:
625 	g_hash_table_destroy (fsd->folder_sizes);
626 	g_object_unref (fsd->registry);
627 	g_object_unref (fsd->source);
628 	g_object_unref (fsd->ews_settings);
629 	g_object_unref (fsd->ews_store);
630 	g_object_unref (fsd->cancellable);
631 	g_clear_error (&fsd->error);
632 	g_slice_free (FolderSizeDialogData, fsd);
633 
634 	return FALSE;
635 }
636 
637 static gpointer
ews_settings_get_folder_sizes_thread(gpointer user_data)638 ews_settings_get_folder_sizes_thread (gpointer user_data)
639 {
640 	FolderSizeDialogData *fsd = user_data;
641 	EEwsConnection *cnc;
642 
643 	g_return_val_if_fail (fsd != NULL, NULL);
644 
645 	cnc = e_ews_config_utils_open_connection_for (
646 			fsd->source,
647 			fsd->ews_settings,
648 			NULL, NULL, NULL,
649 			fsd->cancellable,
650 			&fsd->error);
651 
652 	if (cnc) {
653 		EEwsAdditionalProps *add_props;
654 		EEwsExtendedFieldURI *ext_uri;
655 		GSList *ids, *l, *folders_ids = NULL, *folders_list = NULL;
656 
657 		fsd->folder_sizes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
658 
659 		/* Use MAPI property to retrieve folder size */
660 		add_props = e_ews_additional_props_new ();
661 		ext_uri = e_ews_extended_field_uri_new ();
662 		ext_uri->prop_tag = g_strdup_printf ("%d", 0x0e08); /* Folder size property tag */
663 		ext_uri->prop_type = g_strdup ("Integer");
664 		add_props->extended_furis = g_slist_prepend (add_props->extended_furis, ext_uri);
665 
666 		ids = camel_ews_store_summary_get_folders (fsd->ews_store->summary, NULL, FALSE);
667 		for (l = ids; l != NULL; l = l->next) {
668 			EwsFolderId *fid;
669 			fid = e_ews_folder_id_new (l->data, NULL, FALSE);
670 			folders_ids = g_slist_prepend (folders_ids, fid);
671 		}
672 		folders_ids = g_slist_reverse (folders_ids);
673 
674 		e_ews_connection_get_folder_sync (
675 				cnc, EWS_PRIORITY_MEDIUM, "Default",
676 				add_props, folders_ids, &folders_list,
677 				fsd->cancellable, &fsd->error);
678 
679 		for (l = folders_list; l != NULL; l = l->next) {
680 			const EEwsFolder *folder = l->data;
681 			const EwsFolderId *folder_id;
682 			gchar *folder_full_name;
683 			gchar *folder_size;
684 
685 			if (!folder || e_ews_folder_is_error (folder))
686 				continue;
687 
688 			folder_id = e_ews_folder_get_id (folder);
689 			if (!folder_id)
690 				continue;
691 
692 			folder_full_name = camel_ews_store_summary_get_folder_full_name (
693 				fsd->ews_store->summary, folder_id->id, NULL);
694 			folder_size = g_format_size (e_ews_folder_get_size (folder));
695 
696 			g_hash_table_insert (fsd->folder_sizes, folder_full_name, folder_size);
697 		}
698 
699 		g_slist_free_full (folders_list, g_object_unref);
700 		g_slist_free_full (folders_ids, (GDestroyNotify) e_ews_folder_id_free);
701 		g_slist_free_full (ids, g_free);
702 		e_ews_additional_props_free (add_props);
703 		g_object_unref (cnc);
704 	}
705 
706 	g_idle_add (ews_settings_get_folder_sizes_idle, fsd);
707 
708 	return NULL;
709 }
710 
711 static void
folder_sizes_dialog_response_cb(GObject * dialog,gint response_id,gpointer data)712 folder_sizes_dialog_response_cb (GObject *dialog,
713 				 gint response_id,
714 				 gpointer data)
715 {
716 	GCancellable *cancellable = data;
717 
718 	g_cancellable_cancel (cancellable);
719 	g_object_unref (cancellable);
720 
721 	gtk_widget_destroy (GTK_WIDGET (dialog));
722 }
723 
724 void
e_ews_config_utils_run_folder_sizes_dialog(GtkWindow * parent,ESourceRegistry * registry,ESource * source,CamelEwsStore * ews_store)725 e_ews_config_utils_run_folder_sizes_dialog (GtkWindow *parent,
726 					    ESourceRegistry *registry,
727 					    ESource *source,
728 					    CamelEwsStore *ews_store)
729 {
730 	GtkBox *content_area;
731 	GtkWidget *spinner, *alignment, *dialog;
732 	GtkWidget *spinner_label;
733 	GCancellable *cancellable;
734 	GThread *thread;
735 	FolderSizeDialogData *fsd;
736 
737 	g_return_if_fail (ews_store != NULL);
738 
739 	cancellable = g_cancellable_new ();
740 
741 	dialog = gtk_dialog_new_with_buttons (
742 			_("Folder Sizes"),
743 			parent,
744 			GTK_DIALOG_DESTROY_WITH_PARENT,
745 			_("_Close"), GTK_RESPONSE_ACCEPT,
746 			NULL);
747 
748 	g_signal_connect (dialog, "response", G_CALLBACK (folder_sizes_dialog_response_cb), cancellable);
749 
750 	fsd = g_slice_new0 (FolderSizeDialogData);
751 	fsd->dialog = GTK_DIALOG (dialog);
752 
753 	gtk_window_set_default_size (GTK_WINDOW (fsd->dialog), 250, 300);
754 
755 	content_area = GTK_BOX (gtk_dialog_get_content_area (fsd->dialog));
756 
757 	spinner = e_spinner_new ();
758 	e_spinner_start (E_SPINNER (spinner));
759 	spinner_label = gtk_label_new (_("Fetching folder list…"));
760 
761 	fsd->spinner_grid = GTK_GRID (gtk_grid_new ());
762 	gtk_grid_set_column_spacing (fsd->spinner_grid, 6);
763 	gtk_grid_set_column_homogeneous (fsd->spinner_grid, FALSE);
764 	gtk_orientable_set_orientation (GTK_ORIENTABLE (fsd->spinner_grid), GTK_ORIENTATION_HORIZONTAL);
765 
766 	alignment = gtk_alignment_new (1.0, 0.5, 0.0, 1.0);
767 	gtk_container_add (GTK_CONTAINER (alignment), spinner);
768 	gtk_misc_set_alignment (GTK_MISC (spinner_label), 0.0, 0.5);
769 
770 	gtk_container_add (GTK_CONTAINER (fsd->spinner_grid), alignment);
771 	gtk_container_add (GTK_CONTAINER (fsd->spinner_grid), spinner_label);
772 
773 	/* Pack the TreeView into dialog's content area */
774 	gtk_box_pack_start (content_area, GTK_WIDGET (fsd->spinner_grid), TRUE, TRUE, 6);
775 	gtk_widget_show_all (GTK_WIDGET (fsd->dialog));
776 
777 	fsd->registry = g_object_ref (registry);
778 	fsd->source = g_object_ref (source);
779 	fsd->ews_store = g_object_ref (ews_store);
780 	fsd->ews_settings = CAMEL_EWS_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (ews_store)));
781 	fsd->cancellable = g_object_ref (cancellable);
782 
783 	thread = g_thread_new (NULL, ews_settings_get_folder_sizes_thread, fsd);
784 	g_thread_unref (thread);
785 
786 	/* Start the dialog */
787 	gtk_widget_show (GTK_WIDGET (dialog));
788 }
789 
790 static void
action_global_subscribe_foreign_folder_cb(GtkAction * action,EShellView * shell_view)791 action_global_subscribe_foreign_folder_cb (GtkAction *action,
792 					   EShellView *shell_view)
793 {
794 	EShell *shell;
795 	EShellBackend *shell_backend;
796 	EShellWindow *shell_window;
797 	EClientCache *client_cache;
798 	CamelSession *session = NULL;
799 
800 	g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
801 
802 	shell_window = e_shell_view_get_shell_window (shell_view);
803 	shell = e_shell_window_get_shell (shell_window);
804 	shell_backend = e_shell_get_backend_by_name (shell, "mail");
805 
806 	if (shell_backend)
807 		g_object_get (G_OBJECT (shell_backend), "session", &session, NULL);
808 
809 	if (!session)
810 		return;
811 
812 	client_cache = e_shell_get_client_cache (shell);
813 
814 	e_ews_subscribe_foreign_folder (GTK_WINDOW (shell_window), session, NULL, client_cache);
815 
816 	g_object_unref (session);
817 }
818 
819 static GtkActionEntry global_ews_entries[] = {
820 	{ "ews-global-subscribe-foreign-folder",
821 	  NULL,
822 	  N_("Subscribe to folder of other EWS user…"),
823 	  NULL,
824 	  NULL,  /* XXX Add a tooltip! */
825 	  G_CALLBACK (action_global_subscribe_foreign_folder_cb) }
826 };
827 
828 static gboolean
ews_ui_has_ews_account(EShellView * shell_view,CamelSession * in_session)829 ews_ui_has_ews_account (EShellView *shell_view,
830 			CamelSession *in_session)
831 {
832 	CamelSession *session = in_session;
833 	EShell *shell;
834 	gboolean has_any = FALSE;
835 
836 	g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), FALSE);
837 	if (in_session)
838 		g_return_val_if_fail (CAMEL_IS_SESSION (in_session), FALSE);
839 
840 	shell = e_shell_window_get_shell (e_shell_view_get_shell_window (shell_view));
841 
842 	if (!session) {
843 		EShellBackend *shell_backend;
844 
845 		shell_backend = e_shell_get_backend_by_name (shell, "mail");
846 
847 		if (shell_backend) {
848 			g_object_get (G_OBJECT (shell_backend), "session", &session, NULL);
849 		}
850 	}
851 
852 	if (session) {
853 		ESourceRegistry *registry;
854 		GList *services, *link;
855 
856 		registry = e_shell_get_registry (shell);
857 		services = camel_session_list_services (session);
858 
859 		for (link = services; link && !has_any; link = g_list_next (link)) {
860 			CamelService *service = link->data;
861 
862 			if (CAMEL_IS_EWS_STORE (service)) {
863 				ESource *source;
864 
865 				source = e_source_registry_ref_source (registry, camel_service_get_uid (service));
866 				has_any = source && e_source_registry_check_enabled (registry, source);
867 
868 				g_clear_object (&source);
869 			}
870 		}
871 
872 		g_list_free_full (services, g_object_unref);
873 	}
874 
875 	if (session && session != in_session)
876 		g_object_unref (session);
877 
878 	return has_any;
879 }
880 
881 static gboolean
get_ews_store_from_folder_tree(EShellView * shell_view,gchar ** pfolder_path,CamelStore ** pstore)882 get_ews_store_from_folder_tree (EShellView *shell_view,
883                                 gchar **pfolder_path,
884                                 CamelStore **pstore)
885 {
886 	EShellSidebar *shell_sidebar;
887 	EMFolderTree *folder_tree;
888 	gchar *selected_path = NULL;
889 	CamelStore *selected_store = NULL;
890 	gboolean found = FALSE;
891 
892 	/* Get hold of Folder Tree */
893 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
894 	g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL);
895 	if (em_folder_tree_get_selected (folder_tree, &selected_store, &selected_path) ||
896 	    em_folder_tree_store_root_selected (folder_tree, &selected_store)) {
897 		if (selected_store) {
898 			CamelProvider *provider = camel_service_get_provider (CAMEL_SERVICE (selected_store));
899 
900 			if (provider && g_ascii_strcasecmp (provider->protocol, "ews") == 0) {
901 				found = TRUE;
902 
903 				if (pstore)
904 					*pstore = g_object_ref (selected_store);
905 
906 				if (pfolder_path)
907 					*pfolder_path = selected_path;
908 				else
909 					g_free (selected_path);
910 
911 				selected_path = NULL;
912 			}
913 
914 			g_object_unref (selected_store);
915 		}
916 
917 		g_free (selected_path);
918 	}
919 
920 	g_object_unref (folder_tree);
921 
922 	return found;
923 }
924 
925 static void
action_folder_sizes_cb(GtkAction * action,EShellView * shell_view)926 action_folder_sizes_cb (GtkAction *action,
927 			EShellView *shell_view)
928 {
929 	GtkWindow *parent;
930 	CamelSession *session;
931 	CamelStore *store = NULL;
932 	ESourceRegistry *registry;
933 	ESource *source;
934 
935 	if (!get_ews_store_from_folder_tree (shell_view, NULL, &store))
936 		return;
937 
938 	g_return_if_fail (store != NULL);
939 
940 	parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view));
941 
942 	session = camel_service_ref_session (CAMEL_SERVICE (store));
943 	registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
944 	source = e_source_registry_ref_source (registry, camel_service_get_uid (CAMEL_SERVICE (store)));
945 
946 	e_ews_config_utils_run_folder_sizes_dialog (parent, registry, source, CAMEL_EWS_STORE (store));
947 
948 	g_object_unref (source);
949 	g_object_unref (session);
950 	g_object_unref (store);
951 }
952 
953 static void
action_subscribe_foreign_folder_cb(GtkAction * action,EShellView * shell_view)954 action_subscribe_foreign_folder_cb (GtkAction *action,
955                                     EShellView *shell_view)
956 {
957 	GtkWindow *parent;
958 	EShell *shell;
959 	EShellBackend *backend;
960 	EClientCache *client_cache;
961 	CamelSession *session = NULL;
962 	CamelStore *store = NULL;
963 
964 	if (!get_ews_store_from_folder_tree (shell_view, NULL, &store))
965 		return;
966 
967 	parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view));
968 	backend = e_shell_view_get_shell_backend (shell_view);
969 	g_object_get (G_OBJECT (backend), "session", &session, NULL);
970 
971 	shell = e_shell_backend_get_shell (backend);
972 	client_cache = e_shell_get_client_cache (shell);
973 
974 	e_ews_subscribe_foreign_folder (parent, session, store, client_cache);
975 
976 	g_object_unref (session);
977 	g_object_unref (store);
978 }
979 
980 static void
action_folder_permissions_mail_cb(GtkAction * action,EShellView * shell_view)981 action_folder_permissions_mail_cb (GtkAction *action,
982                                    EShellView *shell_view)
983 {
984 	EShell *shell;
985 	EShellWindow *shell_window;
986 	ESourceRegistry *registry;
987 	GtkWindow *parent;
988 	CamelStore *store = NULL;
989 	CamelEwsStore *ews_store;
990 	EwsFolderId *folder_id = NULL;
991 	gchar *folder_path = NULL;
992 
993 	if (!get_ews_store_from_folder_tree (shell_view, &folder_path, &store))
994 		return;
995 
996 	ews_store = CAMEL_EWS_STORE (store);
997 	g_return_if_fail (ews_store != NULL);
998 
999 	shell_window = e_shell_view_get_shell_window (shell_view);
1000 	parent = GTK_WINDOW (shell_window);
1001 	shell = e_shell_window_get_shell (shell_window);
1002 	registry = e_shell_get_registry (shell);
1003 
1004 	if (folder_path && *folder_path) {
1005 		gchar *str_folder_id = NULL;
1006 
1007 		str_folder_id = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_path);
1008 		if (!str_folder_id) {
1009 			e_notice (parent, GTK_MESSAGE_ERROR, _("Cannot edit permissions of folder “%s”, choose other folder."), folder_path);
1010 		} else {
1011 			gchar *str_change_key;
1012 
1013 			str_change_key = camel_ews_store_summary_get_change_key (
1014 				ews_store->summary, str_folder_id, NULL);
1015 
1016 			folder_id = e_ews_folder_id_new (str_folder_id, str_change_key, FALSE);
1017 
1018 			g_free (str_change_key);
1019 		}
1020 
1021 		g_free (str_folder_id);
1022 	} else {
1023 		g_clear_pointer (&folder_path, g_free);
1024 
1025 		folder_id = e_ews_folder_id_new ("msgfolderroot", NULL, TRUE);
1026 	}
1027 
1028 	if (folder_id) {
1029 		ESource *source;
1030 		CamelService *service;
1031 		CamelSettings *settings;
1032 		const gchar *uid;
1033 
1034 		service = CAMEL_SERVICE (store);
1035 		uid = camel_service_get_uid (service);
1036 		source = e_source_registry_ref_source (registry, uid);
1037 		g_return_if_fail (source != NULL);
1038 
1039 		settings = camel_service_ref_settings (service);
1040 
1041 		e_ews_edit_folder_permissions (
1042 			parent,
1043 			registry,
1044 			source,
1045 			CAMEL_EWS_SETTINGS (settings),
1046 			camel_service_get_display_name (service),
1047 			folder_path ? folder_path : camel_service_get_display_name (service),
1048 			folder_id,
1049 			E_EWS_FOLDER_TYPE_MAILBOX);
1050 
1051 		g_object_unref (settings);
1052 		g_object_unref (source);
1053 	}
1054 
1055 	g_object_unref (store);
1056 	g_free (folder_path);
1057 	e_ews_folder_id_free (folder_id);
1058 }
1059 
1060 static void
ews_ui_enable_actions(GtkActionGroup * action_group,const GtkActionEntry * entries,guint n_entries,gboolean can_show,gboolean is_online)1061 ews_ui_enable_actions (GtkActionGroup *action_group,
1062                        const GtkActionEntry *entries,
1063                        guint n_entries,
1064                        gboolean can_show,
1065                        gboolean is_online)
1066 {
1067 	gint ii;
1068 
1069 	g_return_if_fail (action_group != NULL);
1070 	g_return_if_fail (entries != NULL);
1071 
1072 	for (ii = 0; ii < n_entries; ii++) {
1073 		GtkAction *action;
1074 
1075 		action = gtk_action_group_get_action (action_group, entries[ii].name);
1076 		if (!action)
1077 			continue;
1078 
1079 		gtk_action_set_visible (action, can_show);
1080 		if (can_show)
1081 			gtk_action_set_sensitive (action, is_online);
1082 	}
1083 }
1084 
1085 static GtkActionEntry mail_account_context_entries[] = {
1086 	{ "mail-ews-folder-sizes",
1087 	  NULL,
1088 	  N_("Folder Sizes…"),
1089 	  NULL,
1090 	  NULL, /* XXX Add a tooltip! */
1091 	  G_CALLBACK (action_folder_sizes_cb) },
1092 
1093 	{ "mail-ews-subscribe-foreign-folder",
1094 	  NULL,
1095 	  N_("Subscribe to folder of other user…"),
1096 	  NULL,
1097 	  NULL,  /* XXX Add a tooltip! */
1098 	  G_CALLBACK (action_subscribe_foreign_folder_cb) }
1099 };
1100 
1101 static GtkActionEntry mail_folder_context_entries[] = {
1102 	{ "mail-ews-folder-permissions",
1103 	  "folder-new",
1104 	  N_("Permissions…"),
1105 	  NULL,
1106 	  N_("Edit EWS folder permissions"),
1107 	  G_CALLBACK (action_folder_permissions_mail_cb) }
1108 };
1109 
1110 static const gchar *ews_ui_mail_def =
1111 	"<menubar name='main-menu'>\n"
1112 	"  <menu action='file-menu'>\n"
1113 	"    <placeholder name='long-running-actions'>\n"
1114 	"      <menuitem action=\"ews-global-subscribe-foreign-folder\"/>\n"
1115 	"    </placeholder>\n"
1116 	"  </menu>\n"
1117 	"</menubar>\n"
1118 	"<popup name=\"mail-folder-popup\">\n"
1119 	"  <placeholder name=\"mail-folder-popup-actions\">\n"
1120 	"    <menuitem action=\"mail-ews-folder-sizes\"/>\n"
1121 	"    <menuitem action=\"mail-ews-subscribe-foreign-folder\"/>\n"
1122 	"    <menuitem action=\"mail-ews-folder-permissions\"/>\n"
1123 	"  </placeholder>\n"
1124 	"</popup>\n";
1125 
1126 static void
ews_ui_update_actions_mail_cb(EShellView * shell_view,GtkActionEntry * entries)1127 ews_ui_update_actions_mail_cb (EShellView *shell_view,
1128                                GtkActionEntry *entries)
1129 {
1130 	EShellWindow *shell_window;
1131 	EShellBackend *backend;
1132 	CamelSession *session = NULL;
1133 	GtkActionGroup *action_group;
1134 	GtkUIManager *ui_manager;
1135 	EShellSidebar *shell_sidebar;
1136 	EMFolderTree *folder_tree;
1137 	CamelStore *selected_store = NULL;
1138 	gchar *selected_path = NULL;
1139 	gboolean account_node = FALSE, folder_node = FALSE;
1140 	gboolean online, has_ews_account;
1141 
1142 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
1143 	g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL);
1144 	if (em_folder_tree_get_selected (folder_tree, &selected_store, &selected_path) ||
1145 	    em_folder_tree_store_root_selected (folder_tree, &selected_store)) {
1146 		if (selected_store) {
1147 			CamelProvider *provider = camel_service_get_provider (CAMEL_SERVICE (selected_store));
1148 
1149 			if (provider && g_ascii_strcasecmp (provider->protocol, "ews") == 0) {
1150 				account_node = !selected_path || !*selected_path;
1151 				folder_node = !account_node;
1152 			}
1153 
1154 			g_object_unref (selected_store);
1155 		}
1156 	}
1157 	g_object_unref (folder_tree);
1158 
1159 	g_free (selected_path);
1160 
1161 	shell_window = e_shell_view_get_shell_window (shell_view);
1162 	ui_manager = e_shell_window_get_ui_manager (shell_window);
1163 	action_group = e_lookup_action_group (ui_manager, "mail");
1164 
1165 	backend = e_shell_view_get_shell_backend (shell_view);
1166 	g_object_get (G_OBJECT (backend), "session", &session, NULL);
1167 
1168 	online = session && camel_session_get_online (session);
1169 
1170 	has_ews_account = account_node || folder_node || ews_ui_has_ews_account (shell_view, session);
1171 
1172 	if (session)
1173 		g_object_unref (session);
1174 
1175 	ews_ui_enable_actions (action_group, mail_account_context_entries, G_N_ELEMENTS (mail_account_context_entries), account_node, online);
1176 	ews_ui_enable_actions (action_group, mail_folder_context_entries, G_N_ELEMENTS (mail_folder_context_entries), account_node || folder_node, online);
1177 	ews_ui_enable_actions (action_group, global_ews_entries, G_N_ELEMENTS (global_ews_entries), has_ews_account, online);
1178 }
1179 
1180 static void
ews_ui_init_mail(GtkUIManager * ui_manager,EShellView * shell_view,gchar ** ui_definition)1181 ews_ui_init_mail (GtkUIManager *ui_manager,
1182                   EShellView *shell_view,
1183                   gchar **ui_definition)
1184 {
1185 	EShellWindow *shell_window;
1186 	GtkActionGroup *action_group;
1187 
1188 	g_return_if_fail (ui_definition != NULL);
1189 
1190 	*ui_definition = g_strdup (ews_ui_mail_def);
1191 
1192 	shell_window = e_shell_view_get_shell_window (shell_view);
1193 	action_group = e_shell_window_get_action_group (shell_window, "mail");
1194 
1195 	/* Add actions to the "mail" action group. */
1196 	e_action_group_add_actions_localized (
1197 		action_group, GETTEXT_PACKAGE,
1198 		mail_account_context_entries, G_N_ELEMENTS (mail_account_context_entries), shell_view);
1199 	e_action_group_add_actions_localized (
1200 		action_group, GETTEXT_PACKAGE,
1201 		mail_folder_context_entries, G_N_ELEMENTS (mail_folder_context_entries), shell_view);
1202 
1203 	/* Add global actions */
1204 	e_action_group_add_actions_localized (
1205 		action_group, GETTEXT_PACKAGE,
1206 		global_ews_entries, G_N_ELEMENTS (global_ews_entries), shell_view);
1207 
1208 	/* Decide whether we want this option to be visible or not */
1209 	g_signal_connect (
1210 		shell_view, "update-actions",
1211 		G_CALLBACK (ews_ui_update_actions_mail_cb),
1212 		shell_view);
1213 }
1214 
1215 static gboolean
get_selected_ews_source(EShellView * shell_view,ESource ** selected_source,ESourceRegistry ** registry)1216 get_selected_ews_source (EShellView *shell_view,
1217                          ESource **selected_source,
1218                          ESourceRegistry **registry)
1219 {
1220 	ESource *source;
1221 	EShellSidebar *shell_sidebar;
1222 	ESourceSelector *selector = NULL;
1223 
1224 	g_return_val_if_fail (shell_view != NULL, FALSE);
1225 
1226 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
1227 	g_return_val_if_fail (shell_sidebar != NULL, FALSE);
1228 
1229 	g_object_get (shell_sidebar, "selector", &selector, NULL);
1230 	g_return_val_if_fail (selector != NULL, FALSE);
1231 
1232 	source = e_source_selector_ref_primary_selection (selector);
1233 	if (source) {
1234 		ESourceBackend *backend_ext = NULL;
1235 
1236 		if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK))
1237 			backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
1238 		else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
1239 			backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
1240 		else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
1241 			backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST);
1242 		else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
1243 			backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
1244 		else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT))
1245 			backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
1246 
1247 		if (!backend_ext ||
1248 		    g_strcmp0 (e_source_backend_get_backend_name (backend_ext), "ews") != 0) {
1249 			g_object_unref (source);
1250 			source = NULL;
1251 		}
1252 	}
1253 
1254 	if (source && registry)
1255 		*registry = g_object_ref (e_source_selector_get_registry (selector));
1256 
1257 	g_object_unref (selector);
1258 
1259 	if (selected_source)
1260 		*selected_source = source;
1261 	else if (source)
1262 		g_object_unref (source);
1263 
1264 	return source != NULL;
1265 }
1266 
1267 /* how many menu entries are defined; all calendar/tasks/memos/contacts
1268  * actions should have same count */
1269 #define EWS_ESOURCE_NUM_ENTRIES 1
1270 
1271 static void
update_ews_source_entries_cb(EShellView * shell_view,GtkActionEntry * entries)1272 update_ews_source_entries_cb (EShellView *shell_view,
1273                               GtkActionEntry *entries)
1274 {
1275 	GtkActionGroup *action_group;
1276 	EShell *shell;
1277 	EShellWindow *shell_window;
1278 	ESource *source = NULL;
1279 	const gchar *group;
1280 	gboolean is_ews_source, is_online;
1281 
1282 	g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1283 	g_return_if_fail (entries != NULL);
1284 
1285 	if (strstr (entries->name, "calendar"))
1286 		group = "calendar";
1287 	else if (strstr (entries->name, "tasks"))
1288 		group = "tasks";
1289 	else if (strstr (entries->name, "memos"))
1290 		group = "memos";
1291 	else if (strstr (entries->name, "contacts"))
1292 		group = "contacts";
1293 	else
1294 		g_return_if_reached ();
1295 
1296 	is_ews_source = get_selected_ews_source (shell_view, &source, NULL);
1297 
1298 	if (is_ews_source) {
1299 		if (!source || !e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER))
1300 			is_ews_source = FALSE;
1301 
1302 		if (is_ews_source) {
1303 			ESource *clicked_source = NULL;
1304 
1305 			g_object_get (G_OBJECT (shell_view), "clicked-source", &clicked_source, NULL);
1306 
1307 			if (clicked_source && clicked_source != source)
1308 				is_ews_source = FALSE;
1309 
1310 			g_clear_object (&clicked_source);
1311 		}
1312 
1313 		if (is_ews_source) {
1314 			ESourceEwsFolder *ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER);
1315 
1316 			/* Require both ChangeKey and folder's Id, but GAL can have a ':' in the Id,
1317 			   which should be ignored, because it's not a valid folder Id. */
1318 			if (!e_source_ews_folder_get_id (ews_folder) ||
1319 			    g_strcmp0 (e_source_ews_folder_get_id (ews_folder), "") == 0 ||
1320 			    !e_source_ews_folder_get_change_key (ews_folder) ||
1321 			    g_strcmp0 (e_source_ews_folder_get_change_key (ews_folder), "") == 0 ||
1322 			    strchr (e_source_ews_folder_get_id (ews_folder), ':') != NULL)
1323 				is_ews_source = FALSE;
1324 		}
1325 	}
1326 
1327 	g_clear_object (&source);
1328 
1329 	shell_window = e_shell_view_get_shell_window (shell_view);
1330 	shell = e_shell_window_get_shell (shell_window);
1331 
1332 	is_online = shell && e_shell_get_online (shell);
1333 	action_group = e_shell_window_get_action_group (shell_window, group);
1334 
1335 	ews_ui_enable_actions (action_group, entries, EWS_ESOURCE_NUM_ENTRIES, is_ews_source, is_online);
1336 	ews_ui_enable_actions (action_group, global_ews_entries, G_N_ELEMENTS (global_ews_entries),
1337 		ews_ui_has_ews_account (shell_view, NULL), is_online);
1338 }
1339 
1340 static void
setup_ews_source_actions(EShellView * shell_view,GtkUIManager * ui_manager,GtkActionEntry * entries,guint n_entries)1341 setup_ews_source_actions (EShellView *shell_view,
1342                           GtkUIManager *ui_manager,
1343                           GtkActionEntry *entries,
1344                           guint n_entries)
1345 {
1346 	EShellWindow *shell_window;
1347 	GtkActionGroup *action_group;
1348 	const gchar *group;
1349 
1350 	g_return_if_fail (shell_view != NULL);
1351 	g_return_if_fail (ui_manager != NULL);
1352 	g_return_if_fail (entries != NULL);
1353 	g_return_if_fail (n_entries > 0);
1354 	g_return_if_fail (n_entries == EWS_ESOURCE_NUM_ENTRIES);
1355 
1356 	if (strstr (entries->name, "calendar"))
1357 		group = "calendar";
1358 	else if (strstr (entries->name, "tasks"))
1359 		group = "tasks";
1360 	else if (strstr (entries->name, "memos"))
1361 		group = "memos";
1362 	else if (strstr (entries->name, "contacts"))
1363 		group = "contacts";
1364 	else
1365 		g_return_if_reached ();
1366 
1367 	shell_window = e_shell_view_get_shell_window (shell_view);
1368 	action_group = e_shell_window_get_action_group (shell_window, group);
1369 
1370 	e_action_group_add_actions_localized (
1371 		action_group, GETTEXT_PACKAGE,
1372 		entries, EWS_ESOURCE_NUM_ENTRIES, shell_view);
1373 
1374 	/* Add global actions */
1375 	e_action_group_add_actions_localized (
1376 		action_group, GETTEXT_PACKAGE,
1377 		global_ews_entries, G_N_ELEMENTS (global_ews_entries), shell_view);
1378 
1379 	g_signal_connect (shell_view, "update-actions", G_CALLBACK (update_ews_source_entries_cb), entries);
1380 }
1381 
1382 static void
action_folder_permissions_source_cb(GtkAction * action,EShellView * shell_view)1383 action_folder_permissions_source_cb (GtkAction *action,
1384                                      EShellView *shell_view)
1385 {
1386 	ESourceRegistry *registry = NULL;
1387 	ESource *source = NULL, *parent_source;
1388 	ESourceEwsFolder *folder_ext;
1389 	ESourceCamel *extension;
1390 	CamelSettings *settings;
1391 	const gchar *extension_name;
1392 	EwsFolderId *folder_id;
1393 	EEwsFolderType folder_type;
1394 
1395 	g_return_if_fail (action != NULL);
1396 	g_return_if_fail (shell_view != NULL);
1397 	g_return_if_fail (get_selected_ews_source (shell_view, &source, &registry));
1398 	g_return_if_fail (source != NULL);
1399 	g_return_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER));
1400 	g_return_if_fail (gtk_action_get_name (action) != NULL);
1401 
1402 	folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER);
1403 	folder_id = e_source_ews_folder_dup_folder_id (folder_ext);
1404 	g_return_if_fail (folder_id != NULL);
1405 
1406 	parent_source = e_source_registry_ref_source (registry, e_source_get_parent (source));
1407 
1408 	extension_name = e_source_camel_get_extension_name ("ews");
1409 	extension = e_source_get_extension (parent_source, extension_name);
1410 	settings = e_source_camel_get_settings (extension);
1411 
1412 	folder_type = E_EWS_FOLDER_TYPE_MAILBOX;
1413 	if (strstr (gtk_action_get_name (action), "calendar") != NULL)
1414 		folder_type = E_EWS_FOLDER_TYPE_CALENDAR;
1415 	else if (strstr (gtk_action_get_name (action), "contacts") != NULL)
1416 		folder_type = E_EWS_FOLDER_TYPE_CONTACTS;
1417 	else if (strstr (gtk_action_get_name (action), "tasks") != NULL)
1418 		folder_type = E_EWS_FOLDER_TYPE_TASKS;
1419 
1420 	e_ews_edit_folder_permissions (
1421 		NULL,
1422 		registry,
1423 		source,
1424 		CAMEL_EWS_SETTINGS (settings),
1425 		e_source_get_display_name (parent_source),
1426 		e_source_get_display_name (source),
1427 		folder_id,
1428 		folder_type);
1429 
1430 	g_object_unref (source);
1431 	g_object_unref (parent_source);
1432 	g_object_unref (registry);
1433 	e_ews_folder_id_free (folder_id);
1434 }
1435 
1436 static GtkActionEntry calendar_context_entries[] = {
1437 
1438 	{ "calendar-ews-folder-permissions",
1439 	  "folder-new",
1440 	  N_("Permissions…"),
1441 	  NULL,
1442 	  N_("Edit EWS calendar permissions"),
1443 	  G_CALLBACK (action_folder_permissions_source_cb) }
1444 };
1445 
1446 static const gchar *ews_ui_cal_def =
1447 	"<menubar name='main-menu'>\n"
1448 	"  <menu action='file-menu'>\n"
1449 	"    <placeholder name='long-running-actions'>\n"
1450 	"      <menuitem action=\"ews-global-subscribe-foreign-folder\"/>\n"
1451 	"    </placeholder>\n"
1452 	"  </menu>\n"
1453 	"</menubar>\n"
1454 	"<popup name=\"calendar-popup\">\n"
1455 	"  <placeholder name=\"calendar-popup-actions\">\n"
1456 	"    <menuitem action=\"calendar-ews-folder-permissions\"/>\n"
1457 	"  </placeholder>\n"
1458 	"</popup>\n";
1459 
1460 static void
ews_ui_init_calendar(GtkUIManager * ui_manager,EShellView * shell_view,gchar ** ui_definition)1461 ews_ui_init_calendar (GtkUIManager *ui_manager,
1462                       EShellView *shell_view,
1463                       gchar **ui_definition)
1464 {
1465 	g_return_if_fail (ui_definition != NULL);
1466 
1467 	*ui_definition = g_strdup (ews_ui_cal_def);
1468 
1469 	setup_ews_source_actions (
1470 		shell_view, ui_manager,
1471 		calendar_context_entries, G_N_ELEMENTS (calendar_context_entries));
1472 }
1473 
1474 static GtkActionEntry tasks_context_entries[] = {
1475 
1476 	{ "tasks-ews-folder-permissions",
1477 	  "folder-new",
1478 	  N_("Permissions…"),
1479 	  NULL,
1480 	  N_("Edit EWS tasks permissions"),
1481 	  G_CALLBACK (action_folder_permissions_source_cb) }
1482 };
1483 
1484 static const gchar *ews_ui_task_def =
1485 	"<menubar name='main-menu'>\n"
1486 	"  <menu action='file-menu'>\n"
1487 	"    <placeholder name='long-running-actions'>\n"
1488 	"      <menuitem action=\"ews-global-subscribe-foreign-folder\"/>\n"
1489 	"    </placeholder>\n"
1490 	"  </menu>\n"
1491 	"</menubar>\n"
1492 	"<popup name=\"task-list-popup\">\n"
1493 	"  <placeholder name=\"task-list-popup-actions\">\n"
1494 	"    <menuitem action=\"tasks-ews-folder-permissions\"/>\n"
1495 	"  </placeholder>\n"
1496 	"</popup>\n";
1497 
1498 static void
ews_ui_init_tasks(GtkUIManager * ui_manager,EShellView * shell_view,gchar ** ui_definition)1499 ews_ui_init_tasks (GtkUIManager *ui_manager,
1500                    EShellView *shell_view,
1501                    gchar **ui_definition)
1502 {
1503 	g_return_if_fail (ui_definition != NULL);
1504 
1505 	*ui_definition = g_strdup (ews_ui_task_def);
1506 
1507 	setup_ews_source_actions (
1508 		shell_view, ui_manager,
1509 		tasks_context_entries, G_N_ELEMENTS (tasks_context_entries));
1510 }
1511 
1512 static GtkActionEntry memos_context_entries[] = {
1513 
1514 	{ "memos-ews-folder-permissions",
1515 	  "folder-new",
1516 	  N_("Permissions…"),
1517 	  NULL,
1518 	  N_("Edit EWS memos permissions"),
1519 	  G_CALLBACK (action_folder_permissions_source_cb) }
1520 };
1521 
1522 static const gchar *ews_ui_memo_def =
1523 	"<menubar name='main-menu'>\n"
1524 	"  <menu action='file-menu'>\n"
1525 	"    <placeholder name='long-running-actions'>\n"
1526 	"      <menuitem action=\"ews-global-subscribe-foreign-folder\"/>\n"
1527 	"    </placeholder>\n"
1528 	"  </menu>\n"
1529 	"</menubar>\n"
1530 	"<popup name=\"memo-list-popup\">\n"
1531 	"  <placeholder name=\"memo-list-popup-actions\">\n"
1532 	"    <menuitem action=\"memos-ews-folder-permissions\"/>\n"
1533 	"  </placeholder>\n"
1534 	"</popup>\n";
1535 
1536 static void
ews_ui_init_memos(GtkUIManager * ui_manager,EShellView * shell_view,gchar ** ui_definition)1537 ews_ui_init_memos (GtkUIManager *ui_manager,
1538                    EShellView *shell_view,
1539                    gchar **ui_definition)
1540 {
1541 	g_return_if_fail (ui_definition != NULL);
1542 
1543 	*ui_definition = g_strdup (ews_ui_memo_def);
1544 
1545 	setup_ews_source_actions (
1546 		shell_view, ui_manager,
1547 		memos_context_entries, G_N_ELEMENTS (memos_context_entries));
1548 }
1549 
1550 static GtkActionEntry contacts_context_entries[] = {
1551 
1552 	{ "contacts-ews-folder-permissions",
1553 	  "folder-new",
1554 	  N_("Permissions…"),
1555 	  NULL,
1556 	  N_("Edit EWS contacts permissions"),
1557 	  G_CALLBACK (action_folder_permissions_source_cb) }
1558 };
1559 
1560 static const gchar *ews_ui_book_def =
1561 	"<menubar name='main-menu'>\n"
1562 	"  <menu action='file-menu'>\n"
1563 	"    <placeholder name='long-running-actions'>\n"
1564 	"      <menuitem action=\"ews-global-subscribe-foreign-folder\"/>\n"
1565 	"    </placeholder>\n"
1566 	"  </menu>\n"
1567 	"</menubar>\n"
1568 	"<popup name=\"address-book-popup\">\n"
1569 	"  <placeholder name=\"address-book-popup-actions\">\n"
1570 	"    <menuitem action=\"contacts-ews-folder-permissions\"/>\n"
1571 	"  </placeholder>\n"
1572 	"</popup>\n";
1573 
1574 static void
ews_ui_init_contacts(GtkUIManager * ui_manager,EShellView * shell_view,gchar ** ui_definition)1575 ews_ui_init_contacts (GtkUIManager *ui_manager,
1576                       EShellView *shell_view,
1577                       gchar **ui_definition)
1578 {
1579 	g_return_if_fail (ui_definition != NULL);
1580 
1581 	*ui_definition = g_strdup (ews_ui_book_def);
1582 
1583 	setup_ews_source_actions (
1584 		shell_view, ui_manager,
1585 		contacts_context_entries, G_N_ELEMENTS (contacts_context_entries));
1586 }
1587 
1588 void
e_ews_config_utils_init_ui(EShellView * shell_view,const gchar * ui_manager_id,gchar ** ui_definition)1589 e_ews_config_utils_init_ui (EShellView *shell_view,
1590                             const gchar *ui_manager_id,
1591                             gchar **ui_definition)
1592 {
1593 	EShellWindow *shell_window;
1594 	GtkUIManager *ui_manager;
1595 
1596 	g_return_if_fail (shell_view != NULL);
1597 	g_return_if_fail (ui_manager_id != NULL);
1598 	g_return_if_fail (ui_definition != NULL);
1599 
1600 	shell_window = e_shell_view_get_shell_window (shell_view);
1601 	ui_manager = e_shell_window_get_ui_manager (shell_window);
1602 
1603 	if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.mail") == 0)
1604 		ews_ui_init_mail (ui_manager, shell_view, ui_definition);
1605 	else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.calendars") == 0)
1606 		ews_ui_init_calendar (ui_manager, shell_view, ui_definition);
1607 	else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.tasks") == 0)
1608 		ews_ui_init_tasks (ui_manager, shell_view, ui_definition);
1609 	else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.memos") == 0)
1610 		ews_ui_init_memos (ui_manager, shell_view, ui_definition);
1611 	else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.contacts") == 0)
1612 		ews_ui_init_contacts (ui_manager, shell_view, ui_definition);
1613 }
1614 
1615 gboolean
e_ews_config_utils_is_online(void)1616 e_ews_config_utils_is_online (void)
1617 {
1618 	EShell *shell;
1619 
1620 	shell = e_shell_get_default ();
1621 
1622 	return shell && e_shell_get_online (shell);
1623 }
1624 
1625 GtkWindow *
e_ews_config_utils_get_widget_toplevel_window(GtkWidget * widget)1626 e_ews_config_utils_get_widget_toplevel_window (GtkWidget *widget)
1627 {
1628 	if (!widget)
1629 		return NULL;
1630 
1631 	if (!GTK_IS_WINDOW (widget))
1632 		widget = gtk_widget_get_toplevel (widget);
1633 
1634 	if (GTK_IS_WINDOW (widget))
1635 		return GTK_WINDOW (widget);
1636 
1637 	return NULL;
1638 }
1639 
1640 static gpointer
ews_config_utils_unref_in_thread(gpointer user_data)1641 ews_config_utils_unref_in_thread (gpointer user_data)
1642 {
1643 	g_object_unref (user_data);
1644 
1645 	return NULL;
1646 }
1647 
1648 void
e_ews_config_utils_unref_in_thread(GObject * object)1649 e_ews_config_utils_unref_in_thread (GObject *object)
1650 {
1651 	GThread *thread;
1652 
1653 	g_return_if_fail (object != NULL);
1654 	g_return_if_fail (G_IS_OBJECT (object));
1655 
1656 	thread = g_thread_new (NULL, ews_config_utils_unref_in_thread, object);
1657 	g_thread_unref (thread);
1658 }
1659