6 * Copyright (C) 2013-2018 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 * FindPeople operation
31 * <http://msdn.microsoft.com/en-us/library/office/jj191039.aspx>
43 #include "sipe-backend.h"
44 #include "sipe-buddy.h"
45 #include "sipe-common.h"
46 #include "sipe-core.h"
47 #include "sipe-core-private.h"
48 #include "sipe-ews-autodiscover.h"
49 #include "sipe-group.h"
50 #include "sipe-http.h"
52 #include "sipe-subscriptions.h"
54 #include "sipe-utils.h"
57 struct sipe_ucs_transaction
{
58 GSList
*pending_requests
;
61 typedef void (ucs_callback
)(struct sipe_core_private
*sipe_private
,
62 struct sipe_ucs_transaction
*trans
,
64 gpointer callback_data
);
70 struct sipe_ucs_transaction
*transaction
;
71 struct sipe_http_request
*request
;
75 struct ucs_request
*active_request
;
77 GSList
*default_transaction
;
82 gboolean shutting_down
;
85 static void sipe_ucs_request_free(struct sipe_core_private
*sipe_private
,
86 struct ucs_request
*data
)
88 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
89 struct sipe_ucs_transaction
*trans
= data
->transaction
;
91 /* remove request from transaction */
92 trans
->pending_requests
= g_slist_remove(trans
->pending_requests
,
94 sipe_private
->ucs
->active_request
= NULL
;
96 /* remove completed transactions (except default transaction) */
97 if (!trans
->pending_requests
&&
98 (trans
!= ucs
->default_transaction
->data
)) {
99 ucs
->transactions
= g_slist_remove(ucs
->transactions
,
105 sipe_http_request_cancel(data
->request
);
107 /* Callback: aborted */
108 (*data
->cb
)(sipe_private
, NULL
, NULL
, data
->cb_data
);
113 static void sipe_ucs_next_request(struct sipe_core_private
*sipe_private
);
114 static void sipe_ucs_http_response(struct sipe_core_private
*sipe_private
,
116 SIPE_UNUSED_PARAMETER GSList
*headers
,
118 gpointer callback_data
)
120 struct ucs_request
*data
= callback_data
;
122 SIPE_DEBUG_INFO("sipe_ucs_http_response: code %d", status
);
123 data
->request
= NULL
;
125 if ((status
== SIPE_HTTP_STATUS_OK
) && body
) {
126 sipe_xml
*xml
= sipe_xml_parse(body
, strlen(body
));
127 const sipe_xml
*soap_body
= sipe_xml_child(xml
, "Body");
128 /* Callback: success */
129 (*data
->cb
)(sipe_private
,
135 /* Callback: failed */
136 (*data
->cb
)(sipe_private
, NULL
, NULL
, data
->cb_data
);
139 /* already been called */
142 sipe_ucs_request_free(sipe_private
, data
);
143 sipe_ucs_next_request(sipe_private
);
146 static void sipe_ucs_next_request(struct sipe_core_private
*sipe_private
)
148 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
149 struct sipe_ucs_transaction
*trans
;
151 if (ucs
->active_request
|| ucs
->shutting_down
|| !ucs
->ews_url
)
154 trans
= ucs
->transactions
->data
;
155 while (trans
->pending_requests
) {
156 struct ucs_request
*data
= trans
->pending_requests
->data
;
157 gchar
*soap
= g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
159 " xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\""
160 " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
161 " xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\""
164 " <t:RequestServerVersion Version=\"Exchange2013\" />"
171 struct sipe_http_request
*request
= sipe_http_request_post(sipe_private
,
175 "text/xml; charset=UTF-8",
176 sipe_ucs_http_response
,
183 data
->request
= request
;
185 ucs
->active_request
= data
;
187 sipe_core_email_authentication(sipe_private
,
189 sipe_http_request_allow_redirect(request
);
190 sipe_http_request_ready(request
);
194 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_next_request: failed to create HTTP connection");
195 sipe_ucs_request_free(sipe_private
, data
);
200 static gboolean
sipe_ucs_http_request(struct sipe_core_private
*sipe_private
,
201 struct sipe_ucs_transaction
*trans
,
202 gchar
*body
, /* takes ownership */
203 ucs_callback
*callback
,
204 gpointer callback_data
)
206 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
208 if (!ucs
|| ucs
->shutting_down
) {
209 SIPE_DEBUG_ERROR("sipe_ucs_http_request: new UCS request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
211 body
? body
: "<EMPTY>");
216 struct ucs_request
*data
= g_new0(struct ucs_request
, 1);
219 data
->cb_data
= callback_data
;
223 trans
= ucs
->default_transaction
->data
;
224 data
->transaction
= trans
;
225 trans
->pending_requests
= g_slist_append(trans
->pending_requests
,
228 sipe_ucs_next_request(sipe_private
);
233 struct sipe_ucs_transaction
*sipe_ucs_transaction(struct sipe_core_private
*sipe_private
)
235 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
236 struct sipe_ucs_transaction
*trans
;
241 /* always insert new transactions before default transaction */
242 trans
= g_new0(struct sipe_ucs_transaction
, 1);
243 ucs
->transactions
= g_slist_insert_before(ucs
->transactions
,
244 ucs
->default_transaction
,
250 static void sipe_ucs_search_response(struct sipe_core_private
*sipe_private
,
251 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
252 const sipe_xml
*body
,
253 gpointer callback_data
)
255 const sipe_xml
*persona_node
;
256 struct sipe_backend_search_results
*results
= NULL
;
257 guint match_count
= 0;
259 for (persona_node
= sipe_xml_child(body
,
260 "FindPeopleResponse/People/Persona");
262 persona_node
= sipe_xml_twin(persona_node
)) {
263 const sipe_xml
*address
= sipe_xml_child(persona_node
,
266 /* only display Persona nodes which have an "ImAddress" node */
273 /* OK, we found something - show the results to the user */
276 results
= sipe_backend_search_results_start(SIPE_CORE_PUBLIC
,
279 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_search_response: Unable to display the search results.");
280 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
282 _("Unable to display the search results"));
287 uri
= sipe_xml_data(address
);
288 displayname
= sipe_xml_data(sipe_xml_child(persona_node
,
290 company
= sipe_xml_data(sipe_xml_child(persona_node
,
292 email
= sipe_xml_data(sipe_xml_child(persona_node
,
293 "EmailAddress/EmailAddress"));
295 sipe_backend_search_results_add(SIPE_CORE_PUBLIC
,
297 sipe_get_no_sip_uri(uri
),
311 sipe_buddy_search_contacts_finalize(sipe_private
,
316 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
318 _("No contacts found"));
321 void sipe_ucs_search(struct sipe_core_private
*sipe_private
,
322 struct sipe_backend_search_token
*token
,
323 const gchar
*given_name
,
324 const gchar
*surname
,
327 const gchar
*company
,
328 const gchar
*country
)
331 GString
*query
= g_string_new(NULL
);
334 * Search GAL for matching entries
336 * QueryString should support field properties and quoting ("")
337 * according to the specification. But in my trials I couldn't get
338 * them to work. Concatenate all query words to a single string.
339 * Only items that match ALL words will be returned by this query.
341 #define ADD_QUERY_VALUE(val) \
344 g_string_append_c(query, ' '); \
345 g_string_append(query, val); \
348 ADD_QUERY_VALUE(given_name
);
349 ADD_QUERY_VALUE(surname
);
350 ADD_QUERY_VALUE(email
);
351 ADD_QUERY_VALUE(sipid
);
352 ADD_QUERY_VALUE(company
);
353 ADD_QUERY_VALUE(country
);
356 gchar
*body
= g_markup_printf_escaped("<m:FindPeople>"
358 " <t:BaseShape>IdOnly</t:BaseShape>"
359 " <t:AdditionalProperties>"
360 " <t:FieldURI FieldURI=\"persona:CompanyName\"/>"
361 " <t:FieldURI FieldURI=\"persona:DisplayName\"/>"
362 " <t:FieldURI FieldURI=\"persona:EmailAddress\"/>"
363 " <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
364 /* Locations doesn't seem to work
365 " <t:FieldURI FieldURI=\"persona:Locations\"/>"
367 " </t:AdditionalProperties>"
369 " <m:IndexedPageItemView BasePoint=\"Beginning\" MaxEntriesReturned=\"100\" Offset=\"0\"/>"
371 * I have no idea why Exchnage doesn't accept this
372 * FieldURI for restrictions. Without it the search
373 * will return users that don't have an ImAddress
374 * and we need to filter them out ourselves :-(
377 " <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
381 " <m:ParentFolderId>"
382 " <t:DistinguishedFolderId Id=\"directory\"/>"
383 " </m:ParentFolderId>"
384 " <m:QueryString>%s</m:QueryString>"
388 if (!sipe_ucs_http_request(sipe_private
,
391 sipe_ucs_search_response
,
393 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
395 _("Contact search failed"));
397 sipe_backend_search_failed(SIPE_CORE_PUBLIC
,
399 _("Invalid contact search query"));
401 g_string_free(query
, TRUE
);
404 static void sipe_ucs_ignore_response(struct sipe_core_private
*sipe_private
,
405 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
406 SIPE_UNUSED_PARAMETER
const sipe_xml
*body
,
407 SIPE_UNUSED_PARAMETER gpointer callback_data
)
409 SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_ignore_response: done");
410 sipe_private
->ucs
->last_response
= time(NULL
);
413 static void ucs_extract_keys(const sipe_xml
*persona_node
,
415 const gchar
**change
)
417 const sipe_xml
*attr_node
;
420 * extract Exchange key - play the guessing game :-(
422 * We can't use the "DisplayName" node, because the text is localized.
424 * Assume that IsQuickContact == "true" and IsHidden == "false" means
425 * this Attribution node contains the information for the Lync contact.
427 for (attr_node
= sipe_xml_child(persona_node
,
428 "Attributions/Attribution");
430 attr_node
= sipe_xml_twin(attr_node
)) {
431 const sipe_xml
*id_node
= sipe_xml_child(attr_node
,
433 gchar
*hidden
= sipe_xml_data(sipe_xml_child(attr_node
,
435 gchar
*quick
= sipe_xml_data(sipe_xml_child(attr_node
,
438 sipe_strcase_equal(hidden
, "false") &&
439 sipe_strcase_equal(quick
, "true")) {
440 *key
= sipe_xml_attribute(id_node
, "Id");
441 *change
= sipe_xml_attribute(id_node
, "ChangeKey");
451 static void sipe_ucs_add_new_im_contact_to_group_response(struct sipe_core_private
*sipe_private
,
452 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
453 const sipe_xml
*body
,
454 gpointer callback_data
)
456 gchar
*who
= callback_data
;
457 struct sipe_buddy
*buddy
= sipe_buddy_find_by_uri(sipe_private
, who
);
458 const sipe_xml
*persona_node
= sipe_xml_child(body
,
459 "AddNewImContactToGroupResponse/Persona");
461 sipe_private
->ucs
->last_response
= time(NULL
);
465 is_empty(buddy
->exchange_key
) &&
466 is_empty(buddy
->change_key
)) {
467 const gchar
*key
= NULL
;
468 const gchar
*change
= NULL
;
470 ucs_extract_keys(persona_node
, &key
, &change
);
472 if (!is_empty(key
) && !is_empty(change
)) {
474 sipe_buddy_add_keys(sipe_private
,
479 SIPE_DEBUG_INFO("sipe_ucs_add_new_im_contact_to_group_response: persona URI '%s' key '%s' change '%s'",
480 buddy
->name
, key
, change
);
487 void sipe_ucs_group_add_buddy(struct sipe_core_private
*sipe_private
,
488 struct sipe_ucs_transaction
*trans
,
489 struct sipe_group
*group
,
490 struct sipe_buddy
*buddy
,
493 /* existing or new buddy? */
494 if (buddy
&& buddy
->exchange_key
) {
495 gchar
*body
= g_strdup_printf("<m:AddImContactToGroup>"
496 " <m:ContactId Id=\"%s\" ChangeKey=\"%s\"/>"
497 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
498 "</m:AddImContactToGroup>",
504 sipe_ucs_http_request(sipe_private
,
507 sipe_ucs_ignore_response
,
510 gchar
*payload
= g_strdup(who
);
511 gchar
*body
= g_strdup_printf("<m:AddNewImContactToGroup>"
512 " <m:ImAddress>%s</m:ImAddress>"
513 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
514 "</m:AddNewImContactToGroup>",
515 sipe_get_no_sip_uri(who
),
519 if (!sipe_ucs_http_request(sipe_private
,
522 sipe_ucs_add_new_im_contact_to_group_response
,
528 void sipe_ucs_group_remove_buddy(struct sipe_core_private
*sipe_private
,
529 struct sipe_ucs_transaction
*trans
,
530 struct sipe_group
*group
,
531 struct sipe_buddy
*buddy
)
535 * If a contact is removed from last group, it will also be
536 * removed from contact list completely. The documentation has
537 * a RemoveContactFromImList operation, but that doesn't seem
538 * to work at all, i.e. it is always rejected by the server.
540 gchar
*body
= g_strdup_printf("<m:RemoveImContactFromGroup>"
541 " <m:ContactId Id=\"%s\" ChangeKey=\"%s\"/>"
542 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
543 "</m:RemoveImContactFromGroup>",
549 sipe_ucs_http_request(sipe_private
,
552 sipe_ucs_ignore_response
,
557 static struct sipe_group
*ucs_create_group(struct sipe_core_private
*sipe_private
,
558 const sipe_xml
*group_node
)
560 const sipe_xml
*id_node
= sipe_xml_child(group_node
,
562 const gchar
*key
= sipe_xml_attribute(id_node
, "Id");
563 const gchar
*change
= sipe_xml_attribute(id_node
, "ChangeKey");
564 struct sipe_group
*group
= NULL
;
566 if (!(is_empty(key
) || is_empty(change
))) {
567 gchar
*name
= sipe_xml_data(sipe_xml_child(group_node
,
569 group
= sipe_group_add(sipe_private
,
573 /* sipe_group must have unique ID */
574 ++sipe_private
->ucs
->group_id
);
581 static void sipe_ucs_add_im_group_response(struct sipe_core_private
*sipe_private
,
582 struct sipe_ucs_transaction
*trans
,
583 const sipe_xml
*body
,
584 gpointer callback_data
)
586 gchar
*who
= callback_data
;
587 const sipe_xml
*group_node
= sipe_xml_child(body
,
588 "AddImGroupResponse/ImGroup");
589 struct sipe_group
*group
= ucs_create_group(sipe_private
, group_node
);
591 sipe_private
->ucs
->last_response
= time(NULL
);
594 struct sipe_buddy
*buddy
= sipe_buddy_find_by_uri(sipe_private
,
598 sipe_buddy_insert_group(buddy
, group
);
600 sipe_ucs_group_add_buddy(sipe_private
,
610 void sipe_ucs_group_create(struct sipe_core_private
*sipe_private
,
611 struct sipe_ucs_transaction
*trans
,
615 gchar
*payload
= g_strdup(who
);
616 /* new_name can contain restricted characters */
617 gchar
*body
= g_markup_printf_escaped("<m:AddImGroup>"
618 " <m:DisplayName>%s</m:DisplayName>"
622 if (!sipe_ucs_http_request(sipe_private
,
625 sipe_ucs_add_im_group_response
,
630 void sipe_ucs_group_rename(struct sipe_core_private
*sipe_private
,
631 struct sipe_group
*group
,
632 const gchar
*new_name
)
634 /* new_name can contain restricted characters */
635 gchar
*body
= g_markup_printf_escaped("<m:SetImGroup>"
636 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
637 " <m:NewDisplayName>%s</m:NewDisplayName>"
643 sipe_ucs_http_request(sipe_private
,
646 sipe_ucs_ignore_response
,
650 void sipe_ucs_group_remove(struct sipe_core_private
*sipe_private
,
651 struct sipe_group
*group
)
653 gchar
*body
= g_strdup_printf("<m:RemoveImGroup>"
654 " <m:GroupId Id=\"%s\" ChangeKey=\"%s\"/>"
655 "</m:RemoveImGroup>",
659 sipe_ucs_http_request(sipe_private
,
662 sipe_ucs_ignore_response
,
666 static void ucs_init_failure(struct sipe_core_private
*sipe_private
)
668 /* Did the user specify any email settings? */
669 gboolean default_settings
=
670 is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC
,
671 SIPE_SETTING_EMAIL_URL
)) &&
672 is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC
,
673 SIPE_SETTING_EMAIL_LOGIN
)) &&
674 is_empty(sipe_backend_setting(SIPE_CORE_PUBLIC
,
675 SIPE_SETTING_EMAIL_PASSWORD
));
677 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
678 _("UCS initialization failed!"),
680 _("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.") :
681 _("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."));
684 static void sipe_ucs_get_im_item_list_response(struct sipe_core_private
*sipe_private
,
685 SIPE_UNUSED_PARAMETER
struct sipe_ucs_transaction
*trans
,
686 const sipe_xml
*body
,
687 SIPE_UNUSED_PARAMETER gpointer callback_data
)
689 const sipe_xml
*node
= sipe_xml_child(body
,
690 "GetImItemListResponse/ImItemList");
693 const sipe_xml
*persona_node
;
694 const sipe_xml
*group_node
;
695 GHashTable
*uri_to_alias
= g_hash_table_new_full(g_str_hash
,
700 /* Start processing contact list */
701 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
702 sipe_group_update_start(sipe_private
);
703 sipe_buddy_update_start(sipe_private
);
705 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC
);
707 for (persona_node
= sipe_xml_child(node
, "Personas/Persona");
709 persona_node
= sipe_xml_twin(persona_node
)) {
710 gchar
*address
= sipe_xml_data(sipe_xml_child(persona_node
,
712 const gchar
*key
= NULL
;
713 const gchar
*change
= NULL
;
715 ucs_extract_keys(persona_node
, &key
, &change
);
717 if (!(is_empty(address
) || is_empty(key
) || is_empty(change
))) {
718 gchar
*alias
= sipe_xml_data(sipe_xml_child(persona_node
,
721 * it seems to be undefined if ImAddress node
722 * contains "sip:" prefix or not...
724 gchar
*uri
= sip_uri(address
);
725 struct sipe_buddy
*buddy
= sipe_buddy_add(sipe_private
,
731 /* hash table takes ownership of alias */
732 g_hash_table_insert(uri_to_alias
,
736 SIPE_DEBUG_INFO("sipe_ucs_get_im_item_list_response: persona URI '%s' key '%s' change '%s'",
737 buddy
->name
, key
, change
);
742 for (group_node
= sipe_xml_child(node
, "Groups/ImGroup");
744 group_node
= sipe_xml_twin(group_node
)) {
745 struct sipe_group
*group
= ucs_create_group(sipe_private
,
749 const sipe_xml
*member_node
;
751 for (member_node
= sipe_xml_child(group_node
,
752 "MemberCorrelationKey/ItemId");
754 member_node
= sipe_xml_twin(member_node
)) {
755 struct sipe_buddy
*buddy
= sipe_buddy_find_by_exchange_key(sipe_private
,
756 sipe_xml_attribute(member_node
,
759 sipe_buddy_add_to_group(sipe_private
,
762 g_hash_table_lookup(uri_to_alias
,
768 g_hash_table_destroy(uri_to_alias
);
770 /* Finished processing contact list */
771 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
772 sipe_buddy_update_finish(sipe_private
);
773 sipe_group_update_finish(sipe_private
);
775 sipe_buddy_cleanup_local_list(sipe_private
);
776 sipe_backend_buddy_list_processing_finish(SIPE_CORE_PUBLIC
);
777 sipe_subscribe_presence_initial(sipe_private
);
779 } else if (sipe_private
->ucs
) {
780 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_get_im_item_list_response: query failed, contact list operations will not work!");
781 ucs_init_failure(sipe_private
);
785 static void ucs_get_im_item_list(struct sipe_core_private
*sipe_private
)
787 if (sipe_private
->ucs
->migrated
)
788 sipe_ucs_http_request(sipe_private
,
789 /* prioritize over pending default requests */
790 sipe_ucs_transaction(sipe_private
),
791 g_strdup("<m:GetImItemList/>"),
792 sipe_ucs_get_im_item_list_response
,
796 static void ucs_set_ews_url(struct sipe_core_private
*sipe_private
,
797 const gchar
*ews_url
)
799 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
801 SIPE_DEBUG_INFO("ucs_set_ews_url: '%s'", ews_url
);
802 ucs
->ews_url
= g_strdup(ews_url
);
804 /* this will trigger sending of the first deferred request */
805 ucs_get_im_item_list(sipe_private
);
808 static void ucs_ews_autodiscover_cb(struct sipe_core_private
*sipe_private
,
809 const struct sipe_ews_autodiscover_data
*ews_data
,
810 SIPE_UNUSED_PARAMETER gpointer callback_data
)
812 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
813 const gchar
*ews_url
= NULL
;
819 ews_url
= ews_data
->ews_url
;
821 if (is_empty(ews_url
)) {
822 SIPE_DEBUG_ERROR_NOFORMAT("ucs_ews_autodiscover_cb: can't detect EWS URL, contact list operations will not work!");
823 ucs_init_failure(sipe_private
);
825 ucs_set_ews_url(sipe_private
, ews_url
);
829 gboolean
sipe_ucs_is_migrated(struct sipe_core_private
*sipe_private
)
831 return(sipe_private
->ucs
? sipe_private
->ucs
->migrated
: FALSE
);
834 const gchar
*sipe_ucs_ews_url(struct sipe_core_private
*sipe_private
)
836 return(sipe_private
->ucs
? sipe_private
->ucs
->ews_url
: NULL
);
839 void sipe_ucs_init(struct sipe_core_private
*sipe_private
,
842 struct sipe_ucs
*ucs
;
844 if (sipe_private
->ucs
) {
845 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
848 * contact list update trigger -> request list again
850 * If the trigger arrives less than 10 seconds after our
851 * last UCS response, then ignore it, because it is caused
852 * by our own changes to the contact list.
854 if (SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
855 if ((time(NULL
) - ucs
->last_response
) >= 10)
856 ucs_get_im_item_list(sipe_private
);
858 SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_init: ignoring this contact list update - triggered by our last change");
861 ucs
->last_response
= 0;
865 sipe_private
->ucs
= ucs
= g_new0(struct sipe_ucs
, 1);
866 ucs
->migrated
= migrated
;
868 /* create default transaction */
869 sipe_ucs_transaction(sipe_private
);
870 ucs
->default_transaction
= ucs
->transactions
;
873 /* user specified a service URL? */
874 const gchar
*ews_url
= sipe_backend_setting(SIPE_CORE_PUBLIC
, SIPE_SETTING_EMAIL_URL
);
876 if (is_empty(ews_url
))
877 sipe_ews_autodiscover_start(sipe_private
,
878 ucs_ews_autodiscover_cb
,
881 ucs_set_ews_url(sipe_private
, ews_url
);
885 void sipe_ucs_free(struct sipe_core_private
*sipe_private
)
887 struct sipe_ucs
*ucs
= sipe_private
->ucs
;
893 /* UCS stack is shutting down: reject all new requests */
894 ucs
->shutting_down
= TRUE
;
896 entry
= ucs
->transactions
;
898 struct sipe_ucs_transaction
*trans
= entry
->data
;
899 GSList
*entry2
= trans
->pending_requests
;
901 /* transactions get deleted by sipe_ucs_request_free() */
905 struct ucs_request
*request
= entry2
->data
;
907 /* transactions get deleted by sipe_ucs_request_free() */
908 entry2
= entry2
->next
;
910 sipe_ucs_request_free(sipe_private
, request
);
914 /* only default transaction is left... */
915 sipe_utils_slist_free_full(ucs
->transactions
, g_free
);
917 g_free(ucs
->ews_url
);
919 sipe_private
->ucs
= NULL
;