Warning, /network/kdeconnect-ios/KDE Connect/KDE Connect/ObjC Backend/BackgroundService.m is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-FileCopyrightText: 2014 YANG Qiao <yangqiao0505@me.com>
0003  *                         2020 Weixuan Xiao <veyx.shaw@gmail.com>
0004  *                         2021 Lucas Wang <lucas.wang@tuta.io>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007  */
0008 
0009 // Original header below:
0010 //Copyright 2/5/14  YANG Qiao yangqiao0505@me.com
0011 //kdeconnect is distributed under two licenses.
0012 //
0013 //* The Mozilla Public License (MPL) v2.0
0014 //
0015 //or
0016 //
0017 //* The General Public License (GPL) v2.1
0018 //
0019 //----------------------------------------------------------------------
0020 //
0021 //Software distributed under these licenses is distributed on an "AS
0022 //IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
0023 //implied. See the License for the specific language governing rights
0024 //and limitations under the License.
0025 //kdeconnect is distributed under both the GPL and the MPL. The MPL
0026 //notice, reproduced below, covers the use of either of the licenses.
0027 //
0028 //---------------------------------------------------------------------
0029 
0030 #import "BackgroundService.h"
0031 #import "LanLinkProvider.h"
0032 //#import "SettingsStore.h"
0033 //#import "PluginFactory.h"
0034 //#import "KeychainItemWrapper.h"
0035 //#import "Device.h"
0036 #import "KDE_Connect-Swift.h"
0037 @import os.log;
0038 
0039 @interface BackgroundService () <NetworkChangeMonitorDelegate> {
0040     NSMutableDictionary<NSString *, Device *> *_devices;
0041     NSMutableDictionary<NSString *, NSData *> *_settings;
0042     os_log_t logger;
0043 }
0044 
0045 @property(nonatomic) NetworkChangeMonitor *networkChangeMonitor;
0046 @property(nonatomic) NSMutableArray<BaseLinkProvider *> *_linkProviders;
0047 @property(nonatomic) NSMutableArray<Device *> *visibleDevices;
0048 @property(nonatomic) NSMutableDictionary<NSString *, Device *> *_savedDevices;
0049 @property(nonatomic, assign) ConnectedDevicesViewModel *_backgroundServiceDelegate;
0050 @property(nonatomic, assign) CertificateService *_certificateService;
0051 
0052 @end
0053 
0054 @implementation BackgroundService
0055 
0056 @synthesize _backgroundServiceDelegate;
0057 @synthesize _certificateService;
0058 - (void)setDevices:(NSDictionary<NSString *, Device *> *)devices
0059 {
0060     _devices = [[NSMutableDictionary alloc] initWithDictionary:devices];
0061 }
0062 @synthesize _linkProviders;
0063 - (void)setSettings:(NSDictionary<NSString *, NSData *> *)settings
0064 {
0065     _settings = [[NSMutableDictionary alloc] initWithDictionary:settings];
0066 }
0067 @synthesize _savedDevices;
0068 
0069 //+ (id) sharedInstance
0070 //{
0071 //    DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
0072 //        return [[self alloc] init];
0073 //    });
0074 //}
0075 //
0076 //+ (id) allocWithZone:(struct _NSZone *)zone
0077 //{
0078 //    DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
0079 //        return [super allocWithZone:zone];
0080 //    });
0081 //}
0082 //
0083 //- (id)copyWithZone:(NSZone *)zone;{
0084 //    return self;
0085 //}
0086 
0087 - (BackgroundService*) initWithconnectedDeviceViewModel:(ConnectedDevicesViewModel*)connectedDeviceViewModel certificateService:(CertificateService*) certificateService
0088 {
0089     if ((self=[super init])) {
0090         logger = os_log_create([NSString kdeConnectOSLogSubsystem].UTF8String,
0091                                         NSStringFromClass([self class]).UTF8String);
0092         // MARK: comment this out for production, this is for debugging, for clearing the saved devices dictionary in UserDefaults
0093         //[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"savedDevices"];
0094         //[_certificateService deleteAllItemsFromKeychain];
0095         //NSLog(@"Host identity deleted from keychain with exit code %i", [KeychainOperations deleteHostCertificateFromKeychain]);
0096         
0097         _linkProviders=[NSMutableArray arrayWithCapacity:1];
0098         _devices=[NSMutableDictionary dictionaryWithCapacity:1];
0099         _visibleDevices=[NSMutableArray arrayWithCapacity:1];
0100         _settings=[NSMutableDictionary dictionaryWithCapacity:1];
0101         _savedDevices = [NSMutableDictionary dictionary];
0102        //[[SettingsStore alloc] initWithPath:KDECONNECT_REMEMBERED_DEV_FILE_PATH];
0103         
0104         _backgroundServiceDelegate = connectedDeviceViewModel;
0105         _certificateService = certificateService;
0106         
0107         _networkChangeMonitor = [[NetworkChangeMonitor alloc] init];
0108         _networkChangeMonitor.delegate = self;
0109         
0110         NSDictionary* tempDic = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"savedDevices"];
0111         if (tempDic != nil) {
0112             for (NSString* deviceId in [tempDic allKeys]) {
0113                 NSData* deviceData = tempDic[deviceId];
0114                 [_settings setObject:deviceData forKey:deviceId]; // do this here since Settings holds exclusively encoded Data, NOT Device objects, otherwise will throw "non-property list" error upon trying to save to UserDefaults
0115                 NSError* error;
0116                 Device* device = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[Device class], [NSString class], [NSArray class], nil] fromData:deviceData error:&error];
0117                 os_log_with_type(logger, OS_LOG_TYPE_DEFAULT, "device with pair status %lu is decoded from UserDefaults as: %{public}@ with error %{public}@", [device _pairStatus], device, error);
0118                 if ([device _pairStatus] == Paired) {
0119                     device.deviceDelegate = self;
0120                     //[device reloadPlugins];
0121                     [_savedDevices setObject:device forKey:deviceId];
0122                 } else {
0123                     os_log_with_type(logger, self.debugLogLevel, "Not loading device above since it's previous status is NOT paired.");
0124                 }
0125             }
0126         }
0127         
0128         os_log_with_type(logger, self.debugLogLevel, "%{public}@", _savedDevices);
0129         //[[NSUserDefaults standardUserDefaults] registerDefaults:_settings];
0130         //[[NSUserDefaults standardUserDefaults] synchronize];
0131         [self registerLinkProviders];
0132         [self loadRemenberedDevices];
0133         //[PluginFactory sharedInstance];
0134         
0135 //#ifdef DEBUG
0136 //        NSString* deviceId = @"test-purpose-device";
0137 //        Device* device=[[Device alloc] initTest];
0138 //        [_devices setObject:device forKey:deviceId];
0139 //#endif
0140         // [_visibleDevices addObject:device];
0141     }
0142     return self;
0143 }
0144 
0145 - (os_log_type_t)debugLogLevel {
0146     if ([SelfDeviceData shared].isDebuggingDiscovery) {
0147         return OS_LOG_TYPE_INFO;
0148     }
0149     return OS_LOG_TYPE_DEBUG;
0150 }
0151 
0152 // TODO: fix typo in this name
0153 - (void) loadRemenberedDevices
0154 {
0155     for (Device* device in [_savedDevices allValues]) {
0156         //Device* device=[[Device alloc] init:deviceId setDelegate:self];
0157         [_devices setObject:device forKey:[device _id]];
0158         //[_settings setObject:device forKey:[device _id]];
0159     }
0160     if (_backgroundServiceDelegate) {
0161         [_backgroundServiceDelegate onDevicesListUpdatedWithDevicesListsMap:[self getDevicesLists]];
0162     }
0163 }
0164 
0165 - (void) registerLinkProviders
0166 {
0167     os_log_with_type(logger, self.debugLogLevel, "bg register linkproviders");
0168     // TO-DO: read setting for linkProvider registration
0169     LanLinkProvider* linkProvider=[[LanLinkProvider alloc] initWithDelegate:self certificateService:_certificateService];
0170     [_linkProviders addObject:linkProvider];
0171 }
0172 
0173 - (void) startDiscovery
0174 {
0175     [_networkChangeMonitor startMonitoring];
0176     os_log_with_type(logger, self.debugLogLevel, "bg start Discovery");
0177     for (BaseLinkProvider* lp in _linkProviders) {
0178         [lp onStart];
0179     }
0180 }
0181 
0182 - (void) refreshDiscovery
0183 {
0184     os_log_with_type(logger, self.debugLogLevel, "bg refresh Discovery");
0185     for (BaseLinkProvider* lp in _linkProviders) {
0186         [lp onRefresh];
0187     }
0188 }
0189 
0190 - (void) stopDiscovery
0191 {
0192     [_networkChangeMonitor stopMonitoring];
0193     os_log_with_type(logger, self.debugLogLevel, "bg stop Discovery");
0194     for (BaseLinkProvider* lp in _linkProviders) {
0195         [lp onStop];
0196     }
0197 }
0198 
0199 - (NSDictionary<NSString *, NSDictionary<NSString *, NSString *> *> *) getDevicesLists
0200 {
0201     os_log_with_type(logger, self.debugLogLevel, "bg get devices lists");
0202     NSMutableDictionary* _visibleDevicesList=[NSMutableDictionary dictionaryWithCapacity:1];
0203     NSMutableDictionary* _connectedDevicesList=[NSMutableDictionary dictionaryWithCapacity:1];
0204     NSMutableDictionary* _rememberedDevicesList=[NSMutableDictionary dictionaryWithCapacity:1];
0205     for (Device *device in [_devices allValues]) {
0206         if ((![device isReachable]) && [device isPaired]) {
0207             [_rememberedDevicesList setValue:[device _name] forKey:[device _id]];
0208             
0209         } else if([device isPaired] && [device isReachable]){
0210             //[device reloadPlugins];
0211             [_connectedDevicesList setValue:[device _name] forKey:[device _id]];
0212             //TODO: move this to a different thread maybe, and also in Swift
0213             //[device reloadPlugins];
0214         } else if ((![device isPaired]) && [device isReachable]) {
0215             [_visibleDevicesList setValue:[device _name] forKey:[device _id]];
0216         }
0217     }
0218     NSDictionary* list=[NSDictionary dictionaryWithObjectsAndKeys:
0219                         _connectedDevicesList,  @"connected",
0220                         _visibleDevicesList,    @"visible",
0221                         _rememberedDevicesList, @"remembered",nil];
0222     return list;
0223 }
0224 
0225 - (void) pairDevice:(NSString*)deviceId;
0226 {
0227     os_log_with_type(logger, self.debugLogLevel, "bg pair device");
0228     Device* device=[_devices valueForKey:deviceId];
0229     if ([device isReachable]) {
0230         [device requestPairing];
0231     }
0232 }
0233 
0234 /// @remark This should be the ONLY method used for unpairing Devices, DO NOT call the device's own unpair() method as it DOES NOT remove the device from the Arrays like this one does. For other files already using _backgroundServiceDelegate AKA ConnectedDevicesViewModel, use unpairFromBackgroundServiceInstance() in that. That's the same thing as calling this
0235 - (void)unpairDevice:(NSString *)deviceId {
0236     os_log_with_type(logger, self.debugLogLevel, "bg unpair device");
0237     Device* device=[_devices valueForKey:deviceId];
0238     if ([device isReachable]) {
0239         [device unpair];
0240     } else {
0241         // we'll also be calling this to unpair remembered (unReachable) devices
0242         [device setAsUnpaired];
0243         [_devices removeObjectForKey:deviceId];
0244     }
0245     [self onDeviceUnpaired:device];
0246 }
0247 
0248 - (void)refreshVisibleDeviceList {
0249     NSMutableArray<Device *> *newVisibleDevices = [[NSMutableArray alloc] init];
0250     
0251     for (Device* device in [_devices allValues]) {
0252         if ([device isReachable]) {
0253             [newVisibleDevices addObject:device];
0254         }
0255     }
0256     BOOL updated;
0257     @synchronized (_visibleDevices) {
0258         updated = ![newVisibleDevices isEqualToArray:_visibleDevices];
0259         os_log_with_type(logger, self.debugLogLevel,
0260                          "bg on device refresh visible device list, %{public}@",
0261                          updated ? @"UPDATED" : @"NO UPDATE");
0262         if (updated) {
0263             [_visibleDevices setArray:newVisibleDevices];
0264         }
0265     }
0266     if (_backgroundServiceDelegate && updated) {
0267         [_backgroundServiceDelegate onDevicesListUpdatedWithDevicesListsMap:[self getDevicesLists]];
0268     }
0269 }
0270 
0271 #pragma mark reactions
0272 - (void) onDeviceReachableStatusChanged:(Device*)device
0273 {   // TODO: Is this what gets called when paired device goes offline/becomes "remembered device"
0274     // FIXME: NOOOOOOOOOO ITS NOT, must be somewhere else, but this is called by Device when links == 0 aka unreachable????
0275     os_log_with_type(logger, self.debugLogLevel, "bg on device reachable status changed");
0276     if (![device isReachable]) {
0277         os_log_with_type(logger, self.debugLogLevel, "bg device not reachable");
0278         os_log_with_type(logger, OS_LOG_TYPE_INFO, "%{mask.hash}@", [device _id]);
0279         //[_backgroundServiceDelegate currDeviceDetailsViewDisconnectedFromRemote:[device _id]];
0280     }
0281     if (![device isPaired] && ![device isReachable]) {
0282         [_devices removeObjectForKey:[device _id]];
0283         os_log_with_type(logger, self.debugLogLevel, "bg destroy device");
0284     }
0285     //[self refreshDiscovery];
0286     [self refreshVisibleDeviceList]; // might want to reverse this after figuring out why refreshDiscovery is causing Plugins to disappear
0287 }
0288 
0289 - (void) onNetworkChange
0290 {
0291     os_log_with_type(logger, self.debugLogLevel, "bg on network change");
0292     for (LanLinkProvider* lp in _linkProviders){
0293         [lp onNetworkChange];
0294     }
0295 }
0296 
0297 - (void) onConnectionReceived:(NetworkPackage *)np link:(BaseLink *)link
0298 {
0299     os_log_with_type(logger, self.debugLogLevel, "bg on connection received");
0300     NSString* deviceId=[np objectForKey:@"deviceId"];
0301     os_log_with_type(logger, OS_LOG_TYPE_INFO, "Device discovered: %{mask.hash}@",deviceId);
0302     if ([_devices valueForKey:deviceId]) {
0303         os_log_with_type(logger, self.debugLogLevel, "known device");
0304         Device* device=[_devices objectForKey:deviceId];
0305         [device updateInfoWithNetworkPackage:np];
0306         [device addLink:link];
0307         [_backgroundServiceDelegate onDevicesListUpdatedWithDevicesListsMap:[self getDevicesLists]];
0308     }
0309     else{
0310         os_log_with_type(logger, OS_LOG_TYPE_INFO,
0311                          "new device from network package: %{public}@",
0312                          np._Id);
0313         Device *device=[[Device alloc] initWithNetworkPackage:np link:link delegate:self];
0314         [_devices setObject:device forKey:deviceId];
0315         [self refreshVisibleDeviceList];
0316     }
0317 }
0318 
0319 - (void)onDeviceIdentityUpdatePackageReceived:(NetworkPackage *)np {
0320     NSString *deviceID = [np objectForKey:@"deviceId"];
0321     os_log_with_type(logger, self.debugLogLevel,
0322                      "on identity update for %{mask.hash}@ received",
0323                      deviceID);
0324     Device *device = [_devices objectForKey:deviceID];
0325     if (device) {
0326         [device updateInfoWithNetworkPackage:np];
0327         [_backgroundServiceDelegate onDevicesListUpdatedWithDevicesListsMap:[self getDevicesLists]];
0328     } else {
0329         os_log_with_type(logger, OS_LOG_TYPE_FAULT,
0330                          "missing device %{mask.hash}@ to update for",
0331                          deviceID);
0332     }
0333 }
0334 
0335 - (void) onLinkDestroyed:(BaseLink *)link
0336 {
0337     os_log_with_type(logger, self.debugLogLevel, "bg on link destroyed");
0338     for (BaseLinkProvider* lp in _linkProviders) {
0339         [lp onLinkDestroyed:link];
0340     }
0341 }
0342 
0343 - (void) onDevicePairRequest:(Device *)device
0344 {
0345     os_log_with_type(logger, self.debugLogLevel, "bg on device pair request");
0346     if (_backgroundServiceDelegate) {
0347         [_backgroundServiceDelegate onPairRequest:[device _id]];
0348     }
0349 }
0350 
0351 - (void) onDevicePairTimeout:(Device*)device
0352 {
0353     os_log_with_type(logger, self.debugLogLevel, "bg on device pair timeout");
0354     if (_backgroundServiceDelegate) {
0355         [_backgroundServiceDelegate onPairTimeout:[device _id]];
0356     }
0357     [_settings removeObjectForKey:[device _id]];
0358     [[NSUserDefaults standardUserDefaults] setObject:_settings forKey:@"savedDevices"];
0359 }
0360 
0361 - (void) onDevicePairSuccess:(Device*)device
0362 {
0363     //NSLog(@"%lu", [device _type]);
0364     os_log_with_type(logger, self.debugLogLevel, "bg on device pair success");
0365     if (_backgroundServiceDelegate) {
0366         [_backgroundServiceDelegate onPairSuccess:[device _id]];
0367     }
0368     //[device setAsPaired]; is already called in the caller of this method
0369     NSError* error;
0370     NSData* deviceData = [NSKeyedArchiver archivedDataWithRootObject:device requiringSecureCoding:YES error:&error];
0371     os_log_with_type(logger, OS_LOG_TYPE_INFO, "device object with pair status %lu encoded into UserDefaults as: %{public}@ with error: %{public}@", [device _pairStatus], deviceData, error);
0372     [_settings setValue:deviceData forKey:[device _id]]; //[device _name]
0373     [[NSUserDefaults standardUserDefaults] setObject:_settings forKey:@"savedDevices"];
0374 }
0375 
0376 - (void) onDevicePairRejected:(Device*)device
0377 {
0378     os_log_with_type(logger, self.debugLogLevel, "bg on device pair rejected");
0379     if (_backgroundServiceDelegate) {
0380         [_backgroundServiceDelegate onPairRejected:[device _id]];
0381     }
0382     [_settings removeObjectForKey:[device _id]];
0383     [[NSUserDefaults standardUserDefaults] setObject:_settings forKey:@"savedDevices"];
0384 }
0385 
0386 - (void)onDeviceUnpaired:(Device *)device {
0387     NSString *deviceId = [device _id];
0388     os_log_with_type(logger, OS_LOG_TYPE_INFO, "bg on device unpair %{mask.hash}@", deviceId);
0389     [_settings removeObjectForKey:deviceId];
0390     [[NSUserDefaults standardUserDefaults] setObject:_settings forKey:@"savedDevices"];
0391     device._SHA256HashFormatted = nil;
0392     BOOL status = [_certificateService deleteRemoteDeviceSavedCertWithDeviceId:deviceId];
0393     os_log_with_type(logger, OS_LOG_TYPE_INFO, "Device remove, stored cert also removed with status %d", status);
0394     if (_backgroundServiceDelegate) {
0395         [_backgroundServiceDelegate onDevicesListUpdatedWithDevicesListsMap:[self getDevicesLists]];
0396     }
0397 }
0398 
0399 - (void) reloadAllPlugins
0400 {
0401     for (Device* device in _devices) { //_visibleDevices
0402         [device reloadPlugins];
0403     }
0404 }
0405 
0406 @end
0407