accountmanager.c 14.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 /*
 tester - liblinphone test suite
 Copyright (C) 2013  Belledonne Communications SARL

 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

19
#include <belle-sip/belle-sip.h>
20
#include <ctype.h>
21
#include "liblinphone_tester.h"
22
#include "tester_utils.h"
23 24 25 26 27

struct _Account{
	LinphoneAddress *identity;
	LinphoneAddress *modified_identity;
	char *password;
28
	int registered;
29
	int done;
30
	int created;
31
	char *phone_alias;
32
	char *uuid;
33 34 35 36
};

typedef struct _Account Account;

37
static Account *account_new(LinphoneAddress *identity, const char *unique_id){
38 39
	char *modified_username;
	Account *obj=ms_new0(Account,1);
40

41
	// we need to inhibit leak detector because the two LinphoneAddress will remain behond the scope of the test being run
42 43 44
	belle_sip_object_inhibit_leak_detector(TRUE);
	obj->identity=linphone_address_clone(identity);
	obj->password=sal_get_random_token(8);
45
	obj->phone_alias = NULL;
46 47 48 49 50 51 52 53 54
	obj->modified_identity=linphone_address_clone(identity);
	modified_username=ms_strdup_printf("%s_%s",linphone_address_get_username(identity), unique_id);
	linphone_address_set_username(obj->modified_identity, modified_username);
	ms_free(modified_username);
	belle_sip_object_inhibit_leak_detector(FALSE);
	return obj;
};

void account_destroy(Account *obj){
55
	if (obj->uuid) bctbx_free(obj->uuid);
56 57 58 59 60 61 62 63
	linphone_address_unref(obj->identity);
	linphone_address_unref(obj->modified_identity);
	ms_free(obj->password);
	ms_free(obj);
}

struct _AccountManager{
	char *unique_id;
64
	bctbx_list_t *accounts;
65 66 67 68 69 70
};

typedef struct _AccountManager AccountManager;

static AccountManager *the_am=NULL;

71 72 73 74 75 76 77 78 79 80 81
static void account_manager_generate_unique_id(AccountManager * am) {
	const int tokenLength = 6;
	if (am->unique_id)
		ms_free(am->unique_id);
	am->unique_id=sal_get_random_token(tokenLength);
	
	ms_message("Using lowercase random token for test username.");
	for (int i=0; i<tokenLength; i++) {
		am->unique_id[i] = tolower(the_am->unique_id[i]);
	}
}
82 83 84
AccountManager *account_manager_get(void){
	if (the_am==NULL){
		the_am=ms_new0(AccountManager,1);
85
		account_manager_generate_unique_id(the_am);
86 87 88 89 90 91 92
	}
	return the_am;
}

void account_manager_destroy(void){
	if (the_am){
		ms_free(the_am->unique_id);
93
		bctbx_list_free_with_data(the_am->accounts,(void(*)(void*))account_destroy);
94 95 96
		ms_free(the_am);
	}
	the_am=NULL;
97
	ms_message("Test account manager destroyed.");
98 99 100
}

Account *account_manager_get_account(AccountManager *m, const LinphoneAddress *identity){
101
	bctbx_list_t *it;
102

103 104 105 106 107 108 109 110 111
	for(it=m->accounts;it!=NULL;it=it->next){
		Account *a=(Account*)it->data;
		if (linphone_address_weak_equal(a->identity,identity)){
			return a;
		}
	}
	return NULL;
}

Ghislain MARY's avatar
Ghislain MARY committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125
LinphoneAddress *account_manager_get_identity_with_modified_identity(const LinphoneAddress *modified_identity){
	AccountManager *m = account_manager_get();
	bctbx_list_t *it;

	for(it=m->accounts;it!=NULL;it=it->next){
		Account *a=(Account*)it->data;
		if (linphone_address_weak_equal(a->modified_identity,modified_identity)){
			return a->identity;
		}
	}
	return NULL;
}


126 127 128
static void account_created_on_server_cb(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const char *info){
	Account *account=(Account*)linphone_core_get_user_data(lc);
	switch(state){
129
		case LinphoneRegistrationOk: {
130
			char * phrase = sal_op_get_error_info(linphone_proxy_config_get_sal_op(cfg))->full_string;
131 132 133 134 135 136 137
			if (phrase && strcasecmp("Test account created", phrase) == 0) {
				account->created=1;
			} else {
				account->registered=1;
			}
			break;
		}
138 139 140 141 142 143 144 145
		case LinphoneRegistrationCleared:
			account->done=1;
		break;
		default:
		break;
	}
}

146
void account_create_on_server(Account *account, const LinphoneProxyConfig *refcfg, const char* phone_alias){
147 148 149 150 151 152
	LinphoneCore *lc;
	LinphoneAddress *tmp_identity=linphone_address_clone(account->modified_identity);
	LinphoneProxyConfig *cfg;
	LinphoneAuthInfo *ai;
	char *tmp;
	LinphoneAddress *server_addr;
153
	LinphoneSipTransports tr;
154
	LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get());
155

156 157 158
	linphone_core_cbs_set_registration_state_changed(cbs, account_created_on_server_cb);
	lc = configure_lc_from(cbs, bc_tester_get_resource_dir_prefix(), NULL, account);
	linphone_core_cbs_unref(cbs);
159 160 161 162
	tr.udp_port=LC_SIP_TRANSPORT_RANDOM;
	tr.tcp_port=LC_SIP_TRANSPORT_RANDOM;
	tr.tls_port=LC_SIP_TRANSPORT_RANDOM;
	linphone_core_set_sip_transports(lc,&tr);
163

164
	cfg=linphone_core_create_proxy_config(lc);
165
	linphone_address_set_secure(tmp_identity, FALSE);
166 167
	linphone_address_set_password(tmp_identity,account->password);
	linphone_address_set_header(tmp_identity,"X-Create-Account","yes");
168
	if (phone_alias) linphone_address_set_header(tmp_identity, "X-Phone-Alias", phone_alias);
169 170 171 172
	tmp=linphone_address_as_string(tmp_identity);
	linphone_proxy_config_set_identity(cfg,tmp);
	ms_free(tmp);
	linphone_address_unref(tmp_identity);
173

174
	server_addr=linphone_address_new(linphone_proxy_config_get_server_addr(refcfg));
175
	linphone_address_set_secure(server_addr, FALSE);
176
	linphone_address_set_transport(server_addr,LinphoneTransportTcp); // use tcp for account creation, we may not have certificates configured at this stage
177
	linphone_address_set_port(server_addr,0);
178 179 180 181
	tmp=linphone_address_as_string(server_addr);
	linphone_proxy_config_set_server_addr(cfg,tmp);
	ms_free(tmp);
	linphone_address_unref(server_addr);
182
	linphone_proxy_config_set_expires(cfg,3*3600); // accounts are valid 3 hours
183

184
	linphone_core_add_proxy_config(lc,cfg);
185 186 187
	/*wait 25 seconds, since the DNS SRV resolution may take a while - and
	especially if router does NOT support DNS SRV and we have to wait its timeout*/
	if (wait_for_until(lc,NULL,&account->created,1,25000)==FALSE){
188 189 190
		ms_fatal("Account for %s could not be created on server.", linphone_proxy_config_get_identity(refcfg));
	}
	linphone_proxy_config_edit(cfg);
191 192 193
	tmp_identity=linphone_address_clone(account->modified_identity);
	linphone_address_set_secure(tmp_identity, FALSE);
	tmp=linphone_address_as_string(tmp_identity);
194
	linphone_proxy_config_set_identity(cfg,tmp); // remove the X-Create-Account header
195
	linphone_address_unref(tmp_identity);
196 197
	ms_free(tmp);
	linphone_proxy_config_done(cfg);
198

199 200 201 202
	ai=linphone_auth_info_new(linphone_address_get_username(account->modified_identity),
				NULL,
				account->password,NULL,NULL,linphone_address_get_domain(account->modified_identity));
	linphone_core_add_auth_info(lc,ai);
203
	linphone_auth_info_unref(ai);
204

205
	if (wait_for_until(lc,NULL,&account->registered,1,3000)==FALSE){
206 207 208 209 210 211 212
		ms_fatal("Account for %s is not working on server.", linphone_proxy_config_get_identity(refcfg));
	}
	linphone_core_remove_proxy_config(lc,cfg);
	linphone_proxy_config_unref(cfg);
	if (wait_for_until(lc,NULL,&account->done,1,3000)==FALSE){
		ms_error("Account creation could not clean the registration context.");
	}
213
	linphone_core_unref(lc);
214 215
}

216
static void account_created_in_db_cb(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status, const char *resp){
217 218
	switch(status){
		case LinphoneAccountCreatorStatusAccountCreated:
219
			creator->account_created = TRUE;
220 221
			break;
		default:
222
			ms_fatal("Account not created on DB for %s.", linphone_account_creator_get_username(creator));
223
			creator->account_created = FALSE;
224 225 226 227 228 229 230
			break;
	}
}

static void get_confirmation_key_cb(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status, const char *resp){
	switch(status){
		case LinphoneAccountCreatorStatusRequestOk:
231
			creator->confirmation_key_received = TRUE;
232 233
			break;
		default:
234
			ms_warning("Confirmation key not received for %s.", linphone_account_creator_get_username(creator));
235
			creator->confirmation_key_received = FALSE;
236 237 238 239 240 241 242 243
			break;
	}
}

static void account_activated_cb(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status, const char *resp){
	switch(status){
		case LinphoneAccountCreatorStatusAccountActivated:
		case LinphoneAccountCreatorStatusAccountAlreadyActivated:
244
			creator->account_activated = TRUE;
245 246
			break;
		default:
247
			ms_message("Account not activated for %s.", linphone_account_creator_get_username(creator));
248
			creator->account_activated = FALSE;
249 250 251 252
			break;
	}
}

253
void account_create_in_db(Account *account, LinphoneProxyConfig *cfg, const char *xmlrpc_url){
254 255
	LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get());
	linphone_core_cbs_set_registration_state_changed(cbs, account_created_on_server_cb);
256
	LinphoneCore *lc = configure_lc_from(cbs, bc_tester_get_resource_dir_prefix(), NULL, account);
257 258
	linphone_core_cbs_unref(cbs);
	LinphoneSipTransports tr;
259 260 261 262
	tr.udp_port = LC_SIP_TRANSPORT_RANDOM;
	tr.tcp_port = LC_SIP_TRANSPORT_RANDOM;
	tr.tls_port = LC_SIP_TRANSPORT_RANDOM;
	linphone_core_set_sip_transports(lc, &tr);
263 264 265 266 267

	LinphoneAccountCreator *creator = linphone_account_creator_new(lc, xmlrpc_url);
	LinphoneAccountCreatorCbs *creator_cbs = linphone_account_creator_get_callbacks(creator);

	// TODO workaround
Matthieu Tanon's avatar
Matthieu Tanon committed
268
	LinphoneProxyConfig *default_cfg = linphone_core_get_default_proxy_config(lc);
269 270 271 272 273 274
	linphone_account_creator_set_proxy_config(creator, cfg);

	linphone_account_creator_service_set_user_data(linphone_account_creator_get_service(creator), (void*)LinphoneAccountCreatorStatusAccountCreated);

	const char *username = linphone_address_get_username(account->modified_identity);
	const char *password = account->password;
275
	const char *domain = linphone_proxy_config_get_domain(cfg);
276

277
	char *email = bctbx_strdup_printf("%s@%s", username, domain);
278

279
	// create account
280
	linphone_account_creator_cbs_set_create_account(creator_cbs, account_created_in_db_cb);
281 282 283
	linphone_account_creator_set_username(creator, username);
	linphone_account_creator_set_password(creator, password);
	linphone_account_creator_set_domain(creator, domain);
284 285 286 287 288
	linphone_account_creator_set_email(creator, email);

	if (account->phone_alias) {
		linphone_account_creator_set_phone_number(creator, account->phone_alias, "33");
	}
289

290
	linphone_account_creator_create_account(creator);
291

292
	if (wait_for_until(lc, NULL, (int*)&creator->account_created, TRUE, 1000) == FALSE)
293
		ms_warning("Could not create account %s on db", linphone_proxy_config_get_identity(cfg));
294

295 296
	LinphoneAuthInfo *ai = linphone_auth_info_new(username, NULL, password, NULL, domain, domain);
	linphone_core_add_auth_info(lc, ai);
297 298
	linphone_auth_info_unref(ai);

299
	// get confirmation key
300 301 302
	linphone_account_creator_cbs_set_get_confirmation_key(creator_cbs, get_confirmation_key_cb);
	linphone_account_creator_get_confirmation_key(creator);

303
	if (wait_for_until(lc, NULL, (int*)&creator->confirmation_key_received, TRUE, 1000) == FALSE)
304
		ms_warning("Could not get confirmation key for account %s", linphone_proxy_config_get_identity(cfg));
305

306
	// activate account
307
	linphone_account_creator_cbs_set_activate_account(creator_cbs, account_activated_cb);
jehan's avatar
jehan committed
308 309 310 311
	if (linphone_account_creator_get_phone_number(creator))
		linphone_account_creator_activate_account_linphone(creator);
	else
		linphone_account_creator_activate_email_account_linphone(creator);
jehan's avatar
jehan committed
312
	
313
	if (wait_for_until(lc, NULL, (int*)&creator->account_activated, TRUE, 1000))
314
		ms_warning("Could not activate account %s", linphone_proxy_config_get_identity(cfg));
315 316

	// TODO workaround
Matthieu Tanon's avatar
Matthieu Tanon committed
317
	linphone_account_creator_set_proxy_config(creator, default_cfg);
318

319
	bctbx_free(email);
320 321 322 323
	linphone_account_creator_unref(creator);
	linphone_core_unref(lc);
}

324
static LinphoneAddress *account_manager_check_account(AccountManager *m, LinphoneProxyConfig *cfg, LinphoneCoreManager *cm){
325 326 327 328
	LinphoneCore *lc = linphone_proxy_config_get_core(cfg);
	const char *identity = linphone_proxy_config_get_identity(cfg);
	LinphoneAddress *id_addr = linphone_address_new(identity);
	Account *account = account_manager_get_account(m, id_addr);
329
	LinphoneAuthInfo *ai;
330 331 332 333 334 335 336
	bool_t create_account = FALSE;
	const LinphoneAuthInfo *original_ai = linphone_core_find_auth_info(
		lc,
		NULL,
		linphone_address_get_username(id_addr),
		linphone_address_get_domain(id_addr)
	);
337
	const char *phone_alias = cm->phone_alias;
338

339
	if (!account || (phone_alias && (!account->phone_alias || strcmp(phone_alias, account->phone_alias) != 0))){
340
		if (account) {
341
			m->accounts = bctbx_list_remove(m->accounts, account);
342 343
			account_destroy(account);
		}
344
		account_manager_generate_unique_id(m); //change unique id to make sure we really create a new one
345 346 347 348 349
		account = account_new(id_addr, m->unique_id);
		account->phone_alias = ms_strdup(phone_alias);
		ms_message("No account for %s exists, going to create one.", identity);
		create_account = TRUE;
		m->accounts = bctbx_list_append(m->accounts, account);
350
	}
351
	// modify the username of the identity of the proxy config
352
	linphone_address_set_username(id_addr, linphone_address_get_username(account->modified_identity));
353
	linphone_proxy_config_set_identity_address(cfg, id_addr);
354

355
	// create account using account creator and flexisip-account-manager
356
	if (create_account){
357 358 359
		if (liblinphonetester_no_account_creator) {
			account_create_on_server(account, cfg, phone_alias);
		} else {
360 361 362 363 364 365
			const char *xmlrpc_url = linphone_config_get_string(
				linphone_core_get_config(lc),
				"misc",
				"xmlrpc_server_url",
				"http://subscribe.example.org/flexisip-account-manager/xmlrpc.php"
			);
366
			account_create_in_db(account, cfg, xmlrpc_url);
367
		}
368
	}
369

370
	if (liblinphone_tester_keep_uuid) {
371
		// create and/or set uuid
372 373
		if (account->uuid == NULL) {
			char tmp[64];
374
			sal_create_uuid(linphone_core_get_sal(cm->lc), tmp, sizeof(tmp));
375 376
			account->uuid = bctbx_strdup(tmp);
		}
377
		sal_set_uuid(linphone_core_get_sal(cm->lc), account->uuid);
378 379
	}

380
	// remove previous auth info to avoid mismatching
381 382 383
	if (original_ai)
		linphone_core_remove_auth_info(lc,original_ai);

384 385 386 387 388 389 390
	ai = linphone_auth_info_new(
		linphone_address_get_username(account->modified_identity),
		NULL,
		account->password,
		NULL,
		linphone_address_get_domain(account->modified_identity),
		linphone_address_get_domain(account->modified_identity) // realm = domain
391
	);
392
	linphone_core_add_auth_info(lc, ai);
393
	linphone_auth_info_unref(ai);
394

395 396 397 398
	linphone_address_unref(id_addr);
	return account->modified_identity;
}

399
void linphone_core_manager_check_accounts(LinphoneCoreManager *m){
400
	const bctbx_list_t *it;
401
	AccountManager *am = account_manager_get();
Ronan's avatar
Ronan committed
402 403
	unsigned int logmask = linphone_core_get_log_level_mask();

404
	if (!liblinphonetester_show_account_manager_logs) linphone_core_set_log_level_mask(ORTP_ERROR|ORTP_FATAL);
405 406 407
	for(it = linphone_core_get_proxy_config_list(m->lc); it != NULL; it = it->next){
		LinphoneProxyConfig *cfg = (LinphoneProxyConfig *)it->data;
		account_manager_check_account(am, cfg, m);
408
	}
409
	if (!liblinphonetester_show_account_manager_logs) linphone_core_set_log_level_mask(logmask);
410
}