1 /*
2 linphone
3 Copyright (C) 2010-2015 Belledonne Communications SARL
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "linphone/core.h"
21 #include "private.h"
22
23 #include <bctoolbox/crypto.h>
24
25 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendListCbs);
26
27 BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendListCbs, belle_sip_object_t,
28 NULL, // destroy
29 NULL, // clone
30 NULL, // Marshall
31 FALSE
32 );
33
linphone_friend_list_cbs_new(void)34 static LinphoneFriendListCbs * linphone_friend_list_cbs_new(void) {
35 return belle_sip_object_new(LinphoneFriendListCbs);
36 }
37
linphone_friend_list_get_callbacks(const LinphoneFriendList * list)38 LinphoneFriendListCbs * linphone_friend_list_get_callbacks(const LinphoneFriendList *list) {
39 return list->cbs;
40 }
41
linphone_friend_list_cbs_ref(LinphoneFriendListCbs * cbs)42 LinphoneFriendListCbs * linphone_friend_list_cbs_ref(LinphoneFriendListCbs *cbs) {
43 belle_sip_object_ref(cbs);
44 return cbs;
45 }
46
linphone_friend_list_cbs_unref(LinphoneFriendListCbs * cbs)47 void linphone_friend_list_cbs_unref(LinphoneFriendListCbs *cbs) {
48 belle_sip_object_unref(cbs);
49 }
50
linphone_friend_list_cbs_get_user_data(const LinphoneFriendListCbs * cbs)51 void *linphone_friend_list_cbs_get_user_data(const LinphoneFriendListCbs *cbs) {
52 return cbs->user_data;
53 }
54
linphone_friend_list_cbs_set_user_data(LinphoneFriendListCbs * cbs,void * ud)55 void linphone_friend_list_cbs_set_user_data(LinphoneFriendListCbs *cbs, void *ud) {
56 cbs->user_data = ud;
57 }
58
linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs * cbs)59 LinphoneFriendListCbsContactCreatedCb linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs *cbs) {
60 return cbs->contact_created_cb;
61 }
62
linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs * cbs,LinphoneFriendListCbsContactCreatedCb cb)63 void linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactCreatedCb cb) {
64 cbs->contact_created_cb = cb;
65 }
66
linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs * cbs)67 LinphoneFriendListCbsContactDeletedCb linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs *cbs) {
68 return cbs->contact_deleted_cb;
69 }
70
linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs * cbs,LinphoneFriendListCbsContactDeletedCb cb)71 void linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactDeletedCb cb) {
72 cbs->contact_deleted_cb = cb;
73 }
74
linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs * cbs)75 LinphoneFriendListCbsContactUpdatedCb linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs *cbs) {
76 return cbs->contact_updated_cb;
77 }
78
linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs * cbs,LinphoneFriendListCbsContactUpdatedCb cb)79 void linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactUpdatedCb cb) {
80 cbs->contact_updated_cb = cb;
81 }
82
linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs * cbs)83 LinphoneFriendListCbsSyncStateChangedCb linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs *cbs) {
84 return cbs->sync_state_changed_cb;
85 }
86
linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs * cbs,LinphoneFriendListCbsSyncStateChangedCb cb)87 void linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsSyncStateChangedCb cb) {
88 cbs->sync_state_changed_cb = cb;
89 }
90
add_uri_entry(xmlTextWriterPtr writer,int err,const char * uri)91 static int add_uri_entry(xmlTextWriterPtr writer, int err, const char *uri) {
92 if (err >= 0) {
93 err = xmlTextWriterStartElement(writer, (const xmlChar *)"entry");
94 }
95 if (err >= 0) {
96 err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"uri", (const xmlChar *)uri);
97 }
98 if (err >= 0) {
99 /* Close the "entry" element. */
100 err = xmlTextWriterEndElement(writer);
101 }
102 return err;
103 }
104
uri_list(const LinphoneFriendList * list)105 static bctbx_list_t * uri_list(const LinphoneFriendList *list) {
106 bctbx_list_t * uri_list = NULL;
107 bctbx_list_t * elem = NULL;
108 for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
109 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
110 bctbx_list_t *iterator;
111 const bctbx_list_t *addresses = linphone_friend_get_addresses(lf);
112 bctbx_list_t *numbers = linphone_friend_get_phone_numbers(lf);
113 iterator = (bctbx_list_t *)addresses;
114 while (iterator) {
115 LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator);
116 if (addr) {
117 char *uri = linphone_address_as_string_uri_only(addr);
118 if (uri) {
119 uri_list = bctbx_list_prepend(uri_list, uri);
120 }
121 }
122 iterator = bctbx_list_next(iterator);
123 }
124 iterator = numbers;
125 while (iterator) {
126 const char *number = (const char *)bctbx_list_get_data(iterator);
127 const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number);
128 if (uri) {
129 uri_list = bctbx_list_prepend(uri_list, ms_strdup(uri));
130 }
131 iterator = bctbx_list_next(iterator);
132 }
133 }
134 return uri_list;
135 }
136
create_resource_list_xml(const LinphoneFriendList * list)137 static char * create_resource_list_xml(const LinphoneFriendList *list) {
138 char *xml_content = NULL;
139 xmlBufferPtr buf;
140 xmlTextWriterPtr writer;
141 int err;
142
143 if (list->friends == NULL) return NULL;
144
145 buf = xmlBufferCreate();
146 if (buf == NULL) {
147 ms_error("%s: Error creating the XML buffer", __FUNCTION__);
148 return NULL;
149 }
150 writer = xmlNewTextWriterMemory(buf, 0);
151 if (writer == NULL) {
152 ms_error("%s: Error creating the XML writer", __FUNCTION__);
153 return NULL;
154 }
155
156 xmlTextWriterSetIndent(writer,1);
157 err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
158 if (err >= 0) {
159 err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"resource-lists", (const xmlChar *)"urn:ietf:params:xml:ns:resource-lists");
160 }
161 if (err >= 0) {
162 err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi",
163 NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance");
164 }
165
166 if (err>= 0) {
167 err = xmlTextWriterStartElement(writer, (const xmlChar *)"list");
168 }
169
170 {
171 bctbx_list_t* entries = uri_list(list);
172 bctbx_list_t* it;
173 for(it = entries; it != NULL; it = it->next){
174 err = add_uri_entry(writer, err, it->data);
175 }
176 bctbx_list_free_with_data(entries, ms_free);
177 }
178 if (err >= 0) {
179 /* Close the "list" element. */
180 err = xmlTextWriterEndElement(writer);
181 }
182
183 if (err >= 0) {
184 /* Close the "resource-lists" element. */
185 err = xmlTextWriterEndElement(writer);
186 }
187 if (err >= 0) {
188 err = xmlTextWriterEndDocument(writer);
189 }
190 if (err > 0) {
191 /* xmlTextWriterEndDocument returns the size of the content. */
192 xml_content = ms_strdup((char *)buf->content);
193 }
194 xmlFreeTextWriter(writer);
195 xmlBufferFree(buf);
196
197 return xml_content;
198 }
199
linphone_friend_list_parse_multipart_related_body(LinphoneFriendList * list,const LinphoneContent * body,const char * first_part_body)200 static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList *list, const LinphoneContent *body, const char *first_part_body) {
201 xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
202 xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
203 xml_ctx->doc = xmlReadDoc((const unsigned char*)first_part_body, 0, NULL, 0);
204 if (xml_ctx->doc != NULL) {
205 LinphoneFriend *lf;
206 LinphoneContent *presence_part;
207 xmlXPathObjectPtr resource_object;
208 const char *version_str = NULL;
209 const char *full_state_str = NULL;
210 const char *uri = NULL;
211 bool_t full_state = FALSE;
212 int version;
213 int i;
214
215 if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end;
216 xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"rlmi", (const xmlChar *)"urn:ietf:params:xml:ns:rlmi");
217
218 version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version");
219 if (version_str == NULL) {
220 ms_warning("rlmi+xml: No version attribute in list");
221 goto end;
222 }
223 version = atoi(version_str);
224 linphone_free_xml_text_content(version_str);
225 if (version < list->expected_notification_version) { /*no longuer an error as dialog may be silently restarting by the refresher*/
226 ms_warning("rlmi+xml: Received notification with version %d expected was %d, dialog may have been reseted", version, list->expected_notification_version);
227 }
228
229 full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState");
230 if (full_state_str == NULL) {
231 ms_warning("rlmi+xml: No fullState attribute in list");
232 goto end;
233 }
234 if ((strcmp(full_state_str, "true") == 0) || (strcmp(full_state_str, "1") == 0)) {
235 bctbx_list_t *l = list->friends;
236 for (; l != NULL; l = bctbx_list_next(l)) {
237 lf = (LinphoneFriend *)bctbx_list_get_data(l);
238 linphone_friend_clear_presence_models(lf);
239 }
240 full_state = TRUE;
241 }
242 linphone_free_xml_text_content(full_state_str);
243 if ((list->expected_notification_version == 0) && (full_state == FALSE)) {
244 ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid");
245 goto end;
246 }
247 list->expected_notification_version = version + 1;
248
249 resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:instance[@state=\"active\"]/..");
250 if ((resource_object != NULL) && (resource_object->nodesetval != NULL)) {
251 for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) {
252 const char *cid = NULL;
253 linphone_xml_xpath_context_set_node(xml_ctx, xmlXPathNodeSetItem(resource_object->nodesetval, i-1));
254 cid = linphone_get_xml_text_content(xml_ctx, "./rlmi:instance/@cid");
255 if (cid != NULL) {
256 presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid);
257 if (presence_part == NULL) {
258 ms_warning("rlmi+xml: Cannot find part with Content-Id: %s", cid);
259 } else {
260 SalPresenceModel *presence = NULL;
261 linphone_notify_parse_presence(linphone_content_get_type(presence_part), linphone_content_get_subtype(presence_part), linphone_content_get_string_buffer(presence_part), &presence);
262 if (presence != NULL) {
263 // Try to reduce CPU cost of linphone_address_new and find_friend_by_address by only doing it when we know for sure we have a presence to notify
264 LinphoneAddress* addr;
265 uri = linphone_get_xml_text_content(xml_ctx, "./@uri");
266 if (uri == NULL) continue;
267 addr = linphone_address_new(uri);
268 if (!addr) continue;
269 lf = linphone_friend_list_find_friend_by_address(list, addr);
270 linphone_address_unref(addr);
271 if (lf) {
272 const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri);
273 lf->presence_received = TRUE;
274 if (phone_number) {
275 char *presence_address = linphone_presence_model_get_contact((LinphonePresenceModel *)presence);
276 bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(presence_address, linphone_friend_ref(lf));
277 bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, presence_address);
278 if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){
279 linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
280 bctbx_map_cchar_erase(list->friends_map_uri, it);
281 }
282 bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
283 linphone_friend_set_presence_model_for_uri_or_tel(lf, phone_number, (LinphonePresenceModel *)presence);
284 } else {
285 linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, (LinphonePresenceModel *)presence);
286 }
287 if (full_state == FALSE) {
288 if (phone_number)
289 linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, phone_number, (LinphonePresenceModel *)presence);
290 else
291 linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, (LinphonePresenceModel *)presence);
292 linphone_core_notify_notify_presence_received(list->lc, lf);
293 }
294 linphone_free_xml_text_content(uri);
295 }
296 linphone_content_unref(presence_part);
297 }
298 }
299 linphone_free_xml_text_content(cid);
300 }
301 }
302 }
303 if (resource_object != NULL) xmlXPathFreeObject(resource_object);
304
305 if (full_state == TRUE) {
306 const bctbx_list_t *addresses;
307 bctbx_list_t *numbers;
308 bctbx_list_t *iterator;
309 bctbx_list_t *l = list->friends;
310 for (; l != NULL; l = bctbx_list_next(l)) {
311 lf = (LinphoneFriend *)bctbx_list_get_data(l);
312 addresses = linphone_friend_get_addresses(lf);
313 numbers = linphone_friend_get_phone_numbers(lf);
314 iterator = (bctbx_list_t *)addresses;
315 while (iterator) {
316 LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator);
317 char *uri = linphone_address_as_string_uri_only(addr);
318 const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri);
319 if (presence) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, presence);
320 ms_free(uri);
321 iterator = bctbx_list_next(iterator);
322 }
323 iterator = numbers;
324 while (iterator) {
325 const char *number = (const char *)bctbx_list_get_data(iterator);
326 const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, number);
327 if (presence) {
328 linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, number, presence);
329 }
330 iterator = bctbx_list_next(iterator);
331 }
332 if (numbers) bctbx_list_free(numbers);
333 if (linphone_friend_is_presence_received(lf) == TRUE) {
334 linphone_core_notify_notify_presence_received(list->lc, lf);
335 }
336 }
337 }
338 } else {
339 ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer);
340 }
341
342 end:
343 linphone_xmlparsing_context_destroy(xml_ctx);
344 }
345
linphone_friend_list_has_subscribe_inactive(const LinphoneFriendList * list)346 static bool_t linphone_friend_list_has_subscribe_inactive(const LinphoneFriendList *list) {
347 bctbx_list_t *l = list->friends;
348 bool_t has_subscribe_inactive = FALSE;
349 for (; l != NULL; l = bctbx_list_next(l)) {
350 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(l);
351 if (lf->subscribe_active != TRUE) {
352 has_subscribe_inactive = TRUE;
353 break;
354 }
355 }
356 return has_subscribe_inactive;
357 }
358
linphone_friend_list_new(void)359 static LinphoneFriendList * linphone_friend_list_new(void) {
360 LinphoneFriendList *list = belle_sip_object_new(LinphoneFriendList);
361 list->cbs = linphone_friend_list_cbs_new();
362 list->enable_subscriptions = TRUE;
363 list->friends_map = bctbx_mmap_cchar_new();
364 list->friends_map_uri = bctbx_mmap_cchar_new();
365 return list;
366 }
367
linphone_friend_list_destroy(LinphoneFriendList * list)368 static void linphone_friend_list_destroy(LinphoneFriendList *list) {
369 if (list->display_name != NULL) ms_free(list->display_name);
370 if (list->rls_addr) linphone_address_unref(list->rls_addr);
371 if (list->rls_uri != NULL) ms_free(list->rls_uri);
372 if (list->content_digest != NULL) ms_free(list->content_digest);
373 if (list->event != NULL) {
374 linphone_event_terminate(list->event);
375 linphone_event_unref(list->event);
376 list->event = NULL;
377 }
378 if (list->uri != NULL) ms_free(list->uri);
379 if (list->cbs) linphone_friend_list_cbs_unref(list->cbs);
380 if (list->dirty_friends_to_update) list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
381 if (list->friends) list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release);
382 if (list->friends_map) bctbx_mmap_cchar_delete_with_data(list->friends_map, (void (*)(void *))linphone_friend_unref);
383 if (list->friends_map_uri) bctbx_mmap_cchar_delete_with_data(list->friends_map_uri, (void (*)(void *))linphone_friend_unref);
384 }
385
386 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendList);
387
388 BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendList, belle_sip_object_t,
389 (belle_sip_object_destroy_t)linphone_friend_list_destroy,
390 NULL, // clone
391 NULL, // marshal
392 FALSE
393 );
394
395
linphone_core_create_friend_list(LinphoneCore * lc)396 LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc) {
397 LinphoneFriendList *list = linphone_friend_list_new();
398 list->lc = lc;
399 return list;
400 }
401
linphone_friend_list_ref(LinphoneFriendList * list)402 LinphoneFriendList * linphone_friend_list_ref(LinphoneFriendList *list) {
403 belle_sip_object_ref(list);
404 return list;
405 }
406
_linphone_friend_list_release(LinphoneFriendList * list)407 void _linphone_friend_list_release(LinphoneFriendList *list){
408 /*drops all references to core and unref*/
409 list->lc = NULL;
410 if (list->event != NULL) {
411 linphone_event_unref(list->event);
412 list->event = NULL;
413 }
414 if (list->cbs) {
415 linphone_friend_list_cbs_unref(list->cbs);
416 list->cbs = NULL;
417 }
418 if (list->dirty_friends_to_update) {
419 list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
420 }
421 if (list->friends) {
422 list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release);
423 }
424 linphone_friend_list_unref(list);
425 }
426
linphone_friend_list_unref(LinphoneFriendList * list)427 void linphone_friend_list_unref(LinphoneFriendList *list) {
428 belle_sip_object_unref(list);
429 }
430
linphone_friend_list_get_user_data(const LinphoneFriendList * list)431 void * linphone_friend_list_get_user_data(const LinphoneFriendList *list) {
432 return list->user_data;
433 }
434
linphone_friend_list_set_user_data(LinphoneFriendList * list,void * ud)435 void linphone_friend_list_set_user_data(LinphoneFriendList *list, void *ud) {
436 list->user_data = ud;
437 }
438
linphone_friend_list_get_display_name(const LinphoneFriendList * list)439 const char * linphone_friend_list_get_display_name(const LinphoneFriendList *list) {
440 return list->display_name;
441 }
442
linphone_friend_list_set_display_name(LinphoneFriendList * list,const char * display_name)443 void linphone_friend_list_set_display_name(LinphoneFriendList *list, const char *display_name) {
444 if (list->display_name != NULL) {
445 ms_free(list->display_name);
446 list->display_name = NULL;
447 }
448 if (display_name != NULL) {
449 list->display_name = ms_strdup(display_name);
450 linphone_core_store_friends_list_in_db(list->lc, list);
451 }
452 }
453
linphone_friend_list_get_rls_address(const LinphoneFriendList * list)454 const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list){
455 return list->rls_addr;
456 }
_linphone_friend_list_get_rls_address(const LinphoneFriendList * list)457 const LinphoneAddress * _linphone_friend_list_get_rls_address(const LinphoneFriendList *list) {
458 if (list->rls_addr)
459 return list->rls_addr;
460 else if (list->lc) {
461 const char* rls_uri = lp_config_get_string(list->lc->config, "sip", "rls_uri", NULL);
462 if (list->lc->default_rls_addr)
463 linphone_address_unref(list->lc->default_rls_addr);
464
465 list->lc->default_rls_addr=NULL;
466
467 if (rls_uri) {
468 /*to make sure changes in config are used if any*/
469 list->lc->default_rls_addr = linphone_address_new(rls_uri);
470 }
471
472 return list->lc->default_rls_addr;
473 }
474 else
475 return NULL;
476 }
linphone_friend_list_set_rls_address(LinphoneFriendList * list,const LinphoneAddress * rls_addr)477 void linphone_friend_list_set_rls_address(LinphoneFriendList *list, const LinphoneAddress *rls_addr){
478 LinphoneAddress *new_rls_addr = rls_addr ? linphone_address_clone(rls_addr) : NULL;
479
480 if (list->rls_addr){
481 linphone_address_unref(list->rls_addr);
482 }
483 list->rls_addr = new_rls_addr;
484 if (list->rls_uri != NULL){
485 ms_free(list->rls_uri);
486 list->rls_uri = NULL;
487 }
488 if (list->rls_addr){
489 list->rls_uri = linphone_address_as_string(list->rls_addr);
490 linphone_core_store_friends_list_in_db(list->lc, list);
491 }
492 }
493
linphone_friend_list_get_rls_uri(const LinphoneFriendList * list)494 const char * linphone_friend_list_get_rls_uri(const LinphoneFriendList *list) {
495 return list->rls_uri;
496 }
497
linphone_friend_list_set_rls_uri(LinphoneFriendList * list,const char * rls_uri)498 void linphone_friend_list_set_rls_uri(LinphoneFriendList *list, const char *rls_uri) {
499 LinphoneAddress *addr = rls_uri ? linphone_core_create_address(list->lc, rls_uri) : NULL;
500 linphone_friend_list_set_rls_address(list, addr);
501 if (addr) linphone_address_unref(addr);
502 }
503
_linphone_friend_list_add_friend(LinphoneFriendList * list,LinphoneFriend * lf,bool_t synchronize)504 static LinphoneFriendListStatus _linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
505 LinphoneFriendListStatus status = LinphoneFriendListInvalidFriend;
506 const LinphoneAddress *addr;
507
508 if (!list || lf->friend_list) {
509 if (!list)
510 ms_error("linphone_friend_list_add_friend(): invalid list, null");
511 if (lf->friend_list)
512 ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
513 return status;
514 }
515 addr = linphone_friend_get_address(lf);
516 bool_t present = FALSE;
517 if (lf->refkey) {
518 present = linphone_friend_list_find_friend_by_ref_key(list, lf->refkey) != NULL;
519 } else {
520 present = bctbx_list_find(list->friends, lf) != NULL;
521 }
522 if (present) {
523 char *tmp = NULL;
524 if (addr) tmp = linphone_address_as_string(addr);
525 ms_warning("Friend %s already in list [%s], ignored.", tmp ? tmp : "unknown", list->display_name);
526 if (tmp) ms_free(tmp);
527 } else {
528 status = linphone_friend_list_import_friend(list, lf, synchronize);
529 linphone_friend_save(lf, lf->lc);
530 }
531
532 if (list->rls_uri == NULL) {
533 /* Mimic the behaviour of linphone_core_add_friend() when a resource list server is not in use */
534 linphone_friend_apply(lf, lf->lc);
535 }
536 return status;
537 }
538
linphone_friend_list_add_friend(LinphoneFriendList * list,LinphoneFriend * lf)539 LinphoneFriendListStatus linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
540 return _linphone_friend_list_add_friend(list, lf, TRUE);
541 }
542
linphone_friend_list_add_local_friend(LinphoneFriendList * list,LinphoneFriend * lf)543 LinphoneFriendListStatus linphone_friend_list_add_local_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
544 return _linphone_friend_list_add_friend(list, lf, FALSE);
545 }
546
linphone_friend_list_import_friend(LinphoneFriendList * list,LinphoneFriend * lf,bool_t synchronize)547 LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
548 bctbx_list_t *iterator;
549 bctbx_list_t *phone_numbers;
550 const bctbx_list_t *addresses;
551 if (lf->friend_list) {
552 if (lf->friend_list)
553 ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
554 return LinphoneFriendListInvalidFriend;
555 }
556 lf->friend_list = list;
557 lf->lc = list->lc;
558 list->friends = bctbx_list_prepend(list->friends, linphone_friend_ref(lf));
559 if (lf->refkey) {
560 bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(lf->refkey, linphone_friend_ref(lf));
561 bctbx_map_cchar_insert_and_delete(list->friends_map, pair);
562 }
563
564 phone_numbers = linphone_friend_get_phone_numbers(lf);
565 iterator = phone_numbers;
566 while (iterator) {
567 const char *number = (const char *)bctbx_list_get_data(iterator);
568 const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number);
569 if(uri) {
570 bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf));
571 bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
572 }
573 iterator = bctbx_list_next(iterator);
574 }
575
576 addresses = linphone_friend_get_addresses(lf);
577 iterator = (bctbx_list_t *)addresses;
578 while (iterator) {
579 LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator);
580 char *uri = linphone_address_as_string_uri_only(lfaddr);
581 if(uri) {
582 bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf));
583 bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
584 ms_free(uri);
585 }
586 iterator = bctbx_list_next(iterator);
587 }
588
589 if (synchronize) {
590 list->dirty_friends_to_update = bctbx_list_prepend(list->dirty_friends_to_update, linphone_friend_ref(lf));
591 }
592 return LinphoneFriendListOK;
593 }
594
carddav_done(LinphoneCardDavContext * cdc,bool_t success,const char * msg)595 static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) {
596 if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
597 cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg);
598 }
599 linphone_carddav_context_destroy(cdc);
600 }
601
_linphone_friend_list_remove_friend(LinphoneFriendList * list,LinphoneFriend * lf,bool_t remove_from_server)602 static LinphoneFriendListStatus _linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t remove_from_server) {
603 bctbx_list_t *iterator;
604 bctbx_list_t *phone_numbers;
605 const bctbx_list_t *addresses;
606 bctbx_list_t *elem = bctbx_list_find(list->friends, lf);
607 if (elem == NULL) return LinphoneFriendListNonExistentFriend;
608
609 #ifdef SQLITE_STORAGE_ENABLED
610 if (lf && lf->lc && lf->lc->friends_db) {
611 linphone_core_remove_friend_from_db(lf->lc, lf);
612 }
613 #endif
614 if (remove_from_server) {
615 LinphoneVcard *lvc = linphone_friend_get_vcard(lf);
616 if (lvc && linphone_vcard_get_uid(lvc)) {
617 LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
618 if (cdc) {
619 cdc->sync_done_cb = carddav_done;
620 if (cdc->friend_list->cbs->sync_state_changed_cb) {
621 cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
622 }
623 linphone_carddav_delete_vcard(cdc, lf);
624 }
625 }
626 }
627 if (!list->lc->friends_db_file) {
628 linphone_core_write_friends_config(list->lc);
629 }
630 list->friends = bctbx_list_erase_link(list->friends, elem);
631 if(lf->refkey) {
632 bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map, lf->refkey);
633 if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map))){
634 linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
635 bctbx_map_cchar_erase(list->friends_map, it);
636 }
637 }
638
639 phone_numbers = linphone_friend_get_phone_numbers(lf);
640 iterator = phone_numbers;
641 while (iterator) {
642 const char *number = (const char *)bctbx_list_get_data(iterator);
643 const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number);
644 if(uri) {
645 bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri);
646 if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){
647 linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
648 bctbx_map_cchar_erase(list->friends_map_uri, it);
649 }
650 bctbx_iterator_cchar_delete(it);
651 }
652 iterator = bctbx_list_next(iterator);
653 }
654
655 addresses = linphone_friend_get_addresses(lf);
656 iterator = (bctbx_list_t *)addresses;
657 while (iterator) {
658 LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator);
659 char *uri = linphone_address_as_string_uri_only(lfaddr);
660 if(uri) {
661 bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri);
662 if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){
663 linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
664 bctbx_map_cchar_erase(list->friends_map_uri, it);
665 }
666 bctbx_iterator_cchar_delete(it);
667 ms_free(uri);
668 }
669 iterator = bctbx_list_next(iterator);
670 }
671
672 lf->friend_list = NULL;
673 linphone_friend_unref(lf);
674 return LinphoneFriendListOK;
675 }
676
linphone_friend_list_remove_friend(LinphoneFriendList * list,LinphoneFriend * lf)677 LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
678 return _linphone_friend_list_remove_friend(list, lf, TRUE);
679 }
680
linphone_friend_list_get_friends(const LinphoneFriendList * list)681 const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) {
682 return list->friends;
683 }
684
linphone_friend_list_update_dirty_friends(LinphoneFriendList * list)685 void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) {
686 bctbx_list_t *dirty_friends = list->dirty_friends_to_update;
687
688 while (dirty_friends) {
689 LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
690 if (cdc) {
691 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(dirty_friends);
692 cdc->sync_done_cb = carddav_done;
693 if (lf) {
694 if (cdc->friend_list->cbs->sync_state_changed_cb) {
695 cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
696 }
697 linphone_carddav_put_vcard(cdc, lf);
698 }
699 }
700 dirty_friends = bctbx_list_next(dirty_friends);
701 }
702 list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
703 }
704
carddav_created(LinphoneCardDavContext * cdc,LinphoneFriend * lf)705 static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
706 if (cdc) {
707 LinphoneFriendList *lfl = cdc->friend_list;
708 linphone_friend_list_import_friend(lfl, lf, FALSE);
709 if (cdc->friend_list->cbs->contact_created_cb) {
710 cdc->friend_list->cbs->contact_created_cb(lfl, lf);
711 }
712 }
713 }
714
carddav_removed(LinphoneCardDavContext * cdc,LinphoneFriend * lf)715 static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
716 if (cdc) {
717 LinphoneFriendList *lfl = cdc->friend_list;
718 _linphone_friend_list_remove_friend(lfl, lf, FALSE);
719 if (cdc->friend_list->cbs->contact_deleted_cb) {
720 cdc->friend_list->cbs->contact_deleted_cb(lfl, lf);
721 }
722 }
723 }
724
carddav_updated(LinphoneCardDavContext * cdc,LinphoneFriend * lf_new,LinphoneFriend * lf_old)725 static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) {
726 if (cdc) {
727 LinphoneFriendList *lfl = cdc->friend_list;
728 bctbx_list_t *elem = bctbx_list_find(lfl->friends, lf_old);
729 if (elem) {
730 elem->data = linphone_friend_ref(lf_new);
731 }
732 linphone_core_store_friend_in_db(lf_new->lc, lf_new);
733
734 if (cdc->friend_list->cbs->contact_updated_cb) {
735 cdc->friend_list->cbs->contact_updated_cb(lfl, lf_new, lf_old);
736 }
737 linphone_friend_unref(lf_old);
738 }
739 }
740
linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList * list)741 void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) {
742 LinphoneCardDavContext *cdc = NULL;
743
744 if (!list || !list->uri || !list->lc) {
745 ms_error("FATAL");
746 return;
747 }
748
749 cdc = linphone_carddav_context_new(list);
750 if (cdc) {
751 cdc->contact_created_cb = carddav_created;
752 cdc->contact_removed_cb = carddav_removed;
753 cdc->contact_updated_cb = carddav_updated;
754 cdc->sync_done_cb = carddav_done;
755 if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
756 cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
757 }
758 linphone_carddav_synchronize(cdc);
759 }
760 }
761
linphone_friend_list_find_friend_by_address(const LinphoneFriendList * list,const LinphoneAddress * address)762 LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) {
763 LinphoneFriend *result = NULL;
764 char *uri = linphone_address_as_string_uri_only(address);
765 bctbx_iterator_t* it = bctbx_map_cchar_find_key(list->friends_map_uri, (void*)uri);
766 if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))) {
767 bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
768 result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
769 }
770 bctbx_iterator_cchar_delete(it);
771 ms_free(uri);
772 return result;
773 }
774
linphone_friend_list_find_friend_by_uri(const LinphoneFriendList * list,const char * uri)775 LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) {
776 LinphoneFriend *result = NULL;
777 LinphoneAddress *address = linphone_address_new(uri);
778 if(address) {
779 result = linphone_friend_list_find_friend_by_address(list, address);
780 linphone_address_unref(address);
781 }
782 return result;
783 }
784
linphone_friend_list_find_friend_by_ref_key(const LinphoneFriendList * list,const char * ref_key)785 LinphoneFriend * linphone_friend_list_find_friend_by_ref_key(const LinphoneFriendList *list, const char *ref_key) {
786 if(list) {
787 bctbx_iterator_t* it = bctbx_map_cchar_find_key(list->friends_map, (void*)ref_key);
788 if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map))) {
789 bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
790 return (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
791 }
792 }
793 return NULL;
794 }
795
linphone_friend_list_find_friend_by_inc_subscribe(const LinphoneFriendList * list,SalOp * op)796 LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe(const LinphoneFriendList *list, SalOp *op) {
797 const bctbx_list_t *elem;
798 for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
799 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
800 if (bctbx_list_find(lf->insubs, op)) return lf;
801 }
802 return NULL;
803 }
804
linphone_friend_list_find_friend_by_out_subscribe(const LinphoneFriendList * list,SalOp * op)805 LinphoneFriend * linphone_friend_list_find_friend_by_out_subscribe(const LinphoneFriendList *list, SalOp *op) {
806 const bctbx_list_t *elem;
807 for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
808 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
809 if (lf->outsub && ((lf->outsub == op) || sal_op_is_forked_of(lf->outsub, op))) return lf;
810 }
811 return NULL;
812 }
813
linphone_friend_list_close_subscriptions(LinphoneFriendList * list)814 static void linphone_friend_list_close_subscriptions(LinphoneFriendList *list) {
815 /* FIXME we should wait until subscription to complete. */
816 if (list->event) {
817 linphone_event_terminate(list->event);
818 linphone_event_unref(list->event);
819 list->event = NULL;
820 }
821 bctbx_list_for_each(list->friends, (void (*)(void *))linphone_friend_close_subscriptions);
822 }
823
linphone_friend_list_send_list_subscription(LinphoneFriendList * list)824 static void linphone_friend_list_send_list_subscription(LinphoneFriendList *list){
825 const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
826 char *xml_content = create_resource_list_xml(list);
827 if ((address != NULL) && (xml_content != NULL) && (linphone_friend_list_has_subscribe_inactive(list) == TRUE)) {
828 unsigned char digest[16];
829 bctbx_md5((unsigned char *)xml_content, strlen(xml_content), digest);
830 if ((list->event != NULL) && (list->content_digest != NULL) && (memcmp(list->content_digest, digest, sizeof(digest)) == 0)) {
831 /* The content has not changed, only refresh the event. */
832 linphone_event_refresh_subscribe(list->event);
833 } else {
834 LinphoneContent *content;
835 bctbx_list_t * elem = NULL;
836 int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600);
837 list->expected_notification_version = 0;
838 if (list->content_digest != NULL) ms_free(list->content_digest);
839 list->content_digest = ms_malloc(sizeof(digest));
840 memcpy(list->content_digest, digest, sizeof(digest));
841 if (list->event != NULL) {
842 linphone_event_terminate(list->event);
843 linphone_event_unref(list->event);
844 }
845 list->event = linphone_core_create_subscribe(list->lc, address, "presence", expires);
846 linphone_event_ref(list->event);
847 linphone_event_set_internal(list->event, TRUE);
848 linphone_event_add_custom_header(list->event, "Require", "recipient-list-subscribe");
849 linphone_event_add_custom_header(list->event, "Supported", "eventlist");
850 linphone_event_add_custom_header(list->event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml");
851 linphone_event_add_custom_header(list->event, "Content-Disposition", "recipient-list");
852 content = linphone_core_create_content(list->lc);
853 linphone_content_set_type(content, "application");
854 linphone_content_set_subtype(content, "resource-lists+xml");
855 linphone_content_set_string_buffer(content, xml_content);
856 if (linphone_core_content_encoding_supported(list->lc, "deflate")) {
857 linphone_content_set_encoding(content, "deflate");
858 linphone_event_add_custom_header(list->event, "Accept-Encoding", "deflate");
859 }
860 for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
861 LinphoneFriend *lf = (LinphoneFriend *)elem->data;
862 lf->subscribe_active = TRUE;
863 }
864 linphone_event_send_subscribe(list->event, content);
865 linphone_content_unref(content);
866 linphone_event_set_user_data(list->event, list);
867 }
868 }
869 if (xml_content != NULL) ms_free(xml_content);
870 }
871
linphone_friend_list_update_subscriptions(LinphoneFriendList * list)872 void linphone_friend_list_update_subscriptions(LinphoneFriendList *list){
873 LinphoneProxyConfig *cfg = NULL;
874 const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
875 bool_t only_when_registered = FALSE;
876 bool_t should_send_list_subscribe = FALSE;
877
878 if (list->lc){
879 if (address)
880 cfg = linphone_core_lookup_known_proxy(list->lc, address);
881 only_when_registered = linphone_core_should_subscribe_friends_only_when_registered(list->lc);
882 should_send_list_subscribe = (!only_when_registered || !cfg || cfg->state == LinphoneRegistrationOk);
883 }
884
885 if (address != NULL) {
886 if (list->enable_subscriptions) {
887 if (should_send_list_subscribe){
888 linphone_friend_list_send_list_subscription(list);
889 }else{
890 if (list->event){
891 linphone_event_terminate(list->event);
892 linphone_event_unref(list->event);
893 list->event = NULL;
894 ms_message("Friends list [%p] subscription terminated because proxy config lost connection", list);
895 }else{
896 ms_message("Friends list [%p] subscription update skipped since dependant proxy config is not yet registered", list);
897 }
898 }
899 } else {
900 ms_message("Friends list [%p] subscription update skipped since subscriptions not enabled yet", list);
901 }
902 } else if (list->enable_subscriptions) {
903 const bctbx_list_t *elem;
904 for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
905 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
906 linphone_friend_update_subscribes(lf, only_when_registered);
907 }
908 }
909 }
910
linphone_friend_list_invalidate_subscriptions(LinphoneFriendList * list)911 void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) {
912 const bctbx_list_t *elem;
913
914 // Terminate subscription event
915 if (list->event) {
916 linphone_event_terminate(list->event);
917 linphone_event_unref(list->event);
918 list->event = NULL;
919 }
920
921 for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
922 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
923 linphone_friend_invalidate_subscription(lf);
924 }
925 }
926
linphone_friend_list_notify_presence(LinphoneFriendList * list,LinphonePresenceModel * presence)927 void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePresenceModel *presence) {
928 const bctbx_list_t *elem;
929 for(elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
930 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
931 linphone_friend_notify(lf, presence);
932 }
933 }
934
linphone_friend_list_notify_presence_received(LinphoneFriendList * list,LinphoneEvent * lev,const LinphoneContent * body)935 void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) {
936 if (linphone_content_is_multipart(body)) {
937 LinphoneContent *first_part;
938 const char *type = linphone_content_get_type(body);
939 const char *subtype = linphone_content_get_subtype(body);
940
941 if ((strcmp(type, "multipart") != 0) || (strcmp(subtype, "related") != 0)) {
942 ms_warning("multipart presence notified but it is not 'multipart/related'");
943 return;
944 }
945
946 first_part = linphone_content_get_part(body, 0);
947 if (first_part == NULL) {
948 ms_warning("'multipart/related' presence notified but it doesn't contain any part");
949 return;
950 }
951
952 type = linphone_content_get_type(first_part);
953 subtype = linphone_content_get_subtype(first_part);
954 if ((strcmp(type, "application") != 0) || (strcmp(subtype, "rlmi+xml") != 0)) {
955 ms_warning("multipart presence notified but first part is not 'application/rlmi+xml'");
956 linphone_content_unref(first_part);
957 return;
958 }
959
960 linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part));
961 linphone_content_unref(first_part);
962 }
963 }
964
linphone_friend_list_get_uri(const LinphoneFriendList * list)965 const char * linphone_friend_list_get_uri(const LinphoneFriendList *list) {
966 return list->uri;
967 }
968
linphone_friend_list_set_uri(LinphoneFriendList * list,const char * uri)969 void linphone_friend_list_set_uri(LinphoneFriendList *list, const char *uri) {
970 if (list->uri != NULL) {
971 ms_free(list->uri);
972 list->uri = NULL;
973 }
974 if (uri != NULL) {
975 list->uri = ms_strdup(uri);
976 linphone_core_store_friends_list_in_db(list->lc, list);
977 }
978 }
979
linphone_friend_list_update_revision(LinphoneFriendList * list,int rev)980 void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev) {
981 list->revision = rev;
982 linphone_core_store_friends_list_in_db(list->lc, list);
983 }
984
linphone_friend_list_subscription_state_changed(LinphoneCore * lc,LinphoneEvent * lev,LinphoneSubscriptionState state)985 void linphone_friend_list_subscription_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state) {
986 LinphoneFriendList *list = (LinphoneFriendList *)linphone_event_get_user_data(lev);
987 if (!list) {
988 ms_warning("core [%p] Receiving unexpected state [%s] for event [%p], no associated friend list",lc
989 , linphone_subscription_state_to_string(state)
990 , lev);
991 } else {
992 ms_message("Receiving new state [%s] for event [%p] for friend list [%p]"
993 , linphone_subscription_state_to_string(state)
994 , lev
995 , list);
996
997 if (state == LinphoneSubscriptionOutgoingProgress && linphone_event_get_reason(lev) == LinphoneReasonNoMatch) {
998 ms_message("Reseting version count for friend list [%p]",list);
999 list->expected_notification_version = 0;
1000 }
1001 }
1002 }
1003
linphone_friend_list_get_core(const LinphoneFriendList * list)1004 LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) {
1005 return list->lc;
1006 }
1007
linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList * list,bctbx_list_t * vcards)1008 static LinphoneStatus linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList *list, bctbx_list_t *vcards) {
1009 bctbx_list_t *vcards_iterator = NULL;
1010 int count = 0;
1011
1012 if (!linphone_core_vcard_supported()) {
1013 ms_error("vCard support wasn't enabled at compilation time");
1014 return -1;
1015 }
1016 if (!list) {
1017 ms_error("Can't import into a NULL list");
1018 return -1;
1019 }
1020
1021 vcards_iterator = vcards;
1022
1023 while (vcards_iterator != NULL && bctbx_list_get_data(vcards_iterator) != NULL) {
1024 LinphoneVcard *vcard = (LinphoneVcard *)bctbx_list_get_data(vcards_iterator);
1025 LinphoneFriend *lf = linphone_friend_new_from_vcard(vcard);
1026 linphone_vcard_unref(vcard);
1027 if (lf) {
1028 if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) {
1029 linphone_friend_save(lf, lf->lc);
1030 count++;
1031 }
1032 linphone_friend_unref(lf);
1033 }
1034 vcards_iterator = bctbx_list_next(vcards_iterator);
1035 }
1036 bctbx_list_free(vcards);
1037 linphone_core_store_friends_list_in_db(list->lc, list);
1038 return count;
1039
1040 }
linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList * list,const char * vcard_file)1041 LinphoneStatus linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList *list, const char *vcard_file) {
1042 bctbx_list_t *vcards = NULL;
1043
1044 if (!linphone_core_vcard_supported()) {
1045 ms_error("vCard support wasn't enabled at compilation time");
1046 return -1;
1047 }
1048 if (!list) {
1049 ms_error("Can't import into a NULL list");
1050 return -1;
1051 }
1052
1053 vcards = linphone_vcard_context_get_vcard_list_from_file(list->lc->vcard_context, vcard_file);
1054 if (!vcards) {
1055 ms_error("Failed to parse the file %s", vcard_file);
1056 return -1;
1057 }
1058 return linphone_friend_list_import_friends_from_vcard4(list,vcards);
1059 }
1060
linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList * list,const char * vcard_buffer)1061 LinphoneStatus linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer) {
1062 bctbx_list_t *vcards = NULL;
1063
1064 if (!linphone_core_vcard_supported()) {
1065 ms_error("vCard support wasn't enabled at compilation time");
1066 return -1;
1067 }
1068 if (!list) {
1069 ms_error("Can't import into a NULL list");
1070 return -1;
1071 }
1072
1073 vcards = linphone_vcard_context_get_vcard_list_from_buffer(list->lc->vcard_context, vcard_buffer);
1074 if (!vcards) {
1075 ms_error("Failed to parse the buffer");
1076 return -1;
1077 }
1078
1079 return linphone_friend_list_import_friends_from_vcard4(list,vcards);}
1080
linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList * list,const char * vcard_file)1081 void linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList *list, const char *vcard_file) {
1082 FILE *file = NULL;
1083 const bctbx_list_t *friends;
1084
1085 if (!linphone_core_vcard_supported()) {
1086 ms_error("vCard support wasn't enabled at compilation time");
1087 return;
1088 }
1089
1090 file = fopen(vcard_file, "wb");
1091 if (file == NULL) {
1092 ms_warning("Could not write %s ! Maybe it is read-only. Contacts will not be saved.", vcard_file);
1093 return;
1094 }
1095
1096 friends = linphone_friend_list_get_friends(list);
1097 while (friends != NULL && bctbx_list_get_data(friends) != NULL) {
1098 LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(friends);
1099 LinphoneVcard *vcard = linphone_friend_get_vcard(lf);
1100 if (vcard) {
1101 const char *vcard_text = linphone_vcard_as_vcard4_string(vcard);
1102 fprintf(file, "%s", vcard_text);
1103 }
1104 friends = bctbx_list_next(friends);
1105 }
1106
1107 fclose(file);
1108 }
1109
linphone_friend_list_enable_subscriptions(LinphoneFriendList * list,bool_t enabled)1110 void linphone_friend_list_enable_subscriptions(LinphoneFriendList *list, bool_t enabled) {
1111 if (list->enable_subscriptions != enabled) {
1112 list->enable_subscriptions = enabled;
1113 if (enabled) {
1114 linphone_friend_list_update_subscriptions(list);
1115 } else {
1116 linphone_friend_list_close_subscriptions(list);
1117 }
1118
1119 }
1120 }
1121
linphone_friend_list_subscriptions_enabled(LinphoneFriendList * list)1122 bool_t linphone_friend_list_subscriptions_enabled(LinphoneFriendList *list) {
1123 return list->enable_subscriptions;
1124 }
1125