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