ucs: process groups in GetImItemList response
[siplcs.git] / src / core / sipe-ucs.c
blob18f0fa129fe5951479aa8937de7086e04590a27a
1 /**
2 * @file sipe-ucs.c
4 * pidgin-sipe
6 * Copyright (C) 2013 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>
28 #include <string.h>
30 #include <glib.h>
32 #include "sipe-backend.h"
33 #include "sipe-common.h"
34 #include "sipe-core.h"
35 #include "sipe-core-private.h"
36 #include "sipe-digest.h"
37 #include "sipe-ews-autodiscover.h"
38 #include "sipe-group.h"
39 #include "sipe-http.h"
40 #include "sipe-subscriptions.h"
41 #include "sipe-ucs.h"
42 #include "sipe-utils.h"
43 #include "sipe-xml.h"
45 typedef void (ucs_callback)(struct sipe_core_private *sipe_private,
46 const sipe_xml *body,
47 gpointer callback_data);
49 struct ucs_deferred {
50 ucs_callback *cb;
51 gpointer cb_data;
52 gchar *body;
55 struct ucs_request {
56 ucs_callback *cb;
57 gpointer cb_data;
58 struct sipe_http_request *request;
61 struct sipe_ucs {
62 gchar *ews_url;
63 GSList *deferred_requests;
64 GSList *pending_requests;
65 gboolean migrated;
66 gboolean shutting_down;
69 static void sipe_ucs_deferred_free(struct sipe_core_private *sipe_private,
70 struct ucs_deferred *data)
72 if (data->cb)
73 /* Callback: aborted */
74 (*data->cb)(sipe_private, NULL, data->cb_data);
75 g_free(data->body);
76 g_free(data);
79 static void sipe_ucs_request_free(struct sipe_core_private *sipe_private,
80 struct ucs_request *data)
82 if (data->request)
83 sipe_http_request_cancel(data->request);
84 if (data->cb)
85 /* Callback: aborted */
86 (*data->cb)(sipe_private, NULL, data->cb_data);
87 g_free(data);
90 static void sipe_ucs_http_response(struct sipe_core_private *sipe_private,
91 guint status,
92 SIPE_UNUSED_PARAMETER GSList *headers,
93 const gchar *body,
94 gpointer callback_data)
96 struct ucs_request *data = callback_data;
97 struct sipe_ucs *ucs = sipe_private->ucs;
99 SIPE_DEBUG_INFO("sipe_ucs_http_response: code %d", status);
100 data->request = NULL;
102 if ((status == SIPE_HTTP_STATUS_OK) && body) {
103 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
104 const sipe_xml *soap_body = sipe_xml_child(xml, "Body");
105 /* Callback: success */
106 (*data->cb)(sipe_private, soap_body, data->cb_data);
107 sipe_xml_free(xml);
108 } else {
109 /* Callback: failed */
110 (*data->cb)(sipe_private, NULL, data->cb_data);
113 /* already been called */
114 data->cb = NULL;
116 ucs->pending_requests = g_slist_remove(ucs->pending_requests,
117 data);
118 sipe_ucs_request_free(sipe_private, data);
121 static gboolean sipe_ucs_http_request(struct sipe_core_private *sipe_private,
122 const gchar *body,
123 ucs_callback *callback,
124 gpointer callback_data)
126 struct sipe_ucs *ucs = sipe_private->ucs;
127 gboolean success = FALSE;
129 if (ucs->shutting_down) {
130 SIPE_DEBUG_ERROR("sipe_ucs_http_request: new UCS request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
131 "Body: %s\n",
132 body ? body : "<EMPTY>");
134 } else if (ucs->ews_url) {
135 struct ucs_request *data = g_new0(struct ucs_request, 1);
136 gchar *soap = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
137 "<soap:Envelope"
138 " xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\""
139 " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
140 " xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\""
141 " >"
142 " <soap:Header>"
143 " <t:RequestServerVersion Version=\"Exchange2013\" />"
144 " </soap:Header>"
145 " <soap:Body>"
146 " %s"
147 " </soap:Body>"
148 "</soap:Envelope>",
149 body);
150 struct sipe_http_request *request = sipe_http_request_post(sipe_private,
151 ucs->ews_url,
152 NULL,
153 soap,
154 "text/xml; charset=UTF-8",
155 sipe_ucs_http_response,
156 data);
157 g_free(soap);
159 if (request) {
160 data->cb = callback;
161 data->cb_data = callback_data;
162 data->request = request;
164 ucs->pending_requests = g_slist_prepend(ucs->pending_requests,
165 data);
167 sipe_core_email_authentication(sipe_private,
168 request);
169 sipe_http_request_allow_redirect(request);
170 sipe_http_request_ready(request);
172 success = TRUE;
173 } else {
174 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_http_request: failed to create HTTP connection");
175 g_free(data);
178 } else {
179 struct ucs_deferred *data = g_new0(struct ucs_deferred, 1);
180 data->cb = callback;
181 data->cb_data = callback_data;
182 data->body = g_strdup(body);
184 ucs->deferred_requests = g_slist_prepend(ucs->deferred_requests,
185 data);
186 success = TRUE;
189 return(success);
192 static void sipe_ucs_get_user_photo_response(struct sipe_core_private *sipe_private,
193 const sipe_xml *body,
194 gpointer callback_data)
196 gchar *uri = callback_data;
197 const sipe_xml *node = sipe_xml_child(body,
198 "GetUserPhotoResponse/PictureData");
200 if (node) {
201 gchar *base64;
202 gsize photo_size;
203 guchar *photo;
204 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
205 gchar *digest_string;
207 /* decode photo data */
208 base64 = sipe_xml_data(node);
209 photo = g_base64_decode(base64, &photo_size);
210 g_free(base64);
212 /* EWS doesn't provide a hash -> calculate SHA-1 digest */
213 sipe_digest_sha1(photo, photo_size, digest);
214 digest_string = buff_to_hex_str(digest,
215 SIPE_DIGEST_SHA1_LENGTH);
217 /* backend frees "photo" */
218 sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC,
219 uri,
220 photo,
221 photo_size,
222 digest_string);
223 g_free(digest_string);
226 g_free(uri);
229 void sipe_ucs_get_photo(struct sipe_core_private *sipe_private,
230 const gchar *uri)
232 gchar *payload = g_strdup(uri);
233 gchar *body = g_strdup_printf("<m:GetUserPhoto>"
234 " <m:Email>%s</m:Email>"
235 " <m:SizeRequested>HR48x48</m:SizeRequested>"
236 "</m:GetUserPhoto>",
237 sipe_get_no_sip_uri(uri));
239 if (!sipe_ucs_http_request(sipe_private,
240 body,
241 sipe_ucs_get_user_photo_response,
242 payload))
243 g_free(payload);
245 g_free(body);
248 static void sipe_ucs_get_im_item_list_response(struct sipe_core_private *sipe_private,
249 const sipe_xml *body,
250 SIPE_UNUSED_PARAMETER gpointer callback_data)
252 const sipe_xml *node = sipe_xml_child(body,
253 "GetImItemListResponse/ImItemList");
255 if (node) {
256 const sipe_xml *group_node;
258 for (group_node = sipe_xml_child(node, "Groups/ImGroup");
259 group_node;
260 group_node = sipe_xml_twin(group_node)) {
261 gchar *name = sipe_xml_data(sipe_xml_child(group_node,
262 "DisplayName"));
264 if (!is_empty(name)) {
265 struct sipe_group *group = g_new0(struct sipe_group, 1);
267 group->name = name;
268 name = NULL; /* group takes ownership */
270 sipe_group_add(sipe_private, group);
273 g_free(name);
276 sipe_subscribe_presence_initial(sipe_private);
280 static void ucs_ews_autodiscover_cb(struct sipe_core_private *sipe_private,
281 const struct sipe_ews_autodiscover_data *ews_data,
282 SIPE_UNUSED_PARAMETER gpointer callback_data)
284 struct sipe_ucs *ucs = sipe_private->ucs;
285 const gchar *ews_url = ews_data->ews_url;
287 if (!ucs)
288 return;
290 if (is_empty(ews_url)) {
291 SIPE_DEBUG_ERROR_NOFORMAT("ucs_ews_autodiscover_cb: can't detect EWS URL, contact list operations will not work!");
292 return;
295 SIPE_DEBUG_INFO("ucs_ews_autodiscover_cb: EWS URL '%s'", ews_url);
296 ucs->ews_url = g_strdup(ews_url);
298 /* Request migrated contact list */
299 if (ucs->migrated)
300 sipe_ucs_http_request(sipe_private,
301 "<m:GetImItemList/>",
302 sipe_ucs_get_im_item_list_response,
303 NULL);
305 /* EWS URL is valid, send all deferred requests now */
306 if (ucs->deferred_requests) {
307 GSList *entry = ucs->deferred_requests;
308 while (entry) {
309 struct ucs_deferred *data = entry->data;
311 sipe_ucs_http_request(sipe_private,
312 data->body,
313 data->cb,
314 data->cb_data);
316 /* callback & data has been forwarded */
317 data->cb = NULL;
318 sipe_ucs_deferred_free(sipe_private, data);
320 entry = entry->next;
322 g_slist_free(ucs->deferred_requests);
323 ucs->deferred_requests = NULL;
327 gboolean sipe_ucs_is_migrated(struct sipe_core_private *sipe_private)
329 return(sipe_private->ucs ? sipe_private->ucs->migrated : FALSE);
332 void sipe_ucs_init(struct sipe_core_private *sipe_private,
333 gboolean migrated)
335 struct sipe_ucs *ucs;
337 if (sipe_private->ucs)
338 return;
340 sipe_private->ucs = ucs = g_new0(struct sipe_ucs, 1);
341 ucs->migrated = migrated;
343 sipe_ews_autodiscover_start(sipe_private,
344 ucs_ews_autodiscover_cb,
345 NULL);
348 void sipe_ucs_free(struct sipe_core_private *sipe_private)
350 struct sipe_ucs *ucs = sipe_private->ucs;
352 if (!ucs)
353 return;
355 /* UCS stack is shutting down: reject all new requests */
356 ucs->shutting_down = TRUE;
358 if (ucs->deferred_requests) {
359 GSList *entry = ucs->deferred_requests;
360 while (entry) {
361 sipe_ucs_deferred_free(sipe_private, entry->data);
362 entry = entry->next;
364 g_slist_free(ucs->deferred_requests);
367 if (ucs->pending_requests) {
368 GSList *entry = ucs->pending_requests;
369 while (entry) {
370 sipe_ucs_request_free(sipe_private, entry->data);
371 entry = entry->next;
373 g_slist_free(ucs->pending_requests);
376 g_free(ucs->ews_url);
377 g_free(ucs);
378 sipe_private->ucs = NULL;
382 Local Variables:
383 mode: c
384 c-file-style: "bsd"
385 indent-tabs-mode: t
386 tab-width: 8
387 End: