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 <mail/e-mail-config-service-page.h>
10 
11 #include "common/e-ews-connection.h"
12 #include "common/e-ews-connection-utils.h"
13 
14 #include "e-ews-config-utils.h"
15 #include "e-mail-config-ews-oal-combo-box.h"
16 
17 struct _EMailConfigEwsOalComboBoxPrivate {
18 	EMailConfigServiceBackend *backend;
19 
20 	/* The try_password() method deposits results here, and the
21 	 * update_finish() function uses the results to re-populate
22 	 * the combo box.  This avoids calling GTK+ functions from
23 	 * multiple threads. */
24 	GSList *oal_items;
25 	GMutex oal_items_lock;
26 };
27 
28 enum {
29 	PROP_0,
30 	PROP_BACKEND
31 };
32 
33 G_DEFINE_DYNAMIC_TYPE_EXTENDED (EMailConfigEwsOalComboBox, e_mail_config_ews_oal_combo_box, GTK_TYPE_COMBO_BOX_TEXT, 0,
34 	G_ADD_PRIVATE_DYNAMIC (EMailConfigEwsOalComboBox))
35 
36 typedef struct _AsyncContext {
37 	EMailConfigEwsOalComboBox *combo_box;
38 	GSimpleAsyncResult *simple;
39 	ESource *source;
40 	GObject *settings;
41 } AsyncContext;
42 
43 static void
async_context_free(gpointer ptr)44 async_context_free (gpointer ptr)
45 {
46 	AsyncContext *async_context = ptr;
47 
48 	if (!async_context)
49 		return;
50 
51 	if (async_context->settings)
52 		g_object_thaw_notify (async_context->settings);
53 
54 	g_clear_object (&async_context->combo_box);
55 	g_clear_object (&async_context->simple);
56 	g_clear_object (&async_context->source);
57 	g_clear_object (&async_context->settings);
58 
59 	g_slice_free (AsyncContext, async_context);
60 }
61 
62 static void
mail_config_ews_oal_combo_box_set_backend(EMailConfigEwsOalComboBox * combo_box,EMailConfigServiceBackend * backend)63 mail_config_ews_oal_combo_box_set_backend (EMailConfigEwsOalComboBox *combo_box,
64                                            EMailConfigServiceBackend *backend)
65 {
66 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
67 	g_return_if_fail (combo_box->priv->backend == NULL);
68 
69 	combo_box->priv->backend = g_object_ref (backend);
70 }
71 
72 static void
mail_config_ews_oal_combo_box_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)73 mail_config_ews_oal_combo_box_set_property (GObject *object,
74                                             guint property_id,
75                                             const GValue *value,
76                                             GParamSpec *pspec)
77 {
78 	switch (property_id) {
79 		case PROP_BACKEND:
80 			mail_config_ews_oal_combo_box_set_backend (
81 				E_MAIL_CONFIG_EWS_OAL_COMBO_BOX (object),
82 				g_value_get_object (value));
83 			return;
84 	}
85 
86 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
87 }
88 
89 static void
mail_config_ews_oal_combo_box_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)90 mail_config_ews_oal_combo_box_get_property (GObject *object,
91                                             guint property_id,
92                                             GValue *value,
93                                             GParamSpec *pspec)
94 {
95 	switch (property_id) {
96 		case PROP_BACKEND:
97 			g_value_set_object (
98 				value,
99 				e_mail_config_ews_oal_combo_box_get_backend (
100 				E_MAIL_CONFIG_EWS_OAL_COMBO_BOX (object)));
101 			return;
102 	}
103 
104 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
105 }
106 
107 static void
mail_config_ews_oal_combo_box_dispose(GObject * object)108 mail_config_ews_oal_combo_box_dispose (GObject *object)
109 {
110 	EMailConfigEwsOalComboBox *ews_combo = E_MAIL_CONFIG_EWS_OAL_COMBO_BOX (object);
111 
112 	g_clear_object (&ews_combo->priv->backend);
113 
114 	/* Chain up to parent's dispose() method. */
115 	G_OBJECT_CLASS (e_mail_config_ews_oal_combo_box_parent_class)->dispose (object);
116 }
117 
118 static void
mail_config_ews_oal_combo_box_finalize(GObject * object)119 mail_config_ews_oal_combo_box_finalize (GObject *object)
120 {
121 	EMailConfigEwsOalComboBox *ews_combo = E_MAIL_CONFIG_EWS_OAL_COMBO_BOX (object);
122 
123 	g_mutex_clear (&ews_combo->priv->oal_items_lock);
124 
125 	/* Chain up to parent's finalize() method. */
126 	G_OBJECT_CLASS (e_mail_config_ews_oal_combo_box_parent_class)->finalize (object);
127 }
128 
129 static void
e_mail_config_ews_oal_combo_box_class_init(EMailConfigEwsOalComboBoxClass * class)130 e_mail_config_ews_oal_combo_box_class_init (EMailConfigEwsOalComboBoxClass *class)
131 {
132 	GObjectClass *object_class;
133 
134 	object_class = G_OBJECT_CLASS (class);
135 	object_class->set_property = mail_config_ews_oal_combo_box_set_property;
136 	object_class->get_property = mail_config_ews_oal_combo_box_get_property;
137 	object_class->dispose = mail_config_ews_oal_combo_box_dispose;
138 	object_class->finalize = mail_config_ews_oal_combo_box_finalize;
139 
140 	g_object_class_install_property (
141 		object_class,
142 		PROP_BACKEND,
143 		g_param_spec_object (
144 			"backend",
145 			"Backend",
146 			"Service backend",
147 			E_TYPE_MAIL_CONFIG_SERVICE_BACKEND,
148 			G_PARAM_READWRITE |
149 			G_PARAM_CONSTRUCT_ONLY));
150 }
151 
152 static void
e_mail_config_ews_oal_combo_box_class_finalize(EMailConfigEwsOalComboBoxClass * class)153 e_mail_config_ews_oal_combo_box_class_finalize (EMailConfigEwsOalComboBoxClass *class)
154 {
155 }
156 
157 static void
e_mail_config_ews_oal_combo_box_init(EMailConfigEwsOalComboBox * combo_box)158 e_mail_config_ews_oal_combo_box_init (EMailConfigEwsOalComboBox *combo_box)
159 {
160 	combo_box->priv = e_mail_config_ews_oal_combo_box_get_instance_private (combo_box);
161 
162 	g_mutex_init (&combo_box->priv->oal_items_lock);
163 }
164 
165 void
e_mail_config_ews_oal_combo_box_type_register(GTypeModule * type_module)166 e_mail_config_ews_oal_combo_box_type_register (GTypeModule *type_module)
167 {
168 	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
169 	 *     function, so we have to wrap it with a public function in
170 	 *     order to register types from a separate compilation unit. */
171 	e_mail_config_ews_oal_combo_box_register_type (type_module);
172 }
173 
174 GtkWidget *
e_mail_config_ews_oal_combo_box_new(EMailConfigServiceBackend * backend)175 e_mail_config_ews_oal_combo_box_new (EMailConfigServiceBackend *backend)
176 {
177 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
178 
179 	return g_object_new (
180 		E_TYPE_MAIL_CONFIG_EWS_OAL_COMBO_BOX,
181 		"backend", backend, NULL);
182 }
183 
184 EMailConfigServiceBackend *
e_mail_config_ews_oal_combo_box_get_backend(EMailConfigEwsOalComboBox * combo_box)185 e_mail_config_ews_oal_combo_box_get_backend (EMailConfigEwsOalComboBox *combo_box)
186 {
187 	g_return_val_if_fail (
188 		E_IS_MAIL_CONFIG_EWS_OAL_COMBO_BOX (combo_box), NULL);
189 
190 	return combo_box->priv->backend;
191 }
192 
193 static ESourceAuthenticationResult
mail_config_ews_aol_combo_box_update_try_credentials_sync(EEwsConnection * connection,const ENamedParameters * credentials,gpointer user_data,GCancellable * cancellable,GError ** error)194 mail_config_ews_aol_combo_box_update_try_credentials_sync (EEwsConnection *connection,
195 							   const ENamedParameters *credentials,
196 							   gpointer user_data,
197 							   GCancellable *cancellable,
198 							   GError **error)
199 {
200 	AsyncContext *async_context = user_data;
201 	EMailConfigEwsOalComboBox *combo_box;
202 	ESourceAuthenticationResult result;
203 	GSList *oal_items = NULL;
204 	GError *local_error = NULL;
205 
206 	combo_box = E_MAIL_CONFIG_EWS_OAL_COMBO_BOX (async_context->combo_box);
207 
208 	e_ews_connection_get_oal_list_sync (connection, &oal_items, cancellable, &local_error);
209 
210 	if (local_error == NULL) {
211 		result = E_SOURCE_AUTHENTICATION_ACCEPTED;
212 
213 		/* Deposit results in the private struct for
214 		 * the update_finish() function to pick up. */
215 		g_mutex_lock (&combo_box->priv->oal_items_lock);
216 		g_slist_free_full (
217 			combo_box->priv->oal_items,
218 			(GDestroyNotify) ews_oal_free);
219 		combo_box->priv->oal_items = oal_items;
220 		g_mutex_unlock (&combo_box->priv->oal_items_lock);
221 
222 	} else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
223 		result = E_SOURCE_AUTHENTICATION_REJECTED;
224 		g_error_free (local_error);
225 
226 	} else {
227 		result = E_SOURCE_AUTHENTICATION_ERROR;
228 		g_propagate_error (error, local_error);
229 	}
230 
231 	return result;
232 }
233 
234 static void
mail_config_ews_aol_combo_box_update_thread_cb(GObject * with_object,gpointer user_data,GCancellable * cancellable,GError ** perror)235 mail_config_ews_aol_combo_box_update_thread_cb (GObject *with_object,
236 						gpointer user_data,
237 						GCancellable *cancellable,
238 						GError **perror)
239 {
240 	AsyncContext *async_context = user_data;
241 	CamelEwsSettings *ews_settings;
242 	const gchar *oab_url;
243 	EEwsConnection *connection;
244 
245 	if (g_cancellable_set_error_if_cancelled (cancellable, perror))
246 		return;
247 
248 	ews_settings = CAMEL_EWS_SETTINGS (async_context->settings);
249 	oab_url = camel_ews_settings_get_oaburl (ews_settings);
250 
251 	connection = e_ews_config_utils_open_connection_for (async_context->source, ews_settings, oab_url,
252 		mail_config_ews_aol_combo_box_update_try_credentials_sync, async_context, cancellable, perror);
253 
254 	g_clear_object (&connection);
255 }
256 
257 static void
mail_config_ews_aol_combo_box_update_idle_cb(GObject * with_object,gpointer user_data,GCancellable * cancellable,GError ** perror)258 mail_config_ews_aol_combo_box_update_idle_cb (GObject *with_object,
259 					      gpointer user_data,
260 					      GCancellable *cancellable,
261 					      GError **perror)
262 {
263 	AsyncContext *async_context;
264 	GError *error = NULL;
265 
266 	async_context = (AsyncContext *) user_data;
267 
268 	if (perror) {
269 		error = *perror;
270 		*perror = NULL;
271 	}
272 
273 	if (error != NULL)
274 		g_simple_async_result_take_error (async_context->simple, error);
275 
276 	g_simple_async_result_complete (async_context->simple);
277 }
278 
279 void
e_mail_config_ews_oal_combo_box_update(EMailConfigEwsOalComboBox * combo_box,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)280 e_mail_config_ews_oal_combo_box_update (EMailConfigEwsOalComboBox *combo_box,
281                                         GCancellable *cancellable,
282                                         GAsyncReadyCallback callback,
283                                         gpointer user_data)
284 {
285 	GSimpleAsyncResult *simple;
286 	EMailConfigServiceBackend *backend;
287 	AsyncContext *async_context;
288 	CamelSettings *settings;
289 	ESource *source;
290 
291 	g_return_if_fail (E_IS_MAIL_CONFIG_EWS_OAL_COMBO_BOX (combo_box));
292 
293 	backend = e_mail_config_ews_oal_combo_box_get_backend (combo_box);
294 	settings = e_mail_config_service_backend_get_settings (backend);
295 	source = e_mail_config_service_backend_get_source (backend);
296 
297 	if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
298 		ESource *collection;
299 
300 		collection = e_mail_config_service_backend_get_collection (backend);
301 		if (collection && e_source_has_extension (collection, E_SOURCE_EXTENSION_AUTHENTICATION)) {
302 			source = collection;
303 		}
304 	}
305 
306 	simple = g_simple_async_result_new (
307 		G_OBJECT (combo_box), callback, user_data,
308 		e_mail_config_ews_oal_combo_box_update);
309 
310 	async_context = g_slice_new0 (AsyncContext);
311 	async_context->combo_box = g_object_ref (combo_box);
312 	async_context->simple = simple;  /* takes ownership */
313 	async_context->source = g_object_ref (source);
314 	async_context->settings = G_OBJECT (g_object_ref (settings));
315 
316 	/* Property changes can cause update of the UI, but this runs in a thread,
317 	   thus freeze the notify till be back in UI thread */
318 	g_object_freeze_notify (async_context->settings);
319 
320 	e_ews_config_utils_run_in_thread (G_OBJECT (combo_box),
321 		mail_config_ews_aol_combo_box_update_thread_cb,
322 		mail_config_ews_aol_combo_box_update_idle_cb,
323 		async_context, async_context_free, cancellable);
324 }
325 
326 gboolean
e_mail_config_ews_oal_combo_box_update_finish(EMailConfigEwsOalComboBox * combo_box,GAsyncResult * result,GError ** error)327 e_mail_config_ews_oal_combo_box_update_finish (EMailConfigEwsOalComboBox *combo_box,
328                                                GAsyncResult *result,
329                                                GError **error)
330 {
331 	GSimpleAsyncResult *simple;
332 	GtkComboBoxText *combo_box_text;
333 	GSList *list, *link;
334 	gchar *active_id;
335 
336 	g_return_val_if_fail (
337 		g_simple_async_result_is_valid (
338 		result, G_OBJECT (combo_box),
339 		e_mail_config_ews_oal_combo_box_update), FALSE);
340 
341 	simple = G_SIMPLE_ASYNC_RESULT (result);
342 
343 	if (g_simple_async_result_propagate_error (simple, error))
344 		return FALSE;
345 
346 	/* Re-populate the combo box using the cached results. */
347 
348 	g_mutex_lock (&combo_box->priv->oal_items_lock);
349 	list = combo_box->priv->oal_items;
350 	combo_box->priv->oal_items = NULL;
351 	g_mutex_unlock (&combo_box->priv->oal_items_lock);
352 
353 	active_id = g_strdup (gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box)));
354 	combo_box_text = GTK_COMBO_BOX_TEXT (combo_box);
355 	gtk_combo_box_text_remove_all (combo_box_text);
356 
357 	for (link = list; link != NULL; link = g_slist_next (link)) {
358 		EwsOAL *oal = link->data;
359 		const gchar *name = oal->name;
360 
361 		while (name && *name == '\\')
362 			name++;
363 
364 		gtk_combo_box_text_append (
365 			combo_box_text, oal->id, name);
366 	}
367 
368 	g_slist_free_full (list, (GDestroyNotify) ews_oal_free);
369 
370 	if (active_id && *active_id)
371 		gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), active_id);
372 	else
373 		gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
374 	g_free (active_id);
375 
376 	return TRUE;
377 }
378 
379