diff --git a/src/rpcclient/rpcclient/darwin/client.py b/src/rpcclient/rpcclient/darwin/client.py index 230dff05..38d9e9e6 100644 --- a/src/rpcclient/rpcclient/darwin/client.py +++ b/src/rpcclient/rpcclient/darwin/client.py @@ -10,6 +10,7 @@ from rpcclient.darwin.consts import kCFNumberSInt64Type, kCFNumberDoubleType from rpcclient.darwin.fs import DarwinFs from rpcclient.darwin.ioregistry import IORegistry +from rpcclient.darwin.location import Location from rpcclient.darwin.media import DarwinMedia from rpcclient.darwin.network import DarwinNetwork from rpcclient.darwin.objective_c_symbol import ObjectiveCSymbol @@ -58,6 +59,7 @@ def __init__(self, sock, sysname: str, hostname: str, port: int = None): self.media = DarwinMedia(self) self.network = DarwinNetwork(self) self.ioregistry = IORegistry(self) + self.location = Location(self) @property def modules(self) -> typing.List[str]: diff --git a/src/rpcclient/rpcclient/darwin/location.py b/src/rpcclient/rpcclient/darwin/location.py new file mode 100644 index 00000000..8cca605a --- /dev/null +++ b/src/rpcclient/rpcclient/darwin/location.py @@ -0,0 +1,57 @@ +from enum import Enum +from typing import Mapping + +from rpcclient.exceptions import MissingLibraryError, PermissionDeniedError + + +class CLAuthorizationStatus(Enum): + kCLAuthorizationStatusNotDetermined = 0 + kCLAuthorizationStatusRestricted = 1 + kCLAuthorizationStatusDenied = 2 + kCLAuthorizationStatusAuthorizedAlways = 3 + kCLAuthorizationStatusAuthorizedWhenInUse = 4 + kCLAuthorizationStatusAuthorized = kCLAuthorizationStatusAuthorizedAlways + + @classmethod + def from_value(cls, value: int): + for i in cls: + if i.value == value: + return i + + +class Location: + """ + Wrapper to CLLocationManager + + For details: https://developer.apple.com/documentation/corelocation/cllocationmanager?language=objc + """ + + def __init__(self, client): + self._client = client + + if not self._client.dlopen('/System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation', 2): + raise MissingLibraryError('failed to load CoreLocation') + + self._location_manager = self._client.symbols.objc_getClass('CLLocationManager').objc_call('sharedManager') + + @property + def authorization_status(self) -> CLAuthorizationStatus: + return CLAuthorizationStatus.from_value(self._location_manager.objc_call('authorizationStatus')) + + @property + def last_sample(self) -> Mapping: + return self._location_manager.objc_call('location').objc_call('jsonObject').py + + def start_updating_location(self): + if self.authorization_status.value < CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways.value: + raise PermissionDeniedError() + self._location_manager.objc_call('startUpdatingLocation') + + def stop_updating_location(self): + self._location_manager.objc_call('stopUpdatingLocation') + + def request_oneshot_location(self): + """ Requests the one-time delivery of the user’s current location. """ + if self.authorization_status.value < CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways.value: + raise PermissionDeniedError() + self._location_manager.objc_call('requestLocation') diff --git a/src/rpcclient/rpcclient/exceptions.py b/src/rpcclient/rpcclient/exceptions.py index 85dd3f51..05067ee4 100644 --- a/src/rpcclient/rpcclient/exceptions.py +++ b/src/rpcclient/rpcclient/exceptions.py @@ -48,3 +48,11 @@ class UnrecognizedSelector(RpcClientException): class GettingObjectiveCClassError(RpcClientException): pass + + +class MissingLibraryError(RpcClientException): + pass + + +class PermissionDeniedError(RpcClientException): + pass