LinphoneManager.m 87.8 KB
Newer Older
DanmeiChen's avatar
DanmeiChen committed
1
/*
DanmeiChen's avatar
DanmeiChen committed
2
 * Copyright (c) 2010-2020 Belledonne Communications SARL.
jehan's avatar
jehan committed
3
 *
DanmeiChen's avatar
DanmeiChen committed
4
 * This file is part of linphone-iphone
jehan's avatar
jehan committed
5
 *
DanmeiChen's avatar
DanmeiChen committed
6 7 8 9
 * 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 3 of the License, or
 * (at your option) any later version.
10
 *
DanmeiChen's avatar
DanmeiChen committed
11 12 13 14
 * 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.
15
 *
DanmeiChen's avatar
DanmeiChen committed
16 17
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18
 */
jehan's avatar
jehan committed
19 20 21 22

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
23 24
#include <sys/sysctl.h>

jehan's avatar
jehan committed
25
#import <AudioToolbox/AudioToolbox.h>
Yann Diorcet's avatar
Yann Diorcet committed
26
#import <CoreTelephony/CTCallCenter.h>
27
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
28 29
#import <SystemConfiguration/CaptiveNetwork.h>
#import <SystemConfiguration/SystemConfiguration.h>
30 31

#import "LinphoneCoreSettingsStore.h"
32
#import "LinphoneAppDelegate.h"
33 34
#import "LinphoneManager.h"
#import "Utils/AudioHelper.h"
35
#import "Utils/FileTransferDelegate.h"
Sylvain Berfini's avatar
Sylvain Berfini committed
36

37
#include "linphone/factory.h"
jehan's avatar
jehan committed
38
#include "linphone/linphonecore_utils.h"
39
#include "linphone/lpconfig.h"
40
#include "mediastreamer2/mscommon.h"
41

42 43
#import "LinphoneIOSVersion.h"

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
44
#import "Utils.h"
45
#import "PhoneMainView.h"
46 47
#import "ChatsListView.h"
#import "ChatConversationView.h"
48
#import <UserNotifications/UserNotifications.h>
49

Yann Diorcet's avatar
Yann Diorcet committed
50
#define LINPHONE_LOGS_MAX_ENTRY 5000
Yann Diorcet's avatar
Yann Diorcet committed
51

52 53
static LinphoneCore *theLinphoneCore = nil;
static LinphoneManager *theLinphoneManager = nil;
54

55
NSString *const LINPHONERC_APPLICATION_KEY = @"app";
Yann Diorcet's avatar
Yann Diorcet committed
56

Yann Diorcet's avatar
Yann Diorcet committed
57
NSString *const kLinphoneCoreUpdate = @"LinphoneCoreUpdate";
Yann Diorcet's avatar
Yann Diorcet committed
58
NSString *const kLinphoneDisplayStatusUpdate = @"LinphoneDisplayStatusUpdate";
59
NSString *const kLinphoneMessageReceived = @"LinphoneMessageReceived";
60
NSString *const kLinphoneTextComposeEvent = @"LinphoneTextComposeStarted";
61 62 63 64
NSString *const kLinphoneCallUpdate = @"LinphoneCallUpdate";
NSString *const kLinphoneRegistrationUpdate = @"LinphoneRegistrationUpdate";
NSString *const kLinphoneAddressBookUpdate = @"LinphoneAddressBookUpdate";
NSString *const kLinphoneMainViewChange = @"LinphoneMainViewChange";
Yann Diorcet's avatar
Yann Diorcet committed
65 66
NSString *const kLinphoneLogsUpdate = @"LinphoneLogsUpdate";
NSString *const kLinphoneSettingsUpdate = @"LinphoneSettingsUpdate";
67
NSString *const kLinphoneBluetoothAvailabilityUpdate = @"LinphoneBluetoothAvailabilityUpdate";
68 69
NSString *const kLinphoneConfiguringStateUpdate = @"LinphoneConfiguringStateUpdate";
NSString *const kLinphoneGlobalStateUpdate = @"LinphoneGlobalStateUpdate";
70
NSString *const kLinphoneNotifyReceived = @"LinphoneNotifyReceived";
71
NSString *const kLinphoneNotifyPresenceReceivedForUriOrTel = @"LinphoneNotifyPresenceReceivedForUriOrTel";
72
NSString *const kLinphoneCallEncryptionChanged = @"LinphoneCallEncryptionChanged";
73 74
NSString *const kLinphoneFileTransferSendUpdate = @"LinphoneFileTransferSendUpdate";
NSString *const kLinphoneFileTransferRecvUpdate = @"LinphoneFileTransferRecvUpdate";
75
NSString *const kLinphoneQRCodeFound = @"LinphoneQRCodeFound";
DanmeiChen's avatar
DanmeiChen committed
76
NSString *const kLinphoneChatCreateViewChange = @"LinphoneChatCreateViewChange";
jehan's avatar
jehan committed
77

Paul Cartier's avatar
naming  
Paul Cartier committed
78
NSString *const kLinphoneMsgNotificationAppGroupId = @"group.org.linphone.phone.msgNotification";
79

80
const int kLinphoneAudioVbrCodecDefaultBitrate = 36; /*you can override this from linphonerc or linphonerc-factory*/
81

82 83 84 85 86
extern void libmsamr_init(MSFactory *factory);
extern void libmsx264_init(MSFactory *factory);
extern void libmsopenh264_init(MSFactory *factory);
extern void libmssilk_init(MSFactory *factory);
extern void libmswebrtc_init(MSFactory *factory);
jehan's avatar
jehan committed
87
extern void libmscodec2_init(MSFactory *factory);
88

89
#define FRONT_CAM_NAME							\
90
	"AV Capture: com.apple.avfoundation.avcapturedevice.built-in_video:1" /*"AV Capture: Front Camera"*/
91
#define BACK_CAM_NAME							\
92
	"AV Capture: com.apple.avfoundation.avcapturedevice.built-in_video:0" /*"AV Capture: Back Camera"*/
93

94
NSString *const kLinphoneOldChatDBFilename = @"chat_database.sqlite";
95 96
NSString *const kLinphoneInternalChatDBFilename = @"linphone_chats.db";

97
@interface LinphoneManager ()
98
	@property(strong, nonatomic) AVAudioPlayer *messagePlayer;
99 100
@end

jehan's avatar
jehan committed
101
@implementation LinphoneManager
102

103
struct codec_name_pref_table {
104 105
	const char *name;
	int rate;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
106
	const char *prefname;
Simon Morlat's avatar
Simon Morlat committed
107 108
};

109
struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_preference"},
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
						   {"speex", 16000, "speex_16k_preference"},
						   {"silk", 24000, "silk_24k_preference"},
						   {"silk", 16000, "silk_16k_preference"},
						   {"amr", 8000, "amr_preference"},
						   {"gsm", 8000, "gsm_preference"},
						   {"ilbc", 8000, "ilbc_preference"},
						   {"isac", 16000, "isac_preference"},
						   {"pcmu", 8000, "pcmu_preference"},
						   {"pcma", 8000, "pcma_preference"},
						   {"g722", 8000, "g722_preference"},
						   {"g729", 8000, "g729_preference"},
						   {"mp4v-es", 90000, "mp4v-es_preference"},
						   {"h264", 90000, "h264_preference"},
						   {"h265", 90000, "h265_preference"},
						   {"vp8", 90000, "vp8_preference"},
						   {"mpeg4-generic", 16000, "aaceld_16k_preference"},
						   {"mpeg4-generic", 22050, "aaceld_22k_preference"},
						   {"mpeg4-generic", 32000, "aaceld_32k_preference"},
						   {"mpeg4-generic", 44100, "aaceld_44k_preference"},
						   {"mpeg4-generic", 48000, "aaceld_48k_preference"},
						   {"opus", 48000, "opus_preference"},
						   {"BV16", 8000, "bv16_preference"},
						   {"CODEC2", 8000, "codec2_preference"},
						   {NULL, 0, Nil}};
134 135

+ (NSString *)getPreferenceForCodec:(const char *)name withRate:(int)rate {
Simon Morlat's avatar
Simon Morlat committed
136
	int i;
137 138
	for (i = 0; codec_pref_table[i].name != NULL; ++i) {
		if (strcasecmp(codec_pref_table[i].name, name) == 0 && codec_pref_table[i].rate == rate)
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
139
			return [NSString stringWithUTF8String:codec_pref_table[i].prefname];
Simon Morlat's avatar
Simon Morlat committed
140 141 142 143
	}
	return Nil;
}

Yann Diorcet's avatar
Yann Diorcet committed
144
+ (NSSet *)unsupportedCodecs {
145
	NSMutableSet *set = [NSMutableSet set];
146 147
	for (int i = 0; codec_pref_table[i].name != NULL; ++i) {
		PayloadType *available = linphone_core_find_payload_type(
148
									 theLinphoneCore, codec_pref_table[i].name, codec_pref_table[i].rate, LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS);
149
		if ((available == NULL)
150 151 152
		    // these two codecs should not be hidden, even if not supported
		    && strcmp(codec_pref_table[i].prefname, "h264_preference") != 0 &&
		    strcmp(codec_pref_table[i].prefname, "mp4v-es_preference") != 0) {
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
153
			[set addObject:[NSString stringWithUTF8String:codec_pref_table[i].prefname]];
Yann Diorcet's avatar
Yann Diorcet committed
154 155
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
156
	return set;
Yann Diorcet's avatar
Yann Diorcet committed
157
}
jehan's avatar
jehan committed
158

159
+ (BOOL)isCodecSupported:(const char *)codecName {
160
	return (codecName != NULL) &&
161 162
		(NULL != linphone_core_find_payload_type(theLinphoneCore, codecName, LINPHONE_FIND_PAYLOAD_IGNORE_RATE,
							 LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS));
163 164
}

Yann Diorcet's avatar
Yann Diorcet committed
165
+ (BOOL)runningOnIpad {
166
	return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
Yann Diorcet's avatar
Yann Diorcet committed
167 168
}

169
+ (BOOL)isRunningTests {
170 171 172
	NSDictionary *environment = [[NSProcessInfo processInfo] environment];
	NSString *injectBundle = environment[@"XCInjectBundle"];
	return [[injectBundle pathExtension] isEqualToString:@"xctest"];
173 174
}

175 176
+ (BOOL)isNotIphone3G {
	static BOOL done = FALSE;
Yann Diorcet's avatar
Yann Diorcet committed
177
	static BOOL result;
178
	if (!done) {
Yann Diorcet's avatar
Yann Diorcet committed
179 180 181 182
		size_t size;
		sysctlbyname("hw.machine", NULL, &size, NULL, 0);
		char *machine = malloc(size);
		sysctlbyname("hw.machine", machine, &size, NULL, 0);
183
		NSString *platform = [[NSString alloc] initWithUTF8String:machine];
Yann Diorcet's avatar
Yann Diorcet committed
184
		free(machine);
185

Yann Diorcet's avatar
Yann Diorcet committed
186
		result = ![platform isEqualToString:@"iPhone1,2"];
187

188
		done = TRUE;
Yann Diorcet's avatar
Yann Diorcet committed
189
	}
190
	return result;
Yann Diorcet's avatar
Yann Diorcet committed
191 192
}

Yann Diorcet's avatar
Yann Diorcet committed
193
+ (NSString *)getUserAgent {
194 195
	return
		[NSString stringWithFormat:@"LinphoneIphone/%@ (Linphone/%s; Apple %@/%@)",
196 197 198
		 [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey],
		 linphone_core_get_version(), [UIDevice currentDevice].systemName,
		 [UIDevice currentDevice].systemVersion];
Yann Diorcet's avatar
Yann Diorcet committed
199 200
}

201 202 203
+ (LinphoneManager *)instance {
	@synchronized(self) {
		if (theLinphoneManager == nil) {
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
204 205
			theLinphoneManager = [[LinphoneManager alloc] init];
		}
206
	}
Yann Diorcet's avatar
Yann Diorcet committed
207 208 209
	return theLinphoneManager;
}

Yann Diorcet's avatar
Yann Diorcet committed
210 211
#ifdef DEBUG
+ (void)instanceRelease {
212
	if (theLinphoneManager != nil) {
213 214
		theLinphoneManager = nil;
	}
Yann Diorcet's avatar
Yann Diorcet committed
215 216
}
#endif
Yann Diorcet's avatar
Yann Diorcet committed
217

218
+ (BOOL)langageDirectionIsRTL {
219 220 221
	static NSLocaleLanguageDirection dir = NSLocaleLanguageDirectionLeftToRight;
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
222 223
			dir = [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
		});
224
	return dir == NSLocaleLanguageDirectionRightToLeft;
225 226
}

Yann Diorcet's avatar
Yann Diorcet committed
227 228
#pragma mark - Lifecycle Functions

Yann Diorcet's avatar
Yann Diorcet committed
229
- (id)init {
230
	if ((self = [super init])) {
231
		[NSNotificationCenter.defaultCenter addObserver:self
232 233 234
		 selector:@selector(audioRouteChangeListenerCallback:)
		 name:AVAudioSessionRouteChangeNotification
		 object:nil];
235

236 237
		NSString *path = [[NSBundle mainBundle] pathForResource:@"msg" ofType:@"wav"];
		self.messagePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:nil];
238

239
		_sounds.vibrate = kSystemSoundID_Vibrate;
240

241
		_logs = [[NSMutableArray alloc] init];
242
		_pushDict = [[NSMutableDictionary alloc] init];
243
		_database = NULL;
244
		_conf = FALSE;
245
		_canConfigurePushTokenForProxyConfigs = FALSE;
246
		_fileTransferDelegates = [[NSMutableArray alloc] init];
247
		_linphoneManagerAddressBookMap = [[OrderedDictionary alloc] init];
248
		pushCallIDs = [[NSMutableArray alloc] init];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
249
		_isTesting = [LinphoneManager isRunningTests];
250
		[self migrateImportantFiles];
251 252 253
		[self renameDefaultSettings];
		[self copyDefaultSettings];
		[self overrideDefaultSettings];
254

255 256
        [self lpConfigSetString:[LinphoneManager dataFile:@"linphone.db"] forKey:@"uri" inSection:@"storage"];
        [self lpConfigSetString:[LinphoneManager dataFile:@"x3dh.c25519.sqlite3"] forKey:@"x3dh_db_path" inSection:@"lime"];
257
		// set default values for first boot
258
		if ([self lpConfigStringForKey:@"debugenable_preference"] == nil) {
259
#ifdef DEBUG
260
			[self lpConfigSetInt:1 forKey:@"debugenable_preference"];
261
#else
262
			[self lpConfigSetInt:0 forKey:@"debugenable_preference"];
263 264
#endif
		}
265

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
266 267 268 269 270 271 272 273 274
		// by default if handle_content_encoding is not set, we use plain text for debug purposes only
		if ([self lpConfigStringForKey:@"handle_content_encoding" inSection:@"misc"] == nil) {
#ifdef DEBUG
			[self lpConfigSetString:@"none" forKey:@"handle_content_encoding" inSection:@"misc"];
#else
			[self lpConfigSetString:@"conflate" forKey:@"handle_content_encoding" inSection:@"misc"];
#endif
		}

275
		[self migrateFromUserPrefs];
276
		[self loadAvatar];
277 278
	}
	return self;
jehan's avatar
jehan committed
279
}
Yann Diorcet's avatar
Yann Diorcet committed
280

Yann Diorcet's avatar
Yann Diorcet committed
281
- (void)dealloc {
282
	[NSNotificationCenter.defaultCenter removeObserver:self];
283 284
}

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
#pragma mark - AddressBookMap

- (void) setLinphoneManagerAddressBookMap:(OrderedDictionary*) addressBook{
	_linphoneManagerAddressBookMap = addressBook;
}

- (OrderedDictionary*) getLinphoneManagerAddressBookMap{
	return _linphoneManagerAddressBookMap;
}

- (void) setContactsUpdated:(BOOL) updated{
	_contactsUpdated = updated;
}
- (BOOL) getContactsUpdated{
	return _contactsUpdated;
}

302
#pragma deploymate push "ignored-api-availability"
303
- (void)silentPushFailed:(NSTimer *)timer {
304 305 306 307
	if (_silentPushCompletion) {
		LOGI(@"silentPush failed, silentPushCompletion block: %p", _silentPushCompletion);
		_silentPushCompletion(UIBackgroundFetchResultNoData);
		_silentPushCompletion = nil;
308 309
	}
}
310
#pragma deploymate pop
Yann Diorcet's avatar
Yann Diorcet committed
311

312
#pragma mark - Migration
Yann Diorcet's avatar
Yann Diorcet committed
313

314
- (void)migrationAllPost {
315
	[self migrationLinphoneSettings];
316
	[self migrationPerAccount];
317
}
318

319
- (void)migrationAllPre {
320
	// migrate xmlrpc URL if needed
321
	if ([self lpConfigBoolForKey:@"migration_xmlrpc"] == NO) {
322
		[self lpConfigSetString:@"https://subscribe.linphone.org:444/wizard.php"
323 324
		 forKey:@"xmlrpc_url"
		 inSection:@"assistant"];
325 326
		[self lpConfigSetString:@"sip:rls@sip.linphone.org" forKey:@"rls_uri" inSection:@"sip"];
		[self lpConfigSetBool:YES forKey:@"migration_xmlrpc"];
327
	}
328
	[self lpConfigSetBool:NO forKey:@"store_friends" inSection:@"misc"]; //so far, storing friends in files is not needed. may change in the future.
329
    
330 331
}

332 333
static int check_should_migrate_images(void *data, int argc, char **argv, char **cnames) {
	*((BOOL *)data) = TRUE;
334
	return 0;
335 336
}

337
- (void)migrateFromUserPrefs {
338
	static NSString *migration_flag = @"userpref_migration_done";
339

340
	if (_configDb == nil)
341
		return;
342

343
	if ([self lpConfigIntForKey:migration_flag withDefault:0]) {
344 345
		return;
	}
346

347 348 349
	NSDictionary *defaults = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
	NSArray *defaults_keys = [defaults allKeys];
	NSDictionary *values =
350
		@{ @"backgroundmode_preference" : @NO,
351 352 353
		   @"debugenable_preference" : @NO,
		   @"start_at_boot_preference" : @YES };
	BOOL shouldSync = FALSE;
354

355
	LOGI(@"%lu user prefs", (unsigned long)[defaults_keys count]);
356

357 358
	for (NSString *userpref in values) {
		if ([defaults_keys containsObject:userpref]) {
359
			LOGI(@"Migrating %@ from user preferences: %d", userpref, [[defaults objectForKey:userpref] boolValue]);
360
			[self lpConfigSetBool:[[defaults objectForKey:userpref] boolValue] forKey:userpref];
361 362
			[[NSUserDefaults standardUserDefaults] removeObjectForKey:userpref];
			shouldSync = TRUE;
363
		} else if ([self lpConfigStringForKey:userpref] == nil) {
364
			// no default value found in our linphonerc, we need to add them
365
			[self lpConfigSetBool:[[values objectForKey:userpref] boolValue] forKey:userpref];
366 367
		}
	}
368

369
	if (shouldSync) {
370
		LOGI(@"Synchronizing...");
371 372 373
		[[NSUserDefaults standardUserDefaults] synchronize];
	}
	// don't get back here in the future
374
	[self lpConfigSetBool:YES forKey:migration_flag];
375 376
}

377 378 379 380 381 382 383 384
- (void)migrationLinphoneSettings {
	/* AVPF migration */
	if ([self lpConfigBoolForKey:@"avpf_migration_done"] == FALSE) {
		const MSList *proxies = linphone_core_get_proxy_config_list(theLinphoneCore);
		while (proxies) {
			LinphoneProxyConfig *proxy = (LinphoneProxyConfig *)proxies->data;
			const char *addr = linphone_proxy_config_get_addr(proxy);
			// we want to enable AVPF for the proxies
Benjamin REIS's avatar
Benjamin REIS committed
385
			if (addr &&
386 387 388 389
			    strstr(addr, [LinphoneManager.instance lpConfigStringForKey:@"domain_name"
					  inSection:@"app"
					  withDefault:@"sip.linphone.org"]
				   .UTF8String) != 0) {
390 391 392 393 394 395 396 397 398 399 400 401 402 403
				LOGI(@"Migrating proxy config to use AVPF");
				linphone_proxy_config_enable_avpf(proxy, TRUE);
			}
			proxies = proxies->next;
		}
		[self lpConfigSetBool:TRUE forKey:@"avpf_migration_done"];
	}
	/* Quality Reporting migration */
	if ([self lpConfigBoolForKey:@"quality_report_migration_done"] == FALSE) {
		const MSList *proxies = linphone_core_get_proxy_config_list(theLinphoneCore);
		while (proxies) {
			LinphoneProxyConfig *proxy = (LinphoneProxyConfig *)proxies->data;
			const char *addr = linphone_proxy_config_get_addr(proxy);
			// we want to enable quality reporting for the proxies that are on linphone.org
Benjamin REIS's avatar
Benjamin REIS committed
404
			if (addr &&
405 406 407 408
			    strstr(addr, [LinphoneManager.instance lpConfigStringForKey:@"domain_name"
					  inSection:@"app"
					  withDefault:@"sip.linphone.org"]
				   .UTF8String) != 0) {
409
				LOGI(@"Migrating proxy config to send quality report");
410
				linphone_proxy_config_set_quality_reporting_collector(
411
										      proxy, "sip:voip-metrics@sip.linphone.org;transport=tls");
412 413 414 415 416 417 418 419 420
				linphone_proxy_config_set_quality_reporting_interval(proxy, 180);
				linphone_proxy_config_enable_quality_reporting(proxy, TRUE);
			}
			proxies = proxies->next;
		}
		[self lpConfigSetBool:TRUE forKey:@"quality_report_migration_done"];
	}
	/* File transfer migration */
	if ([self lpConfigBoolForKey:@"file_transfer_migration_done"] == FALSE) {
421
		const char *newURL = "https://www.linphone.org:444/lft.php";
422 423
		LOGI(@"Migrating sharing server url from %s to %s", linphone_core_get_file_transfer_server(LC), newURL);
		linphone_core_set_file_transfer_server(LC, newURL);
424 425
		[self lpConfigSetBool:TRUE forKey:@"file_transfer_migration_done"];
	}
426 427 428 429 430 431 432 433 434 435 436 437
	
	if ([self lpConfigBoolForKey:@"lime_migration_done"] == FALSE) {
		const MSList *proxies = linphone_core_get_proxy_config_list(LC);
		while (proxies) {
			if (!strcmp(linphone_proxy_config_get_domain((LinphoneProxyConfig *)proxies->data),"sip.linphone.org")) {
				linphone_core_set_lime_x3dh_server_url(LC, "https://lime.linphone.org/lime-server/lime-server.php");
				break;
			}
			proxies = proxies->next;
		}
		[self lpConfigSetBool:TRUE forKey:@"lime_migration_done"];
	}
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

	if ([self lpConfigBoolForKey:@"push_notification_migration_done"] == FALSE) {
		const MSList *proxies = linphone_core_get_proxy_config_list(LC);
		bool_t pushEnabled;
		while (proxies) {
			const char *refkey = linphone_proxy_config_get_ref_key(proxies->data);
			if (refkey) {
				pushEnabled = (strcmp(refkey, "push_notification") == 0);
			} else {
				pushEnabled = true;
			}
			linphone_proxy_config_set_push_notification_allowed(proxies->data, pushEnabled);
			proxies = proxies->next;
		}
		[self lpConfigSetBool:TRUE forKey:@"push_notification_migration_done"];
	}
454 455 456
}

- (void)migrationPerAccount {
457 458
	const bctbx_list_t * proxies = linphone_core_get_proxy_config_list(LC);
	NSString *appDomain  = [LinphoneManager.instance lpConfigStringForKey:@"domain_name"
459 460
				inSection:@"app"
				withDefault:@"sip.linphone.org"];
461 462
	while (proxies) {
		LinphoneProxyConfig *config = proxies->data;
463
		// can not create group chat without conference factory
464 465 466 467 468 469 470
		if (!linphone_proxy_config_get_conference_factory_uri(config)) {
			if (strcmp(appDomain.UTF8String, linphone_proxy_config_get_domain(config)) == 0) {
				linphone_proxy_config_set_conference_factory_uri(config, "sip:conference-factory@sip.linphone.org");
			}
		}
		proxies = proxies->next;
	}
471
	
472 473 474 475 476 477
	NSString *s = [self lpConfigStringForKey:@"pushnotification_preference"];
	if (s && s.boolValue) {
		LOGI(@"Migrating push notification per account, enabling for ALL");
		[self lpConfigSetBool:NO forKey:@"pushnotification_preference"];
		const MSList *proxies = linphone_core_get_proxy_config_list(LC);
		while (proxies) {
478
			linphone_proxy_config_set_push_notification_allowed(proxies->data, true);
479 480 481 482 483 484
			[self configurePushTokenForProxyConfig:proxies->data];
			proxies = proxies->next;
		}
	}
}

485 486 487 488
static void migrateWizardToAssistant(const char *entry, void *user_data) {
	LinphoneManager *thiz = (__bridge LinphoneManager *)(user_data);
	NSString *key = [NSString stringWithUTF8String:entry];
	[thiz lpConfigSetString:[thiz lpConfigStringForKey:key inSection:@"wizard"] forKey:key inSection:@"assistant"];
DanmeiChen's avatar
DanmeiChen committed
489 490
}

Yann Diorcet's avatar
Yann Diorcet committed
491
#pragma mark - Linphone Core Functions
492

493 494 495
+ (LinphoneCore *)getLc {
	if (theLinphoneCore == nil) {
		@throw([NSException exceptionWithName:@"LinphoneCoreException"
496 497
			reason:@"Linphone core not initialized yet"
			userInfo:nil]);
jehan's avatar
jehan committed
498 499 500 501
	}
	return theLinphoneCore;
}

502 503 504 505 506 507 508
+ (BOOL)isLcInitialized {
    if (theLinphoneCore == nil) {
        return NO;
    }
    return YES;
}

509
#pragma mark Debug functions
510

511
+ (void)dumpLcConfig {
512
	if (theLinphoneCore) {
513
		LpConfig *conf = LinphoneManager.instance.configDb;
514 515 516
		char *config = lp_config_dump(conf);
		LOGI(@"\n%s", config);
		ms_free(config);
517
	}
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
518 519
}

520 521
#pragma mark - Logs Functions handlers
static void linphone_iphone_log_user_info(struct _LinphoneCore *lc, const char *message) {
522
	linphone_iphone_log_handler(NULL, ORTP_MESSAGE, message, NULL);
jehan's avatar
jehan committed
523
}
524
static void linphone_iphone_log_user_warning(struct _LinphoneCore *lc, const char *message) {
525
	linphone_iphone_log_handler(NULL, ORTP_WARNING, message, NULL);
jehan's avatar
jehan committed
526
}
Yann Diorcet's avatar
Yann Diorcet committed
527 528 529

#pragma mark - Display Status Functions

530
- (void)displayStatus:(NSString *)message {
531
	// Post event
532
	[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneDisplayStatusUpdate
533 534 535 536
	 object:self
	 userInfo:@{
			@"message" : message
				}];
Yann Diorcet's avatar
Yann Diorcet committed
537 538
}

539 540
static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char *message) {
	NSString *status = [[NSString alloc] initWithCString:message encoding:[NSString defaultCStringEncoding]];
541
	[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) displayStatus:status];
jehan's avatar
jehan committed
542 543
}

Yann Diorcet's avatar
Yann Diorcet committed
544 545
#pragma mark - Call State Functions

546 547 548
- (void)localNotifContinue:(NSTimer *)timer {
	UILocalNotification *notif = [timer userInfo];
	if (notif) {
549
		LOGI(@"cancelling/presenting local notif");
550
		[[UIApplication sharedApplication] cancelAllLocalNotifications];
551 552
		[[UIApplication sharedApplication] presentLocalNotificationNow:notif];
	}
553 554
}

555
- (void)userNotifContinue:(NSTimer *)timer {
556
	UNNotificationContent *content = [timer userInfo];
557
	if (content && [UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
558 559 560 561
		LOGI(@"cancelling/presenting user notif");
		UNNotificationRequest *req =
			[UNNotificationRequest requestWithIdentifier:@"call_request" content:content trigger:NULL];
		[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req
562 563 564 565 566 567 568
		 withCompletionHandler:^(NSError *_Nullable error) {
				// Enable or disable features based on authorization.
				if (error) {
					LOGD(@"Error while adding notification request :");
					LOGD(error.description);
				}
			}];
569
	}
570 571
}

Yann Diorcet's avatar
Yann Diorcet committed
572 573
#pragma mark - Transfert State Functions

574
static void linphone_iphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state) {
jehan's avatar
jehan committed
575 576
}

577 578 579
#pragma mark - Global state change

static void linphone_iphone_global_state_changed(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message) {
580
	[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) onGlobalStateChanged:gstate withMessage:message];
581 582
}

583
- (void)onGlobalStateChanged:(LinphoneGlobalState)state withMessage:(const char *)message {
584
	LOGI(@"onGlobalStateChanged: %d (message: %s)", state, message);
585

586
	NSDictionary *dict = [NSDictionary
587 588
			      dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:state], @"state",
			      [NSString stringWithUTF8String:message ? message : ""], @"message", nil];
589

590 591 592
	if (theLinphoneCore && linphone_core_get_global_state(theLinphoneCore) == LinphoneGlobalOff) {
		[CoreManager.instance stopIterateTimer];
	}
593 594
	// dispatch the notification asynchronously
	dispatch_async(dispatch_get_main_queue(), ^(void) {
595
		if (theLinphoneCore && linphone_core_get_global_state(theLinphoneCore) != LinphoneGlobalOff)
596
			[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneGlobalStateUpdate object:self userInfo:dict];
597
	});
598 599
}

600 601
- (void)globalStateChangedNotificationHandler:(NSNotification *)notif {
	if ((LinphoneGlobalState)[[[notif userInfo] valueForKey:@"state"] integerValue] == LinphoneGlobalOn) {
602 603
		[self finishCoreConfiguration];
	}
604 605 606 607
}

#pragma mark - Configuring status changed

608
static void linphone_iphone_configuring_status_changed(LinphoneCore *lc, LinphoneConfiguringState status,
609
						       const char *message) {
610
	[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) onConfiguringStatusChanged:status withMessage:message];
611 612
}

613
- (void)onConfiguringStatusChanged:(LinphoneConfiguringState)status withMessage:(const char *)message {
614
	LOGI(@"onConfiguringStatusChanged: %s %@", linphone_configuring_state_to_string(status),
615
	     message ? [NSString stringWithFormat:@"(message: %s)", message] : @"");
616
	NSDictionary *dict = [NSDictionary
617 618
			      dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:status], @"state",
			      [NSString stringWithUTF8String:message ? message : ""], @"message", nil];
619

620
	// dispatch the notification asynchronously
621
	dispatch_async(dispatch_get_main_queue(), ^(void) {
622 623 624 625
			[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneConfiguringStateUpdate
			 object:self
			 userInfo:dict];
		});
626
}
Yann Diorcet's avatar
Yann Diorcet committed
627

628
- (void)configuringStateChangedNotificationHandler:(NSNotification *)notif {
629
	_wasRemoteProvisioned = ((LinphoneConfiguringState)[[[notif userInfo] valueForKey:@"state"] integerValue] ==
630
				 LinphoneConfiguringSuccessful);
631 632 633 634 635 636
	if (_wasRemoteProvisioned) {
		LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(LC);
		if (cfg) {
			[self configurePushTokenForProxyConfig:cfg];
		}
	}
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
637 638
}

Yann Diorcet's avatar
Yann Diorcet committed
639 640
#pragma mark - Registration State Functions

641
- (void)onRegister:(LinphoneCore *)lc
642 643 644
cfg:(LinphoneProxyConfig *)cfg
state:(LinphoneRegistrationState)state
message:(const char *)cmessage {
645 646 647 648 649
	LOGI(@"New registration state: %s (message: %s)", linphone_registration_state_to_string(state), cmessage);

	LinphoneReason reason = linphone_proxy_config_get_error(cfg);
	NSString *message = nil;
	switch (reason) {
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
	case LinphoneReasonBadCredentials:
		message = NSLocalizedString(@"Bad credentials, check your account settings", nil);
		break;
	case LinphoneReasonNoResponse:
		message = NSLocalizedString(@"No response received from remote", nil);
		break;
	case LinphoneReasonUnsupportedContent:
		message = NSLocalizedString(@"Unsupported content", nil);
		break;
	case LinphoneReasonIOError:
		message = NSLocalizedString(
					    @"Cannot reach the server: either it is an invalid address or it may be temporary down.", nil);
		break;

	case LinphoneReasonUnauthorized:
		message = NSLocalizedString(@"Operation is unauthorized because missing credential", nil);
		break;
	case LinphoneReasonNoMatch:
		message = NSLocalizedString(@"Operation could not be executed by server or remote client because it "
					    @"didn't have any context for it",
					    nil);
		break;
	case LinphoneReasonMovedPermanently:
		message = NSLocalizedString(@"Resource moved permanently", nil);
		break;
	case LinphoneReasonGone:
		message = NSLocalizedString(@"Resource no longer exists", nil);
		break;
	case LinphoneReasonTemporarilyUnavailable:
		message = NSLocalizedString(@"Temporarily unavailable", nil);
		break;
	case LinphoneReasonAddressIncomplete:
		message = NSLocalizedString(@"Address incomplete", nil);
		break;
	case LinphoneReasonNotImplemented:
		message = NSLocalizedString(@"Not implemented", nil);
		break;
	case LinphoneReasonBadGateway:
		message = NSLocalizedString(@"Bad gateway", nil);
		break;
	case LinphoneReasonServerTimeout:
		message = NSLocalizedString(@"Server timeout", nil);
		break;
	case LinphoneReasonNotAcceptable:
	case LinphoneReasonDoNotDisturb:
	case LinphoneReasonDeclined:
	case LinphoneReasonNotFound:
	case LinphoneReasonNotAnswered:
	case LinphoneReasonBusy:
	case LinphoneReasonNone:
	case LinphoneReasonUnknown:
		message = NSLocalizedString(@"Unknown error", nil);
		break;
703
	}
704 705

	// Post event
706 707
	NSDictionary *dict =
		[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:state], @"state",
708
		 [NSValue valueWithPointer:cfg], @"cfg", message, @"message", nil];
709
	[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneRegistrationUpdate object:self userInfo:dict];
jehan's avatar
jehan committed
710
}
Yann Diorcet's avatar
Yann Diorcet committed
711

712
static void linphone_iphone_registration_state(LinphoneCore *lc, LinphoneProxyConfig *cfg,
713
					       LinphoneRegistrationState state, const char *message) {
714
	[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) onRegister:lc cfg:cfg state:state message:message];
jehan's avatar
jehan committed
715
}
716

717
#pragma mark - Auth info Function
Yann Diorcet's avatar
Yann Diorcet committed
718

719
static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAuthInfo *auth_info, LinphoneAuthMethod method) {
720
	// let the wizard handle its own errors
721
	if ([PhoneMainView.instance currentView] != AssistantView.compositeViewDescription) {
722 723 724
		const char * realmC = linphone_auth_info_get_realm(auth_info);
		const char * usernameC = linphone_auth_info_get_username(auth_info);
		const char * domainC = linphone_auth_info_get_domain(auth_info);
725
		static UIAlertController *alertView = nil;
Yann Diorcet's avatar
Yann Diorcet committed
726

727
		// avoid having multiple popups
728
		[PhoneMainView.instance dismissViewControllerAnimated:YES completion:nil];
729

730 731
		// dont pop up if we are in background, in any case we will refresh registers when entering
		// the application again
732
		if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
733 734
			return;
		}
735

DanmeiChen's avatar
DanmeiChen committed
736
		NSString *realm = [NSString stringWithUTF8String:realmC?:domainC];
737 738
		NSString *username = [NSString stringWithUTF8String:usernameC];
		NSString *domain = [NSString stringWithUTF8String:domainC];
739
		alertView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Authentification needed", nil)
740 741 742 743 744
			     message:[NSString stringWithFormat:NSLocalizedString(@"Connection failed because authentication is "
										  @"missing or invalid for %@@%@.\nYou can "
										  @"provide password again, or check your "
										  @"account configuration in the settings.", nil), username, realm]
			     preferredStyle:UIAlertControllerStyleAlert];
745

746
		UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil)
747 748
						style:UIAlertActionStyleDefault
						handler:^(UIAlertAction * action) {}];
749

750
		[alertView addTextFieldWithConfigurationHandler:^(UITextField *textField) {
751 752 753 754