6 * Copyright (C) 2013-2014 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>
33 #include "sipe-backend.h"
34 #include "sipe-buddy.h"
35 #include "sipe-common.h"
36 #include "sipe-core.h"
37 #include "sipe-core-private.h"
38 #include "sipe-digest.h"
39 #include "sipe-ews-autodiscover.h"
40 #include "sipe-group.h"
41 #include "sipe-http.h"
43 #include "sipe-subscriptions.h"
45 #include "sipe-utils.h"
48 struct sipe_ucs_transaction
{
49 GSList
*pending_requests
;
52 typedef void (ucs_callback
)(struct sipe_core_private
*sipe_private
,
53 struct sipe_ucs_transaction
*trans
,
55 gpointer callback_data
);
61 struct sipe_ucs_transaction
*transaction
;
62 struct sipe_http_request
*request
;
66 struct ucs_request
*active_request
;
68 GSList
*default_transaction
;
73 gboolean shutting_down
;
76 static void sipe_ucs_request_free(struct sipe_core_private
*sipe_private
,
77 struct ucs_request
*data
)
79 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
80 struct sipe_ucs_transaction
*trans
= data
->transaction
;
82 /* remove request from transaction */
83 trans
->pending_requests
= g_slist_remove(trans
->pending_requests
,
85 sipe_private
->ucs
->active_request
= NULL
;
87 /* remove completed transactions (except default transaction) */
88 if (!trans
->pending_requests
&&
89 (trans
!= ucs
->default_transaction
->data
)) {
90 ucs
->transactions
= g_slist_remove(ucs
->transactions
,
96 sipe_http_request_cancel(data
->request
);
98 /* Callback: aborted */
99 (*data
->cb
)(sipe_private
, NULL
, NULL
, data
->cb_data
);
104 static void sipe_ucs_next_request(struct sipe_core_private
*sipe_private
);
105 static void sipe_ucs_http_response(struct sipe_core_private
*sipe_private
,
107 SIPE_UNUSED_PARAMETER GSList
*headers
,
109 gpointer callback_data
)
111 struct ucs_request
*data
= callback_data
;
113 SIPE_DEBUG_INFO("sipe_ucs_http_response: code %d", status
);
114 data
->request
= NULL
;
116 if ((status
== SIPE_HTTP_STATUS_OK
) && body
) {
117 sipe_xml
*xml
= sipe_xml_parse(body
, strlen(body
));
118 const sipe_xml
*soap_body
= sipe_xml_child(xml
, "Body");
119 /* Callback: success */
120 (*data
->cb
)(sipe_private
,
126 /* Callback: failed */
127 (*data
->cb
)(sipe_private
, NULL
, NULL
, data
->cb_data
);
130 /* already been called */
133 sipe_ucs_request_free(sipe_private
, data
);
134 sipe_ucs_next_request(sipe_private
);
137 static void sipe_ucs_next_request(struct sipe_core_private
*sipe_private
)
139 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
140 struct sipe_ucs_transaction
*trans
;
142 if (ucs
->active_request
|| ucs
->shutting_down
|| !ucs
->ews_url
)
145 trans
= ucs
->transactions
->data
;
146 while (trans
->pending_requests
) {
147 struct ucs_request
*data
= trans
->pending_requests
->data
;
148 gchar
*soap
= g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
150 " xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\""
151 " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
152 " xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\""
155 " <t:RequestServerVersion Version=\"Exchange2013\" />"
162 struct sipe_http_request
*request
= sipe_http_request_post(sipe_private
,
166 "text/xml; charset=UTF-8",
167 sipe_ucs_http_response
,
174 data
->request
= request
;
176 ucs
->active_request
= data
;
178 sipe_core_email_authentication(sipe_private
,
180 sipe_http_request_allow_redirect(request
);
181 sipe_http_request_ready(request
);
185 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_next_request: failed to create HTTP connection");
186 sipe_ucs_request_free(sipe_private
, data
);
191 static gboolean
sipe_ucs_http_request(struct sipe_core_private
*sipe_private
,
192 struct sipe_ucs_transaction
*trans
,
193 gchar
*body
, /* takes ownership */
194 ucs_callback
*callback
,
195 gpointer callback_data
)
197 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
199 if (!ucs
|| ucs
->shutting_down
) {
200 SIPE_DEBUG_ERROR("sipe_ucs_http_request: new UCS request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
202 body
? body
: "<EMPTY>");
207 struct ucs_request
*data
= g_new0(struct ucs_request
, 1);
210 data
->cb_data
= callback_data
;
214 trans
= ucs
->default_transaction
->data
;
215 data
->transaction
= trans
;
216 trans
->pending_requests
= g_slist_append(trans
->pending_requests
,
219 sipe_ucs_next_request(sipe_private
);
224 struct sipe_ucs_transaction
*sipe_ucs_transaction(struct sipe_core_private
*sipe_private
)
226 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
227 struct sipe_ucs_transaction
*trans
;
232 /* always insert new transactions before default transaction */
233 trans
= g_new0(struct sipe_ucs_transaction
, 1);
234 ucs
->transactions
= g_slist_insert_before(ucs
->transactions
,
235 ucs
->default_transaction
,
241 static void sipe_ucs_get_user_photo_response(struct sipe_core_private
*sipe_private
,
242 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
243 const sipe_xml
*body
,
244 gpointer callback_data
)
246 gchar
*uri
= callback_data
;
247 const sipe_xml
*node
= sipe_xml_child(body
,
248 "GetUserPhotoResponse/PictureData");
254 guchar digest
[SIPE_DIGEST_SHA1_LENGTH
];
255 gchar
*digest_string
;
257 /* decode photo data */
258 base64
= sipe_xml_data(node
);
259 photo
= g_base64_decode(base64
, &photo_size
);
262 /* EWS doesn't provide a hash -> calculate SHA-1 digest */
263 sipe_digest_sha1(photo
, photo_size
, digest
);
264 digest_string
= buff_to_hex_str(digest
,
265 SIPE_DIGEST_SHA1_LENGTH
);
267 /* backend frees "photo" */
268 sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC
,
273 g_free(digest_string
);
279 void sipe_ucs_get_photo(struct sipe_core_private
*sipe_private
,
282 gchar
*payload
= g_strdup(uri
);
283 gchar
*body
= g_strdup_printf("<m:GetUserPhoto>"
284 " <m:Email>%s</m:Email>"
285 " <m:SizeRequested>HR48x48</m:SizeRequested>"
287 sipe_get_no_sip_uri(uri
));
289 if (!sipe_ucs_http_request(sipe_private
,
292 sipe_ucs_get_user_photo_response
,
297 static void sipe_ucs_search_response(struct sipe_core_private
*sipe_private
,
298 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
299 const sipe_xml
*body
,
300 gpointer callback_data
)
302 const sipe_xml
*persona_node
;
303 struct sipe_backend_search_results
*results
= NULL
;
304 guint match_count
= 0;
306 for (persona_node
= sipe_xml_child(body
,
307 "FindPeopleResponse/People/Persona");
309 persona_node
= sipe_xml_twin(persona_node
)) {
310 const sipe_xml
*address
= sipe_xml_child(persona_node
,
313 /* only display Persona nodes which have an "ImAddress" node */
320 /* OK, we found something - show the results to the user */
323 results
= sipe_backend_search_results_start(SIPE_CORE_PUBLIC
,
326 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_search_response: Unable to display the search results.");
327 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
329 _("Unable to display the search results"));
334 uri
= sipe_xml_data(address
);
335 displayname
= sipe_xml_data(sipe_xml_child(persona_node
,
337 company
= sipe_xml_data(sipe_xml_child(persona_node
,
339 email
= sipe_xml_data(sipe_xml_child(persona_node
,
340 "EmailAddress/EmailAddress"));
342 sipe_backend_search_results_add(SIPE_CORE_PUBLIC
,
344 sipe_get_no_sip_uri(uri
),
358 sipe_buddy_search_contacts_finalize(sipe_private
,
363 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
365 _("No contacts found"));
368 void sipe_ucs_search(struct sipe_core_private
*sipe_private
,
369 struct sipe_backend_search_token
*token
,
370 const gchar
*given_name
,
371 SIPE_UNUSED_PARAMETER
const gchar
*surname
,
372 SIPE_UNUSED_PARAMETER
const gchar
*email
,
373 SIPE_UNUSED_PARAMETER
const gchar
*sipid
,
374 SIPE_UNUSED_PARAMETER
const gchar
*company
,
375 SIPE_UNUSED_PARAMETER
const gchar
*country
)
377 /* search GAL for matching entries */
378 gchar
*body
= g_markup_printf_escaped("<m:FindPeople>"
380 " <t:BaseShape>IdOnly</t:BaseShape>"
381 " <t:AdditionalProperties>"
382 " <t:FieldURI FieldURI=\"persona:CompanyName\"/>"
383 " <t:FieldURI FieldURI=\"persona:DisplayName\"/>"
384 " <t:FieldURI FieldURI=\"persona:EmailAddress\"/>"
385 " <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
386 /* Locations doesn't seem to work
387 " <t:FieldURI FieldURI=\"persona:Locations\"/>"
389 " </t:AdditionalProperties>"
391 " <m:IndexedPageItemView BasePoint=\"Beginning\" MaxEntriesReturned=\"100\" Offset=\"0\"/>"
393 * I have no idea why Exchnage doesn't accept this
394 * FieldURI for restrictions. Without it the search
395 * will return users that don't have an ImAddress
396 * and we need to filter them out ourselves :-(
399 " <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
403 " <m:ParentFolderId>"
404 " <t:DistinguishedFolderId Id=\"directory\"/>"
405 " </m:ParentFolderId>"
406 " <m:QueryString>%s</m:QueryString>"
410 if (!sipe_ucs_http_request(sipe_private
,
413 sipe_ucs_search_response
,
415 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
417 _("Contact search failed"));
420 static void sipe_ucs_ignore_response(struct sipe_core_private
*sipe_private
,
421 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
422 SIPE_UNUSED_PARAMETER
const sipe_xml
*body
,
423 SIPE_UNUSED_PARAMETER gpointer callback_data
)
425 SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_ignore_response: done");
426 sipe_private
->ucs
->last_response
= time(NULL
);
429 static void ucs_extract_keys(const sipe_xml
*persona_node
,
431 const gchar
**change
)
433 const sipe_xml
*attr_node
;
436 * extract Exchange key - play the guessing game :-(
438 * We can't use the "DisplayName" node, because the text is localized.
440 * Assume that IsQuickContact == "true" and IsHidden == "false" means
441 * this Attribution node contains the information for the Lync contact.
443 for (attr_node
= sipe_xml_child(persona_node
,
444 "Attributions/Attribution");
446 attr_node
= sipe_xml_twin(attr_node
)) {
447 const sipe_xml
*id_node
= sipe_xml_child(attr_node
,
449 gchar
*hidden
= sipe_xml_data(sipe_xml_child(attr_node
,
451 gchar
*quick
= sipe_xml_data(sipe_xml_child(attr_node
,
454 sipe_strcase_equal(hidden
, "false") &&
455 sipe_strcase_equal(quick
, "true")) {
456 *key
= sipe_xml_attribute(id_node
, "Id");
457 *change
= sipe_xml_attribute(id_node
, "ChangeKey");
467 static void sipe_ucs_add_new_im_contact_to_group_response(struct sipe_core_private
*sipe_private
,
468 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
469 const sipe_xml
*body
,
470 gpointer callback_data
)
472 gchar
*who
= callback_data
;
473 struct sipe_buddy
*buddy
= sipe_buddy_find_by_uri(sipe_private
, who
);
474 const sipe_xml
*persona_node
= sipe_xml_child(body
,
475 "AddNewImContactToGroupResponse/Persona");
477 sipe_private
->ucs
->last_response
= time(NULL
);
481 is_empty(buddy
->exchange_key
) &&
482 is_empty(buddy
->change_key
)) {
483 const gchar
*key
= NULL
;
484 const gchar
*change
= NULL
;
486 ucs_extract_keys(persona_node
, &key
, &change
);
488 if (!is_empty(key
) && !is_empty(change
)) {
490 sipe_buddy_add_keys(sipe_private
,
495 SIPE_DEBUG_INFO("sipe_ucs_add_new_im_contact_to_group_response: persona URI '%s' key '%s' change '%s'",
496 buddy
->name
, key
, change
);
503 void sipe_ucs_group_add_buddy(struct sipe_core_private
*sipe_private
,
504 struct sipe_ucs_transaction
*trans
,
505 struct sipe_group
*group
,
506 struct sipe_buddy
*buddy
,
509 /* existing or new buddy? */
510 if (buddy
&& buddy
->exchange_key
) {
511 gchar
*body
= g_strdup_printf("<m:AddImContactToGroup>"
512 " <m:ContactId Id=\"%s\" ChangeKey=\"%s\"/>"
513 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
514 "</m:AddImContactToGroup>",
520 sipe_ucs_http_request(sipe_private
,
523 sipe_ucs_ignore_response
,
526 gchar
*payload
= g_strdup(who
);
527 gchar
*body
= g_strdup_printf("<m:AddNewImContactToGroup>"
528 " <m:ImAddress>%s</m:ImAddress>"
529 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
530 "</m:AddNewImContactToGroup>",
531 sipe_get_no_sip_uri(who
),
535 if (!sipe_ucs_http_request(sipe_private
,
538 sipe_ucs_add_new_im_contact_to_group_response
,
544 void sipe_ucs_group_remove_buddy(struct sipe_core_private
*sipe_private
,
545 struct sipe_ucs_transaction
*trans
,
546 struct sipe_group
*group
,
547 struct sipe_buddy
*buddy
)
551 * If a contact is removed from last group, it will also be
552 * removed from contact list completely. The documentation has
553 * a RemoveContactFromImList operation, but that doesn't seem
554 * to work at all, i.e. it is always rejected by the server.
556 gchar
*body
= g_strdup_printf("<m:RemoveImContactFromGroup>"
557 " <m:ContactId Id=\"%s\" ChangeKey=\"%s\"/>"
558 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
559 "</m:RemoveImContactFromGroup>",
565 sipe_ucs_http_request(sipe_private
,
568 sipe_ucs_ignore_response
,
573 static struct sipe_group
*ucs_create_group(struct sipe_core_private
*sipe_private
,
574 const sipe_xml
*group_node
)
576 const sipe_xml
*id_node
= sipe_xml_child(group_node
,
578 const gchar
*key
= sipe_xml_attribute(id_node
, "Id");
579 const gchar
*change
= sipe_xml_attribute(id_node
, "ChangeKey");
580 struct sipe_group
*group
= NULL
;
582 if (!(is_empty(key
) || is_empty(change
))) {
583 gchar
*name
= sipe_xml_data(sipe_xml_child(group_node
,
585 group
= sipe_group_add(sipe_private
,
589 /* sipe_group must have unique ID */
590 ++sipe_private
->ucs
->group_id
);
597 static void sipe_ucs_add_im_group_response(struct sipe_core_private
*sipe_private
,
598 struct sipe_ucs_transaction
*trans
,
599 const sipe_xml
*body
,
600 gpointer callback_data
)
602 gchar
*who
= callback_data
;
603 const sipe_xml
*group_node
= sipe_xml_child(body
,
604 "AddImGroupResponse/ImGroup");
605 struct sipe_group
*group
= ucs_create_group(sipe_private
, group_node
);
607 sipe_private
->ucs
->last_response
= time(NULL
);
610 struct sipe_buddy
*buddy
= sipe_buddy_find_by_uri(sipe_private
,
614 sipe_buddy_insert_group(buddy
, group
);
616 sipe_ucs_group_add_buddy(sipe_private
,
626 void sipe_ucs_group_create(struct sipe_core_private
*sipe_private
,
627 struct sipe_ucs_transaction
*trans
,
631 gchar
*payload
= g_strdup(who
);
632 /* new_name can contain restricted characters */
633 gchar
*body
= g_markup_printf_escaped("<m:AddImGroup>"
634 " <m:DisplayName>%s</m:DisplayName>"
638 if (!sipe_ucs_http_request(sipe_private
,
641 sipe_ucs_add_im_group_response
,
646 void sipe_ucs_group_rename(struct sipe_core_private
*sipe_private
,
647 struct sipe_group
*group
,
648 const gchar
*new_name
)
650 /* new_name can contain restricted characters */
651 gchar
*body
= g_markup_printf_escaped("<m:SetImGroup>"
652 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
653 " <m:NewDisplayName>%s</m:NewDisplayName>"
659 sipe_ucs_http_request(sipe_private
,
662 sipe_ucs_ignore_response
,
666 void sipe_ucs_group_remove(struct sipe_core_private
*sipe_private
,
667 struct sipe_group
*group
)
669 gchar
*body
= g_strdup_printf("<m:RemoveImGroup>"
670 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
671 "</m:RemoveImGroup>",
675 sipe_ucs_http_request(sipe_private
,
678 sipe_ucs_ignore_response
,
682 static void sipe_ucs_get_im_item_list_response(struct sipe_core_private
*sipe_private
,
683 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
684 const sipe_xml
*body
,
685 SIPE_UNUSED_PARAMETER gpointer callback_data
)
687 const sipe_xml
*node
= sipe_xml_child(body
,
688 "GetImItemListResponse/ImItemList");
691 const sipe_xml
*persona_node
;
692 const sipe_xml
*group_node
;
693 GHashTable
*uri_to_alias
= g_hash_table_new_full(g_str_hash
,
698 /* Start processing contact list */
699 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
700 sipe_group_update_start(sipe_private
);
701 sipe_buddy_update_start(sipe_private
);
703 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC
);
705 for (persona_node
= sipe_xml_child(node
, "Personas/Persona");
707 persona_node
= sipe_xml_twin(persona_node
)) {
708 gchar
*address
= sipe_xml_data(sipe_xml_child(persona_node
,
710 const gchar
*key
= NULL
;
711 const gchar
*change
= NULL
;
713 ucs_extract_keys(persona_node
, &key
, &change
);
715 if (!(is_empty(address
) || is_empty(key
) || is_empty(change
))) {
716 gchar
*alias
= sipe_xml_data(sipe_xml_child(persona_node
,
719 * it seems to be undefined if ImAddress node
720 * contains "sip:" prefix or not...
722 gchar
*uri
= sip_uri(address
);
723 struct sipe_buddy
*buddy
= sipe_buddy_add(sipe_private
,
729 /* hash table takes ownership of alias */
730 g_hash_table_insert(uri_to_alias
,
734 SIPE_DEBUG_INFO("sipe_ucs_get_im_item_list_response: persona URI '%s' key '%s' change '%s'",
735 buddy
->name
, key
, change
);
740 for (group_node
= sipe_xml_child(node
, "Groups/ImGroup");
742 group_node
= sipe_xml_twin(group_node
)) {
743 struct sipe_group
*group
= ucs_create_group(sipe_private
,
747 const sipe_xml
*member_node
;
749 for (member_node
= sipe_xml_child(group_node
,
750 "MemberCorrelationKey/ItemId");
752 member_node
= sipe_xml_twin(member_node
)) {
753 struct sipe_buddy
*buddy
= sipe_buddy_find_by_exchange_key(sipe_private
,
754 sipe_xml_attribute(member_node
,
757 sipe_buddy_add_to_group(sipe_private
,
760 g_hash_table_lookup(uri_to_alias
,
766 g_hash_table_destroy(uri_to_alias
);
768 /* Finished processing contact list */
769 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
770 sipe_buddy_update_finish(sipe_private
);
771 sipe_group_update_finish(sipe_private
);
773 sipe_buddy_cleanup_local_list(sipe_private
);
774 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC
);
775 sipe_subscribe_presence_initial(sipe_private
);
780 static void ucs_get_im_item_list(struct sipe_core_private
*sipe_private
)
782 if (sipe_private
->ucs
->migrated
)
783 sipe_ucs_http_request(sipe_private
,
784 /* prioritize over pending default requests */
785 sipe_ucs_transaction(sipe_private
),
786 g_strdup("<m:GetImItemList/>"),
787 sipe_ucs_get_im_item_list_response
,
791 static void ucs_set_ews_url(struct sipe_core_private
*sipe_private
,
792 const gchar
*ews_url
)
794 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
796 SIPE_DEBUG_INFO("ucs_set_ews_url: '%s'", ews_url
);
797 ucs
->ews_url
= g_strdup(ews_url
);
799 /* this will trigger sending of the first deferred request */
800 ucs_get_im_item_list(sipe_private
);
803 static void ucs_ews_autodiscover_cb(struct sipe_core_private
*sipe_private
,
804 const struct sipe_ews_autodiscover_data
*ews_data
,
805 SIPE_UNUSED_PARAMETER gpointer callback_data
)
807 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
808 const gchar
*ews_url
;
810 if (!ucs
|| !ews_data
)
813 ews_url
= ews_data
->ews_url
;
814 if (is_empty(ews_url
)) {
815 SIPE_DEBUG_ERROR_NOFORMAT("ucs_ews_autodiscover_cb: can't detect EWS URL, contact list operations will not work!");
819 ucs_set_ews_url(sipe_private
, ews_url
);
822 gboolean
sipe_ucs_is_migrated(struct sipe_core_private
*sipe_private
)
824 return(sipe_private
->ucs
? sipe_private
->ucs
->migrated
: FALSE
);
827 void sipe_ucs_init(struct sipe_core_private
*sipe_private
,
830 struct sipe_ucs
*ucs
;
832 if (sipe_private
->ucs
) {
833 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
836 * contact list update trigger -> request list again
838 * If the trigger arrives less than 10 seconds after our
839 * last UCS response, then ignore it, because it is caused
840 * by our own changes to the contact list.
842 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
843 if ((time(NULL
) - ucs
->last_response
) >= 10)
844 ucs_get_im_item_list(sipe_private
);
846 SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_init: ignoring this contact list update - triggered by our last change");
849 ucs
->last_response
= 0;
853 sipe_private
->ucs
= ucs
= g_new0(struct sipe_ucs
, 1);
854 ucs
->migrated
= migrated
;
856 /* create default transaction */
857 sipe_ucs_transaction(sipe_private
);
858 ucs
->default_transaction
= ucs
->transactions
;
861 /* user specified a service URL? */
862 const gchar
*ews_url
= sipe_backend_setting(SIPE_CORE_PUBLIC
, SIPE_SETTING_EMAIL_URL
);
864 if (is_empty(ews_url
))
865 sipe_ews_autodiscover_start(sipe_private
,
866 ucs_ews_autodiscover_cb
,
869 ucs_set_ews_url(sipe_private
, ews_url
);
873 void sipe_ucs_free(struct sipe_core_private
*sipe_private
)
875 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
881 /* UCS stack is shutting down: reject all new requests */
882 ucs
->shutting_down
= TRUE
;
884 entry
= ucs
->transactions
;
886 struct sipe_ucs_transaction
*trans
= entry
->data
;
887 GSList
*entry2
= trans
->pending_requests
;
889 /* transactions get deleted by sipe_ucs_request_free() */
893 struct ucs_request
*request
= entry2
->data
;
895 /* transactions get deleted by sipe_ucs_request_free() */
896 entry2
= entry2
->next
;
898 sipe_ucs_request_free(sipe_private
, request
);
902 /* only default transaction is left... */
903 sipe_utils_slist_free_full(ucs
->transactions
, g_free
);
905 g_free(ucs
->ews_url
);
907 sipe_private
->ucs
= NULL
;