friend.c 34.3 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4
/***************************************************************************
 *            friend.c
 *
 *  Sat May 15 15:25:16 2004
5
 *  Copyright  2004-2009  Simon Morlat
aymeric's avatar
aymeric committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *  Email
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "linphonecore.h"
#include "private.h"
#include "lpconfig.h"

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#ifdef FRIENDS_SQL_STORAGE_ENABLED
#ifndef _WIN32
#if !defined(ANDROID) && !defined(__QNXNTO__)
#	include <langinfo.h>
#	include <iconv.h>
#	include <string.h>
#endif
#else
#include <Windows.h>
#endif

#define MAX_PATH_SIZE 1024
#include "sqlite3.h"
#endif

aymeric's avatar
aymeric committed
44 45 46
const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){
	const char *str=NULL;
	switch(ss){
47
		case LinphoneStatusOnline:
aymeric's avatar
aymeric committed
48 49
		str=_("Online");
		break;
50
		case LinphoneStatusBusy:
aymeric's avatar
aymeric committed
51 52
		str=_("Busy");
		break;
53
		case LinphoneStatusBeRightBack:
aymeric's avatar
aymeric committed
54 55
		str=_("Be right back");
		break;
56
		case LinphoneStatusAway:
aymeric's avatar
aymeric committed
57 58
		str=_("Away");
		break;
59
		case LinphoneStatusOnThePhone:
aymeric's avatar
aymeric committed
60 61
		str=_("On the phone");
		break;
62
		case LinphoneStatusOutToLunch:
aymeric's avatar
aymeric committed
63 64
		str=_("Out to lunch");
		break;
65
		case LinphoneStatusDoNotDisturb:
aymeric's avatar
aymeric committed
66 67
		str=_("Do not disturb");
		break;
68
		case LinphoneStatusMoved:
aymeric's avatar
aymeric committed
69 70
		str=_("Moved");
		break;
71
		case LinphoneStatusAltService:
aymeric's avatar
aymeric committed
72 73
		str=_("Using another messaging service");
		break;
74
		case LinphoneStatusOffline:
aymeric's avatar
aymeric committed
75 76
		str=_("Offline");
		break;
77
		case LinphoneStatusPending:
aymeric's avatar
aymeric committed
78 79
		str=_("Pending");
		break;
80 81
		case LinphoneStatusVacation:
		str=_("Vacation");
aymeric's avatar
aymeric committed
82
		default:
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
83
		str=_("Unknown status");
aymeric's avatar
aymeric committed
84 85 86 87
	}
	return str;
}

Simon Morlat's avatar
Simon Morlat committed
88
static int friend_compare(const void * a, const void * b){
89 90
	LinphoneAddress *fa=((LinphoneFriend*)a)->uri;
	LinphoneAddress *fb=((LinphoneFriend*)b)->uri;
91
	if (linphone_address_weak_equal(fa,fb)) return 0;
Simon Morlat's avatar
Simon Morlat committed
92
	return 1;
aymeric's avatar
aymeric committed
93 94 95
}


Ghislain MARY's avatar
Ghislain MARY committed
96
MSList *linphone_find_friend_by_address(MSList *fl, const LinphoneAddress *addr, LinphoneFriend **lf){
aymeric's avatar
aymeric committed
97 98 99
	MSList *res=NULL;
	LinphoneFriend dummy;
	if (lf!=NULL) *lf=NULL;
100
	dummy.uri=(LinphoneAddress*)addr;
aymeric's avatar
aymeric committed
101 102 103 104 105 106
	res=ms_list_find_custom(fl,friend_compare,&dummy);
	if (lf!=NULL && res!=NULL) *lf=(LinphoneFriend*)res->data;
	return res;
}

void __linphone_friend_do_subscribe(LinphoneFriend *fr){
Simon Morlat's avatar
Simon Morlat committed
107
	LinphoneCore *lc=fr->lc;
108

109
	if (fr->outsub==NULL){
aymeric's avatar
aymeric committed
110
		/* people for which we don't have yet an answer should appear as offline */
111
		fr->presence=NULL;
112
		/*
113
		if (fr->lc->vtable.notify_recv)
114
			fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr);
115
		 */
116 117 118 119
	}else{
		sal_op_release(fr->outsub);
		fr->outsub=NULL;
	}
Simon Morlat's avatar
Simon Morlat committed
120 121
	fr->outsub=sal_op_new(lc->sal);
	linphone_configure_op(lc,fr->outsub,fr->uri,NULL,TRUE);
Simon Morlat's avatar
Simon Morlat committed
122
	sal_subscribe_presence(fr->outsub,NULL,NULL,lp_config_get_int(lc->config,"sip","subscribe_expires",600));
123
	fr->subscribe_active=TRUE;
aymeric's avatar
aymeric committed
124 125 126
}

LinphoneFriend * linphone_friend_new(){
127 128 129 130
	LinphoneFriend *obj = belle_sip_object_new(LinphoneFriend);
	obj->pol = LinphoneSPAccept;
	obj->presence = NULL;
	obj->subscribe = TRUE;
131
	obj->vcard = NULL;
132
	obj->storage_id = 0;
133
	return obj;
aymeric's avatar
aymeric committed
134 135
}

Ghislain MARY's avatar
Ghislain MARY committed
136
LinphoneFriend *linphone_friend_new_with_address(const char *addr){
jehan's avatar
jehan committed
137
	LinphoneAddress* linphone_address = linphone_address_new(addr);
Sylvain Berfini's avatar
Sylvain Berfini committed
138 139
	LinphoneFriend *fr;

jehan's avatar
jehan committed
140 141 142 143
	if (linphone_address == NULL) {
		ms_error("Cannot create friend for address [%s]",addr?addr:"null");
		return NULL;
	}
Sylvain Berfini's avatar
Sylvain Berfini committed
144
	fr=linphone_friend_new();
Ghislain MARY's avatar
Ghislain MARY committed
145
	linphone_friend_set_address(fr,linphone_address);
146
	linphone_address_destroy(linphone_address);
aymeric's avatar
aymeric committed
147 148 149
	return fr;
}

Simon Morlat's avatar
Simon Morlat committed
150
void linphone_friend_set_user_data(LinphoneFriend *lf, void *data){
151
	lf->user_data=data;
Simon Morlat's avatar
Simon Morlat committed
152 153 154
}

void* linphone_friend_get_user_data(const LinphoneFriend *lf){
155
	return lf->user_data;
Simon Morlat's avatar
Simon Morlat committed
156 157
}

158
bool_t linphone_friend_in_list(const LinphoneFriend *lf){
Simon Morlat's avatar
Simon Morlat committed
159
	return lf->in_list;
160 161
}

smorlat's avatar
smorlat committed
162
void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result){
163
	LinphoneAddress *fr=NULL;
164
	*result=NULL;
165
	fr=linphone_address_new(uri);
166
	if (fr==NULL){
smorlat's avatar
smorlat committed
167 168
		char *tmp=NULL;
		if (strchr(uri,'@')!=NULL){
169
			LinphoneAddress *u;
smorlat's avatar
smorlat committed
170 171
			/*try adding sip:*/
			tmp=ms_strdup_printf("sip:%s",uri);
172
			u=linphone_address_new(tmp);
173 174 175
			if (u!=NULL){
				*result=tmp;
			}
smorlat's avatar
smorlat committed
176 177
		}else if (lc->default_proxy!=NULL){
			/*try adding domain part from default current proxy*/
178
			LinphoneAddress * id=linphone_address_new(linphone_core_get_identity(lc));
179
			if ((id!=NULL) && (uri[0] != '\0')){
180
				linphone_address_set_display_name(id,NULL);
181 182 183
				linphone_address_set_username(id,uri);
				*result=linphone_address_as_string(id);
				linphone_address_destroy(id);
smorlat's avatar
smorlat committed
184 185
			}
		}
186
		if (*result){
smorlat's avatar
smorlat committed
187
			/*looks good */
188 189 190 191
			ms_message("%s interpreted as %s",uri,*result);
		}else{
			ms_warning("Fail to interpret friend uri %s",uri);
		}
jehan's avatar
jehan committed
192 193 194 195
	}else {
		*result=linphone_address_as_string(fr);
		linphone_address_destroy(fr);
	}
smorlat's avatar
smorlat committed
196
}
aymeric's avatar
aymeric committed
197

198
int linphone_friend_set_address(LinphoneFriend *lf, const LinphoneAddress *addr){
199
	LinphoneAddress *fr = linphone_address_clone(addr);
200
	LinphoneVCard *vcard = NULL;
201
	
202
	linphone_address_clean(fr);
203 204 205
	if (lf->uri != NULL) linphone_address_destroy(lf->uri);
	lf->uri = fr;
	
206 207 208 209 210
	vcard = linphone_friend_get_vcard(lf);
	if (vcard) {
		linphone_vcard_edit_main_sip_address(vcard, linphone_address_as_string_uri_only(fr));
	}
	
aymeric's avatar
aymeric committed
211 212 213 214
	return 0;
}

int linphone_friend_set_name(LinphoneFriend *lf, const char *name){
215 216
	LinphoneAddress *fr = lf->uri;
	LinphoneVCard *vcard = NULL;
217
	bool_t vcard_created = FALSE;
218 219 220 221
	
	vcard = linphone_friend_get_vcard(lf);
	if (!vcard) {
		linphone_friend_create_vcard(lf, name);
222
		vcard = linphone_friend_get_vcard(lf);
223 224
		vcard_created = TRUE;
	}
225
	if (vcard) {
226
		linphone_vcard_set_full_name(vcard, name);
227 228 229 230 231 232 233 234
		if (fr && vcard_created) { // SIP address wasn't set yet, let's do it
			linphone_vcard_edit_main_sip_address(vcard, linphone_address_as_string_uri_only(fr));
		}
	}
	
	if (!fr) {
		ms_warning("linphone_friend_set_address() must be called before linphone_friend_set_name() to be able to set display name.");
		return -1;
235
	}
236
	linphone_address_set_display_name(fr, name);
237
	
aymeric's avatar
aymeric committed
238 239 240
	return 0;
}

jehan's avatar
jehan committed
241
int linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val){
aymeric's avatar
aymeric committed
242 243 244 245 246 247 248 249 250 251
	fr->subscribe=val;
	return 0;
}

int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol)
{
	fr->pol=pol;
	return 0;
}

252
void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){
253 254 255 256 257 258 259 260 261
	MSList *elem;
	if (lf->insubs){
		char *addr=linphone_address_as_string(linphone_friend_get_address(lf));
		ms_message("Want to notify %s",addr);
		ms_free(addr);
	}
	for(elem=lf->insubs; elem!=NULL; elem=elem->next){
		SalOp *op = (SalOp*)elem->data;
		sal_notify_presence(op,(SalPresenceModel *)presence);
aymeric's avatar
aymeric committed
262 263 264
	}
}

265
void linphone_friend_add_incoming_subscription(LinphoneFriend *lf, SalOp *op){
266
	/*ownership of the op is transfered from sal to the LinphoneFriend*/
267 268 269 270
	lf->insubs = ms_list_append(lf->insubs, op);
}

void linphone_friend_remove_incoming_subscription(LinphoneFriend *lf, SalOp *op){
271 272
	if (ms_list_find(lf->insubs, op)){
		sal_op_release(op);
Simon Morlat's avatar
Simon Morlat committed
273
		lf->insubs = ms_list_remove(lf->insubs, op);
274
	}
Simon Morlat's avatar
Simon Morlat committed
275
	
276 277
}

aymeric's avatar
aymeric committed
278
static void linphone_friend_unsubscribe(LinphoneFriend *lf){
279 280
	if (lf->outsub!=NULL) {
		sal_unsubscribe(lf->outsub);
281
		lf->subscribe_active=FALSE;
aymeric's avatar
aymeric committed
282 283 284
	}
}

285
void linphone_friend_invalidate_subscription(LinphoneFriend *lf){
286 287
	LinphoneCore *lc=lf->lc;

Simon Morlat's avatar
Simon Morlat committed
288 289 290 291 292
	if (lf->outsub!=NULL) {
		sal_op_release(lf->outsub);
		lf->outsub=NULL;
		lf->subscribe_active=FALSE;
	}
293 294 295 296 297 298 299 300

	/* Notify application that we no longer know the presence activity */
	if (lf->presence != NULL) {
		linphone_presence_model_unref(lf->presence);
	}
	lf->presence = linphone_presence_model_new_with_activity(LinphonePresenceActivityOffline,"unknown activity");
	linphone_core_notify_notify_presence_received(lc,lf);

301
	lf->initial_subscribes_sent=FALSE;
Simon Morlat's avatar
Simon Morlat committed
302 303
}

304
void linphone_friend_close_subscriptions(LinphoneFriend *lf){
aymeric's avatar
aymeric committed
305
	linphone_friend_unsubscribe(lf);
306
	ms_list_for_each(lf->insubs, (MSIterateFunc) sal_notify_presence_close);
307
	lf->insubs = ms_list_free_with_data(lf->insubs, (MSIterateFunc)sal_op_release);
308 309
}

310
static void _linphone_friend_destroy(LinphoneFriend *lf){
311
	lf->insubs = ms_list_free_with_data(lf->insubs, (MSIterateFunc) sal_op_release);
312 313 314 315
	if (lf->outsub){
		sal_op_release(lf->outsub);
		lf->outsub=NULL;
	}
316
	if (lf->presence != NULL) linphone_presence_model_unref(lf->presence);
317
	if (lf->uri!=NULL) linphone_address_destroy(lf->uri);
318
	if (lf->info!=NULL) buddy_info_free(lf->info);
319
	if (lf->vcard != NULL) linphone_vcard_free(lf->vcard);
aymeric's avatar
aymeric committed
320 321
}

322 323 324 325 326 327 328 329 330 331 332
static belle_sip_error_code _linphone_friend_marshall(belle_sip_object_t *obj, char* buff, size_t buff_size, size_t *offset) {
	LinphoneFriend *lf = (LinphoneFriend*)obj;
	belle_sip_error_code err = BELLE_SIP_OK;
	if (lf->uri){
		char *tmp = linphone_address_as_string(lf->uri);
		err = belle_sip_snprintf(buff, buff_size, offset, "%s", tmp);
		ms_free(tmp);
	}
	return err;
}

333
const LinphoneAddress *linphone_friend_get_address(const LinphoneFriend *lf){
334
	return lf->uri;
aymeric's avatar
aymeric committed
335 336
}

337
const char * linphone_friend_get_name(const LinphoneFriend *lf) {
338 339 340 341 342 343 344
	if (lf && lf->vcard) {
		return linphone_vcard_get_full_name(lf->vcard);
	} else if (lf && lf->uri) {
		LinphoneAddress *fr = lf->uri;
		return linphone_address_get_display_name(fr);
	}
	return NULL;
345 346
}

aymeric's avatar
aymeric committed
347 348 349 350 351 352 353 354 355
bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf){
	return lf->subscribe;
}

LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf){
	return lf->pol;
}

LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){
356 357
	LinphoneOnlineStatus online_status = LinphoneStatusOffline;
	LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed;
358
	LinphonePresenceActivity *activity = NULL;
359 360 361 362
	unsigned int nb_activities = 0;

	if (lf->presence != NULL) {
		basic_status = linphone_presence_model_get_basic_status(lf->presence);
363
		nb_activities = linphone_presence_model_get_nb_activities(lf->presence);
364 365 366 367 368 369 370 371 372 373
		online_status = (basic_status == LinphonePresenceBasicStatusOpen) ? LinphoneStatusOnline : LinphoneStatusOffline;
		if (nb_activities > 1) {
			char *tmp = NULL;
			const LinphoneAddress *addr = linphone_friend_get_address(lf);
			if (addr) tmp = linphone_address_as_string(addr);
			ms_warning("Friend %s has several activities, get status from the first one", tmp ? tmp : "unknown");
			if (tmp) ms_free(tmp);
			nb_activities = 1;
		}
		if (nb_activities == 1) {
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
			activity = linphone_presence_model_get_activity(lf->presence);
			switch (linphone_presence_activity_get_type(activity)) {
				case LinphonePresenceActivityBreakfast:
				case LinphonePresenceActivityDinner:
				case LinphonePresenceActivityLunch:
				case LinphonePresenceActivityMeal:
					online_status = LinphoneStatusOutToLunch;
					break;
				case LinphonePresenceActivityAppointment:
				case LinphonePresenceActivityMeeting:
				case LinphonePresenceActivityPerformance:
				case LinphonePresenceActivityPresentation:
				case LinphonePresenceActivitySpectator:
				case LinphonePresenceActivityWorking:
				case LinphonePresenceActivityWorship:
					online_status = LinphoneStatusDoNotDisturb;
					break;
				case LinphonePresenceActivityAway:
				case LinphonePresenceActivitySleeping:
					online_status = LinphoneStatusAway;
					break;
				case LinphonePresenceActivityHoliday:
				case LinphonePresenceActivityTravel:
				case LinphonePresenceActivityVacation:
					online_status = LinphoneStatusVacation;
					break;
				case LinphonePresenceActivityBusy:
				case LinphonePresenceActivityLookingForWork:
				case LinphonePresenceActivityPlaying:
				case LinphonePresenceActivityShopping:
				case LinphonePresenceActivityTV:
					online_status = LinphoneStatusBusy;
					break;
				case LinphonePresenceActivityInTransit:
				case LinphonePresenceActivitySteering:
					online_status = LinphoneStatusBeRightBack;
					break;
				case LinphonePresenceActivityOnThePhone:
					online_status = LinphoneStatusOnThePhone;
					break;
				case LinphonePresenceActivityOther:
				case LinphonePresenceActivityPermanentAbsence:
					online_status = LinphoneStatusMoved;
					break;
				case LinphonePresenceActivityUnknown:
					/* Rely on the basic status information. */
					break;
				case LinphonePresenceActivityOnline:
					/* Should not happen! */
423
					/*ms_warning("LinphonePresenceActivityOnline should not happen here!");*/
424 425 426
					break;
				case LinphonePresenceActivityOffline:
					online_status = LinphoneStatusOffline;
427
					break;
428 429 430 431 432 433 434
			}
		}
	}

	return online_status;
}

435
const LinphonePresenceModel * linphone_friend_get_presence_model(LinphoneFriend *lf) {
436 437 438
	return lf->presence;
}

439 440 441 442 443 444 445
void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceModel *presence) {
	if (lf->presence != NULL) {
		linphone_presence_model_unref(lf->presence);
	}
	lf->presence = presence;
}

446 447 448 449
bool_t linphone_friend_is_presence_received(const LinphoneFriend *lf) {
	return lf->presence_received;
}

450 451 452
BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){
	return lf->info;
}
aymeric's avatar
aymeric committed
453

454 455 456 457 458 459 460 461
/*
 * updates the subscriptions.
 * If only_when_registered is TRUE, subscribe will be sent only if the friend's corresponding proxy config is in registered.
 * Otherwise if the proxy config goes to unregistered state, the subscription refresh will be suspended.
 * An optional proxy whose state has changed can be passed to optimize the processing.
**/
void linphone_friend_update_subscribes(LinphoneFriend *fr, LinphoneProxyConfig *proxy, bool_t only_when_registered){
	int can_subscribe=1;
462

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
	if (only_when_registered && (fr->subscribe || fr->subscribe_active)){
		LinphoneProxyConfig *cfg=linphone_core_lookup_known_proxy(fr->lc,fr->uri);
		if (proxy && proxy!=cfg) return;
		if (cfg && cfg->state!=LinphoneRegistrationOk){
			char *tmp=linphone_address_as_string(fr->uri);
			ms_message("Friend [%s] belongs to proxy config with identity [%s], but this one isn't registered. Subscription is suspended.",
				   tmp,linphone_proxy_config_get_identity(cfg));
			ms_free(tmp);
			can_subscribe=0;
		}
	}
	if (can_subscribe && fr->subscribe && fr->subscribe_active==FALSE){
		ms_message("Sending a new SUBSCRIBE");
		__linphone_friend_do_subscribe(fr);
	}else if (can_subscribe && fr->subscribe_active && !fr->subscribe){
		linphone_friend_unsubscribe(fr);
	}else if (!can_subscribe && fr->outsub){
		fr->subscribe_active=FALSE;
		sal_op_stop_refreshing(fr->outsub);
	}
}

485
void linphone_friend_save(LinphoneFriend *fr, LinphoneCore *lc) {
486 487 488
#ifdef FRIENDS_SQL_STORAGE_ENABLED
	linphone_core_store_friend_in_db(lc, fr);
#else
489
	linphone_core_write_friends_config(lc);
490
#endif
491 492 493
}

void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc) {
494 495
	LinphonePresenceModel *model;

496
	if (!fr->uri) {
aymeric's avatar
aymeric committed
497 498 499
		ms_warning("No sip url defined.");
		return;
	}
jehan's avatar
jehan committed
500

501 502
	if (fr->inc_subscribe_pending) {
		switch(fr->pol) {
aymeric's avatar
aymeric committed
503
			case LinphoneSPWait:
504
				model = linphone_presence_model_new_with_activity(LinphonePresenceActivityOther, "Waiting for user acceptance");
505
				linphone_friend_notify(fr, model);
506
				linphone_presence_model_unref(model);
aymeric's avatar
aymeric committed
507 508
				break;
			case LinphoneSPAccept:
509 510
				if (fr->lc)
					linphone_friend_notify(fr, fr->lc->presence_model);
aymeric's avatar
aymeric committed
511 512
				break;
			case LinphoneSPDeny:
513
				linphone_friend_notify(fr, NULL);
aymeric's avatar
aymeric committed
514 515
				break;
		}
516 517 518
		fr->inc_subscribe_pending = FALSE;
	}
	if (fr->lc) {
519
		linphone_friend_list_update_subscriptions(fr->lc->friendlist, NULL, linphone_core_should_subscribe_friends_only_when_registered(fr->lc));
aymeric's avatar
aymeric committed
520
	}
521
	ms_debug("linphone_friend_apply() done.");
522
	lc->bl_refresh=TRUE;
523
	fr->commit=FALSE;
aymeric's avatar
aymeric committed
524 525
}

526
void linphone_friend_edit(LinphoneFriend *fr) {
aymeric's avatar
aymeric committed
527 528
}

529
void linphone_friend_done(LinphoneFriend *fr) {
530
	ms_return_if_fail(fr);
531
	if (!fr->lc || !fr->in_list) return;
532 533
	linphone_friend_apply(fr, fr->lc);
	linphone_friend_save(fr, fr->lc);
aymeric's avatar
aymeric committed
534 535
}

536 537 538 539 540 541 542 543
LinphoneFriend * linphone_core_create_friend(LinphoneCore *lc) {
	return linphone_friend_new();
}

LinphoneFriend * linphone_core_create_friend_with_address(LinphoneCore *lc, const char *address) {
	return linphone_friend_new_with_address(address);
}

544
void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) {
Ghislain MARY's avatar
Ghislain MARY committed
545
	if ((lc->friendlist == NULL) || (linphone_friend_list_add_friend(lc->friendlist, lf) != LinphoneFriendListOK)) return;
546
	if (ms_list_find(lc->subscribers, lf)) {
547 548 549 550
		/*if this friend was in the pending subscriber list, now remove it from this list*/
		lc->subscribers = ms_list_remove(lc->subscribers, lf);
		linphone_friend_unref(lf);
	}
Simon Morlat's avatar
Simon Morlat committed
551
	lf->lc = lc; /*I would prefer this to be done in linphone_friend_list_add_friend()*/
552 553
	if (linphone_core_ready(lc)) linphone_friend_apply(lf, lc);
	else lf->commit = TRUE;
554
	linphone_friend_save(lf, lc);
aymeric's avatar
aymeric committed
555 556
}

557 558
void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend *lf) {
	if (linphone_friend_list_remove_friend(lc->friendlist, lf) == LinphoneFriendListNonExistentFriend) {
559
		ms_error("linphone_core_remove_friend(): friend [%p] is not part of core's list.", lf);
aymeric's avatar
aymeric committed
560 561 562
	}
}

563
void linphone_core_update_friends_subscriptions(LinphoneCore *lc, LinphoneProxyConfig *cfg, bool_t only_when_registered){
564
	if (lc->friendlist != NULL) {
565
		linphone_friend_list_update_subscriptions(lc->friendlist, cfg, only_when_registered);
Simon Morlat's avatar
Simon Morlat committed
566
	}
567 568 569 570 571
}

bool_t linphone_core_should_subscribe_friends_only_when_registered(const LinphoneCore *lc){
	return lp_config_get_int(lc->config,"sip","subscribe_presence_only_when_registered",1);
}
572

573
void linphone_core_send_initial_subscribes(LinphoneCore *lc){
Ghislain MARY's avatar
Ghislain MARY committed
574 575 576 577
	bool_t proxy_config_for_rls_presence_uri_domain = FALSE;
	LinphoneAddress *rls_address = NULL;
	const MSList *elem;

578 579
	if (lc->initial_subscribes_sent) return;
	lc->initial_subscribes_sent=TRUE;
Ghislain MARY's avatar
Ghislain MARY committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
	if (lc->friendlist->rls_uri != NULL) {
		rls_address = linphone_core_create_address(lc, lc->friendlist->rls_uri);
	}
	if (rls_address != NULL) {
		const char *rls_domain = linphone_address_get_domain(rls_address);
		if (rls_domain != NULL) {
			for (elem = linphone_core_get_proxy_config_list(lc); elem != NULL; elem = elem->next) {
				LinphoneProxyConfig *cfg = (LinphoneProxyConfig *)elem->data;
				const char *proxy_domain = linphone_proxy_config_get_domain(cfg);
				if (strcmp(rls_domain, proxy_domain) == 0) {
					proxy_config_for_rls_presence_uri_domain = TRUE;
					break;
				}
			}
		}
		linphone_address_unref(rls_address);
	}
	if (proxy_config_for_rls_presence_uri_domain == TRUE) {
		ms_message("Presence list activated so do not send initial subscribes it will be done when registered");
	} else {
		linphone_core_update_friends_subscriptions(lc,NULL,linphone_core_should_subscribe_friends_only_when_registered(lc));
	}
Simon Morlat's avatar
Simon Morlat committed
602 603 604
}

void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc){
605 606
	if (lc->friendlist != NULL)
		linphone_friend_list_invalidate_subscriptions(lc->friendlist);
Simon Morlat's avatar
Simon Morlat committed
607
	lc->initial_subscribes_sent=FALSE;
608 609
}

610
void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key){
611
	if (lf->refkey != NULL) {
612
		ms_free(lf->refkey);
613 614 615 616 617 618 619
		lf->refkey = NULL;
	}
	if (key) {
		lf->refkey = ms_strdup(key);
	}
	if (lf->lc) {
		linphone_friend_save(lf, lf->lc);
620 621 622 623 624 625 626
	}
}

const char *linphone_friend_get_ref_key(const LinphoneFriend *lf){
	return lf->refkey;
}

Simon Morlat's avatar
Simon Morlat committed
627
LinphoneFriend *linphone_core_find_friend(const LinphoneCore *lc, const LinphoneAddress *addr){
628
	return linphone_friend_list_find_friend_by_address(lc->friendlist, addr);
Simon Morlat's avatar
Simon Morlat committed
629 630 631
}

LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *uri){
632
	return linphone_friend_list_find_friend_by_uri(lc->friendlist, uri);
633 634
}

635
LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key){
636
	return linphone_friend_list_find_friend_by_ref_key(lc->friendlist, key);
637 638
}

smorlat's avatar
smorlat committed
639
#define key_compare(s1,s2)	strcmp(s1,s2)
aymeric's avatar
aymeric committed
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665

LinphoneSubscribePolicy __policy_str_to_enum(const char* pol){
	if (key_compare("accept",pol)==0){
		return LinphoneSPAccept;
	}
	if (key_compare("deny",pol)==0){
		return LinphoneSPDeny;
	}
	if (key_compare("wait",pol)==0){
		return LinphoneSPWait;
	}
	ms_warning("Unrecognized subscribe policy: %s",pol);
	return LinphoneSPWait;
}

LinphoneProxyConfig *__index_to_proxy(LinphoneCore *lc, int index){
	if (index>=0) return (LinphoneProxyConfig*)ms_list_nth_data(lc->sip_conf.proxies,index);
	else return NULL;
}

LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int index){
	const char *tmp;
	char item[50];
	int a;
	LinphoneFriend *lf;
	LpConfig *config=lc->config;
666

aymeric's avatar
aymeric committed
667
	sprintf(item,"friend_%i",index);
668

aymeric's avatar
aymeric committed
669 670 671
	if (!lp_config_has_section(config,item)){
		return NULL;
	}
672

aymeric's avatar
aymeric committed
673 674 675 676
	tmp=lp_config_get_string(config,item,"url",NULL);
	if (tmp==NULL) {
		return NULL;
	}
Ghislain MARY's avatar
Ghislain MARY committed
677
	lf=linphone_friend_new_with_address(tmp);
aymeric's avatar
aymeric committed
678 679 680 681 682 683 684 685 686 687
	if (lf==NULL) {
		return NULL;
	}
	tmp=lp_config_get_string(config,item,"pol",NULL);
	if (tmp==NULL) linphone_friend_set_inc_subscribe_policy(lf,LinphoneSPWait);
	else{
		linphone_friend_set_inc_subscribe_policy(lf,__policy_str_to_enum(tmp));
	}
	a=lp_config_get_int(config,item,"subscribe",0);
	linphone_friend_send_subscribe(lf,a);
688 689
	a = lp_config_get_int(config, item, "presence_received", 0);
	lf->presence_received = (bool_t)a;
690

691
	linphone_friend_set_ref_key(lf,lp_config_get_string(config,item,"refkey",NULL));
aymeric's avatar
aymeric committed
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
	return lf;
}

const char *__policy_enum_to_str(LinphoneSubscribePolicy pol){
	switch(pol){
		case LinphoneSPAccept:
			return "accept";
			break;
		case LinphoneSPDeny:
			return "deny";
			break;
		case LinphoneSPWait:
			return "wait";
			break;
	}
	ms_warning("Invalid policy enum value.");
	return "wait";
}

void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf, int index){
	char key[50];
	char *tmp;
714
	const char *refkey;
715

aymeric's avatar
aymeric committed
716
	sprintf(key,"friend_%i",index);
717

aymeric's avatar
aymeric committed
718 719 720 721
	if (lf==NULL){
		lp_config_clean_section(config,key);
		return;
	}
722
	if (lf->uri!=NULL){
723
		tmp=linphone_address_as_string(lf->uri);
aymeric's avatar
aymeric committed
724 725 726 727
		if (tmp==NULL) {
			return;
		}
		lp_config_set_string(config,key,"url",tmp);
Simon Morlat's avatar
Simon Morlat committed
728
		ms_free(tmp);
aymeric's avatar
aymeric committed
729 730 731
	}
	lp_config_set_string(config,key,"pol",__policy_enum_to_str(lf->pol));
	lp_config_set_int(config,key,"subscribe",lf->subscribe);
732
	lp_config_set_int(config, key, "presence_received", lf->presence_received);
733 734 735 736 737

	refkey=linphone_friend_get_ref_key(lf);
	if (refkey){
		lp_config_set_string(config,key,"refkey",refkey);
	}
aymeric's avatar
aymeric committed
738
}
739

740
void linphone_core_write_friends_config(LinphoneCore* lc) {
741 742
	MSList *elem;
	int i;
743
	int store_friends;
744 745 746
#ifdef FRIENDS_SQL_STORAGE_ENABLED
	return;
#endif
747
	if (! linphone_core_ready(lc)) return; /*dont write config when reading it !*/
748 749 750 751 752 753
	store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1);
	if (store_friends) {
		for (elem=lc->friendlist->friends,i=0; elem!=NULL; elem=ms_list_next(elem),i++){
			linphone_friend_write_to_config_file(lc->config,(LinphoneFriend*)elem->data,i);
		}
		linphone_friend_write_to_config_file(lc->config,NULL,i);	/* set the end */
754 755 756
	}
}

757 758 759 760
LinphoneCore *linphone_friend_get_core(const LinphoneFriend *fr){
	return fr->lc;
}

761 762 763 764 765 766 767 768 769 770 771 772 773 774
LinphoneFriend *linphone_friend_ref(LinphoneFriend *lf) {
	belle_sip_object_ref(lf);
	return lf;
}

void linphone_friend_unref(LinphoneFriend *lf) {
	belle_sip_object_unref(lf);
}

/* DEPRECATED */
void linphone_friend_destroy(LinphoneFriend *lf) {
	linphone_friend_unref(lf);
}

775 776 777 778 779 780 781 782 783 784 785
LinphoneVCard* linphone_friend_get_vcard(LinphoneFriend *fr) {
	return fr->vcard;
}

void linphone_friend_set_vcard(LinphoneFriend *fr, LinphoneVCard *vcard) {
	if (fr->vcard) {
		linphone_vcard_free(fr->vcard);
	}
	fr->vcard = vcard;
}

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
bool_t linphone_friend_create_vcard(LinphoneFriend *fr, const char *name) {
	LinphoneVCard *vcard = NULL;
	const char *fullName = NULL;
	LinphoneAddress *addr = NULL;
	
	if (!fr || fr->vcard) {
		ms_error("Friend is either null or already has a vcard");
		return FALSE;
	}
	
	addr = fr->uri;
	if (!addr && !name) {
		ms_error("friend doesn't have an URI and name parameter is null");
		return FALSE;
	}
	
	if (name) {
		fullName = name;
	} else {
		const char *displayName = linphone_address_get_display_name(addr);
		if (!displayName) {
807 808 809
			fullName = linphone_address_get_username(addr);
		} else {
			fullName = displayName;
810 811 812 813 814 815 816 817 818 819
		}
	}
	
	if (!fullName) {
		ms_error("Couldn't determine the name to use for the vCard");
		return FALSE;
	}
	
	vcard = linphone_vcard_new();
	linphone_vcard_set_full_name(vcard, fullName);
820
	linphone_friend_set_vcard(fr, vcard);
821 822 823
	return TRUE;
}

824 825 826 827 828 829 830 831 832 833 834 835 836 837
LinphoneFriend *linphone_friend_new_from_vcard(LinphoneVCard *vcard) {
	LinphoneAddress* linphone_address = NULL;
	LinphoneFriend *fr;
	const char *name = NULL;
	MSList *sipAddresses = NULL;

	if (vcard == NULL) {
		ms_error("Cannot create friend from null vcard");
		return NULL;
	}
	name = linphone_vcard_get_full_name(vcard);
	sipAddresses = linphone_vcard_get_sip_addresses(vcard);
	
	fr = linphone_friend_new();
838
	// Currently presence takes too much time when dealing with hundreds of friends, so I disabled it for now
839 840 841
	fr->pol = LinphoneSPDeny;
	fr->subscribe = FALSE;
	
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
	linphone_friend_set_vcard(fr, vcard);
	
	if (sipAddresses) {
		const char *sipAddress = (const char *)sipAddresses->data;
		linphone_address = linphone_address_new(sipAddress);
		if (linphone_address) {
			linphone_friend_set_address(fr, linphone_address);
			linphone_address_destroy(linphone_address);
		}
	}
	linphone_friend_set_name(fr, name);
	
	return fr;
}

int linphone_core_import_friends_from_vcard4_file(LinphoneCore *lc, const char *vcard_file) {
858
	MSList *vcards = linphone_vcard_list_from_vcard4_file(vcard_file);
859 860
	int count = 0;
	
861 862
#ifndef VCARD_ENABLED
	ms_error("vCard support wasn't enabled at compilation time");
Sylvain Berfini's avatar
Sylvain Berfini committed
863
#endif
864 865 866 867
	while (vcards != NULL && vcards->data != NULL) {
		LinphoneVCard *vcard = (LinphoneVCard *)vcards->data;
		LinphoneFriend *lf = linphone_friend_new_from_vcard(vcard);
		if (lf) {
868 869
			if (LinphoneFriendListOK == linphone_friend_list_import_friend(lc->friendlist, lf)) {
				lf->lc = lc;
870
#ifdef FRIENDS_SQL_STORAGE_ENABLED
871
				linphone_core_store_friend_in_db(lc, lf);
872
#endif
873 874
				count++;
			}
875
			linphone_friend_unref(lf);
876 877 878
		}
		vcards = ms_list_next(vcards);
	}
879
#ifndef FRIENDS_SQL_STORAGE_ENABLED
880
	linphone_core_write_friends_config(lc);
881
#endif
882 883 884
	return count;
}

885 886
void linphone_core_export_friends_as_vcard4_file(LinphoneCore *lc, const char *vcard_file) {
	FILE *file = NULL;
887
	const MSList *friends = linphone_core_get_friend_list(lc);
888 889 890 891 892 893 894
	
	file = fopen(vcard_file, "w");
	if (file == NULL) {
		ms_warning("Could not write %s ! Maybe it is read-only. Contacts will not be saved.", vcard_file);
		return;
	}
	
895 896
#ifndef VCARD_ENABLED
	ms_error("vCard support wasn't enabled at compilation time");
Sylvain Berfini's avatar
Sylvain Berfini committed
897
#endif
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
	while (friends != NULL && friends->data != NULL) {
		LinphoneFriend *lf = (LinphoneFriend *)friends->data;
		LinphoneVCard *vcard = linphone_friend_get_vcard(lf);
		if (vcard) {
			const char *vcard_text = linphone_vcard_as_vcard4_string(vcard);
			fprintf(file, "%s", vcard_text);
		} else {
			ms_warning("Couldn't export friend %s because it doesn't have a vCard attached", linphone_address_as_string(linphone_friend_get_address(lf)));
		}
		friends = ms_list_next(friends);
	}
	
	fclose(file);
}

913 914 915 916 917
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriend);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriend, belle_sip_object_t,
	(belle_sip_object_destroy_t) _linphone_friend_destroy,
	NULL, // clone
918
	_linphone_friend_marshall,
919
	FALSE
920 921 922 923 924 925 926 927 928 929 930 931
);

/*******************************************************************************
 * SQL storage related functions                                               *
 ******************************************************************************/

#ifdef FRIENDS_SQL_STORAGE_ENABLED

static void linphone_create_table(sqlite3* db) {
	char* errmsg = NULL;
	int ret;
	ret = sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS friends ("
932 933 934 935 936 937
						"id                INTEGER PRIMARY KEY AUTOINCREMENT,"
						"sip_uri           TEXT NOT NULL,"
						"subscribe_policy  INTEGER,"
						"send_subscribe    INTEGER,"
						"ref_key           TEXT,"
						"vCard             TEXT,"
938 939
						"vCard_etag        TEXT,"
						"vCard_url         TEXT,"
940
						"presence_received INTEGER"
941
						");",
942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
			0, 0, &errmsg);
	if (ret != SQLITE_OK) {
		ms_error("Error in creation: %s.\n", errmsg);
		sqlite3_free(errmsg);
	}
}

static void linphone_update_table(sqlite3* db) {

}

void linphone_core_friends_storage_init(LinphoneCore *lc) {
	int ret;
	const char *errmsg;
	sqlite3 *db;
957
	const MSList *friends = NULL;
958 959 960

	linphone_core_friends_storage_close(lc);

961
	ret = _linphone_sqlite3_open(lc->friends_db_file, &db);
962 963 964 965 966 967 968 969 970 971
	if (ret != SQLITE_OK) {
		errmsg = sqlite3_errmsg(db);
		ms_error("Error in the opening: %s.\n", errmsg);
		sqlite3_close(db);
		return;
	}

	linphone_create_table(db);
	linphone_update_table(db);
	lc->friends_db = db;
972 973 974 975 976 977 978 979 980
	
	friends = linphone_core_fetch_friends_from_db(lc);
	while (friends && friends->data) {
		LinphoneFriend *lf = friends->data;
		linphone_core_add_friend(lc, lf);
		linphone_friend_unref(lf);
		
		friends = ms_list_next(friends);
	}
981 982 983 984 985 986 987 988 989 990 991
}

void linphone_core_friends_storage_close(LinphoneCore *lc) {
	if (lc->friends_db) {
		sqlite3_close(lc->friends_db);
		lc->friends_db = NULL;
	}
}

/* DB layout:
 * | 0  | storage_id
992 993 994 995 996
 * | 1  | sip_uri
 * | 2  | subscribe_policy
 * | 3  | send_subscribe
 * | 4  | ref_key
 * | 5  | vCard
997 998 999
 * | 6  | vCard eTag
 * | 7  | vCard URL
 * | 8  | presence_received
1000 1001 1002 1003
 */
static int create_friend(void *data, int argc, char **argv, char **colName) {
	MSList **list = (MSList **)data;
	LinphoneFriend *lf = NULL;
1004
	LinphoneVCard *vcard = NULL;
1005
	unsigned int storage_id = atoi(argv[0]);
1006
	
1007
	vcard = linphone_vcard_new_from_vcard4_buffer(argv[5]);
1008 1009 1010 1011 1012
	if (vcard) {
		linphone_vcard_set_etag(vcard, argv[6]);
		linphone_vcard_set_url(vcard, argv[7]);
		lf = linphone_friend_new_from_vcard(vcard);
	}
1013
	if (!lf) {
1014
		LinphoneAddress *addr = linphone_address_new(argv[1]);
1015
		lf = linphone_friend_new();
1016
		linphone_friend_set_address(lf, addr);
1017
	}
1018 1019
	linphone_friend_set_inc_subscribe_policy(lf, atoi(argv[2]));
	linphone_friend_send_subscribe(lf, atoi(argv[3]));
1020 1021
	linphone_friend_set_ref_key(lf, ms_strdup(argv[4]));
	lf->presence_received = atoi(argv[8]);
Sylvain Berfini's avatar