ucs: always initialize on Lync 2013
[siplcs.git] / src / core / sipe-ucs.c
blob0757627d9c524046057c45845b7743b3736bc044
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-http.h"
39 #include "sipe-ucs.h"
40 #include "sipe-utils.h"
41 #include "sipe-xml.h"
43 typedef void (ucs_callback)(struct sipe_core_private *sipe_private,
44 const sipe_xml *xml,
45 gpointer callback_data);
47 struct ucs_deferred {
48 ucs_callback *cb;
49 gpointer cb_data;
50 gchar *body;
53 struct ucs_request {
54 ucs_callback *cb;
55 gpointer cb_data;
56 struct sipe_http_request *request;
59 struct sipe_ucs {
60 gchar *ews_url;
61 GSList *deferred_requests;
62 GSList *pending_requests;
63 gboolean migrated;
64 gboolean shutting_down;
67 static void sipe_ucs_deferred_free(struct sipe_core_private *sipe_private,
68 struct ucs_deferred *data)
70 if (data->cb)
71 /* Callback: aborted */
72 (*data->cb)(sipe_private, NULL, data->cb_data);
73 g_free(data->body);
74 g_free(data);
77 static void sipe_ucs_request_free(struct sipe_core_private *sipe_private,
78 struct ucs_request *data)
80 if (data->request)
81 sipe_http_request_cancel(data->request);
82 if (data->cb)
83 /* Callback: aborted */
84 (*data->cb)(sipe_private, NULL, data->cb_data);
85 g_free(data);
88 static void sipe_ucs_http_response(struct sipe_core_private *sipe_private,
89 guint status,
90 SIPE_UNUSED_PARAMETER GSList *headers,
91 const gchar *body,
92 gpointer callback_data)
94 struct ucs_request *data = callback_data;
95 struct sipe_ucs *ucs = sipe_private->ucs;
97 SIPE_DEBUG_INFO("sipe_ucs_http_response: code %d", status);
98 data->request = NULL;
100 if ((status == SIPE_HTTP_STATUS_OK) && body) {
101 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
102 /* Callback: success */
103 (*data->cb)(sipe_private, xml, data->cb_data);
104 sipe_xml_free(xml);
105 } else {
106 /* Callback: failed */
107 (*data->cb)(sipe_private, NULL, data->cb_data);
110 /* already been called */
111 data->cb = NULL;
113 ucs->pending_requests = g_slist_remove(ucs->pending_requests,
114 data);
115 sipe_ucs_request_free(sipe_private, data);
118 static gboolean sipe_ucs_http_request(struct sipe_core_private *sipe_private,
119 const gchar *body,
120 ucs_callback *callback,
121 gpointer callback_data)
123 struct sipe_ucs *ucs = sipe_private->ucs;
124 gboolean success = FALSE;
126 if (ucs->shutting_down) {
127 SIPE_DEBUG_ERROR("sipe_ucs_http_request: new UCS request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
128 "Body: %s\n",
129 body ? body : "<EMPTY>");
131 } else if (ucs->ews_url) {
132 struct ucs_request *data = g_new0(struct ucs_request, 1);
133 gchar *soap = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
134 "<soap:Envelope"
135 " xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\""
136 " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
137 " xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\""
138 " >"
139 " <soap:Header>"
140 " <t:RequestServerVersion Version=\"Exchange2013\" />"
141 " </soap:Header>"
142 " <soap:Body>"
143 " %s"
144 " </soap:Body>"
145 "</soap:Envelope>",
146 body);
147 struct sipe_http_request *request = sipe_http_request_post(sipe_private,
148 ucs->ews_url,
149 NULL,
150 soap,
151 "text/xml; charset=UTF-8",
152 sipe_ucs_http_response,
153 data);
154 g_free(soap);
156 if (request) {
157 data->cb = callback;
158 data->cb_data = callback_data;
159 data->request = request;
161 ucs->pending_requests = g_slist_prepend(ucs->pending_requests,
162 data);
164 sipe_core_email_authentication(sipe_private,
165 request);
166 sipe_http_request_allow_redirect(request);
167 sipe_http_request_ready(request);
169 success = TRUE;
170 } else {
171 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ucs_http_request: failed to create HTTP connection");
172 g_free(data);
175 } else {
176 struct ucs_deferred *data = g_new0(struct ucs_deferred, 1);
177 data->cb = callback;
178 data->cb_data = callback_data;
179 data->body = g_strdup(body);
181 ucs->deferred_requests = g_slist_prepend(ucs->deferred_requests,
182 data);
183 success = TRUE;
186 return(success);
189 static void sipe_ucs_get_user_photo_response(struct sipe_core_private *sipe_private,
190 const sipe_xml *xml,
191 gpointer callback_data)
193 gchar *uri = callback_data;
194 const sipe_xml *node = sipe_xml_child(xml,
195 "Body/GetUserPhotoResponse/PictureData");
197 if (node) {
198 gchar *base64;
199 gsize photo_size;
200 guchar *photo;
201 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
202 gchar *digest_string;
204 /* decode photo data */
205 base64 = sipe_xml_data(node);
206 photo = g_base64_decode(base64, &photo_size);
207 g_free(base64);
209 /* EWS doesn't provide a hash -> calculate SHA-1 digest */
210 sipe_digest_sha1(photo, photo_size, digest);
211 digest_string = buff_to_hex_str(digest,
212 SIPE_DIGEST_SHA1_LENGTH);
214 /* backend frees "photo" */
215 sipe_backend_buddy_set_photo(SIPE_CORE_PUBLIC,
216 uri,
217 photo,
218 photo_size,
219 digest_string);
220 g_free(digest_string);
223 g_free(uri);
226 void sipe_ucs_get_photo(struct sipe_core_private *sipe_private,
227 const gchar *uri)
229 gchar *payload = g_strdup(uri);
230 gchar *body = g_strdup_printf("<m:GetUserPhoto>"
231 " <m:Email>%s</m:Email>"
232 " <m:SizeRequested>HR48x48</m:SizeRequested>"
233 "</m:GetUserPhoto>",
234 sipe_get_no_sip_uri(uri));
236 if (!sipe_ucs_http_request(sipe_private,
237 body,
238 sipe_ucs_get_user_photo_response,
239 payload))
240 g_free(payload);
242 g_free(body);
245 static void sipe_ucs_get_im_item_list_response(struct sipe_core_private *sipe_private,
246 const sipe_xml *xml,
247 gpointer callback_data)
249 /* temporary */
250 (void)sipe_private;
251 (void)xml;
252 (void)callback_data;
255 static void ucs_ews_autodiscover_cb(struct sipe_core_private *sipe_private,
256 const struct sipe_ews_autodiscover_data *ews_data,
257 SIPE_UNUSED_PARAMETER gpointer callback_data)
259 struct sipe_ucs *ucs = sipe_private->ucs;
260 const gchar *ews_url = ews_data->ews_url;
262 if (!ucs)
263 return;
265 if (is_empty(ews_url)) {
266 SIPE_DEBUG_ERROR_NOFORMAT("ucs_ews_autodiscover_cb: can't detect EWS URL, contact list operations will not work!");
267 return;
270 SIPE_DEBUG_INFO("ucs_ews_autodiscover_cb: EWS URL '%s'", ews_url);
271 ucs->ews_url = g_strdup(ews_url);
273 /* Request migrated contact list */
274 if (ucs->migrated)
275 sipe_ucs_http_request(sipe_private,
276 "<m:GetImItemList/>",
277 sipe_ucs_get_im_item_list_response,
278 NULL);
280 /* EWS URL is valid, send all deferred requests now */
281 if (ucs->deferred_requests) {
282 GSList *entry = ucs->deferred_requests;
283 while (entry) {
284 struct ucs_deferred *data = entry->data;
286 sipe_ucs_http_request(sipe_private,
287 data->body,
288 data->cb,
289 data->cb_data);
291 /* callback & data has been forwarded */
292 data->cb = NULL;
293 sipe_ucs_deferred_free(sipe_private, data);
295 entry = entry->next;
297 g_slist_free(ucs->deferred_requests);
298 ucs->deferred_requests = NULL;
302 void sipe_ucs_init(struct sipe_core_private *sipe_private,
303 gboolean migrated)
305 struct sipe_ucs *ucs;
307 if (sipe_private->ucs)
308 return;
310 sipe_private->ucs = ucs = g_new0(struct sipe_ucs, 1);
311 ucs->migrated = migrated;
313 sipe_ews_autodiscover_start(sipe_private,
314 ucs_ews_autodiscover_cb,
315 NULL);
318 void sipe_ucs_free(struct sipe_core_private *sipe_private)
320 struct sipe_ucs *ucs = sipe_private->ucs;
322 if (!ucs)
323 return;
325 /* UCS stack is shutting down: reject all new requests */
326 ucs->shutting_down = TRUE;
328 if (ucs->deferred_requests) {
329 GSList *entry = ucs->deferred_requests;
330 while (entry) {
331 sipe_ucs_deferred_free(sipe_private, entry->data);
332 entry = entry->next;
334 g_slist_free(ucs->deferred_requests);
337 if (ucs->pending_requests) {
338 GSList *entry = ucs->pending_requests;
339 while (entry) {
340 sipe_ucs_request_free(sipe_private, entry->data);
341 entry = entry->next;
343 g_slist_free(ucs->pending_requests);
346 g_free(ucs->ews_url);
347 g_free(ucs);
348 sipe_private->ucs = NULL;
352 Local Variables:
353 mode: c
354 c-file-style: "bsd"
355 indent-tabs-mode: t
356 tab-width: 8
357 End: