LinphoneManager.m 88.1 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";
Christophe Deschamps's avatar
Christophe Deschamps committed
77 78
NSString *const kLinphoneEphemeralMessageDeletedInRoom = @"LinphoneEphemeralMessageDeletedInRoom";

jehan's avatar
jehan committed
79

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

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

84 85 86 87 88
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
89
extern void libmscodec2_init(MSFactory *factory);
90

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

96
NSString *const kLinphoneOldChatDBFilename = @"chat_database.sqlite";
97 98
NSString *const kLinphoneInternalChatDBFilename = @"linphone_chats.db";

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

jehan's avatar
jehan committed
103
@implementation LinphoneManager
104

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

111
struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_preference"},
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
						   {"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}};
136 137

+ (NSString *)getPreferenceForCodec:(const char *)name withRate:(int)rate {
Simon Morlat's avatar
Simon Morlat committed
138
	int i;
139 140
	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
141
			return [NSString stringWithUTF8String:codec_pref_table[i].prefname];
Simon Morlat's avatar
Simon Morlat committed
142 143 144 145
	}
	return Nil;
}

Yann Diorcet's avatar
Yann Diorcet committed
146
+ (NSSet *)unsupportedCodecs {
147
	NSMutableSet *set = [NSMutableSet set];
148 149
	for (int i = 0; codec_pref_table[i].name != NULL; ++i) {
		PayloadType *available = linphone_core_find_payload_type(
150
									 theLinphoneCore, codec_pref_table[i].name, codec_pref_table[i].rate, LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS);
151
		if ((available == NULL)
152 153 154
		    // 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
155
			[set addObject:[NSString stringWithUTF8String:codec_pref_table[i].prefname]];
Yann Diorcet's avatar
Yann Diorcet committed
156 157
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
158
	return set;
Yann Diorcet's avatar
Yann Diorcet committed
159
}
jehan's avatar
jehan committed
160

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

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

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

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

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

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

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

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

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

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

Yann Diorcet's avatar
Yann Diorcet committed
229 230
#pragma mark - Lifecycle Functions

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

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

241
		_sounds.vibrate = kSystemSoundID_Vibrate;
242

243
		_logs = [[NSMutableArray alloc] init];
244
		_pushDict = [[NSMutableDictionary alloc] init];
245
		_database = NULL;
246
		_conf = FALSE;
247
		_fileTransferDelegates = [[NSMutableArray alloc] init];
248
		_linphoneManagerAddressBookMap = [[OrderedDictionary alloc] init];
249
		pushCallIDs = [[NSMutableArray alloc] init];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
250
		_isTesting = [LinphoneManager isRunningTests];
251
		[self migrateImportantFiles];
252 253 254
		[self renameDefaultSettings];
		[self copyDefaultSettings];
		[self overrideDefaultSettings];
255 256 257
		if (![self lpConfigBoolForKey:@"migration_images_done" withDefault:FALSE]) {
			[self migrationAllImages];
		}
258

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

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
270 271 272 273 274 275 276 277 278
		// 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
		}

279
		[self migrateFromUserPrefs];
280
		[self loadAvatar];
281 282
	}
	return self;
jehan's avatar
jehan committed
283
}
Yann Diorcet's avatar
Yann Diorcet committed
284

Yann Diorcet's avatar
Yann Diorcet committed
285
- (void)dealloc {
286
	[NSNotificationCenter.defaultCenter removeObserver:self];
287 288
}

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
#pragma mark - AddressBookMap

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

- (OrderedDictionary*) getLinphoneManagerAddressBookMap{
	return _linphoneManagerAddressBookMap;
}

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

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

316
#pragma mark - Migration
Yann Diorcet's avatar
Yann Diorcet committed
317

318
- (void)migrationAllPost {
319
	[self migrationLinphoneSettings];
320
	[self migrationPerAccount];
321
}
322

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

336 337
static int check_should_migrate_images(void *data, int argc, char **argv, char **cnames) {
	*((BOOL *)data) = TRUE;
338
	return 0;
339 340
}

341
- (void)migrateFromUserPrefs {
342
	static NSString *migration_flag = @"userpref_migration_done";
343

344
	if (_configDb == nil)
345
		return;
346

347
	if ([self lpConfigIntForKey:migration_flag withDefault:0]) {
348 349
		return;
	}
350

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

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

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

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

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

	if ([self lpConfigBoolForKey:@"push_notification_migration_done"] == FALSE) {
454
		const MSList *accounts = linphone_core_get_account_list(theLinphoneCore);
455
		bool_t pushEnabled;
456 457 458 459 460
		while (accounts)
		{
			LinphoneAccount *account = (LinphoneAccount *)accounts->data;
			LinphoneAccountParams *newAccountParams = linphone_account_params_clone(linphone_account_get_params(account));
			const char *refkey = linphone_account_params_get_ref_key(newAccountParams);
461 462 463 464 465
			if (refkey) {
				pushEnabled = (strcmp(refkey, "push_notification") == 0);
			} else {
				pushEnabled = true;
			}
466 467 468 469 470
			linphone_account_params_set_push_notification_allowed(newAccountParams, pushEnabled);
			linphone_account_params_set_remote_push_notification_allowed(newAccountParams, pushEnabled);
			linphone_account_set_params(account, newAccountParams);
			linphone_account_params_unref(newAccountParams);
			accounts = accounts->next;
471 472 473
		}
		[self lpConfigSetBool:TRUE forKey:@"push_notification_migration_done"];
	}
474 475 476
}

- (void)migrationPerAccount {
477
	const MSList *accounts = linphone_core_get_account_list(theLinphoneCore);
478
	NSString *appDomain  = [LinphoneManager.instance lpConfigStringForKey:@"domain_name"
479 480
				inSection:@"app"
				withDefault:@"sip.linphone.org"];
481 482 483 484 485 486 487 488 489 490 491 492 493
	   while (accounts) {
		   LinphoneAccount *account = accounts->data;
		   LinphoneAccountParams *newAccountParams = linphone_account_params_clone(linphone_account_get_params(account));
		   // can not create group chat without conference factory
		   if (!linphone_account_params_get_conference_factory_uri(newAccountParams)) {
			   if (strcmp(appDomain.UTF8String, linphone_account_params_get_domain(newAccountParams)) == 0) {
				   linphone_account_params_set_conference_factory_uri(newAccountParams, "sip:conference-factory@sip.linphone.org");
				   linphone_account_set_params(account, newAccountParams);
			   }
		   }
		   linphone_account_params_unref(newAccountParams);
		   accounts = accounts->next;
	   }
494
	
495 496 497 498
	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"];
499 500 501 502 503 504 505 506 507
		const MSList *accounts = linphone_core_get_account_list(theLinphoneCore);
		while (accounts) {
			LinphoneAccount *account = accounts->data;
			LinphoneAccountParams *newAccountParams = linphone_account_params_clone(linphone_account_get_params(account));
			linphone_account_params_set_push_notification_allowed(newAccountParams, true);
			linphone_account_params_set_remote_push_notification_allowed(newAccountParams, true);
			linphone_account_set_params(account, newAccountParams);
			linphone_account_params_unref(newAccountParams);
			accounts = accounts->next;
508 509 510 511
		}
	}
}

512 513 514 515
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
516 517
}

Yann Diorcet's avatar
Yann Diorcet committed
518
#pragma mark - Linphone Core Functions
519

520 521 522
+ (LinphoneCore *)getLc {
	if (theLinphoneCore == nil) {
		@throw([NSException exceptionWithName:@"LinphoneCoreException"
523 524
			reason:@"Linphone core not initialized yet"
			userInfo:nil]);
jehan's avatar
jehan committed
525 526 527 528
	}
	return theLinphoneCore;
}

529 530 531 532 533 534 535
+ (BOOL)isLcInitialized {
    if (theLinphoneCore == nil) {
        return NO;
    }
    return YES;
}

536
#pragma mark Debug functions
537

538
+ (void)dumpLcConfig {
539
	if (theLinphoneCore) {
540
		LpConfig *conf = LinphoneManager.instance.configDb;
DanmeiChen's avatar
DanmeiChen committed
541
		char *config = linphone_config_dump(conf);
542 543
		LOGI(@"\n%s", config);
		ms_free(config);
544
	}
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
545 546
}

547 548
#pragma mark - Logs Functions handlers
static void linphone_iphone_log_user_info(struct _LinphoneCore *lc, const char *message) {
549
	linphone_iphone_log_handler(NULL, ORTP_MESSAGE, message, NULL);
jehan's avatar
jehan committed
550
}
551
static void linphone_iphone_log_user_warning(struct _LinphoneCore *lc, const char *message) {
552
	linphone_iphone_log_handler(NULL, ORTP_WARNING, message, NULL);
jehan's avatar
jehan committed
553
}
Yann Diorcet's avatar
Yann Diorcet committed
554 555 556

#pragma mark - Display Status Functions

557
- (void)displayStatus:(NSString *)message {
558
	// Post event
559
	[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneDisplayStatusUpdate
560 561 562 563
	 object:self
	 userInfo:@{
			@"message" : message
				}];
Yann Diorcet's avatar
Yann Diorcet committed
564 565
}

566 567
static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char *message) {
	NSString *status = [[NSString alloc] initWithCString:message encoding:[NSString defaultCStringEncoding]];
568
	[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) displayStatus:status];
jehan's avatar
jehan committed
569 570
}

Yann Diorcet's avatar
Yann Diorcet committed
571 572
#pragma mark - Call State Functions

573 574 575
- (void)localNotifContinue:(NSTimer *)timer {
	UILocalNotification *notif = [timer userInfo];
	if (notif) {
576
		LOGI(@"cancelling/presenting local notif");
577
		[[UIApplication sharedApplication] cancelAllLocalNotifications];
578 579
		[[UIApplication sharedApplication] presentLocalNotificationNow:notif];
	}
580 581
}

