1 /*
2  * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
3  *
4  * This library 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 library 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 Lesser 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 library. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include "evolution-data-server-config.h"
18 
19 #include <glib/gi18n-lib.h>
20 
21 #include "e-oauth2-service.h"
22 #include "e-oauth2-service-base.h"
23 
24 #include "e-oauth2-service-outlook.h"
25 
26 /* https://apps.dev.microsoft.com/
27    https://msdn.microsoft.com/en-us/library/office/dn631845.aspx
28    https://www.limilabs.com/blog/oauth2-outlook-com-imap-installed-applications
29 */
30 
31 #define OUTLOOK_SCOPE "wl.offline_access wl.emails wl.imap"
32 
33 /* Forward Declarations */
34 static void e_oauth2_service_outlook_oauth2_service_init (EOAuth2ServiceInterface *iface);
35 
G_DEFINE_TYPE_WITH_CODE(EOAuth2ServiceOutlook,e_oauth2_service_outlook,E_TYPE_OAUTH2_SERVICE_BASE,G_IMPLEMENT_INTERFACE (E_TYPE_OAUTH2_SERVICE,e_oauth2_service_outlook_oauth2_service_init))36 G_DEFINE_TYPE_WITH_CODE (EOAuth2ServiceOutlook, e_oauth2_service_outlook, E_TYPE_OAUTH2_SERVICE_BASE,
37 	G_IMPLEMENT_INTERFACE (E_TYPE_OAUTH2_SERVICE, e_oauth2_service_outlook_oauth2_service_init))
38 
39 static gboolean
40 eos_outlook_guess_can_process (EOAuth2Service *service,
41 			       const gchar *protocol,
42 			       const gchar *hostname)
43 {
44 	return hostname && e_util_utf8_strstrcase (hostname, ".outlook.com");
45 }
46 
47 static const gchar *
eos_outlook_get_name(EOAuth2Service * service)48 eos_outlook_get_name (EOAuth2Service *service)
49 {
50 	return "Outlook";
51 }
52 
53 static const gchar *
eos_outlook_get_display_name(EOAuth2Service * service)54 eos_outlook_get_display_name (EOAuth2Service *service)
55 {
56 	/* Translators: This is a user-visible string, display name of an OAuth2 service. */
57 	return C_("OAuth2Service", "Outlook");
58 }
59 
60 static const gchar *
eos_outlook_read_settings(EOAuth2Service * service,const gchar * key_name)61 eos_outlook_read_settings (EOAuth2Service *service,
62 			   const gchar *key_name)
63 {
64 	G_LOCK_DEFINE_STATIC (user_settings);
65 	gchar *value;
66 
67 	G_LOCK (user_settings);
68 
69 	value = g_object_get_data (G_OBJECT (service), key_name);
70 	if (!value) {
71 		GSettings *settings;
72 
73 		settings = g_settings_new ("org.gnome.evolution-data-server");
74 		value = g_settings_get_string (settings, key_name);
75 		g_object_unref (settings);
76 
77 		if (value && *value) {
78 			g_object_set_data_full (G_OBJECT (service), key_name, value, g_free);
79 		} else {
80 			g_free (value);
81 			value = (gchar *) "";
82 
83 			g_object_set_data (G_OBJECT (service), key_name, value);
84 		}
85 	}
86 
87 	G_UNLOCK (user_settings);
88 
89 	return value;
90 }
91 
92 static const gchar *
eos_outlook_get_client_id(EOAuth2Service * service,ESource * source)93 eos_outlook_get_client_id (EOAuth2Service *service,
94 			   ESource *source)
95 {
96 	const gchar *client_id;
97 
98 	client_id = eos_outlook_read_settings (service, "oauth2-outlook-client-id");
99 
100 	if (client_id && *client_id)
101 		return client_id;
102 
103 	return OUTLOOK_CLIENT_ID;
104 }
105 
106 static const gchar *
eos_outlook_get_client_secret(EOAuth2Service * service,ESource * source)107 eos_outlook_get_client_secret (EOAuth2Service *service,
108 			       ESource *source)
109 {
110 	const gchar *client_secret;
111 
112 	client_secret = eos_outlook_read_settings (service, "oauth2-outlook-client-secret");
113 
114 	if (client_secret && *client_secret)
115 		return client_secret;
116 
117 	client_secret = OUTLOOK_CLIENT_SECRET;
118 
119 	if (client_secret && !*client_secret)
120 		return NULL;
121 
122 	return client_secret;
123 }
124 
125 static const gchar *
eos_outlook_get_authentication_uri(EOAuth2Service * service,ESource * source)126 eos_outlook_get_authentication_uri (EOAuth2Service *service,
127 				    ESource *source)
128 {
129 	return "https://login.live.com/oauth20_authorize.srf";
130 }
131 
132 static const gchar *
eos_outlook_get_refresh_uri(EOAuth2Service * service,ESource * source)133 eos_outlook_get_refresh_uri (EOAuth2Service *service,
134 			     ESource *source)
135 {
136 	return "https://login.live.com/oauth20_token.srf";
137 }
138 
139 static const gchar *
eos_outlook_get_redirect_uri(EOAuth2Service * service,ESource * source)140 eos_outlook_get_redirect_uri (EOAuth2Service *service,
141 			      ESource *source)
142 {
143 	return "https://login.live.com/oauth20_desktop.srf";
144 }
145 
146 static void
eos_outlook_prepare_authentication_uri_query(EOAuth2Service * service,ESource * source,GHashTable * uri_query)147 eos_outlook_prepare_authentication_uri_query (EOAuth2Service *service,
148 					      ESource *source,
149 					      GHashTable *uri_query)
150 {
151 	g_return_if_fail (uri_query != NULL);
152 
153 	e_oauth2_service_util_set_to_form (uri_query, "response_mode", "query");
154 	e_oauth2_service_util_set_to_form (uri_query, "scope", OUTLOOK_SCOPE);
155 }
156 
157 static gboolean
eos_outlook_extract_authorization_code(EOAuth2Service * service,ESource * source,const gchar * page_title,const gchar * page_uri,const gchar * page_content,gchar ** out_authorization_code)158 eos_outlook_extract_authorization_code (EOAuth2Service *service,
159 					ESource *source,
160 					const gchar *page_title,
161 					const gchar *page_uri,
162 					const gchar *page_content,
163 					gchar **out_authorization_code)
164 {
165 	SoupURI *suri;
166 	gboolean known = FALSE;
167 
168 	g_return_val_if_fail (out_authorization_code != NULL, FALSE);
169 
170 	*out_authorization_code = NULL;
171 
172 	if (!page_uri || !*page_uri)
173 		return FALSE;
174 
175 	suri = soup_uri_new (page_uri);
176 	if (!suri)
177 		return FALSE;
178 
179 	if (suri->query) {
180 		GHashTable *uri_query = soup_form_decode (suri->query);
181 
182 		if (uri_query) {
183 			const gchar *code;
184 
185 			code = g_hash_table_lookup (uri_query, "code");
186 
187 			if (code && *code) {
188 				*out_authorization_code = g_strdup (code);
189 				known = TRUE;
190 			} else if (g_hash_table_lookup (uri_query, "error")) {
191 				known = TRUE;
192 			}
193 
194 			g_hash_table_unref (uri_query);
195 		}
196 	}
197 
198 	soup_uri_free (suri);
199 
200 	return known;
201 }
202 
203 static void
eos_outlook_prepare_refresh_token_form(EOAuth2Service * service,ESource * source,const gchar * refresh_token,GHashTable * form)204 eos_outlook_prepare_refresh_token_form (EOAuth2Service *service,
205 					ESource *source,
206 					const gchar *refresh_token,
207 					GHashTable *form)
208 {
209 	g_return_if_fail (form != NULL);
210 
211 	e_oauth2_service_util_set_to_form (form, "scope", OUTLOOK_SCOPE);
212 	e_oauth2_service_util_set_to_form (form, "redirect_uri", e_oauth2_service_get_redirect_uri (service, source));
213 }
214 
215 static void
e_oauth2_service_outlook_oauth2_service_init(EOAuth2ServiceInterface * iface)216 e_oauth2_service_outlook_oauth2_service_init (EOAuth2ServiceInterface *iface)
217 {
218 	iface->guess_can_process = eos_outlook_guess_can_process;
219 	iface->get_name = eos_outlook_get_name;
220 	iface->get_display_name = eos_outlook_get_display_name;
221 	iface->get_client_id = eos_outlook_get_client_id;
222 	iface->get_client_secret = eos_outlook_get_client_secret;
223 	iface->get_authentication_uri = eos_outlook_get_authentication_uri;
224 	iface->get_refresh_uri = eos_outlook_get_refresh_uri;
225 	iface->get_redirect_uri = eos_outlook_get_redirect_uri;
226 	iface->prepare_authentication_uri_query = eos_outlook_prepare_authentication_uri_query;
227 	iface->extract_authorization_code = eos_outlook_extract_authorization_code;
228 	iface->prepare_refresh_token_form = eos_outlook_prepare_refresh_token_form;
229 }
230 
231 static void
e_oauth2_service_outlook_class_init(EOAuth2ServiceOutlookClass * klass)232 e_oauth2_service_outlook_class_init (EOAuth2ServiceOutlookClass *klass)
233 {
234 }
235 
236 static void
e_oauth2_service_outlook_init(EOAuth2ServiceOutlook * oauth2_outlook)237 e_oauth2_service_outlook_init (EOAuth2ServiceOutlook *oauth2_outlook)
238 {
239 }
240