1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * SPDX-FileCopyrightText: (C) 2020 Red Hat (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 
11 #include <camel/camel.h>
12 #include <libebackend/libebackend.h>
13 
14 #include <mail/e-mail-config-auth-check.h>
15 #include <mail/e-mail-config-receiving-page.h>
16 
17 #include "common/camel-m365-settings.h"
18 #include "common/e-m365-connection.h"
19 
20 #include "e-mail-config-m365-backend.h"
21 
22 struct _EMailConfigM365BackendPrivate {
23 	GtkWidget *user_entry;
24 	GtkWidget *impersonate_user_entry;
25 	GtkGrid *oauth2_settings_grid;
26 	GtkWidget *oauth2_override_check;
27 	GtkWidget *oauth2_tenant_entry;
28 	GtkWidget *oauth2_client_id_entry;
29 	GtkWidget *oauth2_redirect_uri_entry;
30 	GtkWidget *oauth2_endpoint_host_entry;
31 };
32 
33 G_DEFINE_DYNAMIC_TYPE_EXTENDED (EMailConfigM365Backend, e_mail_config_m365_backend, E_TYPE_MAIL_CONFIG_SERVICE_BACKEND, 0,
34 	G_ADD_PRIVATE_DYNAMIC (EMailConfigM365Backend))
35 
36 static ESource *
mail_config_m365_backend_new_collection(EMailConfigServiceBackend * backend)37 mail_config_m365_backend_new_collection (EMailConfigServiceBackend *backend)
38 {
39 	EMailConfigServiceBackendClass *class;
40 	ESourceBackend *extension;
41 	ESource *source;
42 	const gchar *extension_name;
43 
44 	/* This backend serves double duty.  One instance holds the
45 	 * mail account source, another holds the mail transport source.
46 	 * We can differentiate by examining the EMailConfigServicePage
47 	 * the backend is associated with.  We return a new collection
48 	 * for both the Receiving Page and Sending Page.  Although the
49 	 * Sending Page instance ultimately gets discarded, it's still
50 	 * needed to avoid creating a [Microsoft365 Backend] extension
51 	 * in the mail transport source. */
52 
53 	class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
54 
55 	source = e_source_new (NULL, NULL, NULL);
56 	extension_name = E_SOURCE_EXTENSION_COLLECTION;
57 	extension = e_source_get_extension (source, extension_name);
58 	e_source_backend_set_backend_name (extension, class->backend_name);
59 
60 	return source;
61 }
62 
63 static void
mail_config_m365_backend_set_oauth2_tooltip(GtkWidget * widget,const gchar * value,const gchar * when_value_empty,gchar * when_value_filled)64 mail_config_m365_backend_set_oauth2_tooltip (GtkWidget *widget,
65 					     const gchar *value,
66 					     const gchar *when_value_empty,
67 					     gchar *when_value_filled) /* takes ownership */
68 {
69 	g_return_if_fail (GTK_IS_WIDGET (widget));
70 
71 	gtk_widget_set_tooltip_text (widget, value && *value ? when_value_filled : when_value_empty);
72 
73 	g_free (when_value_filled);
74 }
75 
76 static void
mail_config_m365_backend_insert_widgets(EMailConfigServiceBackend * backend,GtkBox * parent)77 mail_config_m365_backend_insert_widgets (EMailConfigServiceBackend *backend,
78 					 GtkBox *parent)
79 {
80 	EMailConfigM365Backend *m365_backend;
81 	EMailConfigServicePage *page;
82 	ESource *source;
83 	ESourceExtension *extension;
84 	ESourceAuthentication *auth_extension;
85 	CamelSettings *settings;
86 	CamelM365Settings *m365_settings;
87 	GtkLabel *label;
88 	GtkWidget *widget;
89 	GtkWidget *container;
90 	GtkWidget *expander;
91 	GtkWidget *advanced_help;
92 	GtkWidget *endpoint_host_label;
93 	GtkWidget *redirect_uri_label;
94 	const gchar *extension_name;
95 	const gchar *text;
96 	gchar *markup;
97 
98 	m365_backend = E_MAIL_CONFIG_M365_BACKEND (backend);
99 	page = e_mail_config_service_backend_get_page (backend);
100 
101 	/* This backend serves double duty.  One instance holds the
102 	 * mail account source, another holds the mail transport source.
103 	 * We can differentiate by examining the EMailConfigServicePage
104 	 * the backend is associated with.  This method only applies to
105 	 * the Receiving Page. */
106 	if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
107 		return;
108 
109 	/* This needs to come _after_ the page type check so we don't
110 	 * introduce a backend extension in the mail transport source. */
111 	settings = e_mail_config_service_backend_get_settings (backend);
112 
113 	text = _("Configuration");
114 	markup = g_markup_printf_escaped ("<b>%s</b>", text);
115 	widget = gtk_label_new (markup);
116 	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
117 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
118 	gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0);
119 	gtk_widget_show (widget);
120 	g_free (markup);
121 
122 	widget = gtk_grid_new ();
123 	gtk_widget_set_margin_left (widget, 12);
124 	gtk_grid_set_row_spacing (GTK_GRID (widget), 6);
125 	gtk_grid_set_column_spacing (GTK_GRID (widget), 6);
126 	gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0);
127 	gtk_widget_show (widget);
128 
129 	container = widget;
130 
131 	widget = gtk_label_new_with_mnemonic (_("User_name:"));
132 	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
133 	gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
134 	gtk_widget_show (widget);
135 
136 	label = GTK_LABEL (widget);
137 
138 	widget = gtk_entry_new ();
139 	gtk_widget_set_hexpand (widget, TRUE);
140 	gtk_label_set_mnemonic_widget (label, widget);
141 	gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 2, 1);
142 	m365_backend->priv->user_entry = widget;  /* do not reference */
143 	gtk_widget_show (widget);
144 
145 	widget = gtk_check_button_new_with_mnemonic (_("Open _Mailbox of other user"));
146 	gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1);
147 	gtk_widget_show (widget);
148 
149 	m365_settings = CAMEL_M365_SETTINGS (settings);
150 
151 	camel_m365_settings_lock (m365_settings);
152 
153 	if (camel_m365_settings_get_use_impersonation (m365_settings)) {
154 		const gchar *impersonate_user = camel_m365_settings_get_impersonate_user (m365_settings);
155 
156 		if (impersonate_user && !*impersonate_user) {
157 			camel_m365_settings_unlock (m365_settings);
158 
159 			camel_m365_settings_set_impersonate_user (m365_settings, NULL);
160 			camel_m365_settings_set_use_impersonation (m365_settings, FALSE);
161 
162 			camel_m365_settings_lock (m365_settings);
163 		}
164 	}
165 
166 	camel_m365_settings_unlock (m365_settings);
167 
168 	e_binding_bind_property (
169 		settings, "use-impersonation",
170 		widget, "active",
171 		G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
172 
173 	widget = gtk_entry_new ();
174 	gtk_widget_set_hexpand (widget, TRUE);
175 	gtk_grid_attach (GTK_GRID (container), widget, 1, 2, 1, 1);
176 	gtk_widget_show (widget);
177 	m365_backend->priv->impersonate_user_entry = widget;  /* do not reference */
178 
179 	e_binding_bind_object_text_property (
180 		settings, "impersonate-user",
181 		widget, "text",
182 		G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
183 
184 	e_binding_bind_property (
185 		settings, "use-impersonation",
186 		widget, "sensitive",
187 		G_BINDING_SYNC_CREATE);
188 
189 	text = _("Authentication");
190 	markup = g_markup_printf_escaped ("<b>%s</b>", text);
191 	widget = gtk_label_new (markup);
192 	gtk_widget_set_margin_top (widget, 6);
193 	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
194 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
195 	gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0);
196 	gtk_widget_show (widget);
197 	g_free (markup);
198 
199 	widget = gtk_grid_new ();
200 	gtk_widget_set_margin_left (widget, 12);
201 	gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0);
202 	m365_backend->priv->oauth2_settings_grid = GTK_GRID (widget);
203 
204 	gtk_grid_set_column_spacing (m365_backend->priv->oauth2_settings_grid, 4);
205 	gtk_grid_set_row_spacing (m365_backend->priv->oauth2_settings_grid, 4);
206 
207 	container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
208 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, container, 0, 0, 2, 1);
209 
210 	widget = gtk_check_button_new_with_mnemonic (_("_Override Microsoft 365 OAuth2 settings"));
211 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
212 	m365_backend->priv->oauth2_override_check = widget;
213 
214 	markup = g_markup_printf_escaped ("(<a href=\"https://wiki.gnome.org/Apps/Evolution/EWS/OAuth2\">%s</a>)", _("Help…"));
215 	widget = gtk_label_new (markup);
216 	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
217 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
218 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
219 	g_free (markup);
220 
221 	widget = gtk_label_new_with_mnemonic (_("Application I_D:"));
222 	gtk_widget_set_margin_left (widget, 12);
223 	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
224 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 0, 1, 1, 1);
225 	label = GTK_LABEL (widget);
226 
227 	e_binding_bind_property (
228 		m365_backend->priv->oauth2_override_check, "active",
229 		widget, "sensitive",
230 		G_BINDING_SYNC_CREATE);
231 
232 	widget = gtk_entry_new ();
233 	gtk_widget_set_hexpand (widget, TRUE);
234 	gtk_label_set_mnemonic_widget (label, widget);
235 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 1, 1, 1, 1);
236 	m365_backend->priv->oauth2_client_id_entry = widget;
237 
238 	e_binding_bind_property (
239 		m365_backend->priv->oauth2_override_check, "active",
240 		widget, "sensitive",
241 		G_BINDING_SYNC_CREATE);
242 
243 	mail_config_m365_backend_set_oauth2_tooltip (widget, MICROSOFT365_CLIENT_ID,
244 		_("There is not set any default application ID"),
245 		g_strdup_printf (_("Default application ID is “%s”"), MICROSOFT365_CLIENT_ID));
246 
247 	/* Translators: 'Tenant ID' here means a term used by Microsoft to identify a company or organization in a Microsoft 365 world.
248 	   You probably do not want to translate it. More for example here: https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */
249 	widget = gtk_label_new_with_mnemonic (_("_Tenant ID:"));
250 	gtk_widget_set_margin_left (widget, 12);
251 	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
252 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 0, 2, 1, 1);
253 	label = GTK_LABEL (widget);
254 
255 	e_binding_bind_property (
256 		m365_backend->priv->oauth2_override_check, "active",
257 		widget, "sensitive",
258 		G_BINDING_SYNC_CREATE);
259 
260 	widget = gtk_entry_new ();
261 	gtk_widget_set_hexpand (widget, TRUE);
262 	gtk_label_set_mnemonic_widget (label, widget);
263 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 1, 2, 1, 1);
264 	m365_backend->priv->oauth2_tenant_entry = widget;
265 
266 	e_binding_bind_property (
267 		m365_backend->priv->oauth2_override_check, "active",
268 		widget, "sensitive",
269 		G_BINDING_SYNC_CREATE);
270 
271 	mail_config_m365_backend_set_oauth2_tooltip (widget, MICROSOFT365_TENANT,
272 		/* Translators: 'Tenant' here means a term used by Microsoft to identify a company or organization in a Microsoft 365 world. Same for 'common', it's a default URL path.
273 		   You probably do not want to translate it. More for example here: https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */
274 		_("Default tenant is “common“"),
275 		/* Translators: 'Tenant' here means a term used by Microsoft to identify a company or organization in a Microsoft 365 world.
276 		   You probably do not want to translate it. More for example here: https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */
277 		g_strdup_printf (_("Default tenant is “%s”"), MICROSOFT365_TENANT));
278 
279 	container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
280 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, container, 0, 3, 2, 1);
281 
282 	widget = gtk_expander_new_with_mnemonic (_("_Advanced Settings"));
283 	gtk_widget_set_margin_left (widget, 12);
284 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
285 	expander = widget;
286 
287 	e_binding_bind_property (
288 		m365_backend->priv->oauth2_override_check, "active",
289 		widget, "sensitive",
290 		G_BINDING_SYNC_CREATE);
291 
292 	markup = g_markup_printf_escaped ("(<a href=\"https://wiki.gnome.org/Apps/Evolution/EWS/OAuth2#Alternative_endpoints\">%s</a>)", _("Help…"));
293 	widget = gtk_label_new (markup);
294 	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
295 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
296 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
297 	g_free (markup);
298 	advanced_help = widget;
299 
300 	widget = gtk_label_new_with_mnemonic (_("_Endpoint host:"));
301 	gtk_widget_set_margin_left (widget, 12);
302 	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
303 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 0, 4, 1, 1);
304 	label = GTK_LABEL (widget);
305 	endpoint_host_label = widget;
306 
307 	e_binding_bind_property (
308 		m365_backend->priv->oauth2_override_check, "active",
309 		widget, "sensitive",
310 		G_BINDING_SYNC_CREATE);
311 
312 	widget = gtk_entry_new ();
313 	gtk_widget_set_hexpand (widget, TRUE);
314 	gtk_label_set_mnemonic_widget (label, widget);
315 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 1, 4, 1, 1);
316 	m365_backend->priv->oauth2_endpoint_host_entry = widget;
317 
318 	e_binding_bind_property (
319 		m365_backend->priv->oauth2_override_check, "active",
320 		widget, "sensitive",
321 		G_BINDING_SYNC_CREATE);
322 
323 	markup = g_strdup_printf (_("Default endpoint host is “%s”"), "login.microsoftonline.com");
324 	mail_config_m365_backend_set_oauth2_tooltip (widget, OFFICE365_ENDPOINT_HOST,
325 		markup,
326 		g_strdup_printf (_("Default endpoint host is “%s”"), OFFICE365_ENDPOINT_HOST));
327 	g_free (markup);
328 
329 	widget = gtk_label_new_with_mnemonic (_("_Redirect URI:"));
330 	gtk_widget_set_margin_left (widget, 12);
331 	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
332 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 0, 5, 1, 1);
333 	label = GTK_LABEL (widget);
334 	redirect_uri_label = widget;
335 
336 	e_binding_bind_property (
337 		m365_backend->priv->oauth2_override_check, "active",
338 		widget, "sensitive",
339 		G_BINDING_SYNC_CREATE);
340 
341 	widget = gtk_entry_new ();
342 	gtk_widget_set_hexpand (widget, TRUE);
343 	gtk_label_set_mnemonic_widget (label, widget);
344 	gtk_grid_attach (m365_backend->priv->oauth2_settings_grid, widget, 1, 5, 1, 1);
345 	m365_backend->priv->oauth2_redirect_uri_entry = widget;
346 
347 	e_binding_bind_property (
348 		m365_backend->priv->oauth2_override_check, "active",
349 		widget, "sensitive",
350 		G_BINDING_SYNC_CREATE);
351 
352 	markup = g_strdup_printf (_("Default redirect URI is “%s”"), "https://login.microsoftonline.com/common/oauth2/nativeclient");
353 	mail_config_m365_backend_set_oauth2_tooltip (widget, MICROSOFT365_REDIRECT_URI,
354 		markup,
355 		g_strdup_printf (_("Default redirect URI is “%s”"), MICROSOFT365_REDIRECT_URI));
356 	g_free (markup);
357 
358 	gtk_widget_show_all (GTK_WIDGET (m365_backend->priv->oauth2_settings_grid));
359 
360 	camel_m365_settings_lock (m365_settings);
361 
362 	gtk_expander_set_expanded (GTK_EXPANDER (expander),
363 		(e_util_strcmp0 (camel_m365_settings_get_oauth2_endpoint_host (m365_settings), NULL) != 0 &&
364 		 e_util_strcmp0 (camel_m365_settings_get_oauth2_endpoint_host (m365_settings), MICROSOFT365_ENDPOINT_HOST) != 0) ||
365 		(e_util_strcmp0 (camel_m365_settings_get_oauth2_redirect_uri (m365_settings), NULL) != 0 &&
366 		 e_util_strcmp0 (camel_m365_settings_get_oauth2_redirect_uri (m365_settings), MICROSOFT365_REDIRECT_URI) != 0));
367 
368 	camel_m365_settings_unlock (m365_settings);
369 
370 	e_binding_bind_property (
371 		expander, "expanded",
372 		advanced_help, "visible",
373 		G_BINDING_SYNC_CREATE);
374 
375 	e_binding_bind_property (
376 		expander, "expanded",
377 		endpoint_host_label, "visible",
378 		G_BINDING_SYNC_CREATE);
379 
380 	e_binding_bind_property (
381 		expander, "expanded",
382 		m365_backend->priv->oauth2_endpoint_host_entry, "visible",
383 		G_BINDING_SYNC_CREATE);
384 
385 	e_binding_bind_property (
386 		expander, "expanded",
387 		redirect_uri_label, "visible",
388 		G_BINDING_SYNC_CREATE);
389 
390 	e_binding_bind_property (
391 		expander, "expanded",
392 		m365_backend->priv->oauth2_redirect_uri_entry, "visible",
393 		G_BINDING_SYNC_CREATE);
394 
395 	e_binding_bind_object_text_property (
396 		settings, "user",
397 		m365_backend->priv->user_entry, "text",
398 		G_BINDING_BIDIRECTIONAL |
399 		G_BINDING_SYNC_CREATE);
400 
401 	e_binding_bind_property (
402 		settings, "override-oauth2",
403 		m365_backend->priv->oauth2_override_check, "active",
404 		G_BINDING_BIDIRECTIONAL |
405 		G_BINDING_SYNC_CREATE);
406 
407 	e_binding_bind_object_text_property (
408 		settings, "oauth2-tenant",
409 		m365_backend->priv->oauth2_tenant_entry, "text",
410 		G_BINDING_BIDIRECTIONAL |
411 		G_BINDING_SYNC_CREATE);
412 
413 	e_binding_bind_object_text_property (
414 		settings, "oauth2-client-id",
415 		m365_backend->priv->oauth2_client_id_entry, "text",
416 		G_BINDING_BIDIRECTIONAL |
417 		G_BINDING_SYNC_CREATE);
418 
419 	e_binding_bind_object_text_property (
420 		settings, "oauth2-redirect-uri",
421 		m365_backend->priv->oauth2_redirect_uri_entry, "text",
422 		G_BINDING_BIDIRECTIONAL |
423 		G_BINDING_SYNC_CREATE);
424 
425 	e_binding_bind_object_text_property (
426 		settings, "oauth2-endpoint-host",
427 		m365_backend->priv->oauth2_endpoint_host_entry, "text",
428 		G_BINDING_BIDIRECTIONAL |
429 		G_BINDING_SYNC_CREATE);
430 
431 	extension_name = E_SOURCE_EXTENSION_COLLECTION;
432 	source = e_mail_config_service_backend_get_collection (backend);
433 	extension = e_source_get_extension (source, extension_name);
434 
435 	/* The collection identity is the user name. */
436 	e_binding_bind_property (
437 		settings, "user",
438 		extension, "identity",
439 		G_BINDING_BIDIRECTIONAL |
440 		G_BINDING_SYNC_CREATE);
441 
442 	auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
443 	e_source_authentication_set_host (auth_extension, "graph.microsoft.com");
444 	e_source_authentication_set_port (auth_extension, 442);
445 	e_source_authentication_set_method (auth_extension, "Microsoft365");
446 }
447 
448 static void
mail_config_m365_backend_setup_defaults(EMailConfigServiceBackend * backend)449 mail_config_m365_backend_setup_defaults (EMailConfigServiceBackend *backend)
450 {
451 	CamelSettings *settings;
452 	EMailConfigServicePage *page;
453 	const gchar *email_address;
454 
455 	page = e_mail_config_service_backend_get_page (backend);
456 
457 	/* This backend serves double duty.  One instance holds the
458 	 * mail account source, another holds the mail transport source.
459 	 * We can differentiate by examining the EMailConfigServicePage
460 	 * the backend is associated with.  This method only applies to
461 	 * the Receiving Page. */
462 	if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
463 		return;
464 
465 	/* This needs to come _after_ the page type check so we don't
466 	 * introduce a backend extension in the mail transport source. */
467 	email_address = e_mail_config_service_page_get_email_address (page);
468 	settings = e_mail_config_service_backend_get_settings (backend);
469 
470 	camel_m365_settings_set_email (CAMEL_M365_SETTINGS (settings), email_address);
471 	camel_network_settings_set_user (CAMEL_NETWORK_SETTINGS (settings), email_address);
472 }
473 
474 static gboolean
mail_config_m365_backend_auto_configure(EMailConfigServiceBackend * backend,EConfigLookup * config_lookup,gint * out_priority,gboolean * out_is_complete)475 mail_config_m365_backend_auto_configure (EMailConfigServiceBackend *backend,
476 					 EConfigLookup *config_lookup,
477 					 gint *out_priority,
478 					 gboolean *out_is_complete)
479 {
480 	return e_mail_config_service_backend_auto_configure_for_kind (backend, config_lookup,
481 		E_CONFIG_LOOKUP_RESULT_COLLECTION, NULL,
482 		e_mail_config_service_backend_get_collection (backend),
483 		out_priority, out_is_complete);
484 }
485 
486 static gboolean
mail_config_m365_backend_check_complete(EMailConfigServiceBackend * backend)487 mail_config_m365_backend_check_complete (EMailConfigServiceBackend *backend)
488 {
489 	EMailConfigServicePage *page;
490 	EMailConfigM365Backend *m365_backend;
491 	CamelSettings *settings;
492 	CamelNetworkSettings *network_settings;
493 	const gchar *user;
494 	gboolean correct, complete = TRUE;
495 
496 	m365_backend = E_MAIL_CONFIG_M365_BACKEND (backend);
497 	page = e_mail_config_service_backend_get_page (backend);
498 
499 	/* This backend serves double duty.  One instance holds the
500 	 * mail account source, another holds the mail transport source.
501 	 * We can differentiate by examining the EMailConfigServicePage
502 	 * the backend is associated with.  This method only applies to
503 	 * the Receiving Page. */
504 	if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
505 		return TRUE;
506 
507 	/* This needs to come _after_ the page type check so we don't
508 	 * introduce a backend extension in the mail transport source. */
509 	settings = e_mail_config_service_backend_get_settings (backend);
510 
511 	network_settings = CAMEL_NETWORK_SETTINGS (settings);
512 	user = camel_network_settings_get_user (network_settings);
513 
514 	correct = user != NULL && *user != '\0';
515 	complete = complete && correct;
516 
517 	e_util_set_entry_issue_hint (m365_backend->priv->user_entry, correct ? NULL : _("User name cannot be empty"));
518 
519 	if (correct) {
520 		CamelM365Settings *m365_settings = CAMEL_M365_SETTINGS (settings);
521 		const gchar *client_id;
522 
523 		camel_m365_settings_lock (m365_settings);
524 
525 		if (camel_m365_settings_get_override_oauth2 (m365_settings)) {
526 			client_id = camel_m365_settings_get_oauth2_client_id (m365_settings);
527 		} else {
528 			client_id = MICROSOFT365_CLIENT_ID;
529 		}
530 
531 		correct = e_util_strcmp0 (client_id, NULL) != 0;
532 		complete = complete && correct;
533 
534 		camel_m365_settings_unlock (m365_settings);
535 
536 		e_util_set_entry_issue_hint (m365_backend->priv->oauth2_client_id_entry, correct ? NULL : _("Application ID cannot be empty"));
537 	}
538 
539 	return complete;
540 }
541 
542 static void
mail_config_m365_backend_commit_changes(EMailConfigServiceBackend * backend)543 mail_config_m365_backend_commit_changes (EMailConfigServiceBackend *backend)
544 {
545 	CamelSettings *settings;
546 	CamelM365Settings *m365_settings;
547 	EMailConfigServicePage *page;
548 	const gchar *email_address;
549 
550 	page = e_mail_config_service_backend_get_page (backend);
551 
552 	/* This backend serves double duty.  One instance holds the
553 	 * mail account source, another holds the mail transport source.
554 	 * We can differentiate by examining the EMailConfigServicePage
555 	 * the backend is associated with.  This method only applies to
556 	 * the Receiving Page. */
557 	if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
558 		return;
559 
560 	/* This needs to come _after_ the page type check so we don't
561 	 * introduce a backend extension in the mail transport source. */
562 	settings = e_mail_config_service_backend_get_settings (backend);
563 
564 	email_address = e_mail_config_service_page_get_email_address (page);
565 	if (email_address != NULL) {
566 		m365_settings = CAMEL_M365_SETTINGS (settings);
567 		camel_m365_settings_set_email (m365_settings, email_address);
568 	}
569 }
570 
571 static void
e_mail_config_m365_backend_class_init(EMailConfigM365BackendClass * class)572 e_mail_config_m365_backend_class_init (EMailConfigM365BackendClass *class)
573 {
574 	EMailConfigServiceBackendClass *backend_class;
575 
576 	backend_class = E_MAIL_CONFIG_SERVICE_BACKEND_CLASS (class);
577 	backend_class->backend_name = "microsoft365";
578 	backend_class->new_collection = mail_config_m365_backend_new_collection;
579 	backend_class->insert_widgets = mail_config_m365_backend_insert_widgets;
580 	backend_class->setup_defaults = mail_config_m365_backend_setup_defaults;
581 	backend_class->auto_configure = mail_config_m365_backend_auto_configure;
582 	backend_class->check_complete = mail_config_m365_backend_check_complete;
583 	backend_class->commit_changes = mail_config_m365_backend_commit_changes;
584 }
585 
586 static void
e_mail_config_m365_backend_class_finalize(EMailConfigM365BackendClass * class)587 e_mail_config_m365_backend_class_finalize (EMailConfigM365BackendClass *class)
588 {
589 }
590 
591 static void
e_mail_config_m365_backend_init(EMailConfigM365Backend * backend)592 e_mail_config_m365_backend_init (EMailConfigM365Backend *backend)
593 {
594 	backend->priv = e_mail_config_m365_backend_get_instance_private (backend);
595 }
596 
597 void
e_mail_config_m365_backend_type_register(GTypeModule * type_module)598 e_mail_config_m365_backend_type_register (GTypeModule *type_module)
599 {
600 	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
601 	 *     function, so we have to wrap it with a public function in
602 	 *     order to register types from a separate compilation unit. */
603 	e_mail_config_m365_backend_register_type (type_module);
604 }
605