buddy: remove http_conn
[siplcs.git] / src / core / sipe-svc.c
blob6bcd53feb162979dedb418646e9177017f4c9705
1 /**
2 * @file sipe-svc.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 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
24 * Specification references:
26 * - [MS-SIPAE]: http://msdn.microsoft.com/en-us/library/cc431510.aspx
27 * - [MS-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx
28 * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained"
29 * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx
32 #include <stdlib.h>
33 #include <string.h>
35 #include <glib.h>
37 #include "sipe-common.h"
38 #include "sipe-backend.h"
39 #include "sipe-core.h"
40 #include "sipe-core-private.h"
41 #include "sipe-http.h"
42 #include "sipe-svc.h"
43 #include "sipe-tls.h"
44 #include "sipe-utils.h"
45 #include "sipe-xml.h"
46 #include "uuid.h"
48 /* forward declaration */
49 struct svc_request;
50 typedef void (svc_callback)(struct sipe_core_private *sipe_private,
51 struct svc_request *data,
52 const gchar *raw,
53 sipe_xml *xml);
55 struct svc_request {
56 svc_callback *internal_cb;
57 sipe_svc_callback *cb;
58 gpointer *cb_data;
59 struct sipe_http_request *request;
60 gchar *uri;
63 struct sipe_svc {
64 GSList *pending_requests;
67 struct sipe_svc_session {
68 struct sipe_http_session *session;
71 static void sipe_svc_request_free(struct sipe_core_private *sipe_private,
72 struct svc_request *data)
74 if (data->request)
75 sipe_http_request_cancel(data->request);
76 if (data->cb)
77 /* Callback: aborted */
78 (*data->cb)(sipe_private, NULL, NULL, NULL, data->cb_data);
79 g_free(data->uri);
80 g_free(data);
83 void sipe_svc_free(struct sipe_core_private *sipe_private)
85 struct sipe_svc *svc = sipe_private->svc;
86 if (!svc)
87 return;
89 if (svc->pending_requests) {
90 GSList *entry = svc->pending_requests;
91 while (entry) {
92 sipe_svc_request_free(sipe_private, entry->data);
93 entry = entry->next;
95 g_slist_free(svc->pending_requests);
98 g_free(svc);
99 sipe_private->svc = NULL;
102 static void sipe_svc_init(struct sipe_core_private *sipe_private)
104 if (sipe_private->svc)
105 return;
107 sipe_private->svc = g_new0(struct sipe_svc, 1);
110 struct sipe_svc_session *sipe_svc_session_start(void)
112 struct sipe_svc_session *session = g_new0(struct sipe_svc_session, 1);
113 session->session = sipe_http_session_start();
114 return(session);
117 void sipe_svc_session_close(struct sipe_svc_session *session)
119 if (session) {
120 sipe_http_session_close(session->session);
121 g_free(session);
125 static void sipe_svc_https_response(struct sipe_core_private *sipe_private,
126 guint status,
127 SIPE_UNUSED_PARAMETER GSList *headers,
128 const gchar *body,
129 gpointer callback_data)
131 struct svc_request *data = callback_data;
132 struct sipe_svc *svc = sipe_private->svc;
134 SIPE_DEBUG_INFO("sipe_svc_https_response: code %d", status);
135 data->request = NULL;
137 if ((status == SIPE_HTTP_STATUS_OK) && body) {
138 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
139 /* Internal callback: success */
140 (*data->internal_cb)(sipe_private, data, body, xml);
141 sipe_xml_free(xml);
142 } else {
143 /* Internal callback: failed */
144 (*data->internal_cb)(sipe_private, data, NULL, NULL);
147 /* Internal callback has already called this */
148 data->cb = NULL;
150 svc->pending_requests = g_slist_remove(svc->pending_requests,
151 data);
152 sipe_svc_request_free(sipe_private, data);
156 * Send GET request when @c body is NULL, otherwise send POST request
158 * @param content_type MIME type for body content (ignored when body is @c NULL)
159 * @param soap_action SOAP action header value (ignored when body is @c NULL)
160 * @param body body contents (may be @c NULL)
162 static gboolean sipe_svc_https_request(struct sipe_core_private *sipe_private,
163 struct sipe_svc_session *session,
164 const gchar *uri,
165 const gchar *content_type,
166 const gchar *soap_action,
167 const gchar *body,
168 svc_callback *internal_callback,
169 sipe_svc_callback *callback,
170 gpointer callback_data)
172 struct svc_request *data = g_new0(struct svc_request, 1);
173 struct sipe_http_request *request;
175 if (body) {
176 gchar *headers = g_strdup_printf("SOAPAction: \"%s\"\r\n",
177 soap_action);
179 request = sipe_http_request_post(sipe_private,
180 uri,
181 headers,
182 body,
183 content_type,
184 sipe_svc_https_response,
185 data);
186 g_free(headers);
188 } else {
189 request = sipe_http_request_get(sipe_private,
190 uri,
191 NULL,
192 sipe_svc_https_response,
193 data);
196 if (request) {
197 data->internal_cb = internal_callback;
198 data->cb = callback;
199 data->cb_data = callback_data;
200 data->request = request;
201 data->uri = g_strdup(uri);
203 sipe_http_request_session(request, session->session);
205 sipe_svc_init(sipe_private);
206 sipe_private->svc->pending_requests = g_slist_prepend(sipe_private->svc->pending_requests,
207 data);
208 } else {
209 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri);
210 g_free(data);
213 return(request != NULL);
216 static gboolean sipe_svc_wsdl_request(struct sipe_core_private *sipe_private,
217 struct sipe_svc_session *session,
218 const gchar *uri,
219 const gchar *additional_ns,
220 const gchar *soap_action,
221 const gchar *wsse_security,
222 const gchar *soap_body,
223 const gchar *content_type,
224 svc_callback *internal_callback,
225 sipe_svc_callback *callback,
226 gpointer callback_data)
228 /* Only generate SOAP header if we have a security token */
229 gchar *soap_header = wsse_security ?
230 g_strdup_printf("<soap:Header>"
231 " <wsa:To>%s</wsa:To>"
232 " <wsa:ReplyTo>"
233 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
234 " </wsa:ReplyTo>"
235 " <wsa:Action>%s</wsa:Action>"
236 " <wsse:Security>%s</wsse:Security>"
237 "</soap:Header>",
238 uri,
239 soap_action,
240 wsse_security) :
241 g_strdup("");
242 gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
243 "<soap:Envelope %s"
244 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
245 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
246 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
247 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
248 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
249 " >"
250 "%s"
251 " <soap:Body>%s</soap:Body>"
252 "</soap:Envelope>",
253 additional_ns,
254 soap_header,
255 soap_body);
257 gboolean ret = sipe_svc_https_request(sipe_private,
258 session,
259 uri,
260 content_type ? content_type : "text/xml",
261 soap_action,
262 body,
263 internal_callback,
264 callback,
265 callback_data);
266 g_free(soap_header);
267 g_free(body);
269 return(ret);
272 static gboolean new_soap_req(struct sipe_core_private *sipe_private,
273 struct sipe_svc_session *session,
274 const gchar *uri,
275 const gchar *soap_action,
276 const gchar *wsse_security,
277 const gchar *soap_body,
278 svc_callback *internal_callback,
279 sipe_svc_callback *callback,
280 gpointer callback_data)
282 return(sipe_svc_wsdl_request(sipe_private,
283 session,
284 uri,
285 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
286 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
287 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
288 soap_action,
289 wsse_security,
290 soap_body,
291 NULL,
292 internal_callback,
293 callback,
294 callback_data));
297 static void sipe_svc_wsdl_response(struct sipe_core_private *sipe_private,
298 struct svc_request *data,
299 const gchar *raw,
300 sipe_xml *xml)
302 if (xml) {
303 /* Callback: success */
304 (*data->cb)(sipe_private, data->uri, raw, xml, data->cb_data);
305 } else {
306 /* Callback: failed */
307 (*data->cb)(sipe_private, data->uri, NULL, NULL, data->cb_data);
311 gboolean sipe_svc_get_and_publish_cert(struct sipe_core_private *sipe_private,
312 struct sipe_svc_session *session,
313 const gchar *uri,
314 const gchar *wsse_security,
315 const gchar *certreq,
316 sipe_svc_callback *callback,
317 gpointer callback_data)
319 struct sipe_tls_random id;
320 gchar *id_base64;
321 gchar *id_uuid;
322 gchar *uuid = get_uuid(sipe_private);
323 gchar *soap_body;
324 gboolean ret;
326 /* random request ID */
327 sipe_tls_fill_random(&id, 256);
328 id_base64 = g_base64_encode(id.buffer, id.length);
329 sipe_tls_free_random(&id);
330 id_uuid = generateUUIDfromEPID(id_base64);
331 g_free(id_base64);
333 soap_body = g_strdup_printf("<GetAndPublishCert"
334 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
335 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
336 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
337 " DeviceId=\"{%s}\""
338 " Entity=\"%s\""
340 " <wst:RequestSecurityToken>"
341 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
342 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
343 " <wsse:BinarySecurityToken"
344 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
345 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
346 " >\r\n%s</wsse:BinarySecurityToken>"
347 " <wstep:RequestID>%s</wstep:RequestID>"
348 " </wst:RequestSecurityToken>"
349 "</GetAndPublishCert>",
350 uuid,
351 sipe_private->username,
352 certreq,
353 id_uuid);
354 g_free(id_uuid);
355 g_free(uuid);
357 ret = new_soap_req(sipe_private,
358 session,
359 uri,
360 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
361 wsse_security,
362 soap_body,
363 sipe_svc_wsdl_response,
364 callback,
365 callback_data);
366 g_free(soap_body);
368 return(ret);
371 gboolean sipe_svc_ab_entry_request(struct sipe_core_private *sipe_private,
372 struct sipe_svc_session *session,
373 const gchar *uri,
374 const gchar *wsse_security,
375 const gchar *search,
376 guint entries,
377 guint max_returns,
378 sipe_svc_callback *callback,
379 gpointer callback_data)
381 gboolean ret;
382 gchar *soap_body = g_strdup_printf("<SearchAbEntry"
383 " xmlns=\"DistributionListExpander\""
384 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
385 " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\""
387 " <AbEntryRequest>"
388 " <ChangeSearch xmlns:q1=\"DistributionListExpander\" soapenc:arrayType=\"q1:AbEntryRequest.ChangeSearchQuery[%d]\">"
389 " %s"
390 " </ChangeSearch>"
391 " <Metadata>"
392 " <FromDialPad>false</FromDialPad>"
393 " <MaxResultNum>%d</MaxResultNum>"
394 " <ReturnList>displayName,msRTCSIP-PrimaryUserAddress,title,telephoneNumber,homePhone,mobile,otherTelephone,mail,company,country,photoRelPath,photoSize,photoHash</ReturnList>"
395 " </Metadata>"
396 " </AbEntryRequest>"
397 "</SearchAbEntry>",
398 entries,
399 search,
400 max_returns);
402 ret = new_soap_req(sipe_private,
403 session,
404 uri,
405 "DistributionListExpander/IAddressBook/SearchAbEntry",
406 wsse_security,
407 soap_body,
408 sipe_svc_wsdl_response,
409 callback,
410 callback_data);
411 g_free(soap_body);
413 return(ret);
416 /* Requests to login.microsoftonline.com & ADFS */
417 static gboolean request_passport(struct sipe_core_private *sipe_private,
418 struct sipe_svc_session *session,
419 const gchar *service_uri,
420 const gchar *auth_uri,
421 const gchar *wsse_security,
422 const gchar *content_type,
423 const gchar *request_extension,
424 sipe_svc_callback *callback,
425 gpointer callback_data)
427 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken>"
428 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
429 " <wsp:AppliesTo>"
430 " <wsa:EndpointReference>"
431 " <wsa:Address>%s</wsa:Address>"
432 " </wsa:EndpointReference>"
433 " </wsp:AppliesTo>"
434 " %s"
435 "</wst:RequestSecurityToken>",
436 service_uri,
437 request_extension ? request_extension : "");
439 gboolean ret = sipe_svc_wsdl_request(sipe_private,
440 session,
441 auth_uri,
442 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
443 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
444 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
445 wsse_security,
446 soap_body,
447 content_type,
448 sipe_svc_wsdl_response,
449 callback,
450 callback_data);
451 g_free(soap_body);
453 return(ret);
456 static gboolean request_user_password(struct sipe_core_private *sipe_private,
457 struct sipe_svc_session *session,
458 const gchar *service_uri,
459 const gchar *auth_uri,
460 const gchar *authuser,
461 const gchar *content_type,
462 const gchar *request_extension,
463 sipe_svc_callback *callback,
464 gpointer callback_data)
466 /* Only cleartext passwords seem to be accepted... */
467 gchar *wsse_security = g_strdup_printf("<wsse:UsernameToken>"
468 " <wsse:Username>%s</wsse:Username>"
469 " <wsse:Password>%s</wsse:Password>"
470 "</wsse:UsernameToken>",
471 authuser,
472 sipe_private->password ? sipe_private->password : "");
474 gboolean ret = request_passport(sipe_private,
475 session,
476 service_uri,
477 auth_uri,
478 wsse_security,
479 content_type,
480 request_extension,
481 callback,
482 callback_data);
483 g_free(wsse_security);
485 return(ret);
488 gboolean sipe_svc_webticket_adfs(struct sipe_core_private *sipe_private,
489 struct sipe_svc_session *session,
490 const gchar *adfs_uri,
491 sipe_svc_callback *callback,
492 gpointer callback_data)
494 return(request_user_password(sipe_private,
495 session,
496 "urn:federation:MicrosoftOnline",
497 adfs_uri,
498 sipe_private->username,
499 /* ADFS is special, *sigh* */
500 "application/soap+xml; charset=utf-8",
501 "<wst:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</wst:KeyType>",
502 callback,
503 callback_data));
506 #define LMC_URI "https://login.microsoftonline.com:443/RST2.srf"
508 gboolean sipe_svc_webticket_lmc(struct sipe_core_private *sipe_private,
509 struct sipe_svc_session *session,
510 const gchar *service_uri,
511 sipe_svc_callback *callback,
512 gpointer callback_data)
514 return(request_user_password(sipe_private,
515 session,
516 service_uri,
517 LMC_URI,
518 sipe_private->authuser ? sipe_private->authuser : sipe_private->username,
519 NULL,
520 NULL,
521 callback,
522 callback_data));
525 gboolean sipe_svc_webticket_lmc_federated(struct sipe_core_private *sipe_private,
526 struct sipe_svc_session *session,
527 const gchar *wsse_security,
528 const gchar *service_uri,
529 sipe_svc_callback *callback,
530 gpointer callback_data)
532 return(request_passport(sipe_private,
533 session,
534 service_uri,
535 LMC_URI,
536 wsse_security,
537 NULL,
538 NULL,
539 callback,
540 callback_data));
543 gboolean sipe_svc_webticket(struct sipe_core_private *sipe_private,
544 struct sipe_svc_session *session,
545 const gchar *uri,
546 const gchar *wsse_security,
547 const gchar *service_uri,
548 const struct sipe_tls_random *entropy,
549 sipe_svc_callback *callback,
550 gpointer callback_data)
552 gchar *uuid = get_uuid(sipe_private);
553 gchar *secret = g_base64_encode(entropy->buffer, entropy->length);
554 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
555 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
556 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
557 " <wsp:AppliesTo>"
558 " <wsa:EndpointReference>"
559 " <wsa:Address>%s</wsa:Address>"
560 " </wsa:EndpointReference>"
561 " </wsp:AppliesTo>"
562 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
563 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
564 " <auth:Value>sip:%s</auth:Value>"
565 " </auth:ClaimType>"
566 " </wst:Claims>"
567 " <wst:Entropy>"
568 " <wst:BinarySecret>%s</wst:BinarySecret>"
569 " </wst:Entropy>"
570 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
571 "</wst:RequestSecurityToken>",
572 uuid,
573 service_uri,
574 sipe_private->username,
575 secret);
577 gboolean ret = new_soap_req(sipe_private,
578 session,
579 uri,
580 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
581 wsse_security,
582 soap_body,
583 sipe_svc_wsdl_response,
584 callback,
585 callback_data);
586 g_free(soap_body);
587 g_free(secret);
588 g_free(uuid);
590 return(ret);
593 static void sipe_svc_metadata_response(struct sipe_core_private *sipe_private,
594 struct svc_request *data,
595 const gchar *raw,
596 sipe_xml *xml)
598 if (xml) {
599 /* Callback: success */
600 (*data->cb)(sipe_private, data->uri, raw, xml, data->cb_data);
601 } else {
602 /* Callback: failed */
603 (*data->cb)(sipe_private, data->uri, NULL, NULL, data->cb_data);
607 gboolean sipe_svc_realminfo(struct sipe_core_private *sipe_private,
608 struct sipe_svc_session *session,
609 sipe_svc_callback *callback,
610 gpointer callback_data)
612 gchar *realminfo_uri = g_strdup_printf("https://login.microsoftonline.com/getuserrealm.srf?login=%s&xml=1",
613 sipe_private->username);
614 gboolean ret = sipe_svc_https_request(sipe_private,
615 session,
616 realminfo_uri,
617 NULL,
618 NULL,
619 NULL,
620 sipe_svc_metadata_response,
621 callback,
622 callback_data);
623 g_free(realminfo_uri);
624 return(ret);
627 gboolean sipe_svc_metadata(struct sipe_core_private *sipe_private,
628 struct sipe_svc_session *session,
629 const gchar *uri,
630 sipe_svc_callback *callback,
631 gpointer callback_data)
633 gchar *mex_uri = g_strdup_printf("%s/mex", uri);
634 gboolean ret = sipe_svc_https_request(sipe_private,
635 session,
636 mex_uri,
637 NULL,
638 NULL,
639 NULL,
640 sipe_svc_metadata_response,
641 callback,
642 callback_data);
643 g_free(mex_uri);
644 return(ret);
648 Local Variables:
649 mode: c
650 c-file-style: "bsd"
651 indent-tabs-mode: t
652 tab-width: 8
653 End: