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