6 * Copyright (C) 2013-2015 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 * Implementation for Unified Contact Store [MS-OXWSCOS]
25 * <http://msdn.microsoft.com/en-us/library/jj194130.aspx>
27 * <http://msdn.microsoft.com/en-us/library/office/bb204119.aspx>
28 * Photo Web Service Protocol [MS-OXWSPHOTO]
29 * <http://msdn.microsoft.com/en-us/library/jj194353.aspx>
30 * GetUserPhoto operation
31 * <http://msdn.microsoft.com/en-us/library/office/jj900502.aspx>
32 * FindPeople operation
33 * <http://msdn.microsoft.com/en-us/library/office/jj191039.aspx>
41 #include "sipe-backend.h"
42 #include "sipe-buddy.h"
43 #include "sipe-common.h"
44 #include "sipe-core.h"
45 #include "sipe-core-private.h"
46 #include "sipe-digest.h"
47 #include "sipe-ews-autodiscover.h"
48 #include "sipe-group.h"
49 #include "sipe-http.h"
51 #include "sipe-subscriptions.h"
53 #include "sipe-utils.h"
56 struct sipe_ucs_transaction
{
57 GSList
*pending_requests
;
60 typedef void (ucs_callback
)(struct sipe_core_private
*sipe_private
,
61 struct sipe_ucs_transaction
*trans
,
63 gpointer callback_data
);
69 struct sipe_ucs_transaction
*transaction
;
70 struct sipe_http_request
*request
;
74 struct ucs_request
*active_request
;
76 GSList
*default_transaction
;
81 gboolean shutting_down
;
84 static void sipe_ucs_request_free(struct sipe_core_private
*sipe_private
,
85 struct ucs_request
*data
)
87 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
88 struct sipe_ucs_transaction
*trans
= data
->transaction
;
90 /* remove request from transaction */
91 trans
->pending_requests
= g_slist_remove(trans
->pending_requests
,
93 sipe_private
->ucs
->active_request
= NULL
;
95 /* remove completed transactions (except default transaction) */
96 if (!trans
->pending_requests
&&
97 (trans
!= ucs
->default_transaction
->data
)) {
98 ucs
->transactions
= g_slist_remove(ucs
->transactions
,
104 sipe_http_request_cancel(data
->request
);
106 /* Callback: aborted */
107 (*data
->cb
)(sipe_private
, NULL
, NULL
, data
->cb_data
);
112 static void sipe_ucs_next_request(struct sipe_core_private
*sipe_private
);
113 static void sipe_ucs_http_response(struct sipe_core_private
*sipe_private
,
115 SIPE_UNUSED_PARAMETER GSList
*headers
,
117 gpointer callback_data
)
119 struct ucs_request
*data
= callback_data
;
121 SIPE_DEBUG_INFO("sipe_ucs_http_response: code %d", status
);
122 data
->request
= NULL
;
124 if ((status
== SIPE_HTTP_STATUS_OK
) && body
) {
125 sipe_xml
*xml
= sipe_xml_parse(body
, strlen(body
));
126 const sipe_xml
*soap_body
= sipe_xml_child(xml
, "Body");
127 /* Callback: success */
128 (*data
->cb
)(sipe_private
,
134 /* Callback: failed */
135 (*data
->cb
)(sipe_private
, NULL
, NULL
, data
->cb_data
);
138 /* already been called */
141 sipe_ucs_request_free(sipe_private
, data
);
142 sipe_ucs_next_request(sipe_private
);
145 static void sipe_ucs_next_request(struct sipe_core_private
*sipe_private
)
147 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
148 struct sipe_ucs_transaction
*trans
;
150 if (ucs
->active_request
|| ucs
->shutting_down
|| !ucs
->ews_url
)
153 trans
= ucs
->transactions
->data
;
154 while (trans
->pending_requests
) {
155 struct ucs_request
*data
= trans
->pending_requests
->data
;
156 gchar
*soap
= g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
158 " xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\""
159 " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
160 " xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\""
163 " <t:RequestServerVersion Version=\"Exchange2013\" />"
170 struct sipe_http_request
*request
= sipe_http_request_post(sipe_private
,
174 "text/xml; charset=UTF-8",
175 sipe_ucs_http_response
,
182 data
->request
= request
;
184 ucs
->active_request
= data
;
186 sipe_core_email_authentication(sipe_private
,
188 sipe_http_request_allow_redirect(request
);
189 sipe_http_request_ready(request
);
193 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_next_request: failed to create HTTP connection");
194 sipe_ucs_request_free(sipe_private
, data
);
199 static gboolean
sipe_ucs_http_request(struct sipe_core_private
*sipe_private
,
200 struct sipe_ucs_transaction
*trans
,
201 gchar
*body
, /* takes ownership */
202 ucs_callback
*callback
,
203 gpointer callback_data
)
205 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
207 if (!ucs
|| ucs
->shutting_down
) {
208 SIPE_DEBUG_ERROR("sipe_ucs_http_request: new UCS request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
210 body
? body
: "<EMPTY>");
215 struct ucs_request
*data
= g_new0(struct ucs_request
, 1);
218 data
->cb_data
= callback_data
;
222 trans
= ucs
->default_transaction
->data
;
223 data
->transaction
= trans
;
224 trans
->pending_requests
= g_slist_append(trans
->pending_requests
,
227 sipe_ucs_next_request(sipe_private
);
232 struct sipe_ucs_transaction
*sipe_ucs_transaction(struct sipe_core_private
*sipe_private
)
234 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
235 struct sipe_ucs_transaction
*trans
;
240 /* always insert new transactions before default transaction */
241 trans
= g_new0(struct sipe_ucs_transaction
, 1);
242 ucs
->transactions
= g_slist_insert_before(ucs
->transactions
,
243 ucs
->default_transaction
,
249 static void sipe_ucs_get_user_photo_response(struct sipe_core_private
*sipe_private
,
250 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
251 const sipe_xml
*body
,
252 gpointer callback_data
)
254 gchar
*uri
= callback_data
;
255 const sipe_xml
*node
= sipe_xml_child(body
,
256 "GetUserPhotoResponse/PictureData");
262 guchar digest
[SIPE_DIGEST_SHA1_LENGTH
];
263 gchar
*digest_string
;
265 /* decode photo data */
266 base64
= sipe_xml_data(node
);
267 photo
= g_base64_decode(base64
, &photo_size
);
270 /* EWS doesn't provide a hash -> calculate SHA-1 digest */
271 sipe_digest_sha1(photo
, photo_size
, digest
);
272 digest_string
= buff_to_hex_str(digest
,
273 SIPE_DIGEST_SHA1_LENGTH
);
275 /* backend frees "photo" */
276 sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC
,
281 g_free(digest_string
);
287 void sipe_ucs_get_photo(struct sipe_core_private
*sipe_private
,
290 gchar
*payload
= g_strdup(uri
);
291 gchar
*body
= g_strdup_printf("<m:GetUserPhoto>"
292 " <m:Email>%s</m:Email>"
293 " <m:SizeRequested>HR48x48</m:SizeRequested>"
295 sipe_get_no_sip_uri(uri
));
297 if (!sipe_ucs_http_request(sipe_private
,
300 sipe_ucs_get_user_photo_response
,
305 static void sipe_ucs_search_response(struct sipe_core_private
*sipe_private
,
306 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
307 const sipe_xml
*body
,
308 gpointer callback_data
)
310 const sipe_xml
*persona_node
;
311 struct sipe_backend_search_results
*results
= NULL
;
312 guint match_count
= 0;
314 for (persona_node
= sipe_xml_child(body
,
315 "FindPeopleResponse/People/Persona");
317 persona_node
= sipe_xml_twin(persona_node
)) {
318 const sipe_xml
*address
= sipe_xml_child(persona_node
,
321 /* only display Persona nodes which have an "ImAddress" node */
328 /* OK, we found something - show the results to the user */
331 results
= sipe_backend_search_results_start(SIPE_CORE_PUBLIC
,
334 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_search_response: Unable to display the search results.");
335 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
337 _("Unable to display the search results"));
342 uri
= sipe_xml_data(address
);
343 displayname
= sipe_xml_data(sipe_xml_child(persona_node
,
345 company
= sipe_xml_data(sipe_xml_child(persona_node
,
347 email
= sipe_xml_data(sipe_xml_child(persona_node
,
348 "EmailAddress/EmailAddress"));
350 sipe_backend_search_results_add(SIPE_CORE_PUBLIC
,
352 sipe_get_no_sip_uri(uri
),
366 sipe_buddy_search_contacts_finalize(sipe_private
,
371 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
373 _("No contacts found"));
376 void sipe_ucs_search(struct sipe_core_private
*sipe_private
,
377 struct sipe_backend_search_token
*token
,
378 const gchar
*given_name
,
379 const gchar
*surname
,
382 const gchar
*company
,
383 const gchar
*country
)
386 GString
*query
= g_string_new(NULL
);
389 * Search GAL for matching entries
391 * QueryString should support field properties and quoting ("")
392 * according to the specification. But in my trials I couldn't get
393 * them to work. Concatenate all query words to a single string.
394 * Only items that match ALL words will be returned by this query.
396 #define ADD_QUERY_VALUE(val) \
399 g_string_append_c(query, ' '); \
400 g_string_append(query, val); \
403 ADD_QUERY_VALUE(given_name
);
404 ADD_QUERY_VALUE(surname
);
405 ADD_QUERY_VALUE(email
);
406 ADD_QUERY_VALUE(sipid
);
407 ADD_QUERY_VALUE(company
);
408 ADD_QUERY_VALUE(country
);
411 gchar
*body
= g_markup_printf_escaped("<m:FindPeople>"
413 " <t:BaseShape>IdOnly</t:BaseShape>"
414 " <t:AdditionalProperties>"
415 " <t:FieldURI FieldURI=\"persona:CompanyName\"/>"
416 " <t:FieldURI FieldURI=\"persona:DisplayName\"/>"
417 " <t:FieldURI FieldURI=\"persona:EmailAddress\"/>"
418 " <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
419 /* Locations doesn't seem to work
420 " <t:FieldURI FieldURI=\"persona:Locations\"/>"
422 " </t:AdditionalProperties>"
424 " <m:IndexedPageItemView BasePoint=\"Beginning\" MaxEntriesReturned=\"100\" Offset=\"0\"/>"
426 * I have no idea why Exchnage doesn't accept this
427 * FieldURI for restrictions. Without it the search
428 * will return users that don't have an ImAddress
429 * and we need to filter them out ourselves :-(
432 " <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
436 " <m:ParentFolderId>"
437 " <t:DistinguishedFolderId Id=\"directory\"/>"
438 " </m:ParentFolderId>"
439 " <m:QueryString>%s</m:QueryString>"
443 if (!sipe_ucs_http_request(sipe_private
,
446 sipe_ucs_search_response
,
448 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
450 _("Contact search failed"));
452 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
454 _("Invalid contact search query"));
456 g_string_free(query
, TRUE
);
459 static void sipe_ucs_ignore_response(struct sipe_core_private
*sipe_private
,
460 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
461 SIPE_UNUSED_PARAMETER
const sipe_xml
*body
,
462 SIPE_UNUSED_PARAMETER gpointer callback_data
)
464 SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_ignore_response: done");
465 sipe_private
->ucs
->last_response
= time(NULL
);
468 static void ucs_extract_keys(const sipe_xml
*persona_node
,
470 const gchar
**change
)
472 const sipe_xml
*attr_node
;
475 * extract Exchange key - play the guessing game :-(
477 * We can't use the "DisplayName" node, because the text is localized.
479 * Assume that IsQuickContact == "true" and IsHidden == "false" means
480 * this Attribution node contains the information for the Lync contact.
482 for (attr_node
= sipe_xml_child(persona_node
,
483 "Attributions/Attribution");
485 attr_node
= sipe_xml_twin(attr_node
)) {
486 const sipe_xml
*id_node
= sipe_xml_child(attr_node
,
488 gchar
*hidden
= sipe_xml_data(sipe_xml_child(attr_node
,
490 gchar
*quick
= sipe_xml_data(sipe_xml_child(attr_node
,
493 sipe_strcase_equal(hidden
, "false") &&
494 sipe_strcase_equal(quick
, "true")) {
495 *key
= sipe_xml_attribute(id_node
, "Id");
496 *change
= sipe_xml_attribute(id_node
, "ChangeKey");
506 static void sipe_ucs_add_new_im_contact_to_group_response(struct sipe_core_private
*sipe_private
,
507 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
508 const sipe_xml
*body
,
509 gpointer callback_data
)
511 gchar
*who
= callback_data
;
512 struct sipe_buddy
*buddy
= sipe_buddy_find_by_uri(sipe_private
, who
);
513 const sipe_xml
*persona_node
= sipe_xml_child(body
,
514 "AddNewImContactToGroupResponse/Persona");
516 sipe_private
->ucs
->last_response
= time(NULL
);
520 is_empty(buddy
->exchange_key
) &&
521 is_empty(buddy
->change_key
)) {
522 const gchar
*key
= NULL
;
523 const gchar
*change
= NULL
;
525 ucs_extract_keys(persona_node
, &key
, &change
);
527 if (!is_empty(key
) && !is_empty(change
)) {
529 sipe_buddy_add_keys(sipe_private
,
534 SIPE_DEBUG_INFO("sipe_ucs_add_new_im_contact_to_group_response: persona URI '%s' key '%s' change '%s'",
535 buddy
->name
, key
, change
);
542 void sipe_ucs_group_add_buddy(struct sipe_core_private
*sipe_private
,
543 struct sipe_ucs_transaction
*trans
,
544 struct sipe_group
*group
,
545 struct sipe_buddy
*buddy
,
548 /* existing or new buddy? */
549 if (buddy
&& buddy
->exchange_key
) {
550 gchar
*body
= g_strdup_printf("<m:AddImContactToGroup>"
551 " <m:ContactId Id=\"%s\" ChangeKey=\"%s\"/>"
552 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
553 "</m:AddImContactToGroup>",
559 sipe_ucs_http_request(sipe_private
,
562 sipe_ucs_ignore_response
,
565 gchar
*payload
= g_strdup(who
);
566 gchar
*body
= g_strdup_printf("<m:AddNewImContactToGroup>"
567 " <m:ImAddress>%s</m:ImAddress>"
568 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
569 "</m:AddNewImContactToGroup>",
570 sipe_get_no_sip_uri(who
),
574 if (!sipe_ucs_http_request(sipe_private
,
577 sipe_ucs_add_new_im_contact_to_group_response
,
583 void sipe_ucs_group_remove_buddy(struct sipe_core_private
*sipe_private
,
584 struct sipe_ucs_transaction
*trans
,
585 struct sipe_group
*group
,
586 struct sipe_buddy
*buddy
)
590 * If a contact is removed from last group, it will also be
591 * removed from contact list completely. The documentation has
592 * a RemoveContactFromImList operation, but that doesn't seem
593 * to work at all, i.e. it is always rejected by the server.
595 gchar
*body
= g_strdup_printf("<m:RemoveImContactFromGroup>"
596 " <m:ContactId Id=\"%s\" ChangeKey=\"%s\"/>"
597 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
598 "</m:RemoveImContactFromGroup>",
604 sipe_ucs_http_request(sipe_private
,
607 sipe_ucs_ignore_response
,
612 static struct sipe_group
*ucs_create_group(struct sipe_core_private
*sipe_private
,
613 const sipe_xml
*group_node
)
615 const sipe_xml
*id_node
= sipe_xml_child(group_node
,
617 const gchar
*key
= sipe_xml_attribute(id_node
, "Id");
618 const gchar
*change
= sipe_xml_attribute(id_node
, "ChangeKey");
619 struct sipe_group
*group
= NULL
;
621 if (!(is_empty(key
) || is_empty(change
))) {
622 gchar
*name
= sipe_xml_data(sipe_xml_child(group_node
,
624 group
= sipe_group_add(sipe_private
,
628 /* sipe_group must have unique ID */
629 ++sipe_private
->ucs
->group_id
);
636 static void sipe_ucs_add_im_group_response(struct sipe_core_private
*sipe_private
,
637 struct sipe_ucs_transaction
*trans
,
638 const sipe_xml
*body
,
639 gpointer callback_data
)
641 gchar
*who
= callback_data
;
642 const sipe_xml
*group_node
= sipe_xml_child(body
,
643 "AddImGroupResponse/ImGroup");
644 struct sipe_group
*group
= ucs_create_group(sipe_private
, group_node
);
646 sipe_private
->ucs
->last_response
= time(NULL
);
649 struct sipe_buddy
*buddy
= sipe_buddy_find_by_uri(sipe_private
,
653 sipe_buddy_insert_group(buddy
, group
);
655 sipe_ucs_group_add_buddy(sipe_private
,
665 void sipe_ucs_group_create(struct sipe_core_private
*sipe_private
,
666 struct sipe_ucs_transaction
*trans
,
670 gchar
*payload
= g_strdup(who
);
671 /* new_name can contain restricted characters */
672 gchar
*body
= g_markup_printf_escaped("<m:AddImGroup>"
673 " <m:DisplayName>%s</m:DisplayName>"
677 if (!sipe_ucs_http_request(sipe_private
,
680 sipe_ucs_add_im_group_response
,
685 void sipe_ucs_group_rename(struct sipe_core_private
*sipe_private
,
686 struct sipe_group
*group
,
687 const gchar
*new_name
)
689 /* new_name can contain restricted characters */
690 gchar
*body
= g_markup_printf_escaped("<m:SetImGroup>"
691 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
692 " <m:NewDisplayName>%s</m:NewDisplayName>"
698 sipe_ucs_http_request(sipe_private
,
701 sipe_ucs_ignore_response
,
705 void sipe_ucs_group_remove(struct sipe_core_private
*sipe_private
,
706 struct sipe_group
*group
)
708 gchar
*body
= g_strdup_printf("<m:RemoveImGroup>"
709 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
710 "</m:RemoveImGroup>",
714 sipe_ucs_http_request(sipe_private
,
717 sipe_ucs_ignore_response
,
721 static void ucs_init_failure(struct sipe_core_private
*sipe_private
)
723 /* Did the user specify any email settings? */
724 gboolean default_settings
=
725 is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC
,
726 SIPE_SETTING_EMAIL_URL
)) &&
727 is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC
,
728 SIPE_SETTING_EMAIL_LOGIN
)) &&
729 is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC
,
730 SIPE_SETTING_EMAIL_PASSWORD
));
732 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
733 _("UCS initialization failed!"),
735 _("Couldn't find an Exchange server with the default Email settings. Therefore the contacts list will not work.\n\nYou'll need to provide Email settings in the account setup.") :
736 _("Couldn't find an Exchange server with the Email settings provided in the account setup. Therefore the contacts list will not work.\n\nPlease correct your Email settings."));
739 static void sipe_ucs_get_im_item_list_response(struct sipe_core_private
*sipe_private
,
740 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
741 const sipe_xml
*body
,
742 SIPE_UNUSED_PARAMETER gpointer callback_data
)
744 const sipe_xml
*node
= sipe_xml_child(body
,
745 "GetImItemListResponse/ImItemList");
748 const sipe_xml
*persona_node
;
749 const sipe_xml
*group_node
;
750 GHashTable
*uri_to_alias
= g_hash_table_new_full(g_str_hash
,
755 /* Start processing contact list */
756 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
757 sipe_group_update_start(sipe_private
);
758 sipe_buddy_update_start(sipe_private
);
760 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC
);
762 for (persona_node
= sipe_xml_child(node
, "Personas/Persona");
764 persona_node
= sipe_xml_twin(persona_node
)) {
765 gchar
*address
= sipe_xml_data(sipe_xml_child(persona_node
,
767 const gchar
*key
= NULL
;
768 const gchar
*change
= NULL
;
770 ucs_extract_keys(persona_node
, &key
, &change
);
772 if (!(is_empty(address
) || is_empty(key
) || is_empty(change
))) {
773 gchar
*alias
= sipe_xml_data(sipe_xml_child(persona_node
,
776 * it seems to be undefined if ImAddress node
777 * contains "sip:" prefix or not...
779 gchar
*uri
= sip_uri(address
);
780 struct sipe_buddy
*buddy
= sipe_buddy_add(sipe_private
,
786 /* hash table takes ownership of alias */
787 g_hash_table_insert(uri_to_alias
,
791 SIPE_DEBUG_INFO("sipe_ucs_get_im_item_list_response: persona URI '%s' key '%s' change '%s'",
792 buddy
->name
, key
, change
);
797 for (group_node
= sipe_xml_child(node
, "Groups/ImGroup");
799 group_node
= sipe_xml_twin(group_node
)) {
800 struct sipe_group
*group
= ucs_create_group(sipe_private
,
804 const sipe_xml
*member_node
;
806 for (member_node
= sipe_xml_child(group_node
,
807 "MemberCorrelationKey/ItemId");
809 member_node
= sipe_xml_twin(member_node
)) {
810 struct sipe_buddy
*buddy
= sipe_buddy_find_by_exchange_key(sipe_private
,
811 sipe_xml_attribute(member_node
,
814 sipe_buddy_add_to_group(sipe_private
,
817 g_hash_table_lookup(uri_to_alias
,
823 g_hash_table_destroy(uri_to_alias
);
825 /* Finished processing contact list */
826 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
827 sipe_buddy_update_finish(sipe_private
);
828 sipe_group_update_finish(sipe_private
);
830 sipe_buddy_cleanup_local_list(sipe_private
);
831 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC
);
832 sipe_subscribe_presence_initial(sipe_private
);
834 } else if (sipe_private
->ucs
) {
835 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_get_im_item_list_response: query failed, contact list operations will not work!");
836 ucs_init_failure(sipe_private
);
840 static void ucs_get_im_item_list(struct sipe_core_private
*sipe_private
)
842 if (sipe_private
->ucs
->migrated
)
843 sipe_ucs_http_request(sipe_private
,
844 /* prioritize over pending default requests */
845 sipe_ucs_transaction(sipe_private
),
846 g_strdup("<m:GetImItemList/>"),
847 sipe_ucs_get_im_item_list_response
,
851 static void ucs_set_ews_url(struct sipe_core_private
*sipe_private
,
852 const gchar
*ews_url
)
854 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
856 SIPE_DEBUG_INFO("ucs_set_ews_url: '%s'", ews_url
);
857 ucs
->ews_url
= g_strdup(ews_url
);
859 /* this will trigger sending of the first deferred request */
860 ucs_get_im_item_list(sipe_private
);
863 static void ucs_ews_autodiscover_cb(struct sipe_core_private
*sipe_private
,
864 const struct sipe_ews_autodiscover_data
*ews_data
,
865 SIPE_UNUSED_PARAMETER gpointer callback_data
)
867 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
868 const gchar
*ews_url
= NULL
;
874 ews_url
= ews_data
->ews_url
;
876 if (is_empty(ews_url
)) {
877 SIPE_DEBUG_ERROR_NOFORMAT("ucs_ews_autodiscover_cb: can't detect EWS URL, contact list operations will not work!");
878 ucs_init_failure(sipe_private
);
880 ucs_set_ews_url(sipe_private
, ews_url
);
884 gboolean
sipe_ucs_is_migrated(struct sipe_core_private
*sipe_private
)
886 return(sipe_private
->ucs
? sipe_private
->ucs
->migrated
: FALSE
);
889 void sipe_ucs_init(struct sipe_core_private
*sipe_private
,
892 struct sipe_ucs
*ucs
;
894 if (sipe_private
->ucs
) {
895 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
898 * contact list update trigger -> request list again
900 * If the trigger arrives less than 10 seconds after our
901 * last UCS response, then ignore it, because it is caused
902 * by our own changes to the contact list.
904 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
905 if ((time(NULL
) - ucs
->last_response
) >= 10)
906 ucs_get_im_item_list(sipe_private
);
908 SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_init: ignoring this contact list update - triggered by our last change");
911 ucs
->last_response
= 0;
915 sipe_private
->ucs
= ucs
= g_new0(struct sipe_ucs
, 1);
916 ucs
->migrated
= migrated
;
918 /* create default transaction */
919 sipe_ucs_transaction(sipe_private
);
920 ucs
->default_transaction
= ucs
->transactions
;
923 /* user specified a service URL? */
924 const gchar
*ews_url
= sipe_backend_setting(SIPE_CORE_PUBLIC
, SIPE_SETTING_EMAIL_URL
);
926 if (is_empty(ews_url
))
927 sipe_ews_autodiscover_start(sipe_private
,
928 ucs_ews_autodiscover_cb
,
931 ucs_set_ews_url(sipe_private
, ews_url
);
935 void sipe_ucs_free(struct sipe_core_private
*sipe_private
)
937 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
943 /* UCS stack is shutting down: reject all new requests */
944 ucs
->shutting_down
= TRUE
;
946 entry
= ucs
->transactions
;
948 struct sipe_ucs_transaction
*trans
= entry
->data
;
949 GSList
*entry2
= trans
->pending_requests
;
951 /* transactions get deleted by sipe_ucs_request_free() */
955 struct ucs_request
*request
= entry2
->data
;
957 /* transactions get deleted by sipe_ucs_request_free() */
958 entry2
= entry2
->next
;
960 sipe_ucs_request_free(sipe_private
, request
);
964 /* only default transaction is left... */
965 sipe_utils_slist_free_full(ucs
->transactions
, g_free
);
967 g_free(ucs
->ews_url
);
969 sipe_private
->ucs
= NULL
;