lync: add missing _ready() call for HTTP requests
[siplcs.git] / src / core / sipe-lync-autodiscover.c
blobaa58ba5a1e32cd1716ad28577195741f4cf020b0
1 /**
2 * @file sipe-lync-autodiscover.c
4 * pidgin-sipe
6 * Copyright (C) 2016-2018 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 * - [MS-OCDISCWS]: https://msdn.microsoft.com/en-us/library/hh623245.aspx
26 * - Understanding Autodiscover in Lync Server 2013
27 * https://technet.microsoft.com/en-us/library/jj945654.aspx
30 #include <string.h>
32 #include <glib.h>
34 #include "sipe-common.h"
35 #include "sipe-backend.h"
36 #include "sipe-core.h"
37 #include "sipe-core-private.h"
38 #include "sipe-http.h"
39 #include "sipe-lync-autodiscover.h"
40 #include "sipe-utils.h"
41 #include "sipe-svc.h"
42 #include "sipe-webticket.h"
43 #include "sipe-xml.h"
45 #define LYNC_AUTODISCOVER_ACCEPT_HEADER \
46 "Accept: application/vnd.microsoft.rtc.autodiscover+xml;v=1\r\n"
48 struct lync_autodiscover_request {
49 sipe_lync_autodiscover_callback *cb;
50 gpointer cb_data;
51 gpointer id; /* != NULL for active request */
52 struct sipe_http_request *request;
53 struct sipe_svc_session *session;
54 const gchar *protocol;
55 const gchar **method;
56 gchar *uri;
57 gboolean is_pending;
60 struct sipe_lync_autodiscover {
61 GSList *pending_requests;
64 /* Use "lar" inside the code fragment */
65 #define FOR_ALL_REQUESTS_WITH_SAME_ID(code) \
66 { \
67 GSList *entry = sipe_private->lync_autodiscover->pending_requests; \
68 while (entry) { \
69 struct lync_autodiscover_request *lar = entry->data; \
70 entry = entry->next; \
71 if (lar->id == id) { \
72 code; \
73 } \
74 } \
77 static void sipe_lync_autodiscover_request_free(struct sipe_core_private *sipe_private,
78 struct lync_autodiscover_request *request)
80 struct sipe_lync_autodiscover *sla = sipe_private->lync_autodiscover;
82 sla->pending_requests = g_slist_remove(sla->pending_requests, request);
84 if (request->request)
85 sipe_http_request_cancel(request->request);
86 if (request->cb)
87 /* Callback: aborted */
88 (*request->cb)(sipe_private, NULL, request->cb_data);
89 sipe_svc_session_close(request->session);
90 g_free(request->uri);
91 g_free(request);
94 static void sipe_lync_autodiscover_cb(struct sipe_core_private *sipe_private,
95 guint status,
96 GSList *headers,
97 const gchar *body,
98 gpointer callback_data);
99 static void lync_request(struct sipe_core_private *sipe_private,
100 struct lync_autodiscover_request *request,
101 const gchar *uri,
102 const gchar *headers)
104 request->request = sipe_http_request_get(sipe_private,
105 uri,
106 headers ? headers : LYNC_AUTODISCOVER_ACCEPT_HEADER,
107 sipe_lync_autodiscover_cb,
108 request);
110 if (request->request)
111 sipe_http_request_ready(request->request);
114 static GSList *sipe_lync_autodiscover_add(GSList *servers,
115 const sipe_xml *node,
116 const gchar *name)
118 const sipe_xml *child = sipe_xml_child(node, name);
119 const gchar *fqdn = sipe_xml_attribute(child, "fqdn");
120 guint port = sipe_xml_int_attribute(child, "port", 0);
122 /* Add new entry to head of list */
123 if (fqdn && (port != 0)) {
124 struct sipe_lync_autodiscover_data *lync_data = g_new0(struct sipe_lync_autodiscover_data, 1);
125 lync_data->server = g_strdup(fqdn);
126 lync_data->port = port;
127 servers = g_slist_prepend(servers, lync_data);
130 return(servers);
133 GSList *sipe_lync_autodiscover_pop(GSList *servers)
135 if (servers) {
136 struct sipe_lync_autodiscover_data *lync_data = servers->data;
137 servers = g_slist_remove(servers, lync_data);
139 if (lync_data) {
140 g_free((gchar *) lync_data->server);
141 g_free(lync_data);
145 return(servers);
148 static void sipe_lync_autodiscover_queue_request(struct sipe_core_private *sipe_private,
149 struct lync_autodiscover_request *request);
150 static void sipe_lync_autodiscover_parse(struct sipe_core_private *sipe_private,
151 struct lync_autodiscover_request *request,
152 const gchar *body)
154 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
155 const sipe_xml *node;
156 gboolean next = TRUE;
158 /* Root/Link: resources exposed by this server */
159 for (node = sipe_xml_child(xml, "Root/Link");
160 node;
161 node = sipe_xml_twin(node)) {
162 const gchar *token = sipe_xml_attribute(node, "token");
163 const gchar *uri = sipe_xml_attribute(node, "href");
165 if (token && uri) {
166 /* Redirect? */
167 if (sipe_strcase_equal(token, "Redirect")) {
168 SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: redirect to %s",
169 uri);
170 lync_request(sipe_private, request, uri, NULL);
171 next = FALSE;
172 break;
174 /* User? */
175 } else if (sipe_strcase_equal(token, "User")) {
176 SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: user %s",
177 uri);
179 /* remember URI for authentication failure */
180 request->uri = g_strdup(uri);
182 lync_request(sipe_private, request, uri, NULL);
183 next = FALSE;
184 break;
186 } else
187 SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: unknown token %s",
188 token);
192 /* User/Link: topology information of the user’s home server */
193 for (node = sipe_xml_child(xml, "User/Link");
194 node;
195 node = sipe_xml_twin(node)) {
196 const gchar *token = sipe_xml_attribute(node, "token");
197 const gchar *uri = sipe_xml_attribute(node, "href");
199 if (token && uri) {
200 /* Redirect? */
201 if (sipe_strcase_equal(token, "Redirect")) {
202 SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: redirect to %s",
203 uri);
204 lync_request(sipe_private, request, uri, NULL);
205 next = FALSE;
206 break;
207 } else
208 SIPE_DEBUG_INFO("sipe_lync_autodiscover_parse: unknown token %s",
209 token);
213 /* if nothing else matched */
214 if (next) {
215 const gchar *access_location = sipe_xml_attribute(xml, "AccessLocation");
217 /* User: topology information of the user’s home server */
218 if ((node = sipe_xml_child(xml, "User")) != NULL) {
219 gpointer id = request->id;
221 /* Active request? */
222 if (id) {
223 GSList *servers;
225 /* List is reversed, i.e. internal will be tried first */
226 servers = g_slist_prepend(NULL, NULL);
228 if (!access_location ||
229 sipe_strcase_equal(access_location, "external")) {
230 servers = sipe_lync_autodiscover_add(servers,
231 node,
232 "SipClientExternalAccess");
235 if (!access_location ||
236 sipe_strcase_equal(access_location, "internal")) {
237 servers = sipe_lync_autodiscover_add(servers,
238 node,
239 "SipClientInternalAccess");
242 /* Callback takes ownership of servers list */
243 (*request->cb)(sipe_private, servers, request->cb_data);
245 /* We're done with requests for this callback */
246 FOR_ALL_REQUESTS_WITH_SAME_ID( \
247 lar->cb = NULL; \
248 lar->id = NULL \
253 /* Request completed */
254 next = FALSE;
255 sipe_lync_autodiscover_request_free(sipe_private, request);
256 /* request is invalid */
260 sipe_xml_free(xml);
262 if (next)
263 sipe_lync_autodiscover_queue_request(sipe_private, request);
266 static void sipe_lync_autodiscover_webticket(struct sipe_core_private *sipe_private,
267 SIPE_UNUSED_PARAMETER const gchar *base_uri,
268 const gchar *auth_uri,
269 const gchar *wsse_security,
270 SIPE_UNUSED_PARAMETER const gchar *failure_msg,
271 gpointer callback_data)
273 struct lync_autodiscover_request *request = callback_data;
274 gchar *saml;
276 /* Extract SAML Assertion from WSSE Security XML text */
277 if (wsse_security &&
278 ((saml = sipe_xml_extract_raw(wsse_security,
279 "Assertion",
280 TRUE)) != NULL)) {
281 gchar *base64 = g_base64_encode((const guchar *) saml,
282 strlen(saml));
283 gchar *headers = g_strdup_printf(LYNC_AUTODISCOVER_ACCEPT_HEADER
284 "X-MS-WebTicket: opaque=%s\r\n",
285 base64);
286 g_free(base64);
288 SIPE_DEBUG_INFO("sipe_lync_autodiscover_webticket: got ticket for Auth URI %s",
289 auth_uri);
290 g_free(saml);
292 lync_request(sipe_private, request, auth_uri, headers);
293 g_free(headers);
295 } else
296 sipe_lync_autodiscover_queue_request(sipe_private, request);
299 static void sipe_lync_autodiscover_cb(struct sipe_core_private *sipe_private,
300 guint status,
301 GSList *headers,
302 const gchar *body,
303 gpointer callback_data)
305 struct lync_autodiscover_request *request = callback_data;
306 const gchar *type = sipe_utils_nameval_find(headers, "Content-Type");
307 gchar *uri = request->uri;
309 request->request = NULL;
310 request->uri = NULL;
312 switch (status) {
313 case SIPE_HTTP_STATUS_OK:
314 /* only accept Autodiscover XML responses */
315 if (body && g_str_has_prefix(type, "application/vnd.microsoft.rtc.autodiscover+xml"))
316 sipe_lync_autodiscover_parse(sipe_private, request, body);
317 else
318 sipe_lync_autodiscover_queue_request(sipe_private, request);
319 break;
321 case SIPE_HTTP_STATUS_FAILED:
323 if (uri) {
324 /* check for authentication failure */
325 const gchar *webticket_uri = sipe_utils_nameval_find(headers,
326 "X-MS-WebTicketURL");
328 if (!(webticket_uri &&
329 sipe_webticket_request_with_auth(sipe_private,
330 request->session,
331 webticket_uri,
332 uri, /* Auth URI */
333 sipe_lync_autodiscover_webticket,
334 request)))
335 sipe_lync_autodiscover_queue_request(sipe_private, request);
336 } else
337 sipe_lync_autodiscover_queue_request(sipe_private, request);
339 break;
341 case SIPE_HTTP_STATUS_ABORTED:
342 /* we are not allowed to generate new requests */
343 sipe_lync_autodiscover_request_free(sipe_private, request);
344 break;
346 default:
347 sipe_lync_autodiscover_queue_request(sipe_private, request);
348 break;
351 g_free(uri);
354 /* Proceed to next method for request */
355 static void sipe_lync_autodiscover_request(struct sipe_core_private *sipe_private,
356 struct lync_autodiscover_request *request)
358 gpointer id = request->id;
360 /* Active request? */
361 if (id) {
362 static const gchar *methods[] = {
363 "%s://LyncDiscoverInternal.%s/?sipuri=%s",
364 "%s://LyncDiscover.%s/?sipuri=%s",
365 NULL
368 request->is_pending = TRUE;
370 if (request->method)
371 request->method++;
372 else
373 request->method = methods;
375 if (*request->method) {
376 gchar *uri = g_strdup_printf(*request->method,
377 request->protocol,
378 SIPE_CORE_PUBLIC->sip_domain,
379 sipe_private->username);
381 SIPE_DEBUG_INFO("sipe_lync_autodiscover_request: trying '%s'", uri);
383 lync_request(sipe_private, request, uri, NULL);
384 g_free(uri);
386 } else {
387 guint count = 0;
389 /* Count entries with the same request ID */
390 FOR_ALL_REQUESTS_WITH_SAME_ID( \
391 count++; \
394 if (count == 1) {
396 * This is the last pending request for this
397 * ID, i.e. autodiscover has failed. Create
398 * empty server list and return it.
400 GSList *servers = g_slist_prepend(NULL, NULL);
402 /* All methods tried, indicate failure to caller */
403 SIPE_DEBUG_INFO_NOFORMAT("sipe_lync_autodiscover_request: no more methods to try!");
405 /* Callback takes ownership of servers list */
406 (*request->cb)(sipe_private, servers, request->cb_data);
409 /* Request completed */
410 request->cb = NULL;
411 sipe_lync_autodiscover_request_free(sipe_private, request);
412 /* request is invalid */
414 } else {
415 /* Inactive request, callback already NULL */
416 sipe_lync_autodiscover_request_free(sipe_private, request);
417 /* request is invalid */
421 /* Proceed to next method for all requests */
422 static void sipe_lync_autodiscover_queue_request(struct sipe_core_private *sipe_private,
423 struct lync_autodiscover_request *request)
425 gpointer id = request->id;
427 /* This request is ready to proceed to next method */
428 request->is_pending = FALSE;
430 /* Is any request for the same ID still pending? */
431 FOR_ALL_REQUESTS_WITH_SAME_ID( \
432 if (lar->is_pending) \
433 return \
436 SIPE_DEBUG_INFO_NOFORMAT("sipe_lync_autodiscover_queue_request: proceed in lockstep");
438 /* No, proceed to next method for all requests */
439 FOR_ALL_REQUESTS_WITH_SAME_ID( \
440 sipe_lync_autodiscover_request(sipe_private, \
441 lar) \
445 static gpointer sipe_lync_autodiscover_create(struct sipe_core_private *sipe_private,
446 gpointer id,
447 const gchar *protocol,
448 sipe_lync_autodiscover_callback *callback,
449 gpointer callback_data)
451 struct sipe_lync_autodiscover *sla = sipe_private->lync_autodiscover;
452 struct lync_autodiscover_request *request = g_new0(struct lync_autodiscover_request, 1);
454 /* use address of first request structure as unique ID */
455 if (id == NULL)
456 id = request;
458 request->protocol = protocol;
459 request->cb = callback;
460 request->cb_data = callback_data;
461 request->id = id;
462 request->session = sipe_svc_session_start();
464 sla->pending_requests = g_slist_prepend(sla->pending_requests,
465 request);
467 sipe_lync_autodiscover_request(sipe_private, request);
469 return(id);
472 void sipe_lync_autodiscover_start(struct sipe_core_private *sipe_private,
473 sipe_lync_autodiscover_callback *callback,
474 gpointer callback_data)
476 gpointer id = NULL;
478 #define CREATE(protocol) \
479 id = sipe_lync_autodiscover_create(sipe_private, \
480 id, \
481 #protocol, \
482 callback, \
483 callback_data)
484 CREATE(http);
485 CREATE(https);
488 void sipe_lync_autodiscover_init(struct sipe_core_private *sipe_private)
490 struct sipe_lync_autodiscover *sla = g_new0(struct sipe_lync_autodiscover, 1);
492 sipe_private->lync_autodiscover = sla;
495 void sipe_lync_autodiscover_free(struct sipe_core_private *sipe_private)
497 struct sipe_lync_autodiscover *sla = sipe_private->lync_autodiscover;
499 while (sla->pending_requests)
500 sipe_lync_autodiscover_request_free(sipe_private,
501 sla->pending_requests->data);
503 g_free(sla);
504 sipe_private->lync_autodiscover = NULL;
508 Local Variables:
509 mode: c
510 c-file-style: "bsd"
511 indent-tabs-mode: t
512 tab-width: 8
513 End: