svc: remove http_conn
[siplcs.git] / src / core / sipe-svc.c
bloba0b24d9a261a3a0ffafbdbe8dd97b928520cf4ab
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 const gchar *body,
128 gpointer callback_data)
130 struct svc_request *data = callback_data;
131 struct sipe_svc *svc = sipe_private->svc;
133 SIPE_DEBUG_INFO("sipe_svc_https_response: code %d", status);
134 data->request = NULL;
136 if ((status == SIPE_HTTP_STATUS_OK) && body) {
137 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
138 /* Internal callback: success */
139 (*data->internal_cb)(sipe_private, data, body, xml);
140 sipe_xml_free(xml);
141 } else {
142 /* Internal callback: failed */
143 (*data->internal_cb)(sipe_private, data, NULL, NULL);
146 /* Internal callback has already called this */
147 data->cb = NULL;
149 svc->pending_requests = g_slist_remove(svc->pending_requests,
150 data);
151 sipe_svc_request_free(sipe_private, data);
155 * Send GET request when @c body is NULL, otherwise send POST request
157 * @param content_type MIME type for body content (ignored when body is @c NULL)
158 * @param soap_action SOAP action header value (ignored when body is @c NULL)
159 * @param body body contents (may be @c NULL)
161 static gboolean sipe_svc_https_request(struct sipe_core_private *sipe_private,
162 struct sipe_svc_session *session,
163 const gchar *uri,
164 const gchar *content_type,
165 const gchar *soap_action,
166 const gchar *body,
167 svc_callback *internal_callback,
168 sipe_svc_callback *callback,
169 gpointer callback_data)
171 struct svc_request *data = g_new0(struct svc_request, 1);
172 struct sipe_http_request *request;
174 if (body) {
175 gchar *headers = g_strdup_printf("SOAPAction: \"%s\"\r\n",
176 soap_action);
178 request = sipe_http_request_post(sipe_private,
179 uri,
180 headers,
181 body,
182 content_type,
183 sipe_svc_https_response,
184 data);
185 g_free(headers);
187 } else {
188 request = sipe_http_request_get(sipe_private,
189 uri,
190 NULL,
191 sipe_svc_https_response,
192 data);
195 if (request) {
196 data->internal_cb = internal_callback;
197 data->cb = callback;
198 data->cb_data = callback_data;
199 data->request = request;
200 data->uri = g_strdup(uri);
202 sipe_http_request_session(request, session->session);
204 sipe_svc_init(sipe_private);
205 sipe_private->svc->pending_requests = g_slist_prepend(sipe_private->svc->pending_requests,
206 data);
207 } else {
208 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri);
209 g_free(data);
212 return(request != NULL);
215 static gboolean sipe_svc_wsdl_request(struct sipe_core_private *sipe_private,
216 struct sipe_svc_session *session,
217 const gchar *uri,
218 const gchar *additional_ns,
219 const gchar *soap_action,
220 const gchar *wsse_security,
221 const gchar *soap_body,
222 const gchar *content_type,
223 svc_callback *internal_callback,
224 sipe_svc_callback *callback,
225 gpointer callback_data)
227 /* Only generate SOAP header if we have a security token */
228 gchar *soap_header = wsse_security ?
229 g_strdup_printf("<soap:Header>"
230 " <wsa:To>%s</wsa:To>"
231 " <wsa:ReplyTo>"
232 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
233 " </wsa:ReplyTo>"
234 " <wsa:Action>%s</wsa:Action>"
235 " <wsse:Security>%s</wsse:Security>"
236 "</soap:Header>",
237 uri,
238 soap_action,
239 wsse_security) :
240 g_strdup("");
241 gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
242 "<soap:Envelope %s"
243 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
244 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
245 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
246 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
247 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
248 " >"
249 "%s"
250 " <soap:Body>%s</soap:Body>"
251 "</soap:Envelope>",
252 additional_ns,
253 soap_header,
254 soap_body);
256 gboolean ret = sipe_svc_https_request(sipe_private,
257 session,
258 uri,
259 content_type ? content_type : "text/xml",
260 soap_action,
261 body,
262 internal_callback,
263 callback,
264 callback_data);
265 g_free(soap_header);
266 g_free(body);
268 return(ret);
271 static gboolean new_soap_req(struct sipe_core_private *sipe_private,
272 struct sipe_svc_session *session,
273 const gchar *uri,
274 const gchar *soap_action,
275 const gchar *wsse_security,
276 const gchar *soap_body,
277 svc_callback *internal_callback,
278 sipe_svc_callback *callback,
279 gpointer callback_data)
281 return(sipe_svc_wsdl_request(sipe_private,
282 session,
283 uri,
284 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
285 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
286 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
287 soap_action,
288 wsse_security,
289 soap_body,
290 NULL,
291 internal_callback,
292 callback,
293 callback_data));
296 static void sipe_svc_wsdl_response(struct sipe_core_private *sipe_private,
297 struct svc_request *data,
298 const gchar *raw,
299 sipe_xml *xml)
301 if (xml) {
302 /* Callback: success */
303 (*data->cb)(sipe_private, data->uri, raw, xml, data->cb_data);
304 } else {
305 /* Callback: failed */
306 (*data->cb)(sipe_private, data->uri, NULL, NULL, data->cb_data);
310 gboolean sipe_svc_get_and_publish_cert(struct sipe_core_private *sipe_private,
311 struct sipe_svc_session *session,
312 const gchar *uri,
313 const gchar *wsse_security,
314 const gchar *certreq,
315 sipe_svc_callback *callback,
316 gpointer callback_data)
318 struct sipe_tls_random id;
319 gchar *id_base64;
320 gchar *id_uuid;
321 gchar *uuid = get_uuid(sipe_private);
322 gchar *soap_body;
323 gboolean ret;
325 /* random request ID */
326 sipe_tls_fill_random(&id, 256);
327 id_base64 = g_base64_encode(id.buffer, id.length);
328 sipe_tls_free_random(&id);
329 id_uuid = generateUUIDfromEPID(id_base64);
330 g_free(id_base64);
332 soap_body = g_strdup_printf("<GetAndPublishCert"
333 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
334 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
335 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
336 " DeviceId=\"{%s}\""
337 " Entity=\"%s\""
339 " <wst:RequestSecurityToken>"
340 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
341 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
342 " <wsse:BinarySecurityToken"
343 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
344 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
345 " >\r\n%s</wsse:BinarySecurityToken>"
346 " <wstep:RequestID>%s</wstep:RequestID>"
347 " </wst:RequestSecurityToken>"
348 "</GetAndPublishCert>",
349 uuid,
350 sipe_private->username,
351 certreq,
352 id_uuid);
353 g_free(id_uuid);
354 g_free(uuid);
356 ret = new_soap_req(sipe_private,
357 session,
358 uri,
359 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
360 wsse_security,
361 soap_body,
362 sipe_svc_wsdl_response,
363 callback,
364 callback_data);
365 g_free(soap_body);
367 return(ret);
370 gboolean sipe_svc_ab_entry_request(struct sipe_core_private *sipe_private,
371 struct sipe_svc_session *session,
372 const gchar *uri,
373 const gchar *wsse_security,
374 const gchar *search,
375 guint entries,
376 guint max_returns,
377 sipe_svc_callback *callback,
378 gpointer callback_data)
380 gboolean ret;
381 gchar *soap_body = g_strdup_printf("<SearchAbEntry"
382 " xmlns=\"DistributionListExpander\""
383 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
384 " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\""
386 " <AbEntryRequest>"
387 " <ChangeSearch xmlns:q1=\"DistributionListExpander\" soapenc:arrayType=\"q1:AbEntryRequest.ChangeSearchQuery[%d]\">"
388 " %s"
389 " </ChangeSearch>"
390 " <Metadata>"
391 " <FromDialPad>false</FromDialPad>"
392 " <MaxResultNum>%d</MaxResultNum>"
393 " <ReturnList>displayName,msRTCSIP-PrimaryUserAddress,title,telephoneNumber,homePhone,mobile,otherTelephone,mail,company,country,photoRelPath,photoSize,photoHash</ReturnList>"
394 " </Metadata>"
395 " </AbEntryRequest>"
396 "</SearchAbEntry>",
397 entries,
398 search,
399 max_returns);
401 ret = new_soap_req(sipe_private,
402 session,
403 uri,
404 "DistributionListExpander/IAddressBook/SearchAbEntry",
405 wsse_security,
406 soap_body,
407 sipe_svc_wsdl_response,
408 callback,
409 callback_data);
410 g_free(soap_body);
412 return(ret);
415 /* Requests to login.microsoftonline.com & ADFS */
416 static gboolean request_passport(struct sipe_core_private *sipe_private,
417 struct sipe_svc_session *session,
418 const gchar *service_uri,
419 const gchar *auth_uri,
420 const gchar *wsse_security,
421 const gchar *content_type,
422 const gchar *request_extension,
423 sipe_svc_callback *callback,
424 gpointer callback_data)
426 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken>"
427 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
428 " <wsp:AppliesTo>"
429 " <wsa:EndpointReference>"
430 " <wsa:Address>%s</wsa:Address>"
431 " </wsa:EndpointReference>"
432 " </wsp:AppliesTo>"
433 " %s"
434 "</wst:RequestSecurityToken>",
435 service_uri,
436 request_extension ? request_extension : "");
438 gboolean ret = sipe_svc_wsdl_request(sipe_private,
439 session,
440 auth_uri,
441 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
442 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
443 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
444 wsse_security,
445 soap_body,
446 content_type,
447 sipe_svc_wsdl_response,
448 callback,
449 callback_data);
450 g_free(soap_body);
452 return(ret);
455 static gboolean request_user_password(struct sipe_core_private *sipe_private,
456 struct sipe_svc_session *session,
457 const gchar *service_uri,
458 const gchar *auth_uri,
459 const gchar *content_type,
460 const gchar *request_extension,
461 sipe_svc_callback *callback,
462 gpointer callback_data)
464 /* Only cleartext passwords seem to be accepted... */
465 gchar *wsse_security = g_strdup_printf("<wsse:UsernameToken>"
466 " <wsse:Username>%s</wsse:Username>"
467 " <wsse:Password>%s</wsse:Password>"
468 "</wsse:UsernameToken>",
469 sipe_private->username,
470 sipe_private->password ? sipe_private->password : "");
472 gboolean ret = request_passport(sipe_private,
473 session,
474 service_uri,
475 auth_uri,
476 wsse_security,
477 content_type,
478 request_extension,
479 callback,
480 callback_data);
481 g_free(wsse_security);
483 return(ret);
486 gboolean sipe_svc_webticket_adfs(struct sipe_core_private *sipe_private,
487 struct sipe_svc_session *session,
488 const gchar *adfs_uri,
489 sipe_svc_callback *callback,
490 gpointer callback_data)
492 return(request_user_password(sipe_private,
493 session,
494 "urn:federation:MicrosoftOnline",
495 adfs_uri,
496 /* ADFS is special, *sigh* */
497 "application/soap+xml; charset=utf-8",
498 "<wst:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</wst:KeyType>",
499 callback,
500 callback_data));
503 #define LMC_URI "https://login.microsoftonline.com:443/RST2.srf"
505 gboolean sipe_svc_webticket_lmc(struct sipe_core_private *sipe_private,
506 struct sipe_svc_session *session,
507 const gchar *service_uri,
508 sipe_svc_callback *callback,
509 gpointer callback_data)
511 return(request_user_password(sipe_private,
512 session,
513 service_uri,
514 LMC_URI,
515 NULL,
516 NULL,
517 callback,
518 callback_data));
521 gboolean sipe_svc_webticket_lmc_federated(struct sipe_core_private *sipe_private,
522 struct sipe_svc_session *session,
523 const gchar *wsse_security,
524 const gchar *service_uri,
525 sipe_svc_callback *callback,
526 gpointer callback_data)
528 return(request_passport(sipe_private,
529 session,
530 service_uri,
531 LMC_URI,
532 wsse_security,
533 NULL,
534 NULL,
535 callback,
536 callback_data));
539 gboolean sipe_svc_webticket(struct sipe_core_private *sipe_private,
540 struct sipe_svc_session *session,
541 const gchar *uri,
542 const gchar *wsse_security,
543 const gchar *service_uri,
544 const struct sipe_tls_random *entropy,
545 sipe_svc_callback *callback,
546 gpointer callback_data)
548 gchar *uuid = get_uuid(sipe_private);
549 gchar *secret = g_base64_encode(entropy->buffer, entropy->length);
550 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
551 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
552 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
553 " <wsp:AppliesTo>"
554 " <wsa:EndpointReference>"
555 " <wsa:Address>%s</wsa:Address>"
556 " </wsa:EndpointReference>"
557 " </wsp:AppliesTo>"
558 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
559 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
560 " <auth:Value>sip:%s</auth:Value>"
561 " </auth:ClaimType>"
562 " </wst:Claims>"
563 " <wst:Entropy>"
564 " <wst:BinarySecret>%s</wst:BinarySecret>"
565 " </wst:Entropy>"
566 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
567 "</wst:RequestSecurityToken>",
568 uuid,
569 service_uri,
570 sipe_private->username,
571 secret);
573 gboolean ret = new_soap_req(sipe_private,
574 session,
575 uri,
576 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
577 wsse_security,
578 soap_body,
579 sipe_svc_wsdl_response,
580 callback,
581 callback_data);
582 g_free(soap_body);
583 g_free(secret);
584 g_free(uuid);
586 return(ret);
589 static void sipe_svc_metadata_response(struct sipe_core_private *sipe_private,
590 struct svc_request *data,
591 const gchar *raw,
592 sipe_xml *xml)
594 if (xml) {
595 /* Callback: success */
596 (*data->cb)(sipe_private, data->uri, raw, xml, data->cb_data);
597 } else {
598 /* Callback: failed */
599 (*data->cb)(sipe_private, data->uri, NULL, NULL, data->cb_data);
603 gboolean sipe_svc_realminfo(struct sipe_core_private *sipe_private,
604 struct sipe_svc_session *session,
605 sipe_svc_callback *callback,
606 gpointer callback_data)
608 gchar *realminfo_uri = g_strdup_printf("https://login.microsoftonline.com/getuserrealm.srf?login=%s&xml=1",
609 sipe_private->username);
610 gboolean ret = sipe_svc_https_request(sipe_private,
611 session,
612 realminfo_uri,
613 NULL,
614 NULL,
615 NULL,
616 sipe_svc_metadata_response,
617 callback,
618 callback_data);
619 g_free(realminfo_uri);
620 return(ret);
623 gboolean sipe_svc_metadata(struct sipe_core_private *sipe_private,
624 struct sipe_svc_session *session,
625 const gchar *uri,
626 sipe_svc_callback *callback,
627 gpointer callback_data)
629 gchar *mex_uri = g_strdup_printf("%s/mex", uri);
630 gboolean ret = sipe_svc_https_request(sipe_private,
631 session,
632 mex_uri,
633 NULL,
634 NULL,
635 NULL,
636 sipe_svc_metadata_response,
637 callback,
638 callback_data);
639 g_free(mex_uri);
640 return(ret);
644 Local Variables:
645 mode: c
646 c-file-style: "bsd"
647 indent-tabs-mode: t
648 tab-width: 8
649 End: