lpconfig.c 35.5 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/***************************************************************************
 *            lpconfig.c
 *
 *  Thu Mar 10 11:13:44 2005
 *  Copyright  2005  Simon Morlat
 *  Email simon.morlat@linphone.org
 ****************************************************************************/

/*
 *  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.
 */

25
#define MAX_LEN 16384
aymeric's avatar
aymeric committed
26

Ghislain MARY's avatar
Ghislain MARY committed
27
#include "bctoolbox/vfs.h"
28
#include "belle-sip/object.h"
29
#include "xml2lpc.h"
aymeric's avatar
aymeric committed
30 31 32 33 34

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
Jehan Monnier's avatar
Jehan Monnier committed
35
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
36 37 38
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
39 40 41
#if _MSC_VER
#include <io.h>
#endif
Simon Morlat's avatar
Simon Morlat committed
42 43
#endif /*_WIN32_WCE*/

44
#ifdef _MSC_VER
45
#ifdef LINPHONE_WINDOWS_DESKTOP
46
#include <Shlwapi.h>
47 48
#else
#include <stdlib.h>
49
#endif
50 51 52 53
#else
#include <libgen.h>
#endif

54
#ifdef _WIN32
55 56
#define RENAME_REQUIRES_NONEXISTENT_NEW_PATH 1
#endif
aymeric's avatar
aymeric committed
57 58 59

#define lp_new0(type,n)	(type*)calloc(sizeof(type),n)

60
#include "linphone/lpconfig.h"
61
#include "lpc2xml.h"
aymeric's avatar
aymeric committed
62

63 64
#include "c-wrapper/c-wrapper.h"

aymeric's avatar
aymeric committed
65 66 67
typedef struct _LpItem{
	char *key;
	char *value;
68
	int is_comment;
69
	bool_t overwrite; // If set to true, will add overwrite=true when converted to xml
70
	bool_t skip; // If set to true, won't be dumped when converted to xml
aymeric's avatar
aymeric committed
71 72
} LpItem;

73 74 75 76 77
typedef struct _LpSectionParam{
	char *key;
	char *value;
} LpSectionParam;

aymeric's avatar
aymeric committed
78 79
typedef struct _LpSection{
	char *name;
80 81
	bctbx_list_t *items;
	bctbx_list_t *params;
82
	bool_t overwrite; // If set to true, will add overwrite=true to all items of this section when converted to xml
83
	bool_t skip; // If set to true, won't be dumped when converted to xml
aymeric's avatar
aymeric committed
84 85 86
} LpSection;

struct _LpConfig{
87
	belle_sip_object_t base;
88
	bctbx_vfs_file_t* pFile;
aymeric's avatar
aymeric committed
89
	char *filename;
90
	char *tmpfilename;
91
	char *factory_filename;
92
	bctbx_list_t *sections;
93 94
	bool_t modified;
	bool_t readonly;
95
	bctbx_vfs_t* g_bctbx_vfs;
aymeric's avatar
aymeric committed
96 97
};

98
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneConfig);
Ghislain MARY's avatar
Ghislain MARY committed
99
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneConfig);
100

101

102
char* lp_realpath(const char* file, char* name) {
103
#if defined(_WIN32) || defined(__QNX__) || defined(__ANDROID__)
104 105 106 107 108 109 110 111 112
	return ms_strdup(file);
#else
	char * output = realpath(file, name);
	char * msoutput = ms_strdup(output);
	free(output);
	return msoutput;
#endif
}

aymeric's avatar
aymeric committed
113 114
LpItem * lp_item_new(const char *key, const char *value){
	LpItem *item=lp_new0(LpItem,1);
Jehan Monnier's avatar
Jehan Monnier committed
115 116
	item->key=ortp_strdup(key);
	item->value=ortp_strdup(value);
aymeric's avatar
aymeric committed
117 118 119
	return item;
}

120 121
LpItem * lp_comment_new(const char *comment){
	LpItem *item=lp_new0(LpItem,1);
122
	char* pos = NULL;
123
	item->value=ortp_strdup(comment);
124 125 126 127

	pos=strchr(item->value,'\r');
	if (pos==NULL)
		pos=strchr(item->value,'\n');
128

129 130 131
	if(pos) {
		*pos='\0'; /*replace the '\n' */
	}
132 133 134 135
	item->is_comment=TRUE;
	return item;
}

136 137 138 139 140 141 142
LpSectionParam *lp_section_param_new(const char *key, const char *value){
	LpSectionParam *param = lp_new0(LpSectionParam, 1);
	param->key = ortp_strdup(key);
	param->value = ortp_strdup(value);
	return param;
}

aymeric's avatar
aymeric committed
143 144
LpSection *lp_section_new(const char *name){
	LpSection *sec=lp_new0(LpSection,1);
Jehan Monnier's avatar
Jehan Monnier committed
145
	sec->name=ortp_strdup(name);
aymeric's avatar
aymeric committed
146 147 148 149 150
	return sec;
}

void lp_item_destroy(void *pitem){
	LpItem *item=(LpItem*)pitem;
151
	if (item->key) ortp_free(item->key);
152
	ortp_free(item->value);
aymeric's avatar
aymeric committed
153 154 155
	free(item);
}

156 157 158 159 160 161 162
void lp_section_param_destroy(void *section_param){
	LpSectionParam *param = (LpSectionParam*)section_param;
	ortp_free(param->key);
	ortp_free(param->value);
	free(param);
}

aymeric's avatar
aymeric committed
163
void lp_section_destroy(LpSection *sec){
164
	ortp_free(sec->name);
165 166 167
	bctbx_list_for_each(sec->items,lp_item_destroy);
	bctbx_list_for_each(sec->params,lp_section_param_destroy);
	bctbx_list_free(sec->items);
aymeric's avatar
aymeric committed
168 169 170 171
	free(sec);
}

void lp_section_add_item(LpSection *sec,LpItem *item){
172
	sec->items=bctbx_list_append(sec->items,(void *)item);
aymeric's avatar
aymeric committed
173 174
}

175
void linphone_config_add_section(LpConfig *lpconfig, LpSection *section){
176
	lpconfig->sections=bctbx_list_append(lpconfig->sections,(void *)section);
aymeric's avatar
aymeric committed
177 178
}

179
void linphone_config_add_section_param(LpSection *section, LpSectionParam *param){
180
	section->params = bctbx_list_append(section->params, (void *)param);
181 182
}

183
void linphone_config_remove_section(LpConfig *lpconfig, LpSection *section){
184
	lpconfig->sections=bctbx_list_remove(lpconfig->sections,(void *)section);
aymeric's avatar
aymeric committed
185 186 187
	lp_section_destroy(section);
}

188
void lp_section_remove_item(LpSection *sec, LpItem *item){
189
	sec->items=bctbx_list_remove(sec->items,(void *)item);
190 191 192
	lp_item_destroy(item);
}

aymeric's avatar
aymeric committed
193 194 195 196 197 198 199 200
static bool_t is_first_char(const char *start, const char *pos){
	const char *p;
	for(p=start;p<pos;p++){
		if (*p!=' ') return FALSE;
	}
	return TRUE;
}

201 202 203 204 205 206 207 208
static int is_a_comment(const char *str){
	while (*str==' '){
		str++;
	}
	if (*str=='#') return 1;
	return 0;
}

209
LpSection *linphone_config_find_section(const LpConfig *lpconfig, const char *name){
210
	LpSection *sec;
211
	bctbx_list_t *elem;
212
	/*printf("Looking for section %s\n",name);*/
213
	for (elem=lpconfig->sections;elem!=NULL;elem=bctbx_list_next(elem)){
214 215 216 217 218 219 220 221 222
		sec=(LpSection*)elem->data;
		if (strcmp(sec->name,name)==0){
			/*printf("Section %s found\n",name);*/
			return sec;
		}
	}
	return NULL;
}

223
LpSectionParam *lp_section_find_param(const LpSection *sec, const char *key){
224
	bctbx_list_t *elem;
225
	LpSectionParam *param;
226
	for (elem = sec->params; elem != NULL; elem = bctbx_list_next(elem)){
227 228 229 230 231 232 233 234
		param = (LpSectionParam*)elem->data;
		if (strcmp(param->key, key) == 0) {
			return param;
		}
	}
	return NULL;
}

235
LpItem *lp_section_find_comment(const LpSection *sec, const char *comment){
236
	bctbx_list_t *elem;
237 238
	LpItem *item;
	/*printf("Looking for item %s\n",name);*/
239
	for (elem=sec->items;elem!=NULL;elem=bctbx_list_next(elem)){
240 241 242 243 244 245 246 247 248
		item=(LpItem*)elem->data;
		if (item->is_comment && strcmp(item->value,comment)==0) {
			/*printf("Item %s found\n",name);*/
			return item;
		}
	}
	return NULL;
}

249
LpItem *lp_section_find_item(const LpSection *sec, const char *name){
250
	bctbx_list_t *elem;
251 252
	LpItem *item;
	/*printf("Looking for item %s\n",name);*/
253
	for (elem=sec->items;elem!=NULL;elem=bctbx_list_next(elem)){
254
		item=(LpItem*)elem->data;
255
		if (!item->is_comment && strcmp(item->key,name)==0) {
256 257 258 259 260 261 262
			/*printf("Item %s found\n",name);*/
			return item;
		}
	}
	return NULL;
}

Wescoeur's avatar
Wescoeur committed
263
static LpSection* linphone_config_parse_line(LpConfig* lpconfig, char* line, LpSection* cur) {
264
	LpSectionParam *params = NULL;
265 266
	char *pos1,*pos2;
	int nbs;
267
	size_t size=strlen(line)+1;
268 269 270
	char *secname=reinterpret_cast<char *>(ms_malloc(size));
	char *key=reinterpret_cast<char *>(ms_malloc(size));
	char *value=reinterpret_cast<char *>(ms_malloc(size));
271
	LpItem *item;
Jehan Monnier's avatar
Jehan Monnier committed
272

273 274 275 276 277 278 279 280 281 282
	pos1=strchr(line,'[');
	if (pos1!=NULL && is_first_char(line,pos1) ){
		pos2=strchr(pos1,']');
		if (pos2!=NULL){
			secname[0]='\0';
			/* found section */
			*pos2='\0';
			nbs = sscanf(pos1+1, "%s", secname);
			if (nbs >= 1) {
				if (strlen(secname) > 0) {
283
					cur = linphone_config_find_section (lpconfig,secname);
284 285
					if (cur == NULL) {
						cur = lp_section_new(secname);
286
						linphone_config_add_section(lpconfig, cur);
287
					}
Jehan Monnier's avatar
Jehan Monnier committed
288

289 290 291 292 293 294 295 296 297 298 299
					if (pos2 > pos1 + 1 + strlen(secname)) {
						/* found at least one section param */
						pos2 = pos1 + 1 + strlen(secname) + 1; // Remove the white space after the secname
						pos1 = strchr(pos2, '=');
						while (pos1 != NULL) {
							/* for each section param */
							key[0] = '\0';
							value[0] = '\0';
							*pos1 = ' ';
							if (sscanf(pos2, "%s %s", key, value) == 2) {
								params = lp_section_param_new(key, value);
300
								linphone_config_add_section_param(cur, params);
301 302 303 304 305 306

								pos2 += strlen(key) + strlen(value) + 2; // Remove the = sign + the white space after each param
								pos1 = strchr(pos2, '=');
							} else {
								ms_warning("parse section params error !");
								pos1 = NULL;
307
							}
308
						}
aymeric's avatar
aymeric committed
309 310
					}
				}
311 312
			} else {
				ms_warning("parse error!");
aymeric's avatar
aymeric committed
313
			}
314 315
		}
	}else {
316 317 318
		if (is_a_comment(line)){
			if (cur){
				LpItem *comment=lp_comment_new(line);
319 320 321 322
				item=lp_section_find_comment(cur,comment->value);
				if (item!=NULL) {
					lp_section_remove_item(cur, item);
				}
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
				lp_section_add_item(cur,comment);
			}
		}else{
			pos1=strchr(line,'=');
			if (pos1!=NULL){
				key[0]='\0';

				*pos1='\0';
				if (sscanf(line,"%s",key)>0){

					pos1++;
					pos2=strchr(pos1,'\r');
					if (pos2==NULL)
						pos2=strchr(pos1,'\n');
					if (pos2==NULL) pos2=pos1+strlen(pos1);
					else {
						*pos2='\0'; /*replace the '\n' */
					}
					/* remove ending white spaces */
					for (; pos2>pos1 && pos2[-1]==' ';pos2--) pos2[-1]='\0';

344
					if (pos2-pos1>0){
345 346 347 348 349 350 351 352 353 354 355
						/* found a pair key,value */

						if (cur!=NULL){
							item=lp_section_find_item(cur,key);
							if (item==NULL){
								lp_section_add_item(cur,lp_item_new(key,pos1));
							}else{
								ortp_free(item->value);
								item->value=ortp_strdup(pos1);
							}
							/*ms_message("Found %s=%s",key,pos1);*/
aymeric's avatar
aymeric committed
356
						}else{
357
							ms_warning("found key,item but no sections");
aymeric's avatar
aymeric committed
358 359 360 361 362 363
						}
					}
				}
			}
		}
	}
364 365 366
	ms_free(key);
	ms_free(value);
	ms_free(secname);
367 368 369
	return cur;
}

370
void linphone_config_parse(LpConfig *lpconfig, bctbx_vfs_file_t* pFile){
371 372
	char tmp[MAX_LEN]= {'\0'};
	LpSection* current_section = NULL;
373
	int size  =0;
374
	if (pFile==NULL) return;
375
	while(( size = bctbx_file_get_nxtline(pFile, tmp, MAX_LEN)) > 0){
376
		//tmp[size] = '\0';
377
		current_section = linphone_config_parse_line(lpconfig, tmp, current_section);
378
	}
aymeric's avatar
aymeric committed
379 380
}

381 382
LpConfig * linphone_config_new(const char *filename){
	return linphone_config_new_with_factory(filename, NULL);
383 384
}

385
static void _linphone_config_init_from_buffer(LinphoneConfig *conf, const char *buffer) {
386 387 388 389 390 391 392
	LpSection* current_section = NULL;

	char* ptr = ms_strdup(buffer);
	char* strtok_storage = NULL;
	char* line = strtok_r(ptr, "\n", &strtok_storage);

	while( line != NULL ){
393
		current_section = linphone_config_parse_line(conf,line,current_section);
394 395 396 397
		line = strtok_r(NULL, "\n", &strtok_storage);
	}

	ms_free(ptr);
398
}
399

400 401 402 403 404
void _linphone_config_apply_factory_config (LpConfig *config) {
	if (config->factory_filename)
		linphone_config_read_file(config, config->factory_filename);
}

405 406 407
LpConfig * linphone_config_new_from_buffer(const char *buffer){
	LpConfig* conf = belle_sip_object_new(LinphoneConfig);
	_linphone_config_init_from_buffer(conf, buffer);
408 409 410
	return conf;
}

411
static int _linphone_config_init_from_files(LinphoneConfig *lpconfig, const char *config_filename, const char *factory_config_filename) {
Sandrine Avakian's avatar
Sandrine Avakian committed
412
	lpconfig->g_bctbx_vfs = bctbx_vfs_get_default();
413

414
	if (config_filename!=NULL){
415
		if(ortp_file_exist(config_filename) == 0) {
416 417 418 419 420 421 422 423
			lpconfig->filename=lp_realpath(config_filename, NULL);
			if(lpconfig->filename == NULL) {
				ms_error("Could not find the real path of %s: %s", config_filename, strerror(errno));
				goto fail;
			}
		} else {
			lpconfig->filename = ms_strdup(config_filename);
		}
424 425
		lpconfig->tmpfilename=ortp_strdup_printf("%s.tmp",lpconfig->filename);
		ms_message("Using (r/w) config information from %s", lpconfig->filename);
426

427
#if !defined(_WIN32)
428 429
		{
			struct stat fileStat;
430
			if ((stat(lpconfig->filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) {
431
				/* make existing configuration files non-group/world-accessible */
432
				if (chmod(lpconfig->filename, S_IRUSR | S_IWUSR) == -1) {
433 434
					ms_warning("unable to correct permissions on "
						"configuration file: %s", strerror(errno));
435 436
				}
			}
437
		}
438
#endif /*_WIN32*/
Sandrine Avakian's avatar
Sandrine Avakian committed
439

440 441
		/*open with r+ to check if we can write on it later*/
		lpconfig->pFile = bctbx_file_open(lpconfig->g_bctbx_vfs,lpconfig->filename, "r+");
442
#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH
443 444
		if (lpconfig->pFile == NULL){
			lpconfig->pFile = bctbx_file_open(lpconfig->g_bctbx_vfs,lpconfig->tmpfilename, "r+");
445
			if (lpconfig->pFile != NULL){
446 447 448 449
				ms_warning("Could not open %s but %s works, app may have crashed during last sync.",lpconfig->filename,lpconfig->tmpfilename);
			}
		}
#endif
450
		if (lpconfig->pFile != NULL){
451
			linphone_config_parse(lpconfig, lpconfig->pFile);
452
			bctbx_file_close(lpconfig->pFile);
453
			lpconfig->pFile = NULL;
454
			lpconfig->modified = FALSE;
aymeric's avatar
aymeric committed
455 456
		}
	}
457
	_linphone_config_apply_factory_config(lpconfig);
458
	return 0;
459

460
fail:
461 462 463 464 465
	return -1;
}

LpConfig *linphone_config_new_with_factory(const char *config_filename, const char *factory_config_filename) {
	LpConfig *lpconfig=belle_sip_object_new(LinphoneConfig);
466 467
	if (factory_config_filename)
		lpconfig->factory_filename = bctbx_strdup(factory_config_filename);
468 469 470 471 472 473
	if (_linphone_config_init_from_files(lpconfig, config_filename, factory_config_filename) == 0) {
		return lpconfig;
	} else {
		ms_free(lpconfig);
		return NULL;
	}
aymeric's avatar
aymeric committed
474 475
}

476
LinphoneStatus linphone_config_read_file(LpConfig *lpconfig, const char *filename){
477
	char* path = lp_realpath(filename, NULL);
478
	bctbx_vfs_file_t* pFile = bctbx_file_open(lpconfig->g_bctbx_vfs, path, "r");
479
	if (pFile != NULL){
480
		ms_message("Reading config information from %s", path);
481
		linphone_config_parse(lpconfig, pFile);
482
		bctbx_file_close(pFile);
483
		ms_free(path);
484 485
		return 0;
	}
486 487
	ms_warning("Fail to open file %s",path);
	ms_free(path);
488 489 490
	return -1;
}

491 492 493 494 495 496
static const char *xml_to_lpc_failed = "xml to lpc failed";
static const char *invalid_xml = "invalid xml";
static const char *empty_xml = "empty provisioning file";

static const char* _linphone_config_xml_convert(LpConfig *lpc, xml2lpc_context *context, int result) {
	const char* error_msg = NULL;
Erwan Croze's avatar
Erwan Croze committed
497 498 499 500 501 502 503 504 505
	if (result == 0) {
		result = xml2lpc_convert(context, lpc);
		if (result == 0) {
			// if the remote provisioning added a proxy config and none was set before, set it
			if (lp_config_has_section(lpc, "proxy_0") && lp_config_get_int(lpc, "sip", "default_proxy", -1) == -1){
				lp_config_set_int(lpc, "sip", "default_proxy", 0);
			}
			lp_config_sync(lpc);
		} else {
506
			error_msg = xml_to_lpc_failed;
Erwan Croze's avatar
Erwan Croze committed
507 508
		}
	} else {
509
		error_msg = invalid_xml;
Erwan Croze's avatar
Erwan Croze committed
510 511 512 513
	}
	return error_msg;
}

514
const char* linphone_config_load_from_xml_file(LinphoneConfig *lpc, const char *filename) {
515 516
	xml2lpc_context *context = NULL;
	char* path = lp_realpath(filename, NULL);
517
	const char* error_msg = NULL;
518 519

	if (path) {
520
		context = xml2lpc_context_new(NULL, NULL);
Erwan Croze's avatar
Erwan Croze committed
521
		error_msg = _linphone_config_xml_convert(lpc, context, xml2lpc_set_xml_file(context, path));
522
		bctbx_free(path);
Erwan Croze's avatar
Erwan Croze committed
523 524 525 526 527
	}
	if (context) xml2lpc_context_destroy(context);
	return error_msg;
}

528 529 530 531 532 533
static void xml2lpc_callback(void *ctx, xml2lpc_log_level level, const char *fmt, va_list list) {
	BctbxLogLevel bctbx_level;
	switch(level) {
		case XML2LPC_DEBUG: bctbx_level = BCTBX_LOG_DEBUG; break;
		case XML2LPC_MESSAGE: bctbx_level = BCTBX_LOG_MESSAGE;break;
		case XML2LPC_WARNING: bctbx_level = BCTBX_LOG_WARNING;break;
Wescoeur's avatar
Wescoeur committed
534
		case XML2LPC_ERROR:
Sylvain Berfini's avatar
Sylvain Berfini committed
535 536
		default:
			bctbx_level = BCTBX_LOG_ERROR;break;
537 538 539 540
	}
	bctbx_logv(BCTBX_LOG_DOMAIN, bctbx_level,fmt,list);
}

541
const char* _linphone_config_load_from_xml_string(LpConfig *lpc, const char *buffer) {
Erwan Croze's avatar
Erwan Croze committed
542
	xml2lpc_context *context = NULL;
543
	const char* error_msg = NULL;
Erwan Croze's avatar
Erwan Croze committed
544 545

	if (buffer != NULL) {
546
		context = xml2lpc_context_new(xml2lpc_callback, NULL);
Erwan Croze's avatar
Erwan Croze committed
547
		error_msg = _linphone_config_xml_convert(lpc, context, xml2lpc_set_xml_string(context, buffer));
548 549
	}else{
		error_msg = empty_xml;
550 551 552 553
	}
	if (context) xml2lpc_context_destroy(context);
	return error_msg;
}
554

555
LinphoneStatus linphone_config_load_from_xml_string(LpConfig *lpc, const char *buffer) {
556
	const char *status;
557 558 559 560 561 562
	if ((status =_linphone_config_load_from_xml_string(lpc,buffer))) {
		ms_error("%s",status);
		return -1;
	} else
		return 0;
}
563

aymeric's avatar
aymeric committed
564
void lp_item_set_value(LpItem *item, const char *value){
565 566 567 568 569
	if (item->value != value) {
		char *prev_value=item->value;
		item->value=ortp_strdup(value);
		ortp_free(prev_value);
	}
aymeric's avatar
aymeric committed
570 571 572
}


573
static void _linphone_config_uninit(LpConfig *lpconfig){
574
	if (lpconfig->filename!=NULL) ortp_free(lpconfig->filename);
575
	if (lpconfig->tmpfilename) ortp_free(lpconfig->tmpfilename);
576
	if (lpconfig->factory_filename) bctbx_free(lpconfig->factory_filename);
577 578
	bctbx_list_for_each(lpconfig->sections,(void (*)(void*))lp_section_destroy);
	bctbx_list_free(lpconfig->sections);
aymeric's avatar
aymeric committed
579 580
}

581
LpConfig *linphone_config_ref(LpConfig *lpconfig){
582
	return (LinphoneConfig *)belle_sip_object_ref(BELLE_SIP_OBJECT(lpconfig));
583 584
}

585
void linphone_config_unref(LpConfig *lpconfig){
586
	belle_sip_object_unref(BELLE_SIP_OBJECT(lpconfig));
587 588
}

589 590
void linphone_config_destroy(LpConfig *lpconfig){
	linphone_config_unref(lpconfig);
591 592
}

593
const char *linphone_config_get_section_param_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_value){
594 595
	LpSection *sec;
	LpSectionParam *param;
596
	sec = linphone_config_find_section(lpconfig, section);
597 598 599 600 601 602 603
	if (sec != NULL) {
		param = lp_section_find_param(sec, key);
		if (param != NULL) return param->value;
	}
	return default_value;
}

604
const char *linphone_config_get_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_string){
aymeric's avatar
aymeric committed
605 606
	LpSection *sec;
	LpItem *item;
607
	sec=linphone_config_find_section(lpconfig,section);
aymeric's avatar
aymeric committed
608 609 610 611 612 613 614
	if (sec!=NULL){
		item=lp_section_find_item(sec,key);
		if (item!=NULL) return item->value;
	}
	return default_string;
}

615
bctbx_list_t * linphone_config_get_string_list(const LpConfig *lpconfig, const char *section, const char *key, bctbx_list_t *default_list) {
616
	LpItem *item;
617
	LpSection *sec = linphone_config_find_section(lpconfig, section);
618 619 620
	if (sec != NULL) {
		item = lp_section_find_item(sec, key);
		if (item != NULL) {
621
			bctbx_list_t *l = NULL;
622 623 624 625 626 627 628 629
			char *str;
			char *ptr;
			str = ptr = ms_strdup(item->value);
			while (ptr != NULL) {
				char *next = strstr(ptr, ",");
				if (next != NULL) {
					*(next++) = '\0';
				}
630
				l = bctbx_list_append(l, ms_strdup(ptr));
631 632 633 634 635 636 637 638 639
				ptr = next;
			}
			ms_free(str);
			return l;
		}
	}
	return default_list;
}

640 641
bool_t linphone_config_get_range(const LpConfig *lpconfig, const char *section, const char *key, int *min, int *max, int default_min, int default_max) {
	const char *str = linphone_config_get_string(lpconfig, section, key, NULL);
642
	if (str != NULL) {
Wescoeur's avatar
Wescoeur committed
643
		const char *minusptr = strchr(str, '-');
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
		if ((minusptr == NULL) || (minusptr == str)) {
			*min = default_min;
			*max = default_max;
			return FALSE;
		}
		*min = atoi(str);
		*max = atoi(minusptr + 1);
		return TRUE;
	} else {
		*min = default_min;
		*max = default_max;
		return TRUE;
	}
}

659 660
int linphone_config_get_int(const LpConfig *lpconfig,const char *section, const char *key, int default_value){
	const char *str=linphone_config_get_string(lpconfig,section,key,NULL);
661 662
	if (str!=NULL) {
		int ret=0;
663

664 665
		if (strstr(str,"0x")==str){
			sscanf(str,"%x",&ret);
666
		}else
667
			sscanf(str,"%i",&ret);
668 669
		return ret;
	}
aymeric's avatar
aymeric committed
670 671 672
	else return default_value;
}

673 674 675 676 677 678 679 680 681 682
bool_t linphone_config_get_bool(const LpConfig *lpconfig, const char *section, const char *key, bool_t default_value) {
	const char *str = linphone_config_get_string(lpconfig, section, key, NULL);
	if (str != NULL) {
		int ret = 0;
		sscanf(str, "%i", &ret);
		return ret != 0;
	}
	return default_value;
}

683 684
int64_t linphone_config_get_int64(const LpConfig *lpconfig,const char *section, const char *key, int64_t default_value){
	const char *str=linphone_config_get_string(lpconfig,section,key,NULL);
685
	if (str!=NULL) {
686
#ifdef _WIN32
687 688 689 690 691 692 693 694
		return (int64_t)_atoi64(str);
#else
		return atoll(str);
#endif
	}
	else return default_value;
}

695 696
float linphone_config_get_float(const LpConfig *lpconfig,const char *section, const char *key, float default_value){
	const char *str=linphone_config_get_string(lpconfig,section,key,NULL);
smorlat's avatar
smorlat committed
697
	float ret=default_value;
smorlat's avatar
smorlat committed
698 699 700 701 702
	if (str==NULL) return default_value;
	sscanf(str,"%f",&ret);
	return ret;
}

703
bool_t linphone_config_get_overwrite_flag_for_entry(const LpConfig *lpconfig, const char *section, const char *key) {
704 705
	LpSection *sec;
	LpItem *item;
706
	sec = linphone_config_find_section(lpconfig, section);
707 708 709 710
	if (sec != NULL){
		item = lp_section_find_item(sec, key);
		if (item != NULL) return item->overwrite;
	}
711
	return FALSE;
712 713
}

714
bool_t linphone_config_get_overwrite_flag_for_section(const LpConfig *lpconfig, const char *section) {
715
	LpSection *sec;
716
	sec = linphone_config_find_section(lpconfig, section);
717 718 719
	if (sec != NULL){
		return sec->overwrite;
	}
720 721 722
	return FALSE;
}

723
bool_t linphone_config_get_skip_flag_for_entry(const LpConfig *lpconfig, const char *section, const char *key) {
724 725
	LpSection *sec;
	LpItem *item;
726
	sec = linphone_config_find_section(lpconfig, section);
727 728 729 730 731 732 733
	if (sec != NULL){
		item = lp_section_find_item(sec, key);
		if (item != NULL) return item->skip;
	}
	return FALSE;
}

734
bool_t linphone_config_get_skip_flag_for_section(const LpConfig *lpconfig, const char *section) {
735
	LpSection *sec;
736
	sec = linphone_config_find_section(lpconfig, section);
737 738 739 740
	if (sec != NULL){
		return sec->skip;
	}
	return FALSE;
741 742
}

743
void linphone_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value){
aymeric's avatar
aymeric committed
744
	LpItem *item;
745
	LpSection *sec=linphone_config_find_section(lpconfig,section);
aymeric's avatar
aymeric committed
746 747 748
	if (sec!=NULL){
		item=lp_section_find_item(sec,key);
		if (item!=NULL){
749 750 751 752 753 754
			if ((value != NULL) && (value[0] != '\0')) {
				if (strcmp(value, item->value) == 0) return;
				lp_item_set_value(item, value);
			} else {
				lp_section_remove_item(sec, item);
			}
aymeric's avatar
aymeric committed
755
		}else{
756
			if (value!=NULL && value[0] != '\0')
aymeric's avatar
aymeric committed
757 758
				lp_section_add_item(sec,lp_item_new(key,value));
		}
759
	}else if (value!=NULL && value[0] != '\0'){
aymeric's avatar
aymeric committed
760
		sec=lp_section_new(section);
761
		linphone_config_add_section(lpconfig,sec);
aymeric's avatar
aymeric committed
762 763
		lp_section_add_item(sec,lp_item_new(key,value));
	}
764
	lpconfig->modified = TRUE;
aymeric's avatar
aymeric committed
765 766
}

767
void linphone_config_set_string_list(LpConfig *lpconfig, const char *section, const char *key, const bctbx_list_t *value) {
768 769
	char *strvalue = NULL;
	char *tmp = NULL;
770
	const bctbx_list_t *elem;
771 772 773 774 775 776 777 778
	for (elem = value; elem != NULL; elem = elem->next) {
		if (strvalue) {
			tmp = ms_strdup_printf("%s,%s", strvalue, (const char *)elem->data);
			ms_free(strvalue);
			strvalue = tmp;
		}
		else strvalue = ms_strdup((const char *)elem->data);
	}
779
	linphone_config_set_string(lpconfig, section, key, strvalue);
780 781 782
	if (strvalue) ms_free(strvalue);
}

783
void linphone_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value) {
784 785
	char tmp[30];
	snprintf(tmp, sizeof(tmp), "%i-%i", min_value, max_value);
786
	linphone_config_set_string(lpconfig, section, key, tmp);
787 788
}

789
void linphone_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value){
aymeric's avatar
aymeric committed
790
	char tmp[30];
791
	snprintf(tmp,sizeof(tmp),"%i",value);
792
	linphone_config_set_string(lpconfig,section,key,tmp);
793 794
}

795 796 797 798 799 800 801 802
void linphone_config_set_bool(LpConfig *lpconfig, const char *section, const char *key, bool_t value) {
	if (value) {
		linphone_config_set_int(lpconfig, section, key, 1);
	} else {
		linphone_config_set_int(lpconfig, section, key, 0);
	}
}

803
void linphone_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value){
804 805
	char tmp[30];
	snprintf(tmp,sizeof(tmp),"0x%x",value);
806
	linphone_config_set_string(lpconfig,section,key,tmp);
807 808
}

809
void linphone_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value){
810 811
	char tmp[30];
	snprintf(tmp,sizeof(tmp),"%lli",(long long)value);
812
	linphone_config_set_string(lpconfig,section,key,tmp);
813 814 815
}


816
void linphone_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value){
817 818
	char tmp[30];
	snprintf(tmp,sizeof(tmp),"%f",value);
819
	linphone_config_set_string(lpconfig,section,key,tmp);
aymeric's avatar
aymeric committed
820 821
}

822
void linphone_config_set_overwrite_flag_for_entry(LpConfig *lpconfig, const char *section, const char *key, bool_t value) {
823 824
	LpSection *sec;
	LpItem *item;
825
	sec = linphone_config_find_section(lpconfig, section);
826
	if (sec != NULL) {
827 828 829 830 831
		item = lp_section_find_item(sec, key);
		if (item != NULL) item->overwrite = value;
	}
}

832
void linphone_config_set_overwrite_flag_for_section(LpConfig *lpconfig, const char *section, bool_t value) {
833
	LpSection *sec;
834
	sec = linphone_config_find_section(lpconfig, section);
835 836 837 838 839
	if (sec != NULL) {
		sec->overwrite = value;
	}
}

840
void linphone_config_set_skip_flag_for_entry(LpConfig *lpconfig, const char *section, const char *key, bool_t value) {
841 842
	LpSection *sec;
	LpItem *item;
843
	sec = linphone_config_find_section(lpconfig, section);
844 845 846 847 848 849
	if (sec != NULL) {
		item = lp_section_find_item(sec, key);
		if (item != NULL) item->skip = value;
	}
}

850
void linphone_config_set_skip_flag_for_section(LpConfig *lpconfig, const char *section, bool_t value) {
851
	LpSection *sec;
852
	sec = linphone_config_find_section(lpconfig, section);
853 854 855 856 857
	if (sec != NULL) {
		sec->skip = value;
	}
}

858
void lp_item_write(LpItem *item, LpConfig *lpconfig){
859
	int ret =-1 ;
860
	if (item->is_comment){
861
		ret = (int)bctbx_file_fprintf(lpconfig->pFile, 0, "%s\n",item->value);
862

863 864
	}
	else if (item->value && item->value[0] != '\0' ){
865
		ret = (int)bctbx_file_fprintf(lpconfig->pFile, 0, "%s=%s\n",item->key,item->value);
866
	}
867

868 869 870
	else {
		ms_warning("Not writing item %s to file, it is empty", item->key);
	}
871 872
	if (ret < 0){
		ms_error("lp_item_write : not writing item to file" );
873
	}
aymeric's avatar
aymeric committed
874 875
}

876
void lp_section_param_write(LpSectionParam *param, LpConfig *lpconfig){
877
	if( param->value && param->value[0] != '\0') {
878
		bctbx_file_fprintf(lpconfig->pFile, 0, " %s=%s", param->key, param->value);
879

880 881 882
	} else {
		ms_warning("Not writing param %s to file, it is empty", param->key);
	}
883 884
}

885
void lp_section_write(LpSection *sec,LpConfig *lpconfig){
aymeric's avatar
aymeric committed
886

887
	if (bctbx_file_fprintf(lpconfig->pFile, 0, "[%s",sec->name) < 0) ms_error("lp_section_write : write error on %s", sec->name);
888
	bctbx_list_for_each2(sec->params, (void (*)(void*, void*))lp_section_param_write, (void *)lpconfig);
889

890
	if (bctbx_file_fprintf(lpconfig->pFile, 0, "]\n")< 0) ms_error("lp_section_write : write error ");
891
	bctbx_list_for_each2(sec->items, (void (*)(void*, void*))lp_item_write, (void *)lpconfig);
892

893
	if (bctbx_file_fprintf(lpconfig->pFile, 0, "\n")< 0) ms_error("lp_section_write : write error");
894

895 896
}

897
LinphoneStatus linphone_config_sync(LpConfig *lpconfig){
Sandrine Avakian's avatar
Sandrine Avakian committed
898
	bctbx_vfs_file_t *pFile = NULL;
aymeric's avatar
aymeric committed
899
	if (lpconfig->filename==NULL) return -1;
900
	if (lpconfig->readonly) return 0;
901

902
#ifndef _WIN32
aymeric's avatar
aymeric committed
903 904 905
	/* don't create group/world-accessible files */
	(void) umask(S_IRWXG | S_IRWXO);
#endif
906
	pFile  = bctbx_file_open(lpconfig->g_bctbx_vfs,lpconfig->tmpfilename, "w");
907
	lpconfig->pFile = pFile;
908
	if (pFile == NULL){
909
		ms_warning("Could not write %s ! Maybe it is read-only. Configuration will not be saved.",lpconfig->filename);
910
		lpconfig->readonly = TRUE;
aymeric's avatar
aymeric committed
911 912
		return -1;
	}
913

914
	bctbx_list_for_each2(lpconfig->sections,(void (*)(void *,void*))lp_section_write,(void *)lpconfig);
915
	bctbx_file_close(pFile);
916

917 918 919 920 921 922 923
#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH
	/* On windows, rename() does not accept that the newpath is an existing file, while it is accepted on Unix.
	 * As a result, we are forced to first delete the linphonerc file, and then rename.*/
	if (remove(lpconfig->filename)!=0){
		ms_error("Cannot remove %s: %s",lpconfig->filename, strerror(errno));
	}
#endif
924 925 926
	if (rename(lpconfig->tmpfilename,lpconfig->filename)!=0){
		ms_error("Cannot rename %s into %s: %s",lpconfig->tmpfilename,lpconfig->filename,strerror(errno));
	}
927
	lpconfig->modified = FALSE;
aymeric's avatar
aymeric committed
928 929 930
	return 0;
}

931 932
int linphone_config_has_section(const LpConfig *lpconfig, const char *section){
	if (linphone_config_find_section(lpconfig,section)!=NULL) return 1;
aymeric's avatar
aymeric committed
933 934 935
	return 0;
}

936
void linphone_config_for_each_section(const LpConfig *lpconfig, void (*callback)(const char *section, void *ctx), void *ctx) {
937
	LpSection *sec;
938 939
	bctbx_list_t *elem;
	for (elem=lpconfig->sections;elem!=NULL;elem=bctbx_list_next(elem)){
940 941 942 943 944
		sec=(LpSection*)elem->data;
		callback(sec->name, ctx);
	}
}

945
void linphone_config_for_each_entry(const LpConfig *lpconfig, const char *section, void (*callback)(const char *entry, void *ctx), void *ctx) {
946
	LpItem *item;
947
	bctbx_list_t *elem;
948
	LpSection *sec=linphone_config_find_section(lpconfig,section);
949
	if (sec!=NULL){
950
		for (elem=sec->items;elem!=NULL;elem=bctbx_list_next(elem)){
951
			item=(LpItem*)elem->data;
952 953
			if (!item->is_comment)
				callback(item->key, ctx);
954 955 956 957
		}
	}
}

958 959
void linphone_config_clean_section(LpConfig *lpconfig, const char *section){
	LpSection *sec=linphone_config_find_section(lpconfig,section);
aymeric's avatar
aymeric committed
960
	if (sec!=NULL){
961
		linphone_config_remove_section(lpconfig,sec);
aymeric's avatar
aymeric committed
962
	}
963
	lpconfig->modified = TRUE;
964 965
}

966 967
bool_t linphone_config_needs_commit(const LpConfig *lpconfig){
	return lpconfig->modified;
aymeric's avatar
aymeric committed
968
}
969 970 971

static const char *DEFAULT_VALUES_SUFFIX = "_default_values";

972
int linphone_config_get_default_int(const LpConfig *lpconfig, const char *section, const char *key, int default_value) {
973 974 975
	char default_section[MAX_LEN];
	strcpy(default_section, section);
	strcat(default_section, DEFAULT_VALUES_SUFFIX);
976

977
	return linphone_config_get_int(lpconfig, default_section, key, default_value);
978 979
}

980
int64_t linphone_config_get_default_int64(const LpConfig *lpconfig, const char *section, const char *key, int64_t default_value) {
981 982 983
	char default_section[MAX_LEN];
	strcpy(default_section, section);
	strcat(default_section, DEFAULT_VALUES_SUFFIX);
984

985
	return linphone_config_get_int64(lpconfig, default_section, key, default_value);
986 987
}

988
float linphone_config_get_default_float(const LpConfig *lpconfig, const char *section, const char *key, float default_value) {
989 990 991
	char default_section[MAX_LEN];
	strcpy(default_section, section);
	strcat(default_section, DEFAULT_VALUES_SUFFIX);
992

993
	return linphone_config_get_float(lpconfig, default_section, key, default_value);
994 995
}

996
const char* linphone_config_get_default_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_value) {
997 998 999
	char default_section[MAX_LEN];
	strcpy(default_section, section);
	strcat(default_section, DEFAULT_VALUES_SUFFIX);
1000

1001
	return linphone_config_get_string(lpconfig, default_section, key, default_value);
1002
}
1003

1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
/*
 * WARNING: this function is very dangerous.
 * Read carefuly the folowing notices:
 * 1. The 'path' parameter may be modify by
 *    the function. Be care to keep a copy of
 *    the original string.
 * 2. The return pointer may points on a part of
 *    'path'. So, be care to not free the string
 *    pointed by 'path' before the last used of
 *    the returned pointer.
 * 3. Do not feed it after midnight
 */
1016
static const char *_linphone_config_dirname(char *path) {
1017
#ifdef _MSC_VER
1018 1019 1020 1021
	char drive[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fname[_MAX_FNAME];
	char ext[_MAX_EXT];
1022
	static char dirname[_MAX_DRIVE + _MAX_DIR];
1023
	_splitpath(path, drive, dir, fname, ext);
1024 1025
	snprintf(dirname, sizeof(dirname), "%s%s", drive, dir);
	return dirname;
1026
#else
1027
	return dirname(path);
1028 1029 1030
#endif
}

1031
bool_t linphone_config_relative_file_exists(const LpConfig *lpconfig, const char *filename) {
1032
	bctbx_vfs_file_t *pFile;
1033
	if (lpconfig->filename == NULL) {
1034
		return FALSE;
1035
	} else {
1036
		char *conf_path = ms_strdup(lpconfig->filename);
1037
		const char *dir = _linphone_config_dirname(conf_path);
1038
		char *filepath = ms_strdup_printf("%s/%s", dir, filename);
1039
		char *realfilepath = lp_realpath(filepath, NULL);
1040

1041
		ms_free(conf_path);
1042
		ms_free(filepath);
1043

1044
		if(realfilepath == NULL) return FALSE;
1045

1046
		pFile = bctbx_file_open(lpconfig->g_bctbx_vfs,realfilepath, "r");
1047
		ms_free(realfilepath);
1048
		if (pFile != NULL) {
1049
			bctbx_file_close(pFile);
1050
		}
1051
		return pFile != NULL;
1052 1053 1054
	}
}

1055
void linphone_config_write_relative_file(const LpConfig *lpconfig, const char *filename, const char *data) {
1056 1057 1058 1059
	char *dup_config_file = NULL;
	const char *dir = NULL;
	char *filepath = NULL;
	char *realfilepath = NULL;
1060
	bctbx_vfs_file_t *pFile;
1061

1062
	if (lpconfig->filename == NULL) return;
1063

1064
	if(strlen(data) == 0) {
1065
		ms_warning("%s has not been created because there is no data to write", filename);
1066 1067
		return;
	}
1068

1069
	dup_config_file = ms_strdup(lpconfig->filename);
1070
	dir = _linphone_config_dirname(dup_config_file);
1071 1072 1073 1074 1075 1076
	filepath = ms_strdup_printf("%s/%s", dir, filename);
	realfilepath = lp_realpath(filepath, NULL);
	if(realfilepath == NULL) {
		ms_error("Could not resolv %s: %s", filepath, strerror(errno));
		goto end;
	}
1077

1078 1079
	pFile = bctbx_file_open(lpconfig->g_bctbx_vfs,realfilepath, "w");
	if(pFile == NULL) {
1080 1081
		ms_error("Could not open %s for write", realfilepath);
		goto end;
1082
	}
1083
	bctbx_file_fprintf(pFile, 0, "%s",data);
1084
	bctbx_file_close(pFile);
1085

1086 1087 1088 1089
end:
	ms_free(dup_config_file);
	ms_free(filepath);
	if(realfilepath) ms_free(realfilepath);
1090 1091
}

1092
LinphoneStatus linphone_config_read_relative_file(const LpConfig *lpconfig, const char *filename, char *data, size_t max_length) {
1093 1094 1095
	char *dup_config_file = NULL;
	const char *dir = NULL;
	char *filepath = NULL;
1096
	bctbx_vfs_file_t* pFile = NULL;
1097

1098
	char* realfilepath = NULL;
1099

1100
	if (lpconfig->filename == NULL) return -1;