582
- (void)userNotifContinue:(NSTimer *)timer {
583
	UNNotificationContent *content = [timer userInfo];
584
	if (content && [UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
585 586 587 588
		LOGI(@"cancelling/presenting user notif");
		UNNotificationRequest *req =
			[UNNotificationRequest requestWithIdentifier:@"call_request" content:content trigger:NULL];
		[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req
589 590 591 592 593 594 595
		 withCompletionHandler:^(NSError *_Nullable error) {
				// Enable or disable features based on authorization.
				if (error) {
					LOGD(@"Error while adding notification request :");
					LOGD(error.description);
				}
			}];
596
	}
597 598
}

Christophe Deschamps's avatar
Christophe Deschamps committed
599 600 601 602 603 604 605 606 607 608 609 610
#pragma mark - Ephemeral State Functions
static void linphone_iphone_ephemeral_message_deleted(LinphoneCore *lc, LinphoneChatRoom *cr) {
	LinphoneManager *lm = (__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc));
	NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithPointer:cr], @"room", nil];
	
	// dispatch the notification asynchronously
	dispatch_async(dispatch_get_main_queue(), ^(void) {
		[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneEphemeralMessageDeletedInRoom object:lm userInfo:dict];
	});
	
}

Yann Diorcet's avatar
Yann Diorcet committed
611 612
#pragma mark - Transfert State Functions

613
static void linphone_iphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state) {
jehan's avatar
jehan committed
614 615
}

616 617 618
#pragma mark - Global state change

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

622
- (void)onGlobalStateChanged:(LinphoneGlobalState)state withMessage:(const char *)message {
623
	LOGI(@"onGlobalStateChanged: %d (message: %s)", state, message);
624

625
	NSDictionary *dict = [NSDictionary
626 627
			      dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:state], @"state",
			      [NSString stringWithUTF8String:message ? message : ""], @"message", nil];
628 629
	// dispatch the notification asynchronously
	dispatch_async(dispatch_get_main_queue(), ^(void) {
630
		if (theLinphoneCore && linphone_core_get_global_state(theLinphoneCore) != LinphoneGlobalOff)
631
			[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneGlobalStateUpdate object:self userInfo:dict];
632
	});
633 634
}

635 636
- (void)globalStateChangedNotificationHandler:(NSNotification *)notif {
	if ((LinphoneGlobalState)[[[notif userInfo] valueForKey:@"state"] integerValue] == LinphoneGlobalOn) {
637 638
		[self finishCoreConfiguration];
	}
639 640 641 642
}

#pragma mark - Configuring status changed

643
static void linphone_iphone_configuring_status_changed(LinphoneCore *lc, LinphoneConfiguringState status,
644
						       const char *message) {
645
	[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) onConfiguringStatusChanged:status withMessage:message];
646 647
}

648
- (void)onConfiguringStatusChanged:(LinphoneConfiguringState)status withMessage:(const char *)message {
649
	LOGI(@"onConfiguringStatusChanged: %s %@", linphone_configuring_state_to_string(status),
650
	     message ? [NSString stringWithFormat:@"(message: %s)", message] : @"");
651
	NSDictionary *dict = [NSDictionary
652 653
			      dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:status], @"state",
			      [NSString stringWithUTF8String:message ? message : ""], @"message", nil];
654

655
	// dispatch the notification asynchronously
656
	dispatch_async(dispatch_get_main_queue(), ^(void) {
657 658 659 660
			[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneConfiguringStateUpdate
			 object:self
			 userInfo:dict];
		});
661
}
Yann Diorcet's avatar
Yann Diorcet committed
662 663 664

#pragma mark - Registration State Functions

665
- (void)onRegister:(LinphoneCore *)lc
666
account:(LinphoneAccount *)account
667 668
state:(LinphoneRegistrationState)state
message:(const char *)cmessage {
669 670
	LOGI(@"New registration state: %s (message: %s)", linphone_registration_state_to_string(state), cmessage);

671
	LinphoneReason reason = linphone_account_get_error(account);
672 673
	NSString *message = nil;
	switch (reason) {
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 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
	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;
727
	}
728 729

	// Post event
730 731
	NSDictionary *dict =
		[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:state], @"state",
732
		 [NSValue valueWithPointer:account], @"account", message, @"message", nil];
733
	[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneRegistrationUpdate object:self userInfo:dict];
jehan's avatar
jehan committed
734
}
Yann Diorcet's avatar
Yann Diorcet committed
735

736
static void linphone_iphone_registration_state(LinphoneCore *lc, LinphoneAccount *account,
737
					       LinphoneRegistrationState state, const char *message) {
738
	[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) onRegister:lc account:account state:state message:message];
jehan's avatar
jehan committed
739
}
740

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