media: also fill zero ports of active candidates
[siplcs.git] / src / core / sipe-ucs.c
blob3aa81f55cf24912e38231cc7aab894629baec952
1 /**
2 * @file sipe-ucs.c
4 * pidgin-sipe
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>
26 * EWS Reference
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>
36 #include <string.h>
38 #include <glib.h>
39 #include <time.h>
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"
50 #include "sipe-nls.h"
51 #include "sipe-subscriptions.h"
52 #include "sipe-ucs.h"
53 #include "sipe-utils.h"
54 #include "sipe-xml.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,
62 const sipe_xml *body,
63 gpointer callback_data);
65 struct ucs_request {
66 gchar *body;
67 ucs_callback *cb;
68 gpointer cb_data;
69 struct sipe_ucs_transaction *transaction;
70 struct sipe_http_request *request;
73 struct sipe_ucs {
74 struct ucs_request *active_request;
75 GSList *transactions;
76 GSList *default_transaction;
77 gchar *ews_url;
78 time_t last_response;
79 guint group_id;
80 gboolean migrated;
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,
92 data);
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,
99 trans);
100 g_free(trans);
103 if (data->request)
104 sipe_http_request_cancel(data->request);
105 if (data->cb)
106 /* Callback: aborted */
107 (*data->cb)(sipe_private, NULL, NULL, data->cb_data);
108 g_free(data->body);
109 g_free(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,
114 guint status,
115 SIPE_UNUSED_PARAMETER GSList *headers,
116 const gchar *body,
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,
129 data->transaction,
130 soap_body,
131 data->cb_data);
132 sipe_xml_free(xml);
133 } else {
134 /* Callback: failed */
135 (*data->cb)(sipe_private, NULL, NULL, data->cb_data);
138 /* already been called */
139 data->cb = NULL;
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)
151 return;
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"
157 "<soap:Envelope"
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\""
161 " >"
162 " <soap:Header>"
163 " <t:RequestServerVersion Version=\"Exchange2013\" />"
164 " </soap:Header>"
165 " <soap:Body>"
166 " %s"
167 " </soap:Body>"
168 "</soap:Envelope>",
169 data->body);
170 struct sipe_http_request *request = sipe_http_request_post(sipe_private,
171 ucs->ews_url,
172 NULL,
173 soap,
174 "text/xml; charset=UTF-8",
175 sipe_ucs_http_response,
176 data);
177 g_free(soap);
179 if (request) {
180 g_free(data->body);
181 data->body = NULL;
182 data->request = request;
184 ucs->active_request = data;
186 sipe_core_email_authentication(sipe_private,
187 request);
188 sipe_http_request_allow_redirect(request);
189 sipe_http_request_ready(request);
191 break;
192 } else {
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"
209 "Body: %s\n",
210 body ? body : "<EMPTY>");
211 g_free(body);
212 return(FALSE);
214 } else {
215 struct ucs_request *data = g_new0(struct ucs_request, 1);
217 data->cb = callback;
218 data->cb_data = callback_data;
219 data->body = body;
221 if (!trans)
222 trans = ucs->default_transaction->data;
223 data->transaction = trans;
224 trans->pending_requests = g_slist_append(trans->pending_requests,
225 data);
227 sipe_ucs_next_request(sipe_private);
228 return(TRUE);
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;
237 if (!ucs)
238 return(NULL);
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,
244 trans);
246 return(trans);
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");
258 if (node) {
259 gchar *base64;
260 gsize photo_size;
261 guchar *photo;
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);
268 g_free(base64);
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,
277 uri,
278 photo,
279 photo_size,
280 digest_string);
281 g_free(digest_string);
284 g_free(uri);
287 void sipe_ucs_get_photo(struct sipe_core_private *sipe_private,
288 const gchar *uri)
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>"
294 "</m:GetUserPhoto>",
295 sipe_get_no_sip_uri(uri));
297 if (!sipe_ucs_http_request(sipe_private,
298 NULL,
299 body,
300 sipe_ucs_get_user_photo_response,
301 payload))
302 g_free(payload);
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");
316 persona_node;
317 persona_node = sipe_xml_twin(persona_node)) {
318 const sipe_xml *address = sipe_xml_child(persona_node,
319 "ImAddress");
321 /* only display Persona nodes which have an "ImAddress" node */
322 if (address) {
323 gchar *uri;
324 gchar *displayname;
325 gchar *company;
326 gchar *email;
328 /* OK, we found something - show the results to the user */
329 match_count++;
330 if (!results) {
331 results = sipe_backend_search_results_start(SIPE_CORE_PUBLIC,
332 callback_data);
333 if (!results) {
334 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_search_response: Unable to display the search results.");
335 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
336 callback_data,
337 _("Unable to display the search results"));
338 return;
342 uri = sipe_xml_data(address);
343 displayname = sipe_xml_data(sipe_xml_child(persona_node,
344 "DisplayName"));
345 company = sipe_xml_data(sipe_xml_child(persona_node,
346 "CompanyName"));
347 email = sipe_xml_data(sipe_xml_child(persona_node,
348 "EmailAddress/EmailAddress"));
350 sipe_backend_search_results_add(SIPE_CORE_PUBLIC,
351 results,
352 sipe_get_no_sip_uri(uri),
353 displayname,
354 company,
355 NULL,
356 email);
358 g_free(email);
359 g_free(company);
360 g_free(displayname);
361 g_free(uri);
365 if (match_count > 0)
366 sipe_buddy_search_contacts_finalize(sipe_private,
367 results,
368 match_count,
369 FALSE);
370 else
371 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
372 callback_data,
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,
380 const gchar *email,
381 const gchar *sipid,
382 const gchar *company,
383 const gchar *country)
385 guint count = 0;
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) \
397 if (val) { \
398 if (count++) \
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);
410 if (count > 0) {
411 gchar *body = g_markup_printf_escaped("<m:FindPeople>"
412 " <m:PersonaShape>"
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>"
423 " </m:PersonaShape>"
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 :-(
430 " <m:Restriction>"
431 " <t:Exists>"
432 " <t:FieldURI FieldURI=\"persona:ImAddress\"/>"
433 " </t:Exists>"
434 " </m:Restriction>"
436 " <m:ParentFolderId>"
437 " <t:DistinguishedFolderId Id=\"directory\"/>"
438 " </m:ParentFolderId>"
439 " <m:QueryString>%s</m:QueryString>"
440 "</m:FindPeople>",
441 query->str);
443 if (!sipe_ucs_http_request(sipe_private,
444 NULL,
445 body,
446 sipe_ucs_search_response,
447 token))
448 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
449 token,
450 _("Contact search failed"));
451 } else
452 sipe_backend_search_failed(SIPE_CORE_PUBLIC,
453 token,
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,
469 const gchar **key,
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");
484 attr_node;
485 attr_node = sipe_xml_twin(attr_node)) {
486 const sipe_xml *id_node = sipe_xml_child(attr_node,
487 "SourceId");
488 gchar *hidden = sipe_xml_data(sipe_xml_child(attr_node,
489 "IsHidden"));
490 gchar *quick = sipe_xml_data(sipe_xml_child(attr_node,
491 "IsQuickContact"));
492 if (id_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");
497 g_free(quick);
498 g_free(hidden);
499 break;
501 g_free(quick);
502 g_free(hidden);
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);
518 if (persona_node &&
519 buddy &&
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,
530 buddy,
531 key,
532 change);
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);
539 g_free(who);
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,
546 const gchar *who)
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>",
554 buddy->exchange_key,
555 buddy->change_key,
556 group->exchange_key,
557 group->change_key);
559 sipe_ucs_http_request(sipe_private,
560 trans,
561 body,
562 sipe_ucs_ignore_response,
563 NULL);
564 } else {
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),
571 group->exchange_key,
572 group->change_key);
574 if (!sipe_ucs_http_request(sipe_private,
575 trans,
576 body,
577 sipe_ucs_add_new_im_contact_to_group_response,
578 payload))
579 g_free(payload);
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)
588 if (group) {
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>",
599 buddy->exchange_key,
600 buddy->change_key,
601 group->exchange_key,
602 group->change_key);
604 sipe_ucs_http_request(sipe_private,
605 trans,
606 body,
607 sipe_ucs_ignore_response,
608 NULL);
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,
616 "ExchangeStoreId");
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,
623 "DisplayName"));
624 group = sipe_group_add(sipe_private,
625 name,
626 key,
627 change,
628 /* sipe_group must have unique ID */
629 ++sipe_private->ucs->group_id);
630 g_free(name);
633 return(group);
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);
648 if (group) {
649 struct sipe_buddy *buddy = sipe_buddy_find_by_uri(sipe_private,
650 who);
652 if (buddy)
653 sipe_buddy_insert_group(buddy, group);
655 sipe_ucs_group_add_buddy(sipe_private,
656 trans,
657 group,
658 buddy,
659 who);
662 g_free(who);
665 void sipe_ucs_group_create(struct sipe_core_private *sipe_private,
666 struct sipe_ucs_transaction *trans,
667 const gchar *name,
668 const gchar *who)
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>"
674 "</m:AddImGroup>",
675 name);
677 if (!sipe_ucs_http_request(sipe_private,
678 trans,
679 body,
680 sipe_ucs_add_im_group_response,
681 payload))
682 g_free(payload);
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>"
693 "</m:SetImGroup>",
694 group->exchange_key,
695 group->change_key,
696 new_name);
698 sipe_ucs_http_request(sipe_private,
699 NULL,
700 body,
701 sipe_ucs_ignore_response,
702 NULL);
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>",
711 group->exchange_key,
712 group->change_key);
714 sipe_ucs_http_request(sipe_private,
715 NULL,
716 body,
717 sipe_ucs_ignore_response,
718 NULL);
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!"),
734 default_settings ?
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");
747 if (node) {
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,
751 g_str_equal,
752 NULL,
753 g_free);
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);
759 } else
760 sipe_backend_buddy_list_processing_start(SIPE_CORE_PUBLIC);
762 for (persona_node = sipe_xml_child(node, "Personas/Persona");
763 persona_node;
764 persona_node = sipe_xml_twin(persona_node)) {
765 gchar *address = sipe_xml_data(sipe_xml_child(persona_node,
766 "ImAddress"));
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,
774 "DisplayName"));
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,
781 uri,
782 key,
783 change);
784 g_free(uri);
786 /* hash table takes ownership of alias */
787 g_hash_table_insert(uri_to_alias,
788 buddy->name,
789 alias);
791 SIPE_DEBUG_INFO("sipe_ucs_get_im_item_list_response: persona URI '%s' key '%s' change '%s'",
792 buddy->name, key, change);
794 g_free(address);
797 for (group_node = sipe_xml_child(node, "Groups/ImGroup");
798 group_node;
799 group_node = sipe_xml_twin(group_node)) {
800 struct sipe_group *group = ucs_create_group(sipe_private,
801 group_node);
803 if (group) {
804 const sipe_xml *member_node;
806 for (member_node = sipe_xml_child(group_node,
807 "MemberCorrelationKey/ItemId");
808 member_node;
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,
812 "Id"));
813 if (buddy)
814 sipe_buddy_add_to_group(sipe_private,
815 buddy,
816 group,
817 g_hash_table_lookup(uri_to_alias,
818 buddy->name));
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);
829 } else {
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,
848 NULL);
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;
870 if (!ucs)
871 return;
873 if (ews_data)
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);
879 } else {
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,
890 gboolean migrated)
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);
907 else
908 SIPE_DEBUG_INFO_NOFORMAT("sipe_ucs_init: ignoring this contact list update - triggered by our last change");
911 ucs->last_response = 0;
912 return;
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;
922 if (migrated) {
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,
929 NULL);
930 else
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;
938 GSList *entry;
940 if (!ucs)
941 return;
943 /* UCS stack is shutting down: reject all new requests */
944 ucs->shutting_down = TRUE;
946 entry = ucs->transactions;
947 while (entry) {
948 struct sipe_ucs_transaction *trans = entry->data;
949 GSList *entry2 = trans->pending_requests;
951 /* transactions get deleted by sipe_ucs_request_free() */
952 entry = entry->next;
954 while (entry2) {
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);
968 g_free(ucs);
969 sipe_private->ucs = NULL;
973 Local Variables:
974 mode: c
975 c-file-style: "bsd"
976 indent-tabs-mode: t
977 tab-width: 8
978 End: