1 /*
2 * e-mail-config-auth-check.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "evolution-config.h"
19
20 #include <glib/gi18n-lib.h>
21
22 #include "e-util/e-util.h"
23 #include "mail/e-mail-config-service-page.h"
24
25 #include "e-mail-ui-session.h"
26
27 #include "e-mail-config-auth-check.h"
28
29 #define E_MAIL_CONFIG_AUTH_CHECK_GET_PRIVATE(obj) \
30 (G_TYPE_INSTANCE_GET_PRIVATE \
31 ((obj), E_TYPE_MAIL_CONFIG_AUTH_CHECK, EMailConfigAuthCheckPrivate))
32
33 typedef struct _AsyncContext AsyncContext;
34
35 struct _EMailConfigAuthCheckPrivate {
36 EMailConfigServiceBackend *backend;
37 gchar *active_mechanism;
38
39 GtkWidget *combo_box; /* not referenced */
40 gulong host_changed_id;
41 CamelServiceAuthType *used_xoauth2;
42 };
43
44 struct _AsyncContext {
45 EMailConfigAuthCheck *auth_check;
46 CamelSession *temporary_session;
47 EActivity *activity;
48 };
49
50 enum {
51 PROP_0,
52 PROP_ACTIVE_MECHANISM,
53 PROP_BACKEND
54 };
55
G_DEFINE_TYPE(EMailConfigAuthCheck,e_mail_config_auth_check,GTK_TYPE_BOX)56 G_DEFINE_TYPE (
57 EMailConfigAuthCheck,
58 e_mail_config_auth_check,
59 GTK_TYPE_BOX)
60
61 static void
62 async_context_free (AsyncContext *async_context)
63 {
64 if (async_context->auth_check != NULL)
65 g_object_unref (async_context->auth_check);
66
67 if (async_context->temporary_session != NULL)
68 g_object_unref (async_context->temporary_session);
69
70 if (async_context->activity != NULL)
71 g_object_unref (async_context->activity);
72
73 g_slice_free (AsyncContext, async_context);
74 }
75
76 static void
mail_config_auth_check_update_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)77 mail_config_auth_check_update_done_cb (GObject *source_object,
78 GAsyncResult *result,
79 gpointer user_data)
80 {
81 AsyncContext *async_context = user_data;
82 EMailConfigAuthCheck *auth_check;
83 EAlertSink *alert_sink;
84 GList *available_authtypes;
85 GError *error = NULL;
86
87 auth_check = async_context->auth_check;
88 alert_sink = e_activity_get_alert_sink (async_context->activity);
89
90 available_authtypes = camel_service_query_auth_types_finish (
91 CAMEL_SERVICE (source_object), result, &error);
92
93 if (e_activity_handle_cancellation (async_context->activity, error)) {
94 g_warn_if_fail (available_authtypes == NULL);
95 g_error_free (error);
96
97 } else if (error != NULL) {
98 g_warn_if_fail (available_authtypes == NULL);
99 e_alert_submit (
100 alert_sink,
101 "mail:checking-service-error",
102 error->message, NULL);
103 g_error_free (error);
104
105 } else {
106 e_auth_combo_box_update_available (
107 E_AUTH_COMBO_BOX (auth_check->priv->combo_box),
108 available_authtypes);
109 e_auth_combo_box_pick_highest_available (E_AUTH_COMBO_BOX (auth_check->priv->combo_box));
110
111 g_list_free (available_authtypes);
112 }
113
114 gtk_widget_set_sensitive (GTK_WIDGET (auth_check), TRUE);
115
116 async_context_free (async_context);
117 }
118
119 static void
mail_config_auth_check_update(EMailConfigAuthCheck * auth_check)120 mail_config_auth_check_update (EMailConfigAuthCheck *auth_check)
121 {
122 EActivity *activity;
123 ESource *source;
124 EMailConfigServicePage *page;
125 EMailConfigServiceBackend *backend;
126 EMailConfigServicePageClass *page_class;
127 EMailConfigServiceBackendClass *backend_class;
128 CamelService *service;
129 CamelSession *session;
130 CamelSettings *settings;
131 GCancellable *cancellable;
132 AsyncContext *async_context;
133 gchar *temp_dir;
134 GError *error = NULL;
135
136 backend = e_mail_config_auth_check_get_backend (auth_check);
137 page = e_mail_config_service_backend_get_page (backend);
138 settings = e_mail_config_service_backend_get_settings (backend);
139 source = e_mail_config_service_backend_get_source (backend);
140
141 page_class = E_MAIL_CONFIG_SERVICE_PAGE_GET_CLASS (page);
142 backend_class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
143
144 temp_dir = e_mkdtemp ("evolution-auth-check-XXXXXX");
145
146 /* Create a temporary session for our temporary service.
147 * Use the same temporary directory for "user-data-dir" and
148 * "user-cache-dir". For our purposes it shouldn't matter. */
149 session = g_object_new (
150 CAMEL_TYPE_SESSION,
151 "user-data-dir", temp_dir,
152 "user-cache-dir", temp_dir,
153 NULL);
154
155 /* to be able to answer for invalid/self-signed server certificates */
156 CAMEL_SESSION_GET_CLASS (session)->trust_prompt = e_mail_ui_session_trust_prompt;
157
158 service = camel_session_add_service (
159 session, "fake-uid",
160 backend_class->backend_name,
161 page_class->provider_type, &error);
162
163 g_free (temp_dir);
164
165 if (error != NULL) {
166 g_warn_if_fail (service == NULL);
167 e_alert_submit (
168 E_ALERT_SINK (page),
169 "mail:checking-service-error",
170 error->message, NULL);
171 g_error_free (error);
172 return;
173 }
174
175 g_return_if_fail (CAMEL_IS_SERVICE (service));
176
177 camel_service_set_settings (service, settings);
178
179 /* Setup Proxy */
180 if (source) {
181 ESourceRegistry *registry;
182 ESource *authentication_source;
183
184 registry = e_mail_config_service_page_get_registry (e_mail_config_service_backend_get_page (backend));
185 authentication_source = e_source_registry_find_extension (registry, source, E_SOURCE_EXTENSION_AUTHENTICATION);
186
187 if (authentication_source) {
188 GProxyResolver *proxy_resolver = NULL;
189 ESourceAuthentication *extension;
190 ESource *proxy_source = NULL;
191 gchar *uid;
192
193 extension = e_source_get_extension (authentication_source, E_SOURCE_EXTENSION_AUTHENTICATION);
194
195 uid = e_source_authentication_dup_proxy_uid (extension);
196 if (uid) {
197 proxy_source = e_source_registry_ref_source (registry, uid);
198 g_free (uid);
199 }
200
201 if (proxy_source) {
202 proxy_resolver = G_PROXY_RESOLVER (proxy_source);
203 if (!g_proxy_resolver_is_supported (proxy_resolver))
204 proxy_resolver = NULL;
205 }
206
207 camel_service_set_proxy_resolver (service, proxy_resolver);
208
209 g_clear_object (&authentication_source);
210 g_clear_object (&proxy_source);
211 }
212 }
213
214 activity = e_mail_config_activity_page_new_activity (
215 E_MAIL_CONFIG_ACTIVITY_PAGE (page));
216 cancellable = e_activity_get_cancellable (activity);
217 e_activity_set_text (activity, _("Querying authentication types…"));
218
219 gtk_widget_set_sensitive (GTK_WIDGET (auth_check), FALSE);
220
221 async_context = g_slice_new (AsyncContext);
222 async_context->auth_check = g_object_ref (auth_check);
223 async_context->temporary_session = session; /* takes ownership */
224 async_context->activity = activity; /* takes ownership */
225
226 camel_service_query_auth_types (
227 service, G_PRIORITY_DEFAULT, cancellable,
228 mail_config_auth_check_update_done_cb, async_context);
229
230 g_object_unref (service);
231 }
232
233 static void
mail_config_auth_check_clicked_cb(GtkButton * button,EMailConfigAuthCheck * auth_check)234 mail_config_auth_check_clicked_cb (GtkButton *button,
235 EMailConfigAuthCheck *auth_check)
236 {
237 mail_config_auth_check_update (auth_check);
238 }
239
240 static void
mail_config_auth_check_init_mechanism(EMailConfigAuthCheck * auth_check)241 mail_config_auth_check_init_mechanism (EMailConfigAuthCheck *auth_check)
242 {
243 EMailConfigServiceBackend *backend;
244 CamelProvider *provider;
245 CamelSettings *settings;
246 const gchar *auth_mechanism = NULL;
247
248 /* Pick an initial active mechanism name by examining both
249 * the corresponding CamelNetworkSettings and CamelProvider. */
250
251 backend = e_mail_config_auth_check_get_backend (auth_check);
252 provider = e_mail_config_service_backend_get_provider (backend);
253 settings = e_mail_config_service_backend_get_settings (backend);
254 g_return_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings));
255
256 auth_mechanism =
257 camel_network_settings_get_auth_mechanism (
258 CAMEL_NETWORK_SETTINGS (settings));
259
260 /* If CamelNetworkSettings does not have a mechanism name set,
261 * choose from the CamelProvider's list of supported mechanisms. */
262 if (auth_mechanism == NULL && provider != NULL) {
263 if (provider->authtypes != NULL) {
264 CamelServiceAuthType *auth_type;
265 auth_type = provider->authtypes->data;
266 auth_mechanism = auth_type->authproto;
267 }
268 }
269
270 if (auth_mechanism != NULL)
271 e_mail_config_auth_check_set_active_mechanism (
272 auth_check, auth_mechanism);
273 }
274
275 static void
mail_config_auth_check_host_changed_cb(CamelNetworkSettings * network_settings,GParamSpec * param,EMailConfigAuthCheck * auth_check)276 mail_config_auth_check_host_changed_cb (CamelNetworkSettings *network_settings,
277 GParamSpec *param,
278 EMailConfigAuthCheck *auth_check)
279 {
280 EMailConfigServiceBackend *backend;
281 ESourceRegistry *registry;
282 EOAuth2Service *oauth2_service;
283 CamelProvider *provider;
284 CamelServiceAuthType *change_authtype = NULL;
285
286 g_return_if_fail (CAMEL_IS_NETWORK_SETTINGS (network_settings));
287 g_return_if_fail (E_IS_MAIL_CONFIG_AUTH_CHECK (auth_check));
288
289 backend = e_mail_config_auth_check_get_backend (auth_check);
290 provider = e_mail_config_service_backend_get_provider (backend);
291
292 registry = e_mail_config_service_page_get_registry (e_mail_config_service_backend_get_page (backend));
293 oauth2_service = e_oauth2_services_find (e_source_registry_get_oauth2_services (registry),
294 e_mail_config_service_backend_get_source (backend));
295 if (!oauth2_service) {
296 oauth2_service = e_oauth2_services_guess (e_source_registry_get_oauth2_services (registry),
297 provider ? provider->protocol : NULL, camel_network_settings_get_host (network_settings));
298 }
299
300 if (oauth2_service)
301 change_authtype = camel_sasl_authtype (e_oauth2_service_get_name (oauth2_service));
302
303 g_clear_object (&oauth2_service);
304
305 if (change_authtype != auth_check->priv->used_xoauth2) {
306 if (auth_check->priv->used_xoauth2)
307 e_auth_combo_box_remove_auth_type (E_AUTH_COMBO_BOX (auth_check->priv->combo_box), auth_check->priv->used_xoauth2);
308
309 auth_check->priv->used_xoauth2 = change_authtype;
310
311 if (auth_check->priv->used_xoauth2)
312 e_auth_combo_box_add_auth_type (E_AUTH_COMBO_BOX (auth_check->priv->combo_box), auth_check->priv->used_xoauth2);
313 }
314 }
315
316 static void
mail_config_auth_check_set_backend(EMailConfigAuthCheck * auth_check,EMailConfigServiceBackend * backend)317 mail_config_auth_check_set_backend (EMailConfigAuthCheck *auth_check,
318 EMailConfigServiceBackend *backend)
319 {
320 g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
321 g_return_if_fail (auth_check->priv->backend == NULL);
322
323 auth_check->priv->backend = g_object_ref (backend);
324 }
325
326 static void
mail_config_auth_check_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)327 mail_config_auth_check_set_property (GObject *object,
328 guint property_id,
329 const GValue *value,
330 GParamSpec *pspec)
331 {
332 switch (property_id) {
333 case PROP_ACTIVE_MECHANISM:
334 e_mail_config_auth_check_set_active_mechanism (
335 E_MAIL_CONFIG_AUTH_CHECK (object),
336 g_value_get_string (value));
337 return;
338
339 case PROP_BACKEND:
340 mail_config_auth_check_set_backend (
341 E_MAIL_CONFIG_AUTH_CHECK (object),
342 g_value_get_object (value));
343 return;
344 }
345
346 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
347 }
348
349 static void
mail_config_auth_check_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)350 mail_config_auth_check_get_property (GObject *object,
351 guint property_id,
352 GValue *value,
353 GParamSpec *pspec)
354 {
355 switch (property_id) {
356 case PROP_ACTIVE_MECHANISM:
357 g_value_set_string (
358 value,
359 e_mail_config_auth_check_get_active_mechanism (
360 E_MAIL_CONFIG_AUTH_CHECK (object)));
361 return;
362
363 case PROP_BACKEND:
364 g_value_set_object (
365 value,
366 e_mail_config_auth_check_get_backend (
367 E_MAIL_CONFIG_AUTH_CHECK (object)));
368 return;
369 }
370
371 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
372 }
373
374 static void
mail_config_auth_check_dispose(GObject * object)375 mail_config_auth_check_dispose (GObject *object)
376 {
377 EMailConfigAuthCheckPrivate *priv;
378
379 priv = E_MAIL_CONFIG_AUTH_CHECK_GET_PRIVATE (object);
380
381 if (priv->backend != NULL) {
382 if (priv->host_changed_id) {
383 CamelSettings *settings;
384
385 settings = e_mail_config_service_backend_get_settings (priv->backend);
386 if (settings)
387 e_signal_disconnect_notify_handler (settings, &priv->host_changed_id);
388 }
389
390 g_object_unref (priv->backend);
391 priv->backend = NULL;
392 }
393
394 /* Chain up to parent's dispose() method. */
395 G_OBJECT_CLASS (e_mail_config_auth_check_parent_class)->
396 dispose (object);
397 }
398
399 static void
mail_config_auth_check_finalize(GObject * object)400 mail_config_auth_check_finalize (GObject *object)
401 {
402 EMailConfigAuthCheckPrivate *priv;
403
404 priv = E_MAIL_CONFIG_AUTH_CHECK_GET_PRIVATE (object);
405
406 g_free (priv->active_mechanism);
407
408 /* Chain up to parent's finalize() method. */
409 G_OBJECT_CLASS (e_mail_config_auth_check_parent_class)->
410 finalize (object);
411 }
412
413 static void
mail_config_auth_check_constructed(GObject * object)414 mail_config_auth_check_constructed (GObject *object)
415 {
416 EMailConfigAuthCheck *auth_check;
417 EMailConfigServiceBackend *backend;
418 CamelProvider *provider;
419 CamelSettings *settings;
420 GtkWidget *widget;
421 const gchar *text;
422
423 /* Chain up to parent's constructed() method. */
424 G_OBJECT_CLASS (e_mail_config_auth_check_parent_class)->constructed (object);
425
426 auth_check = E_MAIL_CONFIG_AUTH_CHECK (object);
427 backend = e_mail_config_auth_check_get_backend (auth_check);
428 provider = e_mail_config_service_backend_get_provider (backend);
429
430 text = _("Check for Supported Types");
431 widget = gtk_button_new_with_label (text);
432 gtk_box_pack_start (GTK_BOX (object), widget, FALSE, FALSE, 0);
433 gtk_widget_show (widget);
434
435 g_signal_connect (
436 widget, "clicked",
437 G_CALLBACK (mail_config_auth_check_clicked_cb),
438 auth_check);
439
440 widget = e_auth_combo_box_new ();
441 e_auth_combo_box_set_provider (E_AUTH_COMBO_BOX (widget), provider);
442 gtk_box_pack_start (GTK_BOX (object), widget, FALSE, FALSE, 0);
443 auth_check->priv->combo_box = widget; /* do not reference */
444 gtk_widget_show (widget);
445
446 settings = e_mail_config_service_backend_get_settings (backend);
447 if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
448 ESourceRegistry *registry;
449 EOAuth2Service *oauth2_service;
450
451 auth_check->priv->host_changed_id = e_signal_connect_notify (
452 settings, "notify::host", G_CALLBACK (mail_config_auth_check_host_changed_cb), auth_check);
453
454 registry = e_mail_config_service_page_get_registry (e_mail_config_service_backend_get_page (backend));
455 oauth2_service = e_oauth2_services_find (e_source_registry_get_oauth2_services (registry),
456 e_mail_config_service_backend_get_source (backend));
457 if (!oauth2_service) {
458 oauth2_service = e_oauth2_services_guess (e_source_registry_get_oauth2_services (registry),
459 provider ? provider->protocol : NULL, camel_network_settings_get_host (CAMEL_NETWORK_SETTINGS (settings)));
460 }
461
462 if (oauth2_service)
463 auth_check->priv->used_xoauth2 = camel_sasl_authtype (e_oauth2_service_get_name (oauth2_service));
464
465 g_clear_object (&oauth2_service);
466
467 if (auth_check->priv->used_xoauth2)
468 e_auth_combo_box_add_auth_type (E_AUTH_COMBO_BOX (auth_check->priv->combo_box), auth_check->priv->used_xoauth2);
469 }
470
471 e_binding_bind_property (
472 widget, "active-id",
473 auth_check, "active-mechanism",
474 G_BINDING_BIDIRECTIONAL |
475 G_BINDING_SYNC_CREATE);
476
477 mail_config_auth_check_init_mechanism (auth_check);
478 }
479
480 static void
e_mail_config_auth_check_class_init(EMailConfigAuthCheckClass * class)481 e_mail_config_auth_check_class_init (EMailConfigAuthCheckClass *class)
482 {
483 GObjectClass *object_class;
484
485 g_type_class_add_private (class, sizeof (EMailConfigAuthCheckPrivate));
486
487 object_class = G_OBJECT_CLASS (class);
488 object_class->set_property = mail_config_auth_check_set_property;
489 object_class->get_property = mail_config_auth_check_get_property;
490 object_class->dispose = mail_config_auth_check_dispose;
491 object_class->finalize = mail_config_auth_check_finalize;
492 object_class->constructed = mail_config_auth_check_constructed;
493
494 g_object_class_install_property (
495 object_class,
496 PROP_ACTIVE_MECHANISM,
497 g_param_spec_string (
498 "active-mechanism",
499 "Active Mechanism",
500 "Active authentication mechanism",
501 NULL,
502 G_PARAM_READWRITE |
503 G_PARAM_STATIC_STRINGS));
504
505 g_object_class_install_property (
506 object_class,
507 PROP_BACKEND,
508 g_param_spec_object (
509 "backend",
510 "Backend",
511 "Mail configuration backend",
512 E_TYPE_MAIL_CONFIG_SERVICE_BACKEND,
513 G_PARAM_READWRITE |
514 G_PARAM_CONSTRUCT_ONLY |
515 G_PARAM_STATIC_STRINGS));
516 }
517
518 static void
e_mail_config_auth_check_init(EMailConfigAuthCheck * auth_check)519 e_mail_config_auth_check_init (EMailConfigAuthCheck *auth_check)
520 {
521 auth_check->priv = E_MAIL_CONFIG_AUTH_CHECK_GET_PRIVATE (auth_check);
522 auth_check->priv->host_changed_id = 0;
523 auth_check->priv->used_xoauth2 = NULL;
524
525 gtk_orientable_set_orientation (
526 GTK_ORIENTABLE (auth_check),
527 GTK_ORIENTATION_HORIZONTAL);
528
529 gtk_box_set_spacing (GTK_BOX (auth_check), 6);
530 }
531
532 GtkWidget *
e_mail_config_auth_check_new(EMailConfigServiceBackend * backend)533 e_mail_config_auth_check_new (EMailConfigServiceBackend *backend)
534 {
535 g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
536
537 return g_object_new (
538 E_TYPE_MAIL_CONFIG_AUTH_CHECK,
539 "backend", backend, NULL);
540 }
541
542 EMailConfigServiceBackend *
e_mail_config_auth_check_get_backend(EMailConfigAuthCheck * auth_check)543 e_mail_config_auth_check_get_backend (EMailConfigAuthCheck *auth_check)
544 {
545 g_return_val_if_fail (E_IS_MAIL_CONFIG_AUTH_CHECK (auth_check), NULL);
546
547 return auth_check->priv->backend;
548 }
549
550 const gchar *
e_mail_config_auth_check_get_active_mechanism(EMailConfigAuthCheck * auth_check)551 e_mail_config_auth_check_get_active_mechanism (EMailConfigAuthCheck *auth_check)
552 {
553 g_return_val_if_fail (E_IS_MAIL_CONFIG_AUTH_CHECK (auth_check), NULL);
554
555 return auth_check->priv->active_mechanism;
556 }
557
558 void
e_mail_config_auth_check_set_active_mechanism(EMailConfigAuthCheck * auth_check,const gchar * active_mechanism)559 e_mail_config_auth_check_set_active_mechanism (EMailConfigAuthCheck *auth_check,
560 const gchar *active_mechanism)
561 {
562 g_return_if_fail (E_IS_MAIL_CONFIG_AUTH_CHECK (auth_check));
563
564 if (g_strcmp0 (auth_check->priv->active_mechanism, active_mechanism) == 0)
565 return;
566
567 g_free (auth_check->priv->active_mechanism);
568 /* Do not allow NULL here, thus Password auth, which uses
569 empty string as ID, will match in the combo */
570 auth_check->priv->active_mechanism = g_strdup (active_mechanism ? active_mechanism : "");
571
572 g_object_notify (G_OBJECT (auth_check), "active-mechanism");
573 }
574