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 <glib/gi18n-lib.h>
10 #include <gtk/gtk.h>
11 
12 #include <libemail-engine/libemail-engine.h>
13 
14 #include "camel/camel-ews-store.h"
15 #include "camel/camel-ews-store-summary.h"
16 #include "camel/camel-ews-utils.h"
17 
18 #include "common/e-ews-calendar-utils.h"
19 
20 #include "e-ews-config-utils.h"
21 #include "e-ews-search-user.h"
22 #include "e-ews-subscribe-foreign-folder.h"
23 
24 #define STR_ACCOUNTS_COMBO		"e-ews-accounts-combo"
25 #define STR_USER_NAME_SELECTOR_ENTRY	"e-ews-name-selector-entry"
26 #define STR_FOLDER_NAME_COMBO		"e-ews-folder-name-combo"
27 #define STR_SUBFOLDERS_CHECK		"e-ews-subfolders-check"
28 #define STR_EWS_CAMEL_SESSION		"e-ews-camel-session"
29 #define STR_EWS_DIRECT_EMAIL		"e-ews-direct-email"
30 
31 enum {
32 	COLUMN_UID = 0,
33 	COLUMN_DISPLAY_NAME,
34 	COLUMN_STORE
35 };
36 
37 static void
announce_new_folder(CamelEwsStore * ews_store,const gchar * fid)38 announce_new_folder (CamelEwsStore *ews_store,
39                      const gchar *fid)
40 {
41 	CamelFolderInfo *fi;
42 
43 	g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store));
44 	g_return_if_fail (fid != NULL);
45 	g_return_if_fail (camel_ews_store_summary_has_folder (ews_store->summary, fid));
46 
47 	fi = camel_ews_utils_build_folder_info (ews_store, fid);
48 	camel_store_folder_created (CAMEL_STORE (ews_store), fi);
49 	camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi);
50 	camel_folder_info_free (fi);
51 }
52 
53 static gboolean
add_foreign_folder_to_camel(CamelEwsStore * ews_store,const gchar * foreign_email,EEwsFolder * folder,gboolean include_subfolders,const gchar * display_username,const gchar * display_foldername,GError ** perror)54 add_foreign_folder_to_camel (CamelEwsStore *ews_store,
55                              const gchar *foreign_email,
56                              EEwsFolder *folder,
57 			     gboolean include_subfolders,
58                              const gchar *display_username,
59                              const gchar *display_foldername,
60                              GError **perror)
61 {
62 	gchar *foreign_mailbox_id;
63 	gchar *mailbox, *fullname;
64 	const EwsFolderId *fid, *parent_fid;
65 
66 	g_return_val_if_fail (ews_store != NULL, FALSE);
67 	g_return_val_if_fail (ews_store->summary != NULL, FALSE);
68 	g_return_val_if_fail (foreign_email != NULL, FALSE);
69 	g_return_val_if_fail (folder != NULL, FALSE);
70 	g_return_val_if_fail (display_username != NULL, FALSE);
71 	g_return_val_if_fail (display_foldername != NULL, FALSE);
72 
73 	fid = e_ews_folder_get_id (folder);
74 	parent_fid = e_ews_folder_get_parent_id (folder);
75 
76 	g_return_val_if_fail (fid != NULL, FALSE);
77 	g_return_val_if_fail (parent_fid != NULL, FALSE);
78 	g_return_val_if_fail (g_strcmp0 (fid->id, parent_fid->id) != 0, FALSE);
79 
80 	if (camel_ews_store_summary_has_folder (ews_store->summary, fid->id)) {
81 		gchar *full_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, fid->id, NULL);
82 
83 		g_propagate_error (
84 			perror,
85 			g_error_new (EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_FOLDEREXISTS,
86 			_("Cannot add folder, folder already exists as “%s”"), full_name));
87 
88 		g_free (full_name);
89 
90 		return FALSE;
91 	}
92 
93 	/* Translators: The '%s' is replaced with user name, to whom the foreign mailbox belongs.
94 	 * Example result: "Mailbox — John Smith"
95 	*/
96 	mailbox = g_strdup_printf (C_("ForeignFolder", "Mailbox — %s"), display_username);
97 
98 	foreign_mailbox_id = g_strdup_printf ("ForeignMailbox::%s", foreign_email);
99 	if (!camel_ews_store_summary_has_folder (ews_store->summary, foreign_mailbox_id)) {
100 		camel_ews_store_summary_new_folder (
101 			ews_store->summary,
102 			foreign_mailbox_id, EWS_FOREIGN_FOLDER_ROOT_ID, NULL,
103 			mailbox, E_EWS_FOLDER_TYPE_MAILBOX,
104 			CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_NOSELECT,
105 			0, FALSE, FALSE);
106 	}
107 
108 	if (camel_ews_store_summary_has_folder (ews_store->summary, parent_fid->id)) {
109 		camel_ews_store_summary_new_folder (
110 			ews_store->summary,
111 			fid->id, parent_fid->id, fid->change_key,
112 			display_foldername, E_EWS_FOLDER_TYPE_MAILBOX,
113 			CAMEL_FOLDER_SUBSCRIBED, e_ews_folder_get_total_count (folder), TRUE, FALSE);
114 	} else {
115 		const gchar *displayname;
116 		gchar *escaped_name;
117 
118 		escaped_name = e_ews_folder_utils_escape_name (display_foldername);
119 		fullname = g_strdup_printf ("%s/%s/%s", EWS_FOREIGN_FOLDER_ROOT_DISPLAY_NAME, mailbox, escaped_name);
120 		g_free (escaped_name);
121 
122 		/* make sure the path is unique */
123 		camel_ews_store_ensure_unique_path (ews_store, &fullname);
124 
125 		displayname = strrchr (fullname, '/');
126 		displayname++;
127 
128 		camel_ews_store_summary_new_folder (
129 			ews_store->summary,
130 			fid->id, foreign_mailbox_id, fid->change_key,
131 			displayname, E_EWS_FOLDER_TYPE_MAILBOX,
132 			CAMEL_FOLDER_SUBSCRIBED, e_ews_folder_get_total_count (folder), TRUE, FALSE);
133 
134 		g_free (fullname);
135 	}
136 
137 	camel_ews_store_ensure_virtual_folders (ews_store);
138 	camel_ews_store_summary_set_foreign_subfolders (ews_store->summary, fid->id, include_subfolders);
139 	camel_ews_store_summary_save (ews_store->summary, perror);
140 
141 	announce_new_folder (ews_store, EWS_FOREIGN_FOLDER_ROOT_ID);
142 	announce_new_folder (ews_store, foreign_mailbox_id);
143 	announce_new_folder (ews_store, fid->id);
144 
145 	g_free (foreign_mailbox_id);
146 	g_free (mailbox);
147 
148 	if (include_subfolders)
149 		camel_ews_store_update_foreign_subfolders (ews_store, fid->id);
150 
151 	return TRUE;
152 }
153 
154 static void
enable_ok_button_by_data(GObject * dialog)155 enable_ok_button_by_data (GObject *dialog)
156 {
157 	GtkEntry *entry;
158 	GtkComboBoxText *combo;
159 	const gchar *entry_text;
160 	gchar *combo_text;
161 
162 	g_return_if_fail (dialog != NULL);
163 
164 	entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
165 	g_return_if_fail (entry != NULL);
166 
167 	combo = g_object_get_data (dialog, STR_FOLDER_NAME_COMBO);
168 	g_return_if_fail (combo != NULL);
169 
170 	entry_text = gtk_entry_get_text (entry);
171 	combo_text = gtk_combo_box_text_get_active_text (combo);
172 
173 	gtk_dialog_set_response_sensitive (
174 		GTK_DIALOG (dialog), GTK_RESPONSE_OK,
175 		entry_text && *entry_text && *entry_text != ' ' && *entry_text != ',' &&
176 		combo_text && *combo_text);
177 
178 	g_free (combo_text);
179 }
180 
181 static void
name_entry_changed_cb(GObject * dialog)182 name_entry_changed_cb (GObject *dialog)
183 {
184 	GtkEntry *entry;
185 
186 	g_return_if_fail (dialog != NULL);
187 
188 	entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
189 	g_return_if_fail (entry != NULL);
190 
191 	g_object_set_data (G_OBJECT (entry), STR_EWS_DIRECT_EMAIL, NULL);
192 
193 	enable_ok_button_by_data (dialog);
194 }
195 
196 static void
folder_name_combo_changed_cb(GObject * dialog,GtkComboBox * combo)197 folder_name_combo_changed_cb (GObject *dialog,
198 			      GtkComboBox *combo)
199 {
200 	enable_ok_button_by_data (dialog);
201 }
202 
203 struct EEwsCheckForeignFolderData
204 {
205 	GtkWidget *dialog;
206 	gboolean include_subfolders;
207 	gchar *email;
208 	gchar *direct_email;
209 	gchar *user_displayname;
210 	gchar *orig_foldername;
211 	gchar *use_foldername;
212 	EEwsFolder *folder;
213 };
214 
215 static void
e_ews_check_foreign_folder_data_free(gpointer ptr)216 e_ews_check_foreign_folder_data_free (gpointer ptr)
217 {
218 	struct EEwsCheckForeignFolderData *cffd = ptr;
219 
220 	if (!cffd)
221 		return;
222 
223 	g_free (cffd->email);
224 	g_free (cffd->direct_email);
225 	g_free (cffd->user_displayname);
226 	g_free (cffd->orig_foldername);
227 	g_free (cffd->use_foldername);
228 
229 	/* folder tells whether successfully finished,
230 	 * then the dialog can be destroyed */
231 	if (cffd->folder && cffd->dialog)
232 		gtk_widget_destroy (cffd->dialog);
233 
234 	if (cffd->folder)
235 		g_object_unref (cffd->folder);
236 
237 	g_slice_free (struct EEwsCheckForeignFolderData, cffd);
238 }
239 
240 static void
check_foreign_folder_thread(GObject * with_object,gpointer user_data,GCancellable * cancellable,GError ** perror)241 check_foreign_folder_thread (GObject *with_object,
242                              gpointer user_data,
243                              GCancellable *cancellable,
244                              GError **perror)
245 {
246 	struct EEwsCheckForeignFolderData *cffd = user_data;
247 	GError *local_error = NULL;
248 	EEwsConnection *conn;
249 	EwsFolderId fid;
250 	EEwsFolder *folder = NULL;
251 
252 	g_return_if_fail (with_object != NULL);
253 	g_return_if_fail (CAMEL_IS_EWS_STORE (with_object));
254 	g_return_if_fail (user_data != NULL);
255 	g_return_if_fail (cffd->email != NULL);
256 
257 	if (g_cancellable_set_error_if_cancelled (cancellable, perror))
258 		return;
259 
260 	conn = camel_ews_store_ref_connection (CAMEL_EWS_STORE (with_object));
261 	if (!conn) {
262 		g_set_error_literal (
263 			perror, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NORESPONSE,
264 			_("Cannot test foreign folder availability when the account is offline"));
265 		return;
266 	}
267 
268 	if (cffd->direct_email && *cffd->direct_email) {
269 		g_return_if_fail (cffd->user_displayname == NULL);
270 
271 		cffd->user_displayname = cffd->email;
272 		cffd->email = g_strdup (cffd->direct_email);
273 	} else {
274 		gchar *display_name = NULL, *email_address = NULL;
275 
276 		if (!e_ews_subscribe_foreign_folder_resolve_name_sync (conn, cffd->email, &display_name, &email_address, cancellable, perror)) {
277 			g_object_unref (conn);
278 			return;
279 		}
280 	}
281 
282 	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
283 		g_object_unref (conn);
284 		return;
285 	}
286 
287 	if (g_strcmp0 (cffd->use_foldername, "freebusy-calendar") == 0) {
288 		EEWSFreeBusyData fbdata;
289 		GSList *free_busy = NULL;
290 		gchar *tmp;
291 		gboolean success;
292 
293 		fbdata.period_start = time (NULL);
294 		fbdata.period_end = fbdata.period_start + (60 * 60);
295 		fbdata.user_mails = g_slist_prepend (NULL, cffd->email);
296 
297 		success = e_ews_connection_get_free_busy_sync (conn, G_PRIORITY_DEFAULT,
298 			e_ews_cal_utils_prepare_free_busy_request, &fbdata,
299 			&free_busy, cancellable, perror);
300 
301 		g_slist_free_full (free_busy, g_object_unref);
302 		g_slist_free (fbdata.user_mails);
303 
304 		if (!success) {
305 			g_object_unref (conn);
306 			return;
307 		}
308 
309 		tmp = g_strconcat (cffd->use_foldername, "::", cffd->email, NULL);
310 
311 		folder = g_object_new (E_TYPE_EWS_FOLDER, NULL);
312 		e_ews_folder_set_id (folder, e_ews_folder_id_new (tmp, NULL, FALSE));
313 		/* Translators: This is used as a calendar name; it constructs "User Name - Availability" string shown in UI */
314 		e_ews_folder_set_name (folder, _("Availability"));
315 		e_ews_folder_set_folder_type (folder, E_EWS_FOLDER_TYPE_CALENDAR);
316 		e_ews_folder_set_foreign_mail (folder, cffd->email);
317 
318 		g_free (tmp);
319 	} else {
320 		fid.id = (gchar *) (cffd->use_foldername ? cffd->use_foldername : cffd->orig_foldername);
321 		fid.change_key = NULL;
322 		fid.is_distinguished_id = cffd->use_foldername != NULL || (cffd->orig_foldername && strlen (cffd->orig_foldername) < 40);
323 
324 		if (!e_ews_connection_get_folder_info_sync (conn, G_PRIORITY_DEFAULT,
325 			cffd->email, &fid, &folder, cancellable, &local_error)) {
326 			if (!local_error ||
327 			    g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND) ||
328 			    g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_FOLDERNOTFOUND)) {
329 				g_clear_error (&local_error);
330 				local_error = g_error_new (
331 					EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_FOLDERNOTFOUND,
332 					_("Folder “%s” not found. Either it does not exist or you do not have permission to access it."),
333 					cffd->orig_foldername);
334 			}
335 
336 			g_propagate_error (perror, local_error);
337 			g_object_unref (conn);
338 			return;
339 		}
340 	}
341 
342 	if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
343 		g_object_unref (folder);
344 		g_object_unref (conn);
345 		return;
346 	}
347 
348 	if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_UNKNOWN) {
349 		g_propagate_error (
350 			perror, g_error_new_literal (EWS_CONNECTION_ERROR,
351 			EWS_CONNECTION_ERROR_FOLDERNOTFOUND, _("Cannot add folder, cannot determine folder’s type")));
352 		g_object_unref (folder);
353 		g_object_unref (conn);
354 		return;
355 	}
356 
357 	e_ews_folder_set_foreign (folder, TRUE);
358 
359 	cffd->folder = folder;
360 	g_object_unref (conn);
361 }
362 
363 static void
check_foreign_folder_idle(GObject * with_object,gpointer user_data,GCancellable * cancellable,GError ** perror)364 check_foreign_folder_idle (GObject *with_object,
365                            gpointer user_data,
366                            GCancellable *cancellable,
367                            GError **perror)
368 {
369 	struct EEwsCheckForeignFolderData *cffd = user_data;
370 
371 	g_return_if_fail (with_object != NULL);
372 	g_return_if_fail (CAMEL_IS_EWS_STORE (with_object));
373 	g_return_if_fail (user_data != NULL);
374 	g_return_if_fail (cffd->email != NULL);
375 
376 	if (!cffd->folder)
377 		return;
378 
379 	if (!e_ews_subscrive_foreign_folder_subscribe_sync (CAMEL_EWS_STORE (with_object),
380 		cffd->folder, cffd->user_displayname, cffd->email, cffd->orig_foldername,
381 		cffd->include_subfolders, cancellable, perror)) {
382 		/* to not destroy the dialog on error */
383 		g_object_unref (cffd->folder);
384 		cffd->folder = NULL;
385 	}
386 }
387 
388 static gpointer
ref_selected_store(GObject * dialog)389 ref_selected_store (GObject *dialog)
390 {
391 	GtkComboBox *combo_box;
392 	CamelStore *store = NULL;
393 	GtkTreeIter iter;
394 
395 	combo_box = g_object_get_data (dialog, STR_ACCOUNTS_COMBO);
396 	g_return_val_if_fail (combo_box != NULL, NULL);
397 
398 	if (gtk_combo_box_get_active_iter (combo_box, &iter)) {
399 		gtk_tree_model_get (gtk_combo_box_get_model (combo_box), &iter,
400 			COLUMN_STORE, &store, -1);
401 	}
402 
403 	return store;
404 }
405 
406 static void
subscribe_foreign_response_cb(GObject * dialog,gint response_id)407 subscribe_foreign_response_cb (GObject *dialog,
408                                gint response_id)
409 {
410 	struct EEwsCheckForeignFolderData *cffd;
411 	ENameSelectorEntry *entry;
412 	GtkComboBoxText *combo_text;
413 	GtkToggleButton *subfolders_check;
414 	EDestinationStore *dest_store;
415 	CamelStore *cstore;
416 	gchar *description;
417 	const gchar *username;
418 	gchar *orig_foldername, *use_foldername = NULL, *show_foldername = NULL;
419 
420 	if (response_id != GTK_RESPONSE_OK) {
421 		gtk_widget_destroy (GTK_WIDGET (dialog));
422 		return;
423 	}
424 
425 	g_return_if_fail (dialog != NULL);
426 
427 	entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
428 	combo_text = g_object_get_data (dialog, STR_FOLDER_NAME_COMBO);
429 	subfolders_check = g_object_get_data (dialog, STR_SUBFOLDERS_CHECK);
430 
431 	g_return_if_fail (entry != NULL);
432 
433 	cstore = ref_selected_store (dialog);
434 	g_return_if_fail (cstore != NULL);
435 
436 	username = NULL;
437 	dest_store = e_name_selector_entry_peek_destination_store (entry);
438 	if (dest_store && e_destination_store_get_destination_count (dest_store) > 0) {
439 		EDestination *dest;
440 		GList *dests = e_destination_store_list_destinations (dest_store);
441 
442 		g_return_if_fail (dests != NULL);
443 
444 		/* pick the first, there is no option to limit to only one destination */
445 		dest = dests->data;
446 		if (dest) {
447 			username = e_destination_get_email (dest);
448 			if (!username || !*username)
449 				username = e_destination_get_name (dest);
450 		}
451 
452 		g_list_free (dests);
453 	}
454 
455 	if (!username || !*username)
456 		username = gtk_entry_get_text (GTK_ENTRY (entry));
457 
458 	orig_foldername = gtk_combo_box_text_get_active_text (combo_text);
459 	if (!orig_foldername)
460 		orig_foldername = g_strdup ("");
461 
462 	/* convert well-known names to their non-localized form */
463 	if (g_strcmp0 (orig_foldername, _("Inbox")) == 0) {
464 		use_foldername = g_strdup ("inbox");
465 	} else if (g_strcmp0 (orig_foldername, _("Contacts")) == 0) {
466 		use_foldername = g_strdup ("contacts");
467 	} else if (g_strcmp0 (orig_foldername, _("Calendar")) == 0) {
468 		use_foldername = g_strdup ("calendar");
469 	} else if (g_strcmp0 (orig_foldername, _("Free/Busy as Calendar")) == 0) {
470 		use_foldername = g_strdup ("freebusy-calendar");
471 	} else if (g_strcmp0 (orig_foldername, _("Memos")) == 0) {
472 		use_foldername = g_strdup ("notes");
473 	} else if (g_strcmp0 (orig_foldername, _("Tasks")) == 0) {
474 		use_foldername = g_strdup ("tasks");
475 	} else if (strlen (orig_foldername) > 13) {
476 		/* if it's a folder ID, then show only first 10 letters of it */
477 		show_foldername = g_strdup_printf ("%.10s…", orig_foldername);
478 	}
479 
480 	cffd = g_slice_new0 (struct EEwsCheckForeignFolderData);
481 	cffd->dialog = GTK_WIDGET (dialog);
482 	cffd->email = g_strdup (username ? username : "");
483 	cffd->direct_email = g_strdup (g_object_get_data (G_OBJECT (entry), STR_EWS_DIRECT_EMAIL));
484 	cffd->orig_foldername = orig_foldername;
485 	cffd->use_foldername = use_foldername;
486 	cffd->include_subfolders = gtk_toggle_button_get_active (subfolders_check);
487 	cffd->folder = NULL;
488 
489 	description = g_strdup_printf (
490 		_("Testing availability of folder “%s” of user “%s”, please wait…"),
491 		show_foldername ? show_foldername : cffd->orig_foldername, cffd->email);
492 
493 	e_ews_config_utils_run_in_thread_with_feedback (
494 		GTK_WINDOW (dialog),
495 		G_OBJECT (cstore),
496 		description,
497 		check_foreign_folder_thread,
498 		check_foreign_folder_idle,
499 		cffd,
500 		e_ews_check_foreign_folder_data_free);
501 
502 	g_free (description);
503 	g_free (show_foldername);
504 	g_object_unref (cstore);
505 }
506 
507 static void
pick_gal_user_clicked_cb(GtkButton * button,GObject * dialog)508 pick_gal_user_clicked_cb (GtkButton *button,
509                           GObject *dialog)
510 {
511 	GtkEntry *entry;
512 	CamelEwsStore *ews_store;
513 	EEwsConnection *conn;
514 	gchar *text, *display_name = NULL, *email = NULL;
515 
516 	g_return_if_fail (dialog != NULL);
517 
518 	entry = g_object_get_data (dialog, STR_USER_NAME_SELECTOR_ENTRY);
519 
520 	g_return_if_fail (entry != NULL);
521 
522 	ews_store = ref_selected_store (dialog);
523 	g_return_if_fail (ews_store != NULL);
524 
525 	text = g_strstrip (g_strdup (gtk_entry_get_text (entry)));
526 	conn = camel_ews_store_ref_connection (ews_store);
527 
528 	if (!conn) {
529 		e_notice (dialog, GTK_MESSAGE_ERROR, "%s", _("Cannot search for user when the account is offline"));
530 	} else if (e_ews_search_user_modal (GTK_WINDOW (dialog), conn, text, &display_name, &email)) {
531 		if (display_name && email && *email) {
532 			gtk_entry_set_text (entry, display_name);
533 			g_object_set_data_full (G_OBJECT (entry), STR_EWS_DIRECT_EMAIL, g_strdup (email), g_free);
534 		}
535 	}
536 
537 	g_free (text);
538 	g_free (display_name);
539 	g_free (email);
540 	g_object_unref (ews_store);
541 	g_clear_object (&conn);
542 }
543 
544 static gint
sort_accounts_by_display_name_cb(gconstpointer ptr1,gconstpointer ptr2)545 sort_accounts_by_display_name_cb (gconstpointer ptr1,
546 				  gconstpointer ptr2)
547 {
548 	CamelService *service1 = (CamelService *) ptr1;
549 	CamelService *service2 = (CamelService *) ptr2;
550 
551 	return g_utf8_collate (camel_service_get_display_name (service1), camel_service_get_display_name (service2));
552 }
553 
554 static GtkWidget *
create_accounts_combo(CamelSession * session,EClientCache * client_cache,CamelStore * store)555 create_accounts_combo (CamelSession *session,
556 		       EClientCache *client_cache,
557 		       CamelStore *store)
558 {
559 	GtkListStore *list_store;
560 	GtkTreeIter iter;
561 	GtkComboBox *combo_box;
562 	ESourceRegistry *registry;
563 	GList *services, *link, *accounts = NULL;
564 	GtkCellRenderer *renderer;
565 
566 	list_store = gtk_list_store_new (3,
567 		G_TYPE_STRING,		/* COLUMN_UID - UID of the CamelEwsStore */
568 		G_TYPE_STRING,		/* COLUMN_DISPLAY_NAME */
569 		CAMEL_TYPE_EWS_STORE);	/* COLUMN_STORE */
570 
571 	registry = e_client_cache_ref_registry (client_cache);
572 	services = camel_session_list_services (session);
573 
574 	for (link = services; link; link = g_list_next (link)) {
575 		CamelService *service = link->data;
576 
577 		if (CAMEL_IS_EWS_STORE (service)) {
578 			ESource *source;
579 
580 			source = e_source_registry_ref_source (registry, camel_service_get_uid (service));
581 			if (source && e_source_registry_check_enabled (registry, source)) {
582 				accounts = g_list_prepend (accounts, service);
583 			}
584 
585 			g_clear_object (&source);
586 		}
587 	}
588 
589 	accounts = g_list_sort (accounts, sort_accounts_by_display_name_cb);
590 
591 	for (link = accounts; link; link = g_list_next (link)) {
592 		CamelService *service = link->data;
593 
594 		gtk_list_store_append (list_store, &iter);
595 		gtk_list_store_set (list_store, &iter,
596 			COLUMN_UID, camel_service_get_uid (service),
597 			COLUMN_DISPLAY_NAME, camel_service_get_display_name (service),
598 			COLUMN_STORE, service,
599 			-1);
600 	}
601 
602 	g_list_free_full (services, g_object_unref);
603 	g_list_free (accounts);
604 	g_clear_object (&registry);
605 
606 	combo_box = GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store)));
607 	g_object_unref (list_store);
608 
609 	renderer = gtk_cell_renderer_text_new ();
610 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
611 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, "text", COLUMN_DISPLAY_NAME, NULL);
612 
613 	gtk_combo_box_set_id_column (combo_box, COLUMN_UID);
614 	if (store)
615 		gtk_combo_box_set_active_id (combo_box, camel_service_get_uid (CAMEL_SERVICE (store)));
616 	else if (accounts)
617 		gtk_combo_box_set_active (combo_box, 0);
618 
619 	return GTK_WIDGET (combo_box);
620 }
621 
622 /* Opens dialog to subscribe to folders of other
623  * users in the given store */
624 void
e_ews_subscribe_foreign_folder(GtkWindow * parent,CamelSession * session,CamelStore * store,EClientCache * client_cache)625 e_ews_subscribe_foreign_folder (GtkWindow *parent,
626                                 CamelSession *session,
627                                 CamelStore *store,
628                                 EClientCache *client_cache)
629 {
630 	ENameSelector *name_selector;
631 	ENameSelectorModel *name_selector_model;
632 	ENameSelectorDialog *name_selector_dialog;
633 	GObject *dialog;
634 	GtkWidget *content;
635 	GtkWidget *label, *widget, *entry, *check, *accounts_combo;
636 	GtkGrid *grid;
637 	GtkComboBoxText *combo_text;
638 	gint row;
639 
640 	g_return_if_fail (session != NULL);
641 	if (store)
642 		g_return_if_fail (CAMEL_IS_EWS_STORE (store));
643 	g_return_if_fail (E_IS_CLIENT_CACHE (client_cache));
644 
645 	dialog = G_OBJECT (gtk_dialog_new_with_buttons (
646 		_("Subscribe to folder of other EWS user…"),
647 		parent,
648 		GTK_DIALOG_DESTROY_WITH_PARENT,
649 		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
650 		GTK_STOCK_OK, GTK_RESPONSE_OK,
651 		NULL));
652 
653 	g_signal_connect (dialog, "response", G_CALLBACK (subscribe_foreign_response_cb), NULL);
654 
655 	content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
656 
657 	grid = GTK_GRID (gtk_grid_new ());
658 	gtk_grid_set_row_homogeneous (grid, FALSE);
659 	gtk_grid_set_row_spacing (grid, 6);
660 	gtk_grid_set_column_homogeneous (grid, FALSE);
661 	gtk_grid_set_column_spacing (grid, 6);
662 	gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
663 	gtk_container_add (GTK_CONTAINER (content), GTK_WIDGET (grid));
664 
665 	row = 0;
666 
667 	label = gtk_label_new (_("Account:"));
668 	g_object_set (
669 		G_OBJECT (label),
670 		"hexpand", FALSE,
671 		"vexpand", FALSE,
672 		"xalign", 0.0,
673 		"halign", GTK_ALIGN_START,
674 		NULL);
675 
676 	widget = create_accounts_combo (session, client_cache, store);
677 	g_object_set (
678 		G_OBJECT (widget),
679 		"hexpand", TRUE,
680 		"vexpand", FALSE,
681 		"halign", GTK_ALIGN_START,
682 		NULL);
683 	accounts_combo = widget;
684 
685 	gtk_grid_attach (grid, label, 0, row, 1, 1);
686 	gtk_grid_attach (grid, widget, 1, row, 2, 1);
687 
688 	row++;
689 
690 	name_selector = e_name_selector_new (client_cache);
691 	name_selector_model = e_name_selector_peek_model (name_selector);
692 	e_name_selector_model_add_section (name_selector_model, "User", _("User"), NULL);
693 	name_selector_dialog = e_name_selector_peek_dialog (name_selector);
694 	g_signal_connect (name_selector_dialog, "response", G_CALLBACK (gtk_widget_hide), name_selector);
695 	e_name_selector_load_books (name_selector);
696 
697 	g_object_set_data_full (dialog, "e-ews-name-selector", name_selector, g_object_unref);
698 
699 	label = gtk_label_new_with_mnemonic (_("_User:"));
700 	g_object_set (
701 		G_OBJECT (label),
702 		"hexpand", FALSE,
703 		"vexpand", FALSE,
704 		"xalign", 0.0,
705 		NULL);
706 
707 	entry = GTK_WIDGET (e_name_selector_peek_section_entry (name_selector, "User"));
708 	g_object_set (
709 		G_OBJECT (entry),
710 		"hexpand", TRUE,
711 		"vexpand", FALSE,
712 		NULL);
713 
714 	widget = gtk_button_new_with_mnemonic (_("C_hoose…"));
715 	g_object_set (
716 		G_OBJECT (entry),
717 		"hexpand", TRUE,
718 		"vexpand", FALSE,
719 		NULL);
720 
721 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
722 	g_signal_connect (widget, "clicked", G_CALLBACK (pick_gal_user_clicked_cb), dialog);
723 
724 	gtk_grid_attach (grid, label, 0, row, 1, 1);
725 	gtk_grid_attach (grid, entry, 1, row, 1, 1);
726 	gtk_grid_attach (grid, widget, 2, row, 1, 1);
727 
728 	row++;
729 
730 	label = gtk_label_new_with_mnemonic (_("_Folder name:"));
731 	g_object_set (
732 		G_OBJECT (label),
733 		"hexpand", FALSE,
734 		"vexpand", FALSE,
735 		"xalign", 0.0,
736 		NULL);
737 
738 	widget = GTK_WIDGET (
739 		g_object_new (gtk_combo_box_text_get_type (),
740 		"has-entry", TRUE,
741 		"entry-text-column", 0,
742 		"hexpand", TRUE,
743 		"vexpand", FALSE,
744 		NULL));
745 
746 	combo_text = GTK_COMBO_BOX_TEXT (widget);
747 	gtk_combo_box_text_append_text (combo_text, _("Inbox"));
748 	gtk_combo_box_text_append_text (combo_text, _("Contacts"));
749 	gtk_combo_box_text_append_text (combo_text, _("Calendar"));
750 	gtk_combo_box_text_append_text (combo_text, _("Free/Busy as Calendar"));
751 	gtk_combo_box_text_append_text (combo_text, _("Memos"));
752 	gtk_combo_box_text_append_text (combo_text, _("Tasks"));
753 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo_text), 0);
754 
755 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
756 	gtk_grid_attach (grid, label, 0, row, 1, 1);
757 	gtk_grid_attach (grid, widget, 1, row, 2, 1);
758 
759 	row++;
760 
761 	check = gtk_check_button_new_with_mnemonic (_("Include _subfolders"));
762 	gtk_grid_attach (grid, check, 1, row, 2, 1);
763 
764 	/* remember widgets for later use */
765 	g_object_set_data (dialog, STR_ACCOUNTS_COMBO, accounts_combo);
766 	g_object_set_data (dialog, STR_USER_NAME_SELECTOR_ENTRY, entry);
767 	g_object_set_data (dialog, STR_FOLDER_NAME_COMBO, widget);
768 	g_object_set_data (dialog, STR_SUBFOLDERS_CHECK, check);
769 
770 	g_object_set_data_full (dialog, STR_EWS_CAMEL_SESSION, g_object_ref (session), g_object_unref);
771 
772 	g_signal_connect_swapped (entry, "changed", G_CALLBACK (name_entry_changed_cb), dialog);
773 	g_signal_connect_swapped (combo_text, "changed", G_CALLBACK (folder_name_combo_changed_cb), dialog);
774 	g_signal_connect_swapped (accounts_combo, "changed", G_CALLBACK (name_entry_changed_cb), dialog);
775 
776 	name_entry_changed_cb (dialog);
777 
778 	gtk_widget_show_all (content);
779 	gtk_widget_show (GTK_WIDGET (dialog));
780 }
781 
782 gboolean
e_ews_subscribe_foreign_folder_resolve_name_sync(EEwsConnection * cnc,const gchar * name,gchar ** out_display_name,gchar ** out_email_address,GCancellable * cancellable,GError ** error)783 e_ews_subscribe_foreign_folder_resolve_name_sync (EEwsConnection *cnc,
784 						  const gchar *name,
785 						  gchar **out_display_name,
786 						  gchar **out_email_address,
787 						  GCancellable *cancellable,
788 						  GError **error)
789 {
790 	GSList *mailboxes = NULL;
791 	EwsMailbox *mailbox = NULL;
792 	gboolean includes_last_item = FALSE;
793 	GError *local_error = NULL;
794 
795 	if (!e_ews_connection_resolve_names_sync (cnc, G_PRIORITY_DEFAULT,
796 		name, EWS_SEARCH_AD, NULL, FALSE,
797 		&mailboxes, NULL, &includes_last_item,
798 		cancellable, &local_error)) {
799 		if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNORESULTS) ||
800 		    g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNOMAILBOX)) {
801 			g_clear_error (&local_error);
802 			mailboxes = NULL;
803 		} else {
804 			if (local_error)
805 				g_propagate_error (error, local_error);
806 			return FALSE;
807 		}
808 	}
809 
810 	if (mailboxes) {
811 		/* is there only one result? */
812 		if (!mailboxes->next) {
813 			mailbox = mailboxes->data;
814 		} else {
815 			GSList *iter;
816 
817 			for (iter = mailboxes; iter; iter = iter->next) {
818 				EwsMailbox *mb = iter->data;
819 
820 				if (!mb)
821 					continue;
822 
823 				if (mb->name && g_utf8_collate (mb->name, name) == 0) {
824 					mailbox = mb;
825 					break;
826 				}
827 			}
828 		}
829 
830 		if (mailbox) {
831 			if (out_display_name)
832 				*out_display_name = g_strdup (mailbox->name);
833 
834 			if (out_email_address)
835 				*out_email_address = g_strdup (mailbox->email);
836 		}
837 
838 		g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free);
839 
840 		if (!mailbox) {
841 			g_set_error (
842 				error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND,
843 				_("User name “%s” is ambiguous, specify it more precisely, please"), name);
844 			return FALSE;
845 		}
846 	}
847 
848 	return TRUE;
849 }
850 
851 gboolean
e_ews_subscrive_foreign_folder_subscribe_sync(CamelEwsStore * ews_store,EEwsFolder * folder,const gchar * user_display_name,const gchar * user_email,const gchar * fallback_folder_name,gboolean include_subfolders,GCancellable * cancellable,GError ** error)852 e_ews_subscrive_foreign_folder_subscribe_sync (CamelEwsStore *ews_store,
853 					       EEwsFolder *folder,
854 					       const gchar *user_display_name,
855 					       const gchar *user_email,
856 					       const gchar *fallback_folder_name,
857 					       gboolean include_subfolders,
858 					       GCancellable *cancellable,
859 					       GError **error)
860 {
861 	gchar *folder_name;
862 	const gchar *base_username, *base_foldername;
863 	CamelSettings *settings;
864 	CamelEwsSettings *ews_settings;
865 	ESourceRegistry *registry = NULL;
866 	CamelSession *session;
867 	EEwsFolderType folder_type;
868 	gboolean success;
869 
870 	folder_type = e_ews_folder_get_folder_type (folder);
871 	base_username = user_display_name ? user_display_name : user_email;
872 	base_foldername = e_ews_folder_get_name (folder) ? e_ews_folder_get_name (folder) : fallback_folder_name;
873 
874 	/* Translators: This is used to name foreign folder.
875 	 * The first '%s' is replaced with user name to whom the folder belongs,
876 	 * the second '%s' is replaced with folder name.
877 	 * Example result: "John Smith — Calendar"
878 	*/
879 	folder_name = g_strdup_printf (C_("ForeignFolder", "%s — %s"), base_username, base_foldername);
880 	if (folder_type != E_EWS_FOLDER_TYPE_MAILBOX)
881 		e_ews_folder_set_name (folder, folder_name);
882 
883 	settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store));
884 	ews_settings = CAMEL_EWS_SETTINGS (settings);
885 	session = camel_service_ref_session (CAMEL_SERVICE (ews_store));
886 	if (E_IS_MAIL_SESSION (session))
887 		registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
888 
889 	success = (folder_type == E_EWS_FOLDER_TYPE_MAILBOX &&
890 	     !add_foreign_folder_to_camel (ews_store,
891 		user_email,
892 		folder,
893 		include_subfolders,
894 		base_username,
895 		base_foldername,
896 		error)) ||
897 	    (folder_type != E_EWS_FOLDER_TYPE_MAILBOX && !e_ews_folder_utils_add_as_esource (registry,
898 		camel_ews_settings_get_hosturl (ews_settings),
899 		camel_network_settings_get_user (CAMEL_NETWORK_SETTINGS (ews_settings)),
900 		folder,
901 		(include_subfolders ? E_EWS_ESOURCE_FLAG_INCLUDE_SUBFOLDERS : 0) | E_EWS_ESOURCE_FLAG_OFFLINE_SYNC,
902 		0,
903 		cancellable,
904 		error));
905 
906 	g_free (folder_name);
907 	g_object_unref (session);
908 	g_object_unref (settings);
909 
910 	return success;
911 }
912