3aaaa7b9e17116bb01ba76411725949f954d92c3
[siplcs.git] / src / core / sipe-ews-autodiscover.c
blob3aaaa7b9e17116bb01ba76411725949f954d92c3
1 /**
2 * @file sipe-ews-autodiscover.c
4 * pidgin-sipe
6 * Copyright (C) 2013-2014 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 * - POX autodiscover: http://msdn.microsoft.com/en-us/library/office/aa581522.aspx
28 * - POX redirect: http://msdn.microsoft.com/en-us/library/office/dn467392.aspx
31 #include <string.h>
33 #include <glib.h>
35 #include "sipe-backend.h"
36 #include "sipe-common.h"
37 #include "sipe-core.h"
38 #include "sipe-core-private.h"
39 #include "sipe-ews-autodiscover.h"
40 #include "sipe-http.h"
41 #include "sipe-utils.h"
42 #include "sipe-xml.h"
44 struct sipe_ews_autodiscover_cb {
45 sipe_ews_autodiscover_callback *cb;
46 gpointer cb_data;
49 struct sipe_ews_autodiscover {
50 struct sipe_ews_autodiscover_data *data;
51 struct sipe_http_request *request;
52 GSList *callbacks;
53 gchar *email;
54 const gchar * const *method;
55 gboolean retry;
56 gboolean completed;
59 static void sipe_ews_autodiscover_complete(struct sipe_core_private *sipe_private,
60 struct sipe_ews_autodiscover_data *ews_data)
62 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
63 GSList *entry = sea->callbacks;
65 while (entry) {
66 struct sipe_ews_autodiscover_cb *sea_cb = entry->data;
67 sea_cb->cb(sipe_private, ews_data, sea_cb->cb_data);
68 g_free(sea_cb);
69 entry = entry->next;
71 g_slist_free(sea->callbacks);
72 sea->callbacks = NULL;
73 sea->completed = TRUE;
76 static void sipe_ews_autodiscover_request(struct sipe_core_private *sipe_private,
77 gboolean next_method);
78 static gboolean sipe_ews_autodiscover_url(struct sipe_core_private *sipe_private,
79 const gchar *url);
80 static void sipe_ews_autodiscover_parse(struct sipe_core_private *sipe_private,
81 const gchar *body)
83 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
84 struct sipe_ews_autodiscover_data *ews_data = sea->data =
85 g_new0(struct sipe_ews_autodiscover_data, 1);
86 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
87 const sipe_xml *account = sipe_xml_child(xml, "Response/Account");
88 gboolean complete = TRUE;
90 /* valid POX autodiscover response? */
91 if (account) {
92 const sipe_xml *node;
94 /* POX autodiscover settings? */
95 if ((node = sipe_xml_child(account, "Protocol")) != NULL) {
97 /* Autodiscover/Response/User/LegacyDN (requires trimming) */
98 gchar *tmp = sipe_xml_data(sipe_xml_child(xml,
99 "Response/User/LegacyDN"));
100 if (tmp)
101 ews_data->legacy_dn = g_strstrip(tmp);
103 /* extract settings */
104 for (; node; node = sipe_xml_twin(node)) {
105 gchar *type = sipe_xml_data(sipe_xml_child(node,
106 "Type"));
108 if (sipe_strequal("EXCH", type)) {
109 g_free(type);
111 #define _URL(name, field) \
113 ews_data->field = sipe_xml_data(sipe_xml_child(node, #name)); \
114 SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: " #field " = '%s'", \
115 ews_data->field ? ews_data->field : "<NOT FOUND>"); \
118 _URL(ASUrl, as_url);
119 _URL(EwsUrl, ews_url);
120 _URL(OABUrl, oab_url);
121 _URL(OOFUrl, oof_url);
122 #undef _URL
124 break;
127 g_free(type);
130 /* POX autodiscover redirect to new email address? */
131 } else if ((node = sipe_xml_child(account, "RedirectAddr")) != NULL) {
132 gchar *addr = sipe_xml_data(node);
135 * Sanity checks for new email address:
136 * - must contain a "@" character
137 * - must be different from current address
139 if (addr && strchr(addr, '@') &&
140 !sipe_strequal(sea->email, addr)) {
141 g_free(sea->email);
142 sea->email = addr;
143 addr = NULL; /* sea takes ownership */
145 SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: restarting with email address '%s'",
146 sea->email);
148 /* restart process with new email address */
149 sea->method = NULL;
150 complete = FALSE;
151 sipe_ews_autodiscover_request(sipe_private,
152 TRUE);
154 g_free(addr);
156 /* POX autodiscover redirect to new URL? */
157 } else if ((node = sipe_xml_child(account, "RedirectUrl")) != NULL) {
158 gchar *url = sipe_xml_data(node);
160 if (!is_empty(url)) {
161 SIPE_DEBUG_INFO("sipe_ews_autodiscover_parse: redirected to URL '%s'",
162 url);
163 complete = !sipe_ews_autodiscover_url(sipe_private,
164 url);
166 g_free(url);
168 /* ignore all other POX autodiscover responses */
169 } else {
170 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ews_autodiscover_parse: unknown response detected");
173 sipe_xml_free(xml);
175 if (complete)
176 sipe_ews_autodiscover_complete(sipe_private, ews_data);
179 static void sipe_ews_autodiscover_response(struct sipe_core_private *sipe_private,
180 guint status,
181 SIPE_UNUSED_PARAMETER GSList *headers,
182 const gchar *body,
183 gpointer data)
185 struct sipe_ews_autodiscover *sea = data;
187 sea->request = NULL;
189 switch (status) {
190 case SIPE_HTTP_STATUS_OK:
191 if (body)
192 sipe_ews_autodiscover_parse(sipe_private, body);
193 else
194 sipe_ews_autodiscover_request(sipe_private, TRUE);
195 break;
197 case SIPE_HTTP_STATUS_CLIENT_FORBIDDEN:
199 * Authentication succeeded but we still weren't allowed to
200 * view the page. At least at our work place this error is
201 * temporary, i.e. the next access with the exact same
202 * authentication succeeds.
204 * Let's try again, but only once...
206 sipe_ews_autodiscover_request(sipe_private, !sea->retry);
207 break;
209 case SIPE_HTTP_STATUS_ABORTED:
210 /* we are not allowed to generate new requests */
211 break;
213 default:
214 sipe_ews_autodiscover_request(sipe_private, TRUE);
215 break;
219 static gboolean sipe_ews_autodiscover_url(struct sipe_core_private *sipe_private,
220 const gchar *url)
222 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
223 gchar *body = g_strdup_printf("<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006\">"
224 " <Request>"
225 " <EMailAddress>%s</EMailAddress>"
226 " <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>"
227 " </Request>"
228 "</Autodiscover>",
229 sea->email);
231 SIPE_DEBUG_INFO("sipe_ews_autodiscover_url: trying '%s'", url);
233 sea->request = sipe_http_request_post(sipe_private,
234 url,
235 NULL,
236 body,
237 "text/xml",
238 sipe_ews_autodiscover_response,
239 sea);
240 g_free(body);
242 if (sea->request) {
243 sipe_core_email_authentication(sipe_private,
244 sea->request);
245 sipe_http_request_allow_redirect(sea->request);
246 sipe_http_request_ready(sea->request);
247 return(TRUE);
250 return(FALSE);
253 static void sipe_ews_autodiscover_request(struct sipe_core_private *sipe_private,
254 gboolean next_method)
256 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
257 static const gchar * const methods[] = {
258 "https://Autodiscover.%s/Autodiscover/Autodiscover.xml",
259 "http://Autodiscover.%s/Autodiscover/Autodiscover.xml",
260 "https://%s/Autodiscover/Autodiscover.xml",
261 NULL
264 sea->retry = next_method;
265 if (sea->method) {
266 if (next_method)
267 sea->method++;
268 } else
269 sea->method = methods;
271 if (*sea->method) {
272 gchar *url = g_strdup_printf(*sea->method,
273 strstr(sea->email, "@") + 1);
275 if (!sipe_ews_autodiscover_url(sipe_private, url))
276 sipe_ews_autodiscover_request(sipe_private, TRUE);
278 g_free(url);
280 } else {
281 SIPE_DEBUG_INFO_NOFORMAT("sipe_ews_autodiscover_request: no more methods to try!");
282 sipe_ews_autodiscover_complete(sipe_private, NULL);
286 void sipe_ews_autodiscover_start(struct sipe_core_private *sipe_private,
287 sipe_ews_autodiscover_callback *callback,
288 gpointer callback_data)
290 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
292 if (sea->completed) {
293 (*callback)(sipe_private, sea->data, callback_data);
294 } else {
295 struct sipe_ews_autodiscover_cb *sea_cb = g_new(struct sipe_ews_autodiscover_cb, 1);
296 sea_cb->cb = callback;
297 sea_cb->cb_data = callback_data;
298 sea->callbacks = g_slist_prepend(sea->callbacks, sea_cb);
300 if (!sea->method)
301 sipe_ews_autodiscover_request(sipe_private, TRUE);
305 void sipe_ews_autodiscover_init(struct sipe_core_private *sipe_private)
307 struct sipe_ews_autodiscover *sea = g_new0(struct sipe_ews_autodiscover, 1);
309 sea->email = g_strdup(sipe_private->email);
311 sipe_private->ews_autodiscover = sea;
314 void sipe_ews_autodiscover_free(struct sipe_core_private *sipe_private)
316 struct sipe_ews_autodiscover *sea = sipe_private->ews_autodiscover;
317 struct sipe_ews_autodiscover_data *ews_data = sea->data;
318 sipe_ews_autodiscover_complete(sipe_private, NULL);
319 if (ews_data) {
320 g_free((gchar *)ews_data->as_url);
321 g_free((gchar *)ews_data->ews_url);
322 g_free((gchar *)ews_data->legacy_dn);
323 g_free((gchar *)ews_data->oab_url);
324 g_free((gchar *)ews_data->oof_url);
325 g_free(ews_data);
327 g_free(sea->email);
328 g_free(sea);
332 Local Variables:
333 mode: c
334 c-file-style: "bsd"
335 indent-tabs-mode: t
336 tab-width: 8
337 End: