lync: add missing _ready() call for HTTP requests
[siplcs.git] / src / core / sipe-ews-autodiscover.c
blob63a6aef69ec0950659e10727cf18abd3ed377961
1 /**
2 * @file sipe-ews-autodiscover.c
4 * pidgin-sipe
6 * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Specification references:
25 * - POX: plain old XML autodiscover
26 * - [MS-OXDSCLI]: http://msdn.microsoft.com/en-us/library/cc463896.aspx
27 * - Autdiscover for Exchange:
28 * http://msdn.microsoft.com/en-us/library/office/jj900169.aspx
29 * - POX autodiscover: http://msdn.microsoft.com/en-us/library/office/aa581522.aspx
30 * - POX redirect: http://msdn.microsoft.com/en-us/library/office/dn467392.aspx
33 #include <string.h>
35 #include <glib.h>
37 #include "sipe-backend.h"
38 #include "sipe-common.h"
39 #include "sipe-core.h"
40 #include "sipe-core-private.h"
41 #include "sipe-ews-autodiscover.h"
42 #include "sipe-http.h"
43 #include "sipe-utils.h"
44 #include "sipe-xml.h"
46 struct sipe_ews_autodiscover_cb {
47 sipe_ews_autodiscover_callback *cb;
48 gpointer cb_data;
51 struct autodiscover_method {
52 const gchar *template;
53 gboolean redirect;
56 struct sipe_ews_autodiscover {
57 struct sipe_ews_autodiscover_data *data;
58 struct sipe_http_request *request;
59 GSList *callbacks;
60 gchar *email;
61 const struct autodiscover_method *method;
62 gboolean retry;
63 gboolean completed;
66 static void sipe_ews_autodiscover_complete(struct sipe_core_private *sipe_private,
67 struct sipe_ews_autodiscover_data *ews_data)
69 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
70 GSList *entry = sea->callbacks;
72 while (entry) {
73 struct sipe_ews_autodiscover_cb *sea_cb = entry->data;
74 sea_cb->cb(sipe_private, ews_data, sea_cb->cb_data);
75 g_free(sea_cb);
76 entry = entry->next;
78 g_slist_free(sea->callbacks);
79 sea->callbacks = NULL;
80 sea->completed = TRUE;
83 static void sipe_ews_autodiscover_request(struct sipe_core_private *sipe_private,
84 gboolean next_method);
85 static gboolean sipe_ews_autodiscover_url(struct sipe_core_private *sipe_private,
86 const gchar *url);
87 static void sipe_ews_autodiscover_parse(struct sipe_core_private *sipe_private,
88 const gchar *body)
90 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
91 struct sipe_ews_autodiscover_data *ews_data = sea->data =
92 g_new0(struct sipe_ews_autodiscover_data, 1);
93 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
94 const sipe_xml *account = sipe_xml_child(xml, "Response/Account");
95 gboolean complete = TRUE;
97 /* valid POX autodiscover response? */
98 if (account) {
99 const sipe_xml *node;
101 /* POX autodiscover settings? */
102 if ((node = sipe_xml_child(account, "Protocol")) != NULL) {
104 /* Autodiscover/Response/User/LegacyDN (requires trimming) */
105 gchar *tmp = sipe_xml_data(sipe_xml_child(xml,
106 "Response/User/LegacyDN"));
107 if (tmp)
108 ews_data->legacy_dn = g_strstrip(tmp);
110 /* extract settings */
111 for (; node; node = sipe_xml_twin(node)) {
112 gchar *type = sipe_xml_data(sipe_xml_child(node,
113 "Type"));
115 /* Exchange or Office 365 */
116 if (sipe_strequal("EXCH", type) ||
117 sipe_strequal("EXPR", type)) {
119 #define _URL(name, field) \
120 if (!ews_data->field) { \
121 ews_data->field = sipe_xml_data(sipe_xml_child(node, #name)); \
122 SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: " #field " = '%s'", \
123 ews_data->field ? ews_data->field : "<NOT FOUND>"); \
126 /* use first entry */
127 _URL(ASUrl, as_url);
128 _URL(EwsUrl, ews_url);
129 _URL(OABUrl, oab_url);
130 _URL(OOFUrl, oof_url);
131 #undef _URL
134 g_free(type);
137 /* POX autodiscover redirect to new email address? */
138 } else if ((node = sipe_xml_child(account, "RedirectAddr")) != NULL) {
139 gchar *addr = sipe_xml_data(node);
142 * Sanity checks for new email address:
143 * - must contain a "@" character
144 * - must be different from current address
146 if (addr && strchr(addr, '@') &&
147 !sipe_strequal(sea->email, addr)) {
148 g_free(sea->email);
149 sea->email = addr;
150 addr = NULL; /* sea takes ownership */
152 SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: restarting with email address '%s'",
153 sea->email);
155 /* restart process with new email address */
156 sea->method = NULL;
157 complete = FALSE;
158 sipe_ews_autodiscover_request(sipe_private,
159 TRUE);
161 g_free(addr);
163 /* POX autodiscover redirect to new URL? */
164 } else if ((node = sipe_xml_child(account, "RedirectUrl")) != NULL) {
165 gchar *url = sipe_xml_data(node);
167 if (!is_empty(url)) {
168 SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: redirected to URL '%s'",
169 url);
170 complete = !sipe_ews_autodiscover_url(sipe_private,
171 url);
173 g_free(url);
175 /* ignore all other POX autodiscover responses */
176 } else {
177 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ews_autodiscover_parse: unknown response detected");
180 sipe_xml_free(xml);
182 if (complete)
183 sipe_ews_autodiscover_complete(sipe_private, ews_data);
186 static void sipe_ews_autodiscover_response(struct sipe_core_private *sipe_private,
187 guint status,
188 GSList *headers,
189 const gchar *body,
190 gpointer data)
192 struct sipe_ews_autodiscover *sea = data;
193 const gchar *type = sipe_utils_nameval_find(headers, "Content-Type");
195 sea->request = NULL;
197 switch (status) {
198 case SIPE_HTTP_STATUS_OK:
199 /* only accept XML responses */
200 if (body && g_str_has_prefix(type, "text/xml"))
201 sipe_ews_autodiscover_parse(sipe_private, body);
202 else
203 sipe_ews_autodiscover_request(sipe_private, TRUE);
204 break;
206 case SIPE_HTTP_STATUS_CLIENT_FORBIDDEN:
208 * Authentication succeeded but we still weren't allowed to
209 * view the page. At least at our work place this error is
210 * temporary, i.e. the next access with the exact same
211 * authentication succeeds.
213 * Let's try again, but only once...
215 sipe_ews_autodiscover_request(sipe_private, !sea->retry);
216 break;
218 case SIPE_HTTP_STATUS_ABORTED:
219 /* we are not allowed to generate new requests */
220 break;
222 default:
223 sipe_ews_autodiscover_request(sipe_private, TRUE);
224 break;
228 static gboolean sipe_ews_autodiscover_url(struct sipe_core_private *sipe_private,
229 const gchar *url)
231 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
232 gchar *body = g_strdup_printf("<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006\">"
233 " <Request>"
234 " <EMailAddress>%s</EMailAddress>"
235 " <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>"
236 " </Request>"
237 "</Autodiscover>",
238 sea->email);
240 SIPE_DEBUG_INFO("sipe_ews_autodiscover_url: trying '%s'", url);
242 sea->request = sipe_http_request_post(sipe_private,
243 url,
244 "Accept: text/xml\r\n",
245 body,
246 "text/xml",
247 sipe_ews_autodiscover_response,
248 sea);
249 g_free(body);
251 if (sea->request) {
252 sipe_core_email_authentication(sipe_private,
253 sea->request);
254 sipe_http_request_allow_redirect(sea->request);
255 sipe_http_request_ready(sea->request);
256 return(TRUE);
259 return(FALSE);
262 static void sipe_ews_autodiscover_redirect_response(struct sipe_core_private *sipe_private,
263 guint status,
264 GSList *headers,
265 SIPE_UNUSED_PARAMETER const gchar *body,
266 gpointer data)
268 struct sipe_ews_autodiscover *sea = data;
269 gboolean failed = (status != (guint) SIPE_HTTP_STATUS_ABORTED);
271 sea->request = NULL;
273 /* Start attempt with URL from redirect (3xx) response */
274 if ((status >= SIPE_HTTP_STATUS_REDIRECTION) &&
275 (status < SIPE_HTTP_STATUS_CLIENT_ERROR)) {
276 const gchar *location = sipe_utils_nameval_find_instance(headers,
277 "Location",
279 if (location)
280 failed = !sipe_ews_autodiscover_url(sipe_private,
281 location);
284 if (failed)
285 sipe_ews_autodiscover_request(sipe_private, TRUE);
288 static gboolean sipe_ews_autodiscover_redirect(struct sipe_core_private *sipe_private,
289 const gchar *url)
291 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
293 SIPE_DEBUG_INFO("sipe_ews_autodiscover_redirect: trying '%s'", url);
295 sea->request = sipe_http_request_get(sipe_private,
296 url,
297 NULL,
298 sipe_ews_autodiscover_redirect_response,
299 sea);
301 if (sea->request) {
302 sipe_http_request_ready(sea->request);
303 return(TRUE);
306 return(FALSE);
309 static void sipe_ews_autodiscover_request(struct sipe_core_private *sipe_private,
310 gboolean next_method)
312 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
313 static const struct autodiscover_method methods[] = {
314 { "https://Autodiscover.%s/Autodiscover/Autodiscover.xml", FALSE },
315 { "http://Autodiscover.%s/Autodiscover/Autodiscover.xml", TRUE },
316 { "http://Autodiscover.%s/Autodiscover/Autodiscover.xml", FALSE },
317 { "https://%s/Autodiscover/Autodiscover.xml", FALSE },
318 { NULL, FALSE },
321 sea->retry = next_method;
322 if (sea->method) {
323 if (next_method)
324 sea->method++;
325 } else
326 sea->method = methods;
328 if (sea->method->template) {
329 gchar *url = g_strdup_printf(sea->method->template,
330 strstr(sea->email, "@") + 1);
332 if (!(sea->method->redirect ?
333 sipe_ews_autodiscover_redirect(sipe_private, url) :
334 sipe_ews_autodiscover_url(sipe_private, url)))
335 sipe_ews_autodiscover_request(sipe_private, TRUE);
337 g_free(url);
339 } else {
340 SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_autodiscover_request: no more methods to try!");
341 sipe_ews_autodiscover_complete(sipe_private, NULL);
345 void sipe_ews_autodiscover_start(struct sipe_core_private *sipe_private,
346 sipe_ews_autodiscover_callback *callback,
347 gpointer callback_data)
349 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
351 if (sea->completed) {
352 (*callback)(sipe_private, sea->data, callback_data);
353 } else {
354 struct sipe_ews_autodiscover_cb *sea_cb = g_new(struct sipe_ews_autodiscover_cb, 1);
355 sea_cb->cb = callback;
356 sea_cb->cb_data = callback_data;
357 sea->callbacks = g_slist_prepend(sea->callbacks, sea_cb);
359 if (!sea->method)
360 sipe_ews_autodiscover_request(sipe_private, TRUE);
364 void sipe_ews_autodiscover_init(struct sipe_core_private *sipe_private)
366 struct sipe_ews_autodiscover *sea = g_new0(struct sipe_ews_autodiscover, 1);
368 sea->email = g_strdup(sipe_private->email);
370 sipe_private->ews_autodiscover = sea;
373 void sipe_ews_autodiscover_free(struct sipe_core_private *sipe_private)
375 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
376 struct sipe_ews_autodiscover_data *ews_data = sea->data;
377 sipe_ews_autodiscover_complete(sipe_private, NULL);
378 if (ews_data) {
379 g_free((gchar *)ews_data->as_url);
380 g_free((gchar *)ews_data->ews_url);
381 g_free((gchar *)ews_data->legacy_dn);
382 g_free((gchar *)ews_data->oab_url);
383 g_free((gchar *)ews_data->oof_url);
384 g_free(ews_data);
386 g_free(sea->email);
387 g_free(sea);
391 Local Variables:
392 mode: c
393 c-file-style: "bsd"
394 indent-tabs-mode: t
395 tab-width: 8
396 End: