2 * @file sipe-webticket.c
6 * Copyright (C) 2011-2016 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-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx
27 * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained"
28 * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx
36 #include "sipe-common.h"
37 #include "sipe-backend.h"
38 #include "sipe-core.h"
39 #include "sipe-core-private.h"
40 #include "sipe-digest.h"
43 #include "sipe-webticket.h"
44 #include "sipe-utils.h"
47 struct webticket_queued_data
{
48 sipe_webticket_callback
*callback
;
49 gpointer callback_data
;
52 struct webticket_callback_data
{
54 const gchar
*service_port
;
55 gchar
*service_auth_uri
;
57 gchar
*webticket_negotiate_uri
;
58 gchar
*webticket_fedbearer_uri
;
60 gboolean tried_fedbearer
;
61 gboolean requires_signing
;
65 TOKEN_STATE_FEDERATION
,
66 TOKEN_STATE_FED_BEARER
,
69 struct sipe_tls_random entropy
;
71 sipe_webticket_callback
*callback
;
72 gpointer callback_data
;
74 struct sipe_svc_session
*session
;
79 struct webticket_token
{
85 struct sipe_webticket
{
89 gchar
*webticket_adfs_uri
;
91 time_t adfs_token_expires
;
93 gboolean retrieved_realminfo
;
94 gboolean shutting_down
;
97 void sipe_webticket_free(struct sipe_core_private
*sipe_private
)
99 struct sipe_webticket
*webticket
= sipe_private
->webticket
;
103 /* Web Ticket stack is shutting down: reject all new requests */
104 webticket
->shutting_down
= TRUE
;
106 g_free(webticket
->webticket_adfs_uri
);
107 g_free(webticket
->adfs_token
);
108 if (webticket
->pending
)
109 g_hash_table_destroy(webticket
->pending
);
110 if (webticket
->cache
)
111 g_hash_table_destroy(webticket
->cache
);
113 sipe_private
->webticket
= NULL
;
116 static void free_token(gpointer data
)
118 struct webticket_token
*wt
= data
;
119 g_free(wt
->auth_uri
);
124 static void sipe_webticket_init(struct sipe_core_private
*sipe_private
)
126 struct sipe_webticket
*webticket
;
128 if (sipe_private
->webticket
)
131 sipe_private
->webticket
= webticket
= g_new0(struct sipe_webticket
, 1);
133 webticket
->cache
= g_hash_table_new_full(g_str_hash
,
137 webticket
->pending
= g_hash_table_new(g_str_hash
,
141 /* takes ownership of "token" */
142 static void cache_token(struct sipe_core_private
*sipe_private
,
143 const gchar
*service_uri
,
144 const gchar
*auth_uri
,
148 struct webticket_token
*wt
= g_new0(struct webticket_token
, 1);
149 wt
->auth_uri
= g_strdup(auth_uri
);
151 wt
->expires
= expires
;
152 g_hash_table_insert(sipe_private
->webticket
->cache
,
153 g_strdup(service_uri
),
157 static const struct webticket_token
*cache_hit(struct sipe_core_private
*sipe_private
,
158 const gchar
*service_uri
)
160 const struct webticket_token
*wt
;
162 /* make sure a cached Web Ticket is still valid for 60 seconds */
163 wt
= g_hash_table_lookup(sipe_private
->webticket
->cache
,
165 if (wt
&& (wt
->expires
< time(NULL
) + 60)) {
166 SIPE_DEBUG_INFO("cache_hit: cached token for URI %s has expired",
174 /* frees just the main request data, when this is called "queued" is cleared */
175 static void callback_data_free(struct webticket_callback_data
*wcd
)
178 sipe_tls_free_random(&wcd
->entropy
);
179 g_free(wcd
->webticket_negotiate_uri
);
180 g_free(wcd
->webticket_fedbearer_uri
);
181 g_free(wcd
->service_auth_uri
);
182 g_free(wcd
->service_uri
);
187 static void queue_request(struct webticket_callback_data
*wcd
,
188 sipe_webticket_callback
*callback
,
189 gpointer callback_data
)
191 struct webticket_queued_data
*wqd
= g_new0(struct webticket_queued_data
, 1);
193 wqd
->callback
= callback
;
194 wqd
->callback_data
= callback_data
;
196 wcd
->queued
= g_slist_prepend(wcd
->queued
, wqd
);
199 static void callback_execute(struct sipe_core_private
*sipe_private
,
200 struct webticket_callback_data
*wcd
,
201 const gchar
*auth_uri
,
202 const gchar
*wsse_security
,
203 const gchar
*failure_msg
)
205 GSList
*entry
= wcd
->queued
;
207 /* complete main request */
208 wcd
->callback(sipe_private
,
215 /* complete queued requests */
217 struct webticket_queued_data
*wqd
= entry
->data
;
219 SIPE_DEBUG_INFO("callback_execute: completing queue request URI %s (Auth URI %s)",
220 wcd
->service_uri
, auth_uri
);
221 wqd
->callback(sipe_private
,
231 g_slist_free(wcd
->queued
);
233 /* drop request from pending hash */
234 g_hash_table_remove(sipe_private
->webticket
->pending
,
238 static gchar
*extract_raw_xml_attribute(const gchar
*xml
,
241 gchar
*attr_start
= g_strdup_printf("%s=\"", name
);
243 const gchar
*start
= strstr(xml
, attr_start
);
246 const gchar
*value
= start
+ strlen(attr_start
);
247 const gchar
*end
= strchr(value
, '"');
249 data
= g_strndup(value
, end
- value
);
257 static gchar
*generate_timestamp(const gchar
*raw
)
259 gchar
*lifetime
= sipe_xml_extract_raw(raw
, "Lifetime", FALSE
);
260 gchar
*timestamp
= NULL
;
262 timestamp
= g_strdup_printf("<wsu:Timestamp xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" wsu:Id=\"timestamp\">%s</wsu:Timestamp>",
268 static gchar
*generate_keydata(const gchar
*raw
)
270 return(sipe_xml_extract_raw(raw
, "Assertion", TRUE
));
273 static gchar
*generate_expires(const gchar
*timestamp
)
275 return(sipe_xml_extract_raw(timestamp
, "Expires", FALSE
));
278 static gchar
*generate_fedbearer_wsse(const gchar
*raw
)
280 gchar
*timestamp
= generate_timestamp(raw
);
281 gchar
*keydata
= sipe_xml_extract_raw(raw
, "EncryptedData", TRUE
);
282 gchar
*wsse_security
= NULL
;
284 if (timestamp
&& keydata
) {
285 SIPE_DEBUG_INFO_NOFORMAT("generate_fedbearer_wsse: found timestamp & keydata");
286 wsse_security
= g_strconcat(timestamp
, keydata
, NULL
);
291 return(wsse_security
);
294 static void generate_federation_wsse(struct sipe_webticket
*webticket
,
297 gchar
*timestamp
= generate_timestamp(raw
);
298 gchar
*keydata
= generate_keydata(raw
);
300 /* clear old ADFS token */
301 g_free(webticket
->adfs_token
);
302 webticket
->adfs_token
= NULL
;
304 if (timestamp
&& keydata
) {
305 gchar
*expires_string
= generate_expires(timestamp
);
307 if (expires_string
) {
309 SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s",
312 /* cache ADFS token */
313 webticket
->adfs_token
= g_strconcat(timestamp
,
316 webticket
->adfs_token_expires
= sipe_utils_str_to_time(expires_string
);
317 g_free(expires_string
);
325 static gchar
*generate_sha1_proof_wsse(const gchar
*raw
,
326 struct sipe_tls_random
*entropy
,
329 gchar
*timestamp
= generate_timestamp(raw
);
330 gchar
*keydata
= generate_keydata(raw
);
331 gchar
*wsse_security
= NULL
;
333 if (timestamp
&& keydata
) {
334 gchar
*expires_string
= generate_expires(timestamp
);
337 gchar
*assertionID
= extract_raw_xml_attribute(keydata
,
343 * http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1:
345 * "The key is computed using P_SHA1() from the TLS sepcification to generate
346 * a bit stream using entropy from both sides. The exact form is:
348 * key = P_SHA1(Entropy_REQ, Entropy_RES)"
350 gchar
*entropy_res_base64
= sipe_xml_extract_raw(raw
, "BinarySecret", FALSE
);
351 gsize entropy_res_length
;
352 guchar
*entropy_response
= g_base64_decode(entropy_res_base64
,
353 &entropy_res_length
);
354 guchar
*key
= sipe_tls_p_sha1(entropy
->buffer
,
359 g_free(entropy_response
);
360 g_free(entropy_res_base64
);
362 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata");
364 if (assertionID
&& key
) {
365 /* same as SIPE_DIGEST_HMAC_SHA1_LENGTH */
366 guchar digest
[SIPE_DIGEST_SHA1_LENGTH
];
371 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found assertionID and successfully computed the key");
373 /* Digest over reference element (#timestamp -> wsu:Timestamp) */
374 sipe_digest_sha1((guchar
*) timestamp
,
377 base64
= g_base64_encode(digest
,
378 SIPE_DIGEST_SHA1_LENGTH
);
380 /* XML-Sig: SignedInfo for reference element */
381 signed_info
= g_strdup_printf("<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
382 "<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
383 "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"/>"
384 "<Reference URI=\"#timestamp\">"
386 "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
388 "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>"
389 "<DigestValue>%s</DigestValue>"
395 /* XML-Sig: SignedInfo in canonical form */
396 canon
= sipe_xml_exc_c14n(signed_info
);
402 /* calculate signature */
403 sipe_digest_hmac_sha1(key
, entropy
->length
,
407 base64
= g_base64_encode(digest
,
408 SIPE_DIGEST_HMAC_SHA1_LENGTH
);
410 /* XML-Sig: Signature from SignedInfo + Key */
411 signature
= g_strdup_printf("<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
413 " <SignatureValue>%s</SignatureValue>"
415 " <wsse:SecurityTokenReference wsse:TokenType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1\">"
416 " <wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID\">%s</wsse:KeyIdentifier>"
417 " </wsse:SecurityTokenReference>"
426 wsse_security
= g_strconcat(timestamp
,
438 /* token doesn't require signature */
439 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata, no signing required");
440 wsse_security
= g_strconcat(timestamp
,
446 if (expires_string
) {
447 *expires
= sipe_utils_str_to_time(expires_string
);
448 g_free(expires_string
);
454 return(wsse_security
);
457 static gboolean
federated_authentication(struct sipe_core_private
*sipe_private
,
458 struct webticket_callback_data
*wcd
);
459 static gboolean
initiate_fedbearer(struct sipe_core_private
*sipe_private
,
460 struct webticket_callback_data
*wcd
);
461 static void webticket_token(struct sipe_core_private
*sipe_private
,
465 gpointer callback_data
)
467 struct webticket_callback_data
*wcd
= callback_data
;
468 gboolean failed
= TRUE
;
471 switch (wcd
->token_state
) {
472 case TOKEN_STATE_NONE
:
473 SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ILLEGAL STATE - should not happen...");
476 case TOKEN_STATE_SERVICE
: {
477 /* WebTicket for Web Service */
479 gchar
*wsse_security
= generate_sha1_proof_wsse(raw
,
480 wcd
->requires_signing
? &wcd
->entropy
: NULL
,
484 /* cache takes ownership of wsse_security */
485 cache_token(sipe_private
,
487 wcd
->service_auth_uri
,
490 callback_execute(sipe_private
,
492 wcd
->service_auth_uri
,
500 case TOKEN_STATE_FEDERATION
:
501 /* WebTicket from ADFS for federated authentication */
502 generate_federation_wsse(sipe_private
->webticket
,
505 if (sipe_private
->webticket
->adfs_token
) {
507 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from ADFS %s",
510 if (federated_authentication(sipe_private
,
512 /* callback data passed down the line */
518 case TOKEN_STATE_FED_BEARER
: {
519 /* WebTicket for federated authentication */
520 gchar
*wsse_security
= generate_fedbearer_wsse(raw
);
524 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s",
527 if (sipe_svc_webticket(sipe_private
,
529 wcd
->webticket_fedbearer_uri
,
531 wcd
->service_auth_uri
,
535 wcd
->token_state
= TOKEN_STATE_SERVICE
;
537 /* callback data passed down the line */
540 g_free(wsse_security
);
545 /* end of: switch (wcd->token_state) { */
549 /* Retry with federated authentication? */
550 if (wcd
->webticket_fedbearer_uri
) {
552 /* Authentication against ADFS failed? */
553 if (wcd
->token_state
== TOKEN_STATE_FEDERATION
) {
554 struct sipe_webticket
*webticket
= sipe_private
->webticket
;
556 SIPE_LOG_WARNING_NOFORMAT("webticket_token: ADFS authentication failed - assuming Multi-Factor Authentication (MFA)");
558 /* forget ADFS URI */
559 g_free(webticket
->webticket_adfs_uri
);
560 webticket
->webticket_adfs_uri
= NULL
;
563 if (!wcd
->tried_fedbearer
) {
564 SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
567 if (initiate_fedbearer(sipe_private
, wcd
)) {
568 /* callback data passed down the line */
577 gchar
*failure_msg
= NULL
;
580 failure_msg
= sipe_xml_data(sipe_xml_child(soap_body
,
581 "Body/Fault/Detail/error/internalerror/text"));
582 /* XML data can end in 
 */
583 g_strstrip(failure_msg
);
586 callback_execute(sipe_private
,
593 callback_data_free(wcd
);
597 static gboolean
federated_authentication(struct sipe_core_private
*sipe_private
,
598 struct webticket_callback_data
*wcd
)
602 if ((success
= sipe_svc_webticket_lmc_federated(sipe_private
,
604 sipe_private
->webticket
->adfs_token
,
605 wcd
->webticket_fedbearer_uri
,
608 wcd
->token_state
= TOKEN_STATE_FED_BEARER
;
610 /* If TRUE then callback data has been passed down the line */
614 static gboolean
fedbearer_authentication(struct sipe_core_private
*sipe_private
,
615 struct webticket_callback_data
*wcd
)
617 struct sipe_webticket
*webticket
= sipe_private
->webticket
;
620 /* make sure a cached ADFS token is still valid for 60 seconds */
621 if (webticket
->adfs_token
&&
622 (webticket
->adfs_token_expires
>= time(NULL
) + 60)) {
624 SIPE_DEBUG_INFO_NOFORMAT("fedbearer_authentication: reusing cached ADFS token");
625 success
= federated_authentication(sipe_private
, wcd
);
627 } else if (webticket
->webticket_adfs_uri
) {
628 if ((success
= sipe_svc_webticket_adfs(sipe_private
,
630 webticket
->webticket_adfs_uri
,
633 wcd
->token_state
= TOKEN_STATE_FEDERATION
;
635 if ((success
= sipe_svc_webticket_lmc(sipe_private
,
637 wcd
->webticket_fedbearer_uri
,
640 wcd
->token_state
= TOKEN_STATE_FED_BEARER
;
643 /* If TRUE then callback data has been passed down the line */
647 static void realminfo(struct sipe_core_private
*sipe_private
,
649 SIPE_UNUSED_PARAMETER
const gchar
*raw
,
651 gpointer callback_data
)
653 struct sipe_webticket
*webticket
= sipe_private
->webticket
;
654 struct webticket_callback_data
*wcd
= callback_data
;
656 /* Only try retrieving of RealmInfo once */
657 webticket
->retrieved_realminfo
= TRUE
;
660 * We must specifically check for abort, because
661 * realminfo == NULL is a valid response
665 /* detect ADFS setup. See also:
667 * http://en.wikipedia.org/wiki/Active_Directory_Federation_Services
669 * NOTE: this is based on observed behaviour.
670 * It is unkown if this is documented somewhere...
672 SIPE_DEBUG_INFO("realminfo: data for user %s retrieved successfully",
673 sipe_private
->username
);
675 webticket
->webticket_adfs_uri
= sipe_xml_data(sipe_xml_child(realminfo
,
679 if (webticket
->webticket_adfs_uri
) {
680 SIPE_LOG_INFO_NOFORMAT("realminfo: ADFS setup detected");
681 SIPE_DEBUG_INFO("realminfo: ADFS URI: %s",
682 webticket
->webticket_adfs_uri
);
684 SIPE_DEBUG_INFO_NOFORMAT("realminfo: no RealmInfo found or no ADFS setup detected - try direct login");
686 if (fedbearer_authentication(sipe_private
, wcd
)) {
687 /* callback data passed down the line */
693 callback_execute(sipe_private
,
698 callback_data_free(wcd
);
702 static gboolean
initiate_fedbearer(struct sipe_core_private
*sipe_private
,
703 struct webticket_callback_data
*wcd
)
707 if (sipe_private
->webticket
->retrieved_realminfo
) {
708 /* skip retrieval and go to authentication */
709 wcd
->tried_fedbearer
= TRUE
;
710 success
= fedbearer_authentication(sipe_private
, wcd
);
712 success
= sipe_svc_realminfo(sipe_private
,
721 static void webticket_metadata(struct sipe_core_private
*sipe_private
,
723 SIPE_UNUSED_PARAMETER
const gchar
*raw
,
725 gpointer callback_data
)
727 struct webticket_callback_data
*wcd
= callback_data
;
730 const sipe_xml
*node
;
732 SIPE_DEBUG_INFO("webticket_metadata: metadata for service %s retrieved successfully",
735 /* Authentication ports accepted by WebTicket Service */
736 for (node
= sipe_xml_child(metadata
, "service/port");
738 node
= sipe_xml_twin(node
)) {
739 const gchar
*auth_uri
= sipe_xml_attribute(sipe_xml_child(node
,
744 if (sipe_strcase_equal(sipe_xml_attribute(node
, "name"),
745 "WebTicketServiceWinNegotiate")) {
746 SIPE_DEBUG_INFO("webticket_metadata: WebTicket Windows Negotiate Auth URI %s", auth_uri
);
747 g_free(wcd
->webticket_negotiate_uri
);
748 wcd
->webticket_negotiate_uri
= g_strdup(auth_uri
);
749 } else if (sipe_strcase_equal(sipe_xml_attribute(node
, "name"),
751 SIPE_DEBUG_INFO("webticket_metadata: WebTicket FedBearer Auth URI %s", auth_uri
);
752 g_free(wcd
->webticket_fedbearer_uri
);
753 wcd
->webticket_fedbearer_uri
= g_strdup(auth_uri
);
758 if (wcd
->webticket_negotiate_uri
|| wcd
->webticket_fedbearer_uri
) {
761 /* Entropy: 256 random bits */
762 if (!wcd
->entropy
.buffer
)
763 sipe_tls_fill_random(&wcd
->entropy
, 256);
765 if (wcd
->webticket_negotiate_uri
) {
766 /* Try Negotiate authentication first */
768 success
= sipe_svc_webticket(sipe_private
,
770 wcd
->webticket_negotiate_uri
,
772 wcd
->service_auth_uri
,
776 wcd
->token_state
= TOKEN_STATE_SERVICE
;
778 success
= initiate_fedbearer(sipe_private
,
783 /* callback data passed down the line */
790 callback_execute(sipe_private
,
795 callback_data_free(wcd
);
799 static void service_metadata(struct sipe_core_private
*sipe_private
,
801 SIPE_UNUSED_PARAMETER
const gchar
*raw
,
803 gpointer callback_data
)
805 struct webticket_callback_data
*wcd
= callback_data
;
808 const sipe_xml
*node
;
809 gchar
*policy
= g_strdup_printf("%s_policy", wcd
->service_port
);
810 gchar
*ticket_uri
= NULL
;
812 SIPE_DEBUG_INFO("service_metadata: metadata for service %s retrieved successfully",
815 /* WebTicket policies accepted by Web Service */
816 for (node
= sipe_xml_child(metadata
, "Policy");
818 node
= sipe_xml_twin(node
)) {
819 if (sipe_strcase_equal(sipe_xml_attribute(node
, "Id"),
822 SIPE_DEBUG_INFO_NOFORMAT("service_metadata: WebTicket policy found");
824 ticket_uri
= sipe_xml_data(sipe_xml_child(node
,
825 "ExactlyOne/All/EndorsingSupportingTokens/Policy/IssuedToken/Issuer/Address"));
827 /* this token type requires signing */
828 wcd
->requires_signing
= TRUE
;
830 /* try alternative token type */
831 ticket_uri
= sipe_xml_data(sipe_xml_child(node
,
832 "ExactlyOne/All/SignedSupportingTokens/Policy/IssuedToken/Issuer/Address"));
835 SIPE_DEBUG_INFO("service_metadata: WebTicket URI %s", ticket_uri
);
844 /* Authentication ports accepted by Web Service */
845 for (node
= sipe_xml_child(metadata
, "service/port");
847 node
= sipe_xml_twin(node
)) {
848 if (sipe_strcase_equal(sipe_xml_attribute(node
, "name"),
849 wcd
->service_port
)) {
850 const gchar
*auth_uri
;
852 SIPE_DEBUG_INFO_NOFORMAT("service_metadata: authentication port found");
854 auth_uri
= sipe_xml_attribute(sipe_xml_child(node
,
858 SIPE_DEBUG_INFO("service_metadata: Auth URI %s", auth_uri
);
860 if (sipe_svc_metadata(sipe_private
,
865 /* Remember for later */
866 wcd
->service_auth_uri
= g_strdup(auth_uri
);
868 /* callback data passed down the line */
880 callback_execute(sipe_private
,
885 callback_data_free(wcd
);
889 static gboolean
webticket_request(struct sipe_core_private
*sipe_private
,
890 struct sipe_svc_session
*session
,
891 const gchar
*base_uri
,
892 const gchar
*auth_uri
,
893 const gchar
*port_name
,
894 sipe_webticket_callback
*callback
,
895 gpointer callback_data
)
897 struct sipe_webticket
*webticket
;
898 gboolean ret
= FALSE
;
900 sipe_webticket_init(sipe_private
);
901 webticket
= sipe_private
->webticket
;
903 if (webticket
->shutting_down
) {
904 SIPE_DEBUG_ERROR("webticket_request: new Web Ticket request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
911 const struct webticket_token
*wt
= cache_hit(sipe_private
, base_uri
);
913 /* cache hit for this URI? */
915 SIPE_DEBUG_INFO("webticket_request: using cached token for URI %s (Auth URI %s)",
916 base_uri
, wt
->auth_uri
);
917 callback(sipe_private
,
925 GHashTable
*pending
= webticket
->pending
;
926 struct webticket_callback_data
*wcd
= g_hash_table_lookup(pending
,
929 /* is there already a pending request for this URI? */
931 SIPE_DEBUG_INFO("webticket_request: pending request found for URI %s - queueing",
933 queue_request(wcd
, callback
, callback_data
);
936 wcd
= g_new0(struct webticket_callback_data
, 1);
938 ret
= sipe_svc_metadata(sipe_private
,
941 port_name
? service_metadata
: webticket_metadata
,
945 wcd
->service_uri
= g_strdup(base_uri
);
946 wcd
->service_port
= port_name
;
947 wcd
->service_auth_uri
= g_strdup(auth_uri
);
948 wcd
->callback
= callback
;
949 wcd
->callback_data
= callback_data
;
950 wcd
->session
= session
;
951 wcd
->token_state
= TOKEN_STATE_NONE
;
952 g_hash_table_insert(pending
,
953 wcd
->service_uri
, /* borrowed */
965 gboolean
sipe_webticket_request_with_port(struct sipe_core_private
*sipe_private
,
966 struct sipe_svc_session
*session
,
967 const gchar
*base_uri
,
968 const gchar
*port_name
,
969 sipe_webticket_callback
*callback
,
970 gpointer callback_data
)
972 return(webticket_request(sipe_private
,
975 NULL
, /* Auth URI is determined via port_name */
981 gboolean
sipe_webticket_request_with_auth(struct sipe_core_private
*sipe_private
,
982 struct sipe_svc_session
*session
,
983 const gchar
*base_uri
,
984 const gchar
*auth_uri
,
985 sipe_webticket_callback
*callback
,
986 gpointer callback_data
)
988 return(webticket_request(sipe_private
,