def icloud_api(): from pyicloud import PyiCloudService api = PyiCloudService('*****@*****.**', 'Div32190') if api.requires_2sa: import click print("Two-step authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) print(api.iphone.location()) print("Files =============>", api.files.dir()) print("Archives ==========>", api.archives.dir())
def _connect(user, password): """ Connect to the icloud :param user: the icloud user id :param password: the icloud password :return a reference to the icloud """ api = PyiCloudService(user, password) if api.requires_2sa: # this attribute is added by the patched pyicloud at https://github.com/picklepete/pyicloud.git import click print( "Two-step authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) return api
def __logIntoiCloud(self): global iCloudApi print("Logging into iCloud") iCloudApi = PyiCloudService(self.icloud_email, self.icloud_password) if iCloudApi.requires_2sa: print( "Two-factor authentication required. Your trusted devices are:" ) devices = iCloudApi.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get("deviceName", "SMS to %s" % device.get("phoneNumber")))) device = click.prompt("Which device would you like to use?", default=0) device = devices[device] if not iCloudApi.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt("Please enter validation code") if not iCloudApi.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1)
def login(): """ Get access to Icloud """ api = PyiCloudService(icloud, passw) if api.requires_2sa: print("Two-step authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) print("Devices available:\n {}".format(api.devices)) device = click.prompt('Which device would you like to select?', default=0) api.devices[device] return api
def authenticate(username, password): print("Signing in...") if password: icloud = PyiCloudService(username, password) else: icloud = PyiCloudService(username) if icloud.requires_2fa: print "Two-factor authentication required. Your trusted devices are:" devices = icloud.trusted_devices for i, device in enumerate(devices): print " %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber'))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not icloud.send_verification_code(device): print "Failed to send verification code" sys.exit(1) code = click.prompt('Please enter validation code') if not icloud.validate_verification_code(device, code): print "Failed to verify verification code" sys.exit(1) return icloud
def initIcloud(username, password): global icloud if not icloud: icloud = PyiCloudService(username, password) # two-factor authentication if icloud.requires_2fa: import click print "Two-factor authentication required. Your trusted devices are:" devices = icloud.trusted_devices for i, device in enumerate(devices): print " %s: %s" % (i, device.get( 'deviceName', "SMS to %s" % device.get('phoneNumber'))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not icloud.send_verification_code(device): print "Failed to send verification code" return False code = click.prompt('Please enter validation code') if not icloud.validate_verification_code(device, code): print "Failed to verify verification code" return False return True
def authenticate(username, password): """attempt to authenticate user using provided credentials""" icloud = PyiCloudService(username, password) if icloud.requires_2sa: print("Two-factor authentication required. Your trusted devices are:") devices = icloud.trusted_devices for i, device in enumerate(devices): print(" {0}: {1}".format( i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not icloud.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not icloud.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) return icloud
def auth(username, password): print("認証を受けます。") sys.stdout.flush() api = PyiCloudService(username, password) print("認証が終わりました。") sys.stdout.flush() if api.requires_2sa: import click print("Two-step authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) return api
def main(): config = conf.Config() username = config.params.get('icloud', {}).get('username', None) password = config.params.get('icloud', {}).get('password', None) api = PyiCloudService(username, password) if api.requires_2fa: print ("Two-step authentisation required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print (" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print ("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print ("Failed to verify verification code") sys.exit(1)
def authenticate(username, password): if password: icloud = PyiCloudService(username, password) else: icloud = PyiCloudService(username) if icloud.requires_2fa: print "Two-factor authentication required. Your trusted devices are:" devices = icloud.trusted_devices for i, device in enumerate(devices): print " %s: %s" % (i, device.get( 'deviceName', "SMS to %s" % device.get('phoneNumber'))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not icloud.send_verification_code(device): print "Failed to send verification code" sys.exit(1) code = click.prompt('Please enter validation code') if not icloud.validate_verification_code(device, code): print "Failed to verify verification code" sys.exit(1) print "Great, you're all set up. Now re-run the script to print out filenames." sys.exit(1) return icloud
def setup_icloud(): # name = raw_input("Enter the name: ") username = raw_input("Username: "******"Password: "******"Two-factor authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print " %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber'))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) # twofactor_choice = device twofactor_timestamp = datetime.datetime.now().strftime(_dateformat) # # except PyiCloudFailedLoginException: print("\n!! Username/password combination is incorrect - please try again !!") print("\n****************************************************************\n") return except: print("\n!! An error has ocurred - please try again !!") print("\n****************************************************************\n") return # new_acc = {} new_acc['account_type'] = 'icloud_account' new_acc['account_name'] = name new_acc['account_id'] = name.lower().replace(' ', '_').replace('\'', '') new_acc['details'] = {'username': username, 'password': password, '2factor': {'2factor_required': api.requires_2fa, '2factor_choice': twofactor_choice, '2factor_timestamp': twofactor_timestamp}} new_acc['details_public'] = {} # return new_acc
def connect_to_icloud( self, config: Dict[str, Union[str, bool, None]]) -> PyiCloudService: self.logger.info("Connecting to iCloud…") if config["password"] == "": api = PyiCloudService(config["username"]) else: api = PyiCloudService(config["username"], config["password"]) if api.requires_2sa: print("Two-step authentication required.") if click.confirm( "Have you received authentication request on any of your devices?" ): verification_function = lambda code: api.validate_2fa_code(code ) else: print("Fallback to SMS verification.") print("Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" {}: {}".format( i, device.get( "deviceName", "SMS to {}".format(device.get("phoneNumber")), ), )) device = click.prompt("Which device would you like to use?", default=0) device = devices[device] if not api.send_verification_code(device): raise Exception("Failed to send verification code") verification_function = lambda code: api.validate_verification_code( device, code) verified = False while not verified: code = click.prompt("Please enter validation code") verified = verification_function(code) self.logger.debug("Verification result: %s", verified) if verified: print("Succeed") else: print( "Failed to verify verification code, retry (Ctrl-C to cancel)" ) return api
def validate_icloud(): api = PyiCloudService('*****@*****.**', 'REd#rED@1577?!?') print("Two-factor authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) if not api.send_verification_code(device[0]): print("Failed to send verification code") code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") print("iCloud Validated")
def authenticate(username, password): """attempt to authenticate user using provided credentials""" api = PyiCloudService(username, password) if api.requires_2fa: print("Two-factor authentication required.") code = input( "Enter the code you received of one of your approved devices: ") result = api.validate_2fa_code(code) print("Code validation result: %s" % result) if not result: print("Failed to verify security code") sys.exit(1) if not api.is_trusted_session: print("Session is not trusted. Requesting trust...") result = api.trust_session() print("Session trust result %s" % result) if not result: print( "Failed to request trust. You will likely be prompted for the code again in the coming weeks" ) elif api.requires_2sa: import click print("Two-step authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) return api
def login(self, username, password): api = PyiCloudService(username, password) if api.requires_2fa: logging.info( f"ICloud with mail {username} required Two-factor authentication!" ) result = api.validate_2fa_code(self.__get_2fa_code()) if not result: logging.critical( f"Failed to verify security code of ICloud-Account with mail {username}" ) self.connected = False if not api.is_trusted_session: logging.info( f"Session for mail, {username} is not trusted. Requesting trust..." ) result = api.trust_session() print(f"Session trust for mail {username} result '{result}'") if not result: logging.critical( "Failed to request trust. You will likely be prompted for the code again in the coming weeks!" ) self.connected = False elif api.requires_2sa: import click devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): logging.critical("Failed to send verification code") code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): logging.critical("Failed to verify verification code!") self.connected = False return api
def authenticate(username, password, \ smtp_username, smtp_password, smtp_host, smtp_port, smtp_no_tls, \ notification_email): if password: icloud = PyiCloudService(username, password) else: icloud = PyiCloudService(username) if icloud.requires_2sa: if smtp_username and smtp_password: # If running in the background, send a notification email. send_two_step_expired_notification(smtp_username, smtp_password, \ smtp_host, smtp_port, smtp_no_tls, notification_email) exit() print("Two-factor authentication required. Your trusted devices are:") devices = icloud.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not icloud.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not icloud.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) print( "Great, you're all set up. The script can now be run without user interaction." ) print( "You can set up email notifications to receive a notification when two-step authentication expires." ) print("Use --help to view information about SMTP options.") sys.exit(1) return icloud
def authenticate_icloud(folder): username = click.prompt('Please enter your ICloud email').strip() password = click.prompt('Please enter your ICloud password').strip() tmp = os.path.join(tempfile.gettempdir(), 'photo-transfer-icloud') api = PyiCloudService(username, password, cookie_directory=folder) if api.requires_2sa: device = api.trusted_devices[0] phone = device.get('phoneNumber') if not api.send_verification_code(device): raise Exception(f'Failed to send verification code to {phone}') code = click.prompt(f'Please enter verification code sent to {phone}') if not api.validate_verification_code(device, code): raise Exception('Failed to verify verification code') with open(os.path.join(folder, 'icloud.json'), 'w') as file: file.write(f'{{"username":"******", "password":"******"}}') print('ICloud authentication succeeded')
class ICloudCalendar: def __init__(self, username, password): self.connected = False self.verification_code_required = False self.api = PyiCloudService(username, password) if self.api.requires_2sa: self.verification_code_required = True else: self.connected = True def get_devices(self): return self.api.devices def send_code(self, device): if not self.connected: if not self.api.send_verification_code(device): raise Exception('Could not send verification code') else: raise Exception("Already connected") def validate_code(self, device, code): if not self.connected: if not self.api.validate_verification_code(device, code): raise Exception("Invalid verification code") else: self.verification_code_required = False self.connected = True else: raise Exception("Already connected") def get_events(self, start, end): if self.connected: return self.api.calendar.events(from_dt=start, to_dt=end) else: if self.verification_code_required: raise Exception("Not connected : Verification code is missing") else: raise Exception("Not connected")
def authenticate(username, password, smtp_username, smtp_password, notification_email): if password: icloud = PyiCloudService(username, password) else: icloud = PyiCloudService(username) # Fixes bug in pyicloud - https://github.com/picklepete/pyicloud/pull/149 # Rename to requires_2sa (with fallback) after this is merged. if hasattr(icloud, 'requires_2sa'): icloud.requires_2fa = icloud.requires_2sa if icloud.requires_2fa: if smtp_username and smtp_password: # If running in the background, send a notification email. send_two_step_expired_notification(smtp_username, smtp_password, notification_email) exit() print("Two-factor authentication required. Your trusted devices are:") devices = icloud.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not icloud.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not icloud.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) print("Great, you're all set up. Now re-run the script to print out filenames.") sys.exit(1) return icloud
def main(): iCloud_Account = os.getenv('ACCOUNT', '') DownloadFolder = '/Downloads' os.chdir(DownloadFolder) if len(iCloud_Account) == 0: iCloud_Account = input('Enter iCloud Account: ') password = getpass('Enter password of "%s": ' % iCloud_Account) api = PyiCloudService(iCloud_Account, password) if api.requires_2sa: if not api.send_verification_code(api.trusted_devices[0]): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(api.trusted_devices[0], code): print("Failed to verify verification code") sys.exit(1) num = len(api.photos.albums) names = dict(zip(range(num), api.photos.albums.keys())) albums = dict(zip(range(num), api.photos.albums.values())) running = True while running: print("Found albums:") for i in range(num): print("%2d) %s" % (i, names[i])) option = click.prompt('Please choose an album to download: ') try: option = int(option) except: print("Not an integer.") sys.exit(1) if option < 0 or option >= num: running = False continue download_album(names[option], albums[option])
def getLight(): global gps apple_id = "" password = "" conn = sqlite3.connect('id_pass.db') cur = conn.cursor() cur.execute("SELECT * FROM pass") data = cur.fetchall() if len(data) == 0: conn.close() print("Error fetching data") elif len(data) > 0: for item in data: apple_id = str(item[0]) password = str(item[1]) conn.close() api = PyiCloudService(apple_id, password) if api.requires_2fa: import click print("Two-factor authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName',"SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) tmp = json.loads(json.dumps(api.iphone.location())) tmp2 = str(tmp["longitude"]) + "," + str(tmp["latitude"]) return {"gps":tmp2}
def log(): global api api = PyiCloudService('*****@*****.**', '') if api.requires_2sa: import click print("Two-step authentication required. Your trusted devices are:") devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = devices[0] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = input("code: ") if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1)
class Icloud(DeviceScanner): """Representation of an iCloud account.""" def __init__(self, hass, username, password, name, see, filter_devices): """Initialize an iCloud account.""" self.hass = hass self.username = username self.password = password self.api = None self.accountname = name self.filter_devices = filter_devices self.devices = {} self.seen_devices = {} self._overridestates = {} self._intervals = {} self.see = see self._trusted_device = None self._verification_code = None self._attrs = {} self._attrs[ATTR_ACCOUNTNAME] = name self.reset_account_icloud() randomseconds = random.randint(10, 59) track_utc_time_change( self.hass, self.keep_alive, second=randomseconds) def reset_account_icloud(self): """Reset an iCloud account.""" from pyicloud import PyiCloudService from pyicloud.exceptions import ( PyiCloudFailedLoginException, PyiCloudNoDevicesException) icloud_dir = self.hass.config.path('icloud') if not os.path.exists(icloud_dir): os.makedirs(icloud_dir) try: self.api = PyiCloudService( self.username, self.password, cookie_directory=icloud_dir, verify=True) except PyiCloudFailedLoginException as error: self.api = None _LOGGER.error("Error logging into iCloud Service: %s", error) return try: self.devices = {} self._overridestates = {} self._intervals = {} for device in self.api.devices: status = device.status(DEVICESTATUSSET) devicename = slugify(status['name'].replace(' ', '', 99)) if devicename not in self.devices: self.devices[devicename] = device self._intervals[devicename] = 1 self._overridestates[devicename] = None except PyiCloudNoDevicesException: _LOGGER.error('No iCloud Devices found!') def icloud_trusted_device_callback(self, callback_data): """Handle chosen trusted devices.""" self._trusted_device = int(callback_data.get('trusted_device')) self._trusted_device = self.api.trusted_devices[self._trusted_device] if not self.api.send_verification_code(self._trusted_device): _LOGGER.error("Failed to send verification code") self._trusted_device = None return if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = self.hass.components.configurator configurator.request_done(request_id) # Trigger the next step immediately self.icloud_need_verification_code() def icloud_need_trusted_device(self): """We need a trusted device.""" configurator = self.hass.components.configurator if self.accountname in _CONFIGURING: return devicesstring = '' devices = self.api.trusted_devices for i, device in enumerate(devices): devicename = device.get( 'deviceName', 'SMS to %s' % device.get('phoneNumber')) devicesstring += "{}: {};".format(i, devicename) _CONFIGURING[self.accountname] = configurator.request_config( 'iCloud {}'.format(self.accountname), self.icloud_trusted_device_callback, description=( 'Please choose your trusted device by entering' ' the index from this list: ' + devicesstring), entity_picture="/static/images/config_icloud.png", submit_caption='Confirm', fields=[{'id': 'trusted_device', 'name': 'Trusted Device'}] ) def icloud_verification_callback(self, callback_data): """Handle the chosen trusted device.""" from pyicloud.exceptions import PyiCloudException self._verification_code = callback_data.get('code') try: if not self.api.validate_verification_code( self._trusted_device, self._verification_code): raise PyiCloudException('Unknown failure') except PyiCloudException as error: # Reset to the initial 2FA state to allow the user to retry _LOGGER.error("Failed to verify verification code: %s", error) self._trusted_device = None self._verification_code = None # Trigger the next step immediately self.icloud_need_trusted_device() if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = self.hass.components.configurator configurator.request_done(request_id) def icloud_need_verification_code(self): """Return the verification code.""" configurator = self.hass.components.configurator if self.accountname in _CONFIGURING: return _CONFIGURING[self.accountname] = configurator.request_config( 'iCloud {}'.format(self.accountname), self.icloud_verification_callback, description=('Please enter the validation code:'), entity_picture="/static/images/config_icloud.png", submit_caption='Confirm', fields=[{'id': 'code', 'name': 'code'}] ) def keep_alive(self, now): """Keep the API alive.""" if self.api is None: self.reset_account_icloud() if self.api is None: return if self.api.requires_2fa: from pyicloud.exceptions import PyiCloudException try: if self._trusted_device is None: self.icloud_need_trusted_device() return if self._verification_code is None: self.icloud_need_verification_code() return self.api.authenticate() if self.api.requires_2fa: raise Exception('Unknown failure') self._trusted_device = None self._verification_code = None except PyiCloudException as error: _LOGGER.error("Error setting up 2FA: %s", error) #else: # self.api.authenticate() currentminutes = dt_util.now().hour * 60 + dt_util.now().minute try: for devicename in self.devices: interval = self._intervals.get(devicename, 1) if ((currentminutes % interval == 0) or (interval > 10 and currentminutes % interval in [2, 4])): if (self.filter_devices in devicename): _LOGGER.debug("Updating device " + devicename) self.api.authenticate() self.update_device(devicename) except ValueError: _LOGGER.debug("iCloud API returned an error") def determine_interval(self, devicename, latitude, longitude, battery): """Calculate new interval.""" distancefromhome = None zone_state = self.hass.states.get('zone.home') zone_state_lat = zone_state.attributes['latitude'] zone_state_long = zone_state.attributes['longitude'] distancefromhome = distance( latitude, longitude, zone_state_lat, zone_state_long) distancefromhome = round(distancefromhome / 1000, 1) currentzone = active_zone(self.hass, latitude, longitude) if ((currentzone is not None and currentzone == self._overridestates.get(devicename)) or (currentzone is None and self._overridestates.get(devicename) == 'away')): return self._overridestates[devicename] = None if currentzone is not None: self._intervals[devicename] = 30 return if distancefromhome is None: return if distancefromhome > 25: self._intervals[devicename] = round(distancefromhome / 2, 0) elif distancefromhome > 10: self._intervals[devicename] = 5 else: self._intervals[devicename] = 1 if battery is not None and battery <= 33 and distancefromhome > 3: self._intervals[devicename] = self._intervals[devicename] * 2 def update_device(self, devicename): """Update the device_tracker entity.""" from pyicloud.exceptions import PyiCloudNoDevicesException # An entity will not be created by see() when track=false in # 'known_devices.yaml', but we need to see() it at least once entity = self.hass.states.get(ENTITY_ID_FORMAT.format(devicename)) if entity is None and devicename in self.seen_devices: return attrs = {} kwargs = {} if self.api is None: return if self.filter_devices not in devicename: return try: for device in self.api.devices: if str(device) != str(self.devices[devicename]): continue status = device.status(DEVICESTATUSSET) dev_id = status['name'].replace(' ', '', 99) dev_id = slugify(dev_id) attrs[ATTR_DEVICESTATUS] = DEVICESTATUSCODES.get( status['deviceStatus'], 'error') attrs[ATTR_LOWPOWERMODE] = status['lowPowerMode'] attrs[ATTR_BATTERYSTATUS] = status['batteryStatus'] attrs[ATTR_ACCOUNTNAME] = self.accountname status = device.status(DEVICESTATUSSET) battery = status.get('batteryLevel', 0) * 100 location = status['location'] if location: self.determine_interval( devicename, location['latitude'], location['longitude'], battery) interval = self._intervals.get(devicename, 1) attrs[ATTR_INTERVAL] = interval accuracy = location['horizontalAccuracy'] kwargs['dev_id'] = dev_id kwargs['host_name'] = status['name'] kwargs['gps'] = (location['latitude'], location['longitude']) kwargs['battery'] = battery kwargs['gps_accuracy'] = accuracy kwargs[ATTR_ATTRIBUTES] = attrs self.see(**kwargs) self.seen_devices[devicename] = True except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found") def lost_iphone(self, devicename): """Call the lost iPhone function if the device is found.""" if self.api is None: return self.api.authenticate() for device in self.api.devices: if devicename is None or device == self.devices[devicename]: device.play_sound() def update_icloud(self, devicename=None): """Authenticate against iCloud and scan for devices.""" from pyicloud.exceptions import PyiCloudNoDevicesException if self.api is None: return try: if devicename is not None: if devicename in self.devices: self.devices[devicename].location() else: _LOGGER.error("devicename %s unknown for account %s", devicename, self._attrs[ATTR_ACCOUNTNAME]) else: for device in self.devices: self.devices[device].location() except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found") def setinterval(self, interval=None, devicename=None): """Set the interval of the given devices.""" devs = [devicename] if devicename else self.devices for device in devs: devid = '{}.{}'.format(DOMAIN, device) devicestate = self.hass.states.get(devid) if interval is not None: if devicestate is not None: self._overridestates[device] = active_zone( self.hass, float(devicestate.attributes.get('latitude', 0)), float(devicestate.attributes.get('longitude', 0))) if self._overridestates[device] is None: self._overridestates[device] = 'away' self._intervals[device] = interval else: self._overridestates[device] = None self.update_device(device)
def main(args=None): """Main commandline entrypoint.""" if args is None: args = sys.argv[1:] parser = argparse.ArgumentParser( description="Find My iPhone CommandLine Tool") parser.add_argument( "--username", action="store", dest="username", default="", help="Apple ID to Use", ) parser.add_argument( "--password", action="store", dest="password", default="", help=("Apple ID Password to Use; if unspecified, password will be " "fetched from the system keyring."), ) parser.add_argument( "-n", "--non-interactive", action="store_false", dest="interactive", default=True, help="Disable interactive prompts.", ) parser.add_argument( "--delete-from-keyring", action="store_true", dest="delete_from_keyring", default=False, help="Delete stored password in system keyring for this username.", ) parser.add_argument( "--list", action="store_true", dest="list", default=False, help="Short Listings for Device(s) associated with account", ) parser.add_argument( "--llist", action="store_true", dest="longlist", default=False, help="Detailed Listings for Device(s) associated with account", ) parser.add_argument( "--locate", action="store_true", dest="locate", default=False, help="Retrieve Location for the iDevice (non-exclusive).", ) # Restrict actions to a specific devices UID / DID parser.add_argument( "--device", action="store", dest="device_id", default=False, help="Only effect this device", ) # Trigger Sound Alert parser.add_argument( "--sound", action="store_true", dest="sound", default=False, help="Play a sound on the device", ) # Trigger Message w/Sound Alert parser.add_argument( "--message", action="store", dest="message", default=False, help="Optional Text Message to display with a sound", ) # Trigger Message (without Sound) Alert parser.add_argument( "--silentmessage", action="store", dest="silentmessage", default=False, help="Optional Text Message to display with no sounds", ) # Lost Mode parser.add_argument( "--lostmode", action="store_true", dest="lostmode", default=False, help="Enable Lost mode for the device", ) parser.add_argument( "--lostphone", action="store", dest="lost_phone", default=False, help="Phone Number allowed to call when lost mode is enabled", ) parser.add_argument( "--lostpassword", action="store", dest="lost_password", default=False, help="Forcibly active this passcode on the idevice", ) parser.add_argument( "--lostmessage", action="store", dest="lost_message", default="", help="Forcibly display this message when activating lost mode.", ) # Output device data to an pickle file parser.add_argument( "--outputfile", action="store_true", dest="output_to_file", default="", help="Save device data to a file in the current directory.", ) command_line = parser.parse_args(args) username = command_line.username password = command_line.password if username and command_line.delete_from_keyring: utils.delete_password_in_keyring(username) failure_count = 0 while True: # Which password we use is determined by your username, so we # do need to check for this first and separately. if not username: parser.error("No username supplied") if not password: password = utils.get_password(username, interactive=command_line.interactive) if not password: parser.error("No password supplied") try: api = PyiCloudService(username.strip(), password.strip()) if (not utils.password_exists_in_keyring(username) and command_line.interactive and confirm("Save password in keyring?")): utils.store_password_in_keyring(username, password) if api.requires_2sa: # fmt: off print("\nTwo-step authentication required.", "\nYour trusted devices are:") # fmt: on devices = api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % ( i, device.get("deviceName", "SMS to %s" % device.get("phoneNumber")), )) print("\nWhich device would you like to use?") device = int(input("(number) --> ")) device = devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) print("\nPlease enter validation code") code = input("(string) --> ") if not api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) print("") break except PyiCloudFailedLoginException: # If they have a stored password; we just used it and # it did not work; let's delete it if there is one. if utils.password_exists_in_keyring(username): utils.delete_password_in_keyring(username) message = "Bad username or password for {username}".format( username=username, ) password = None failure_count += 1 if failure_count >= 3: raise RuntimeError(message) print(message, file=sys.stderr) for dev in api.devices: if not command_line.device_id or ( command_line.device_id.strip().lower() == dev.content["id"].strip().lower()): # List device(s) if command_line.locate: dev.location() if command_line.output_to_file: create_pickled_data( dev, filename=(dev.content["name"].strip().lower() + ".fmip_snapshot"), ) contents = dev.content if command_line.longlist: print("-" * 30) print(contents["name"]) for key in contents: print("%20s - %s" % (key, contents[key])) elif command_line.list: print("-" * 30) print("Name - %s" % contents["name"]) print("Display Name - %s" % contents["deviceDisplayName"]) print("Location - %s" % contents["location"]) print("Battery Level - %s" % contents["batteryLevel"]) print("Battery Status- %s" % contents["batteryStatus"]) print("Device Class - %s" % contents["deviceClass"]) print("Device Model - %s" % contents["deviceModel"]) # Play a Sound on a device if command_line.sound: if command_line.device_id: dev.play_sound() else: raise RuntimeError("\n\n\t\t%s %s\n\n" % ( "Sounds can only be played on a singular device.", DEVICE_ERROR, )) # Display a Message on the device if command_line.message: if command_line.device_id: dev.display_message(subject="A Message", message=command_line.message, sounds=True) else: raise RuntimeError("%s %s" % ( "Messages can only be played on a singular device.", DEVICE_ERROR, )) # Display a Silent Message on the device if command_line.silentmessage: if command_line.device_id: dev.display_message( subject="A Silent Message", message=command_line.silentmessage, sounds=False, ) else: raise RuntimeError("%s %s" % ( "Silent Messages can only be played " "on a singular device.", DEVICE_ERROR, )) # Enable Lost mode if command_line.lostmode: if command_line.device_id: dev.lost_device( number=command_line.lost_phone.strip(), text=command_line.lost_message.strip(), newpasscode=command_line.lost_password.strip(), ) else: raise RuntimeError("%s %s" % ( "Lost Mode can only be activated on a singular device.", DEVICE_ERROR, )) sys.exit(0)
class ICloud(): def __init__(self): # self._icloud = PyiCloudService(get_cfg_details_account_username(), get_cfg_details_account_password()) # cache.cache['_2fa/2sv-complete'] = False # 2FA def check2fa(self): r = self._icloud.requires_2fa if r: return {'2fa': True} else: return {'2fa': False} def get_2fa_trusted_devices(self): if self._icloud.requires_2fa: devices = self._icloud.trusted_devices return {'2fa': True, 'devices': devices} else: return {'2fa': False, 'devices': []} def _get_2fa_trusted_device_default(self): devices = self.get_2fa_trusted_devices() if devices['2fa']: for d in devices['devices']: default_deviceType = get_cfg_details_2fa_deviceType() if d['deviceType'] == default_deviceType: if default_deviceType == 'SMS': return {'device': d} # if no default device found in list return { 'device': False, 'error': 'Jarvis does not hold a default devices that matches those returned by iCloud.' } return { 'device': False, 'error': 'iCloud has reported back that 2fa is not required for access to services.' } # note that 'device' must be in the correct dict structure def request_validation_code(self, device): if self._icloud.send_verification_code(device): return True else: return False def request_validation_code_default(self): # device = self._get_2fa_trusted_device_default() # if device['device']: return {'result': self.request_validation_code(device['device'])} else: return {'result': False, 'error': device['error']} # note that 'device' must be in the correct dict structure def validate_validation_code(self, device, code): if self._icloud.validate_verification_code(device, code): cache.cache['_2fa/2sv-complete'] = True return True else: return False def validate_validation_code_default(self, code): # device = self._get_2fa_trusted_device_default() # if device: return self.validate_validation_code(device, code) else: return False # Devices def get_devices(self): devices = self._icloud.devices return devices def get_iphone(self): devices = self._icloud.iphone return devices # Contacts def get_contacts(self): return self._icloud.contacts.all() # Calendar - events def get_events(self): return self._convert_icloud_events(self._icloud.calendar.events()) def get_events_today(self): return self._get_events(date.today(), date.today()) def get_events_tomorrow(self): return self._get_events(date.today() + timedelta(days=1), date.today() + timedelta(days=1)) def get_events_date(self, _date): return self._get_events(_date, _date) def get_events_daterange(self, dateFrom, dateTo): return self._get_events(dateFrom, dateTo) def _get_events(self, from_dt, to_dt): _events = self._icloud.calendar.events(from_dt, to_dt) _events = self._convert_icloud_events(_events) return _events # Calendar - birthdays def get_birthdays(self): contacts = self.get_contacts() birthdays = [] for contact in contacts: if 'birthday' in contact.keys(): # b = {'birthday': contact['birthday']} # if 'firstName' in contact.keys(): b['firstName'] = contact['firstName'] # if 'lastName' in contact.keys(): b['lastName'] = contact['lastName'] # birthdays.append(b) return birthdays @staticmethod def _get_calendar_details(_event): # calendar_name = get_cfg_details_calendar_name(_event['pGuid']) if not calendar_name: calendar_name = '' # calendar_colour = get_cfg_details_calendar_colour(_event['pGuid']) if not calendar_colour: calendar_colour = 'none' # return {'name': calendar_name, 'colour': calendar_colour} # guid = event id that is used in same event even across invitees # pGuid = calendar id unique to specific calendar and user def _convert_icloud_events(self, _events): # usedGuids = [] tempGuids = {} for event in _events: if event['guid'] in tempGuids.keys(): tempGuids[event['guid']].append( self._get_calendar_details(event)) else: tempGuids[event['guid']] = [self._get_calendar_details(event)] # new_events = [] for event in _events: if not event['guid'] in usedGuids: # put event in structure for json new_e = self._convert_icloud_event(event) # find all calendars for this event and add to new_e if event['guid'] in tempGuids.keys(): new_e['calendars'] = tempGuids[event['guid']] # new_events.append(new_e) usedGuids.append(event['guid']) # return new_events def _convert_icloud_event(self, _event): # return { 'title': _event['title'], 'location': _event['location'], 'start': self._convert_datetime_to_string( self._convert_icloud_to_datetime(_event['localStartDate'])), 'end': self._convert_datetime_to_string( self._convert_icloud_to_datetime(_event['localEndDate'])), 'duration': _event['duration'], 'allDay': _event['allDay'], 'calendars': [] } # Following function will get events for the next 52 weeks and identify all unique calendars def get_icloud_calendars(self): events = self._icloud.calendar.events( date.today(), date.today() + timedelta(weeks=52)) tempGuids = {} for event in events: if not event['pGuid'] in tempGuids.keys(): tempGuids[event['pGuid']] = self._get_calendar_details(event) return tempGuids @staticmethod def _convert_icloud_to_datetime(_icloud_datetime): # datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0) return datetime(_icloud_datetime[1], _icloud_datetime[2], _icloud_datetime[3], _icloud_datetime[4], _icloud_datetime[5]) @staticmethod def _convert_datetime_to_string(_datetime): return _datetime.strftime('%Y-%m-%d %H-%M')
class Icloud(DeviceScanner): """Representation of an iCloud account.""" def __init__(self, hass, username, password, name, see): """Initialize an iCloud account.""" self.hass = hass self.username = username self.password = password self.api = None self.accountname = name self.devices = {} self.seen_devices = {} self._overridestates = {} self._intervals = {} self.see = see self._trusted_device = None self._verification_code = None self._attrs = {} self._attrs[ATTR_ACCOUNTNAME] = name self.reset_account_icloud() randomseconds = random.randint(10, 59) track_utc_time_change( self.hass, self.keep_alive, second=randomseconds) def reset_account_icloud(self): """Reset an iCloud account.""" from pyicloud import PyiCloudService from pyicloud.exceptions import ( PyiCloudFailedLoginException, PyiCloudNoDevicesException) icloud_dir = self.hass.config.path('icloud') if not os.path.exists(icloud_dir): os.makedirs(icloud_dir) try: self.api = PyiCloudService( self.username, self.password, cookie_directory=icloud_dir, verify=True) except PyiCloudFailedLoginException as error: self.api = None _LOGGER.error("Error logging into iCloud Service: %s", error) return try: self.devices = {} self._overridestates = {} self._intervals = {} for device in self.api.devices: status = device.status(DEVICESTATUSSET) devicename = slugify(status['name'].replace(' ', '', 99)) if devicename not in self.devices: self.devices[devicename] = device self._intervals[devicename] = 1 self._overridestates[devicename] = None except PyiCloudNoDevicesException: _LOGGER.error('No iCloud Devices found!') def icloud_trusted_device_callback(self, callback_data): """Handle chosen trusted devices.""" self._trusted_device = int(callback_data.get('trusted_device')) self._trusted_device = self.api.trusted_devices[self._trusted_device] if not self.api.send_verification_code(self._trusted_device): _LOGGER.error("Failed to send verification code") self._trusted_device = None return if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = self.hass.components.configurator configurator.request_done(request_id) # Trigger the next step immediately self.icloud_need_verification_code() def icloud_need_trusted_device(self): """We need a trusted device.""" configurator = self.hass.components.configurator if self.accountname in _CONFIGURING: return devicesstring = '' devices = self.api.trusted_devices for i, device in enumerate(devices): devicename = device.get( 'deviceName', 'SMS to %s' % device.get('phoneNumber')) devicesstring += "{}: {};".format(i, devicename) _CONFIGURING[self.accountname] = configurator.request_config( 'iCloud {}'.format(self.accountname), self.icloud_trusted_device_callback, description=( 'Please choose your trusted device by entering' ' the index from this list: ' + devicesstring), entity_picture="/static/images/config_icloud.png", submit_caption='Confirm', fields=[{'id': 'trusted_device', 'name': 'Trusted Device'}] ) def icloud_verification_callback(self, callback_data): """Handle the chosen trusted device.""" from pyicloud.exceptions import PyiCloudException self._verification_code = callback_data.get('code') try: if not self.api.validate_verification_code( self._trusted_device, self._verification_code): raise PyiCloudException('Unknown failure') except PyiCloudException as error: # Reset to the initial 2FA state to allow the user to retry _LOGGER.error("Failed to verify verification code: %s", error) self._trusted_device = None self._verification_code = None # Trigger the next step immediately self.icloud_need_trusted_device() if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = self.hass.components.configurator configurator.request_done(request_id) def icloud_need_verification_code(self): """Return the verification code.""" configurator = self.hass.components.configurator if self.accountname in _CONFIGURING: return _CONFIGURING[self.accountname] = configurator.request_config( 'iCloud {}'.format(self.accountname), self.icloud_verification_callback, description=('Please enter the validation code:'), entity_picture="/static/images/config_icloud.png", submit_caption='Confirm', fields=[{'id': 'code', 'name': 'code'}] ) def keep_alive(self, now): """Keep the API alive.""" if self.api is None: self.reset_account_icloud() if self.api is None: return if self.api.requires_2fa: from pyicloud.exceptions import PyiCloudException try: if self._trusted_device is None: self.icloud_need_trusted_device() return if self._verification_code is None: self.icloud_need_verification_code() return self.api.authenticate() if self.api.requires_2fa: raise Exception('Unknown failure') self._trusted_device = None self._verification_code = None except PyiCloudException as error: _LOGGER.error("Error setting up 2FA: %s", error) else: self.api.authenticate() currentminutes = dt_util.now().hour * 60 + dt_util.now().minute try: for devicename in self.devices: interval = self._intervals.get(devicename, 1) if ((currentminutes % interval == 0) or (interval > 10 and currentminutes % interval in [2, 4])): self.update_device(devicename) except ValueError: _LOGGER.debug("iCloud API returned an error") def determine_interval(self, devicename, latitude, longitude, battery): """Calculate new interval.""" distancefromhome = None zone_state = self.hass.states.get('zone.home') zone_state_lat = zone_state.attributes['latitude'] zone_state_long = zone_state.attributes['longitude'] distancefromhome = distance( latitude, longitude, zone_state_lat, zone_state_long) distancefromhome = round(distancefromhome / 1000, 1) currentzone = active_zone(self.hass, latitude, longitude) if ((currentzone is not None and currentzone == self._overridestates.get(devicename)) or (currentzone is None and self._overridestates.get(devicename) == 'away')): return self._overridestates[devicename] = None if currentzone is not None: self._intervals[devicename] = 30 return if distancefromhome is None: return if distancefromhome > 25: self._intervals[devicename] = round(distancefromhome / 2, 0) elif distancefromhome > 10: self._intervals[devicename] = 5 else: self._intervals[devicename] = 1 if battery is not None and battery <= 33 and distancefromhome > 3: self._intervals[devicename] = self._intervals[devicename] * 2 def update_device(self, devicename): """Update the device_tracker entity.""" from pyicloud.exceptions import PyiCloudNoDevicesException # An entity will not be created by see() when track=false in # 'known_devices.yaml', but we need to see() it at least once entity = self.hass.states.get(ENTITY_ID_FORMAT.format(devicename)) if entity is None and devicename in self.seen_devices: return attrs = {} kwargs = {} if self.api is None: return try: for device in self.api.devices: if str(device) != str(self.devices[devicename]): continue status = device.status(DEVICESTATUSSET) dev_id = status['name'].replace(' ', '', 99) dev_id = slugify(dev_id) attrs[ATTR_DEVICESTATUS] = DEVICESTATUSCODES.get( status['deviceStatus'], 'error') attrs[ATTR_LOWPOWERMODE] = status['lowPowerMode'] attrs[ATTR_BATTERYSTATUS] = status['batteryStatus'] attrs[ATTR_ACCOUNTNAME] = self.accountname status = device.status(DEVICESTATUSSET) battery = status.get('batteryLevel', 0) * 100 location = status['location'] if location: self.determine_interval( devicename, location['latitude'], location['longitude'], battery) interval = self._intervals.get(devicename, 1) attrs[ATTR_INTERVAL] = interval accuracy = location['horizontalAccuracy'] kwargs['dev_id'] = dev_id kwargs['host_name'] = status['name'] kwargs['gps'] = (location['latitude'], location['longitude']) kwargs['battery'] = battery kwargs['gps_accuracy'] = accuracy kwargs[ATTR_ATTRIBUTES] = attrs self.see(**kwargs) self.seen_devices[devicename] = True except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found") def lost_iphone(self, devicename): """Call the lost iPhone function if the device is found.""" if self.api is None: return self.api.authenticate() for device in self.api.devices: if devicename is None or device == self.devices[devicename]: device.play_sound() def update_icloud(self, devicename=None): """Authenticate against iCloud and scan for devices.""" from pyicloud.exceptions import PyiCloudNoDevicesException if self.api is None: return try: if devicename is not None: if devicename in self.devices: self.devices[devicename].location() else: _LOGGER.error("devicename %s unknown for account %s", devicename, self._attrs[ATTR_ACCOUNTNAME]) else: for device in self.devices: self.devices[device].location() except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found") def setinterval(self, interval=None, devicename=None): """Set the interval of the given devices.""" devs = [devicename] if devicename else self.devices for device in devs: devid = '{}.{}'.format(DOMAIN, device) devicestate = self.hass.states.get(devid) if interval is not None: if devicestate is not None: self._overridestates[device] = active_zone( self.hass, float(devicestate.attributes.get('latitude', 0)), float(devicestate.attributes.get('longitude', 0))) if self._overridestates[device] is None: self._overridestates[device] = 'away' self._intervals[device] = interval else: self._overridestates[device] = None self.update_device(device)
print("Unable to read the apple credentials file '%s': %s" % (apple_creds_file, str(e))) sys.exit(1) api = PyiCloudService(appleid, password, "~/.iCloudLocationFetcher") if api.requires_2sa: import click print("Two-step authentication required. Your trusted devices are:") trusted_devices = api.trusted_devices for i, device in enumerate(trusted_devices): print("%s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = trusted_devices[device] if not api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(dict(), code): print("Failed to verify verification code") sys.exit(1) # provide some information on the devices devices = api.devices for i, device in enumerate(devices): print("%s: type: %s, name: %s, location enabled: %s" % (i, device.content['deviceDisplayName'], device.content['name'], str(device.content['locationEnabled'])))
class Icloud(object): """Represent an icloud account in Home Assistant.""" def __init__(self, hass, username, password, name, see): """Initialize an iCloud account.""" self.hass = hass self.username = username self.password = password self.api = None self.accountname = name self.devices = {} self.seen_devices = {} self._overridestates = {} self._intervals = {} self.see = see self._trusted_device = None self._verification_code = None self._attrs = {} self._attrs[ATTR_ACCOUNTNAME] = name self.reset_account_icloud() randomseconds = random.randint(10, 59) track_utc_time_change(self.hass, self.keep_alive, second=randomseconds) def reset_account_icloud(self): """Reset an icloud account.""" from pyicloud import PyiCloudService from pyicloud.exceptions import PyiCloudFailedLoginException, PyiCloudNoDevicesException icloud_dir = self.hass.config.path("icloud") if not os.path.exists(icloud_dir): os.makedirs(icloud_dir) try: self.api = PyiCloudService(self.username, self.password, cookie_directory=icloud_dir, verify=True) except PyiCloudFailedLoginException as error: self.api = None _LOGGER.error("Error logging into iCloud Service: %s", error) return try: self.devices = {} self._overridestates = {} self._intervals = {} for device in self.api.devices: status = device.status(DEVICESTATUSSET) devicename = slugify(status["name"].replace(" ", "", 99)) if devicename not in self.devices: self.devices[devicename] = device self._intervals[devicename] = 1 self._overridestates[devicename] = None except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found!") def icloud_trusted_device_callback(self, callback_data): """The trusted device is chosen.""" self._trusted_device = int(callback_data.get("0", "0")) self._trusted_device = self.api.trusted_devices[self._trusted_device] if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = get_component("configurator") configurator.request_done(request_id) def icloud_need_trusted_device(self): """We need a trusted device.""" configurator = get_component("configurator") if self.accountname in _CONFIGURING: return devicesstring = "" devices = self.api.trusted_devices for i, device in enumerate(devices): devicesstring += "{}: {};".format(i, device.get("deviceName")) _CONFIGURING[self.accountname] = configurator.request_config( self.hass, "iCloud {}".format(self.accountname), self.icloud_trusted_device_callback, description=("Please choose your trusted device by entering" " the index from this list: " + devicesstring), entity_picture="/static/images/config_icloud.png", submit_caption="Confirm", fields=[{"id": "0"}], ) def icloud_verification_callback(self, callback_data): """The trusted device is chosen.""" self._verification_code = callback_data.get("0") if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = get_component("configurator") configurator.request_done(request_id) def icloud_need_verification_code(self): """We need a verification code.""" configurator = get_component("configurator") if self.accountname in _CONFIGURING: return if self.api.send_verification_code(self._trusted_device): self._verification_code = "waiting" _CONFIGURING[self.accountname] = configurator.request_config( self.hass, "iCloud {}".format(self.accountname), self.icloud_verification_callback, description=("Please enter the validation code:"), entity_picture="/static/images/config_icloud.png", submit_caption="Confirm", fields=[{"code": "0"}], ) def keep_alive(self, now): """Keep the api alive.""" from pyicloud.exceptions import PyiCloud2FARequiredError if self.api is None: self.reset_account_icloud() if self.api is None: return if self.api.requires_2fa: try: self.api.authenticate() except PyiCloud2FARequiredError: if self._trusted_device is None: self.icloud_need_trusted_device() return if self._verification_code is None: self.icloud_need_verification_code() return if self._verification_code == "waiting": return if self.api.validate_verification_code(self._trusted_device, self._verification_code): self._verification_code = None else: self.api.authenticate() currentminutes = dt_util.now().hour * 60 + dt_util.now().minute for devicename in self.devices: interval = self._intervals.get(devicename, 1) if (currentminutes % interval == 0) or (interval > 10 and currentminutes % interval in [2, 4]): self.update_device(devicename) def determine_interval(self, devicename, latitude, longitude, battery): """Calculate new interval.""" distancefromhome = None zone_state = self.hass.states.get("zone.home") zone_state_lat = zone_state.attributes["latitude"] zone_state_long = zone_state.attributes["longitude"] distancefromhome = distance(latitude, longitude, zone_state_lat, zone_state_long) distancefromhome = round(distancefromhome / 1000, 1) currentzone = active_zone(self.hass, latitude, longitude) if (currentzone is not None and currentzone == self._overridestates.get(devicename)) or ( currentzone is None and self._overridestates.get(devicename) == "away" ): return self._overridestates[devicename] = None if currentzone is not None: self._intervals[devicename] = 30 return if distancefromhome is None: return if distancefromhome > 25: self._intervals[devicename] = round(distancefromhome / 2, 0) elif distancefromhome > 10: self._intervals[devicename] = 5 else: self._intervals[devicename] = 1 if battery is not None and battery <= 33 and distancefromhome > 3: self._intervals[devicename] = self._intervals[devicename] * 2 def update_device(self, devicename): """Update the device_tracker entity.""" from pyicloud.exceptions import PyiCloudNoDevicesException # An entity will not be created by see() when track=false in # 'known_devices.yaml', but we need to see() it at least once entity = self.hass.states.get(ENTITY_ID_FORMAT.format(devicename)) if entity is None and devicename in self.seen_devices: return attrs = {} kwargs = {} if self.api is None: return try: for device in self.api.devices: if str(device) != str(self.devices[devicename]): continue status = device.status(DEVICESTATUSSET) dev_id = status["name"].replace(" ", "", 99) dev_id = slugify(dev_id) attrs[ATTR_DEVICESTATUS] = DEVICESTATUSCODES.get(status["deviceStatus"], "error") attrs[ATTR_LOWPOWERMODE] = status["lowPowerMode"] attrs[ATTR_BATTERYSTATUS] = status["batteryStatus"] attrs[ATTR_ACCOUNTNAME] = self.accountname status = device.status(DEVICESTATUSSET) battery = status.get("batteryLevel", 0) * 100 location = status["location"] if location: self.determine_interval(devicename, location["latitude"], location["longitude"], battery) interval = self._intervals.get(devicename, 1) attrs[ATTR_INTERVAL] = interval accuracy = location["horizontalAccuracy"] kwargs["dev_id"] = dev_id kwargs["host_name"] = status["name"] kwargs["gps"] = (location["latitude"], location["longitude"]) kwargs["battery"] = battery kwargs["gps_accuracy"] = accuracy kwargs[ATTR_ATTRIBUTES] = attrs self.see(**kwargs) self.seen_devices[devicename] = True except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found!") def lost_iphone(self, devicename): """Call the lost iphone function if the device is found.""" if self.api is None: return self.api.authenticate() for device in self.api.devices: if devicename is None or device == self.devices[devicename]: device.play_sound() def update_icloud(self, devicename=None): """Authenticate against iCloud and scan for devices.""" from pyicloud.exceptions import PyiCloudNoDevicesException if self.api is None: return try: if devicename is not None: if devicename in self.devices: self.devices[devicename].update_icloud() else: _LOGGER.error("devicename %s unknown for account %s", devicename, self._attrs[ATTR_ACCOUNTNAME]) else: for device in self.devices: self.devices[device].update_icloud() except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found!") def setinterval(self, interval=None, devicename=None): """Set the interval of the given devices.""" devs = [devicename] if devicename else self.devices for device in devs: devid = DOMAIN + "." + device devicestate = self.hass.states.get(devid) if interval is not None: if devicestate is not None: self._overridestates[device] = active_zone( self.hass, float(devicestate.attributes.get("latitude", 0)), float(devicestate.attributes.get("longitude", 0)), ) if self._overridestates[device] is None: self._overridestates[device] = "away" self._intervals[device] = interval else: self._overridestates[device] = None self.update_device(device)
class iCloud: def __init__(self): creds = self.__load_creds(os.getcwd() + "/Secrets.txt") self.api = PyiCloudService(creds[0], creds[1]) self.__two_factor_routine() self.phone = self.Phone() self.update_phone() self.lats, self.longs = self.__load_location() def __load_location(self): with open("location.txt", "r") as f: temp = f.readlines() out = [] for i in temp: i = i.strip() i = i.split(",") out.append([float(j) for j in i]) return out[0], out[1] def update_phone(self): _phone = self.api.devices[1] status = _phone.status() self.phone.update(_phone.content, status) def __load_creds(self, path): with open(path, "r") as f: creds = f.readlines() return [i.strip() for i in creds] # Not my code. This was ripped from pycloud docs. def __two_factor_routine(self): if self.api.requires_2fa: print("Two-factor authentication required.") code = input( "Enter the code you received of one of your approved devices: " ) result = self.api.validate_2fa_code(code) print("Code validation result: %s" % result) if not result: print("Failed to verify security code") sys.exit(1) if not self.api.is_trusted_session: print("Session is not trusted. Requesting trust...") result = self.api.trust_session() print("Session trust result %s" % result) if not result: print( "Failed to request trust. You will likely be prompted for the code again in the coming weeks" ) elif self.api.requires_2sa: import click print( "Two-step authentication required. Your trusted devices are:") devices = self.api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber')))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not self.api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt('Please enter validation code') if not self.api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) def is_charging(self): first = self.api.devices[1].status()["batteryLevel"] count = 0 while True: time.sleep(20) second = self.api.devices[1].status()["batteryLevel"] print("First {} --> Second {}".format(first, second)) if second != first: break count += 1 if second - first > 0.0: return True else: return False def is_home(self): lat_fence = self.lats long_fence = self.longs lat_check = self.phone.location[0] > lat_fence[ 0] and self.phone.location[0] < lat_fence[1] long_check = self.phone.location[1] < long_fence[ 0] and self.phone.location[1] > long_fence[1] return lat_check and long_check class Phone: def __init__(self): self.battery = 0.0 self.location = [] self.id = "" self.timestamp = None def update(self, content, status): self.timestamp = datetime.datetime.now() self.battery = status["batteryLevel"] self.id = content["id"] lat = content["location"]["latitude"] long = content["location"]["longitude"] self.location = [lat, long]
class Icloud(DeviceScanner): """Representation of an iCloud account.""" def __init__(self, hass, username, password, name, max_interval, gps_accuracy_threshold, see): """Initialize an iCloud account.""" self.hass = hass self.username = username self.password = password self.api = None self.accountname = name self.devices = {} self.seen_devices = {} self._overridestates = {} self._intervals = {} self._max_interval = max_interval self._gps_accuracy_threshold = gps_accuracy_threshold self.see = see self._trusted_device = None self._verification_code = None self._attrs = {} self._attrs[ATTR_ACCOUNTNAME] = name self.reset_account_icloud() randomseconds = random.randint(10, 59) track_utc_time_change(self.hass, self.keep_alive, second=randomseconds) def reset_account_icloud(self): """Reset an iCloud account.""" from pyicloud import PyiCloudService from pyicloud.exceptions import ( PyiCloudFailedLoginException, PyiCloudNoDevicesException, ) icloud_dir = self.hass.config.path("icloud") if not os.path.exists(icloud_dir): os.makedirs(icloud_dir) try: self.api = PyiCloudService(self.username, self.password, cookie_directory=icloud_dir, verify=True) except PyiCloudFailedLoginException as error: self.api = None _LOGGER.error("Error logging into iCloud Service: %s", error) return try: self.devices = {} self._overridestates = {} self._intervals = {} for device in self.api.devices: status = device.status(DEVICESTATUSSET) _LOGGER.debug("Device Status is %s", status) devicename = slugify(status["name"].replace(" ", "", 99)) _LOGGER.info("Adding icloud device: %s", devicename) if devicename in self.devices: _LOGGER.error("Multiple devices with name: %s", devicename) continue self.devices[devicename] = device self._intervals[devicename] = 1 self._overridestates[devicename] = None except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found!") def icloud_trusted_device_callback(self, callback_data): """Handle chosen trusted devices.""" self._trusted_device = int(callback_data.get("trusted_device")) self._trusted_device = self.api.trusted_devices[self._trusted_device] if not self.api.send_verification_code(self._trusted_device): _LOGGER.error("Failed to send verification code") self._trusted_device = None return if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = self.hass.components.configurator configurator.request_done(request_id) # Trigger the next step immediately self.icloud_need_verification_code() def icloud_need_trusted_device(self): """We need a trusted device.""" configurator = self.hass.components.configurator if self.accountname in _CONFIGURING: return devicesstring = "" devices = self.api.trusted_devices for i, device in enumerate(devices): devicename = device.get("deviceName", "SMS to %s" % device.get("phoneNumber")) devicesstring += "{}: {};".format(i, devicename) _CONFIGURING[self.accountname] = configurator.request_config( "iCloud {}".format(self.accountname), self.icloud_trusted_device_callback, description=("Please choose your trusted device by entering" " the index from this list: " + devicesstring), entity_picture="/static/images/config_icloud.png", submit_caption="Confirm", fields=[{ "id": "trusted_device", "name": "Trusted Device" }], ) def icloud_verification_callback(self, callback_data): """Handle the chosen trusted device.""" from pyicloud.exceptions import PyiCloudException self._verification_code = callback_data.get("code") try: if not self.api.validate_verification_code( self._trusted_device, self._verification_code): raise PyiCloudException("Unknown failure") except PyiCloudException as error: # Reset to the initial 2FA state to allow the user to retry _LOGGER.error("Failed to verify verification code: %s", error) self._trusted_device = None self._verification_code = None # Trigger the next step immediately self.icloud_need_trusted_device() if self.accountname in _CONFIGURING: request_id = _CONFIGURING.pop(self.accountname) configurator = self.hass.components.configurator configurator.request_done(request_id) def icloud_need_verification_code(self): """Return the verification code.""" configurator = self.hass.components.configurator if self.accountname in _CONFIGURING: return _CONFIGURING[self.accountname] = configurator.request_config( "iCloud {}".format(self.accountname), self.icloud_verification_callback, description=("Please enter the validation code:"), entity_picture="/static/images/config_icloud.png", submit_caption="Confirm", fields=[{ "id": "code", "name": "code" }], ) def keep_alive(self, now): """Keep the API alive.""" if self.api is None: self.reset_account_icloud() if self.api is None: return if self.api.requires_2fa: from pyicloud.exceptions import PyiCloudException try: if self._trusted_device is None: self.icloud_need_trusted_device() return if self._verification_code is None: self.icloud_need_verification_code() return self.api.authenticate() if self.api.requires_2fa: raise Exception("Unknown failure") self._trusted_device = None self._verification_code = None except PyiCloudException as error: _LOGGER.error("Error setting up 2FA: %s", error) else: self.api.authenticate() currentminutes = dt_util.now().hour * 60 + dt_util.now().minute try: for devicename in self.devices: interval = self._intervals.get(devicename, 1) if (currentminutes % interval == 0) or (interval > 10 and currentminutes % interval in [2, 4]): self.update_device(devicename) except ValueError: _LOGGER.debug("iCloud API returned an error") def determine_interval(self, devicename, latitude, longitude, battery): """Calculate new interval.""" currentzone = run_callback_threadsafe(self.hass.loop, async_active_zone, self.hass, latitude, longitude).result() if (currentzone is not None and currentzone == self._overridestates.get(devicename)) or ( currentzone is None and self._overridestates.get(devicename) == "away"): return zones = (self.hass.states.get(entity_id) for entity_id in sorted(self.hass.states.entity_ids("zone"))) distances = [] for zone_state in zones: zone_state_lat = zone_state.attributes["latitude"] zone_state_long = zone_state.attributes["longitude"] zone_distance = distance(latitude, longitude, zone_state_lat, zone_state_long) distances.append(round(zone_distance / 1000, 1)) if distances: mindistance = min(distances) else: mindistance = None self._overridestates[devicename] = None if currentzone is not None: self._intervals[devicename] = self._max_interval return if mindistance is None: return # Calculate out how long it would take for the device to drive to the # nearest zone at 120 km/h: interval = round(mindistance / 2, 0) # Never poll more than once per minute interval = max(interval, 1) if interval > 180: # Three hour drive? This is far enough that they might be flying interval = 30 if battery is not None and battery <= 33 and mindistance > 3: # Low battery - let's check half as often interval = interval * 2 self._intervals[devicename] = interval def update_device(self, devicename): """Update the device_tracker entity.""" from pyicloud.exceptions import PyiCloudNoDevicesException # An entity will not be created by see() when track=false in # 'known_devices.yaml', but we need to see() it at least once entity = self.hass.states.get(ENTITY_ID_FORMAT.format(devicename)) if entity is None and devicename in self.seen_devices: return attrs = {} kwargs = {} if self.api is None: return try: for device in self.api.devices: if str(device) != str(self.devices[devicename]): continue status = device.status(DEVICESTATUSSET) _LOGGER.debug("Device Status is %s", status) dev_id = status["name"].replace(" ", "", 99) dev_id = slugify(dev_id) attrs[ATTR_DEVICESTATUS] = DEVICESTATUSCODES.get( status["deviceStatus"], "error") attrs[ATTR_LOWPOWERMODE] = status["lowPowerMode"] attrs[ATTR_BATTERYSTATUS] = status["batteryStatus"] attrs[ATTR_ACCOUNTNAME] = self.accountname status = device.status(DEVICESTATUSSET) battery = status.get("batteryLevel", 0) * 100 location = status["location"] if location and location["horizontalAccuracy"]: horizontal_accuracy = int(location["horizontalAccuracy"]) if horizontal_accuracy < self._gps_accuracy_threshold: self.determine_interval( devicename, location["latitude"], location["longitude"], battery, ) interval = self._intervals.get(devicename, 1) attrs[ATTR_INTERVAL] = interval accuracy = location["horizontalAccuracy"] kwargs["dev_id"] = dev_id kwargs["host_name"] = status["name"] kwargs["gps"] = (location["latitude"], location["longitude"]) kwargs["battery"] = battery kwargs["gps_accuracy"] = accuracy kwargs[ATTR_ATTRIBUTES] = attrs self.see(**kwargs) self.seen_devices[devicename] = True except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found") def lost_iphone(self, devicename): """Call the lost iPhone function if the device is found.""" if self.api is None: return self.api.authenticate() for device in self.api.devices: if str(device) == str(self.devices[devicename]): _LOGGER.info("Playing Lost iPhone sound for %s", devicename) device.play_sound() def update_icloud(self, devicename=None): """Request device information from iCloud and update device_tracker.""" from pyicloud.exceptions import PyiCloudNoDevicesException if self.api is None: return try: if devicename is not None: if devicename in self.devices: self.update_device(devicename) else: _LOGGER.error( "devicename %s unknown for account %s", devicename, self._attrs[ATTR_ACCOUNTNAME], ) else: for device in self.devices: self.update_device(device) except PyiCloudNoDevicesException: _LOGGER.error("No iCloud Devices found") def setinterval(self, interval=None, devicename=None): """Set the interval of the given devices.""" devs = [devicename] if devicename else self.devices for device in devs: devid = "{}.{}".format(DOMAIN, device) devicestate = self.hass.states.get(devid) if interval is not None: if devicestate is not None: self._overridestates[device] = run_callback_threadsafe( self.hass.loop, async_active_zone, self.hass, float(devicestate.attributes.get("latitude", 0)), float(devicestate.attributes.get("longitude", 0)), ).result() if self._overridestates[device] is None: self._overridestates[device] = "away" self._intervals[device] = interval else: self._overridestates[device] = None self.update_device(device)
filename=DEFAULT_LOGFILE, level=args.verbose and logging.DEBUG or logging.INFO, format='%(asctime)s %(message)s') gConfigurationiCloud, _, _ = configurationManager(args.config) api = PyiCloudService(gConfigurationiCloud['username'], gConfigurationiCloud['password']) if api.requires_2fa: print "Two-factor authentication required. Your trusted devices are:" devices = api.trusted_devices for i, device in enumerate(devices): print " %s: %s" % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber'))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] if not api.send_verification_code(device): print "Failed to send verification code" sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): print "Failed to verify verification code" sys.exit(1) else: print "2 Factor authentication not necessary" print "now run iLocator .. "
def handle(text, mic, profile): """ Makes your iPhone ring Arguments: text -- user-input, typically transcribed speech mic -- used to interact with the user (for both input and output) profile -- contains information related to the user (e.g., phone number) """ try: api = PyiCloudService(ICLOUD_USERNAME, ICLOUD_PASSWORD) #----- 2 FACTOR if api.requires_2fa: mic.say("I'm sorry Toby, I need you to validate iCloud for me.") import click print "Two-factor authentication required. Your trusted devices are:" devices = api.trusted_devices for i, device in enumerate(devices): print " %s: %s" % (i, device.get( 'deviceName', "SMS to %s" % device.get('phoneNumber'))) device = click.prompt('Which device would you like to use?', default=0) device = devices[device] api.send_verification_code(device) #if not api.send_verification_code(device): # print "Failed to send verification code" #sys.exit(1) code = click.prompt('Please enter validation code') if not api.validate_verification_code(device, code): mic.say("Failed to verify verification code") sys.exit(1) #-------END 2 FACTOR except PyiCloudFailedLoginException: mic.say("Invalid Username & Password") return # All Devices devices = api.devices # Just the iPhones iphones = [] # The one to ring phone_to_ring = None for device in devices: current = device.status() if "iPhone" in current['deviceDisplayName']: iphones.append(device) # No iphones if len(iphones) == 0: mic.say("No IPhones Found on your account") return # Many iphones elif len(iphones) > 1: #mic.say("There are multiple iphones on your account.") count = 0 for phone in iphones: #WILL ALWAYS SELECT THE SECOND IPHONE AS THAT IS MINE count += 1 if count != 2: continue #my iphone is second in list, so i removed the first #print(phone.status()['name'].replace(u"’","'")) #mic.say("Did you mean the {type} named {name}?".format(type=phone.status()['deviceDisplayName'], name=(phone.status()['name'].replace("’".decode('utf-8'),"'")))) #command = mic.activeListen() #if any(aff in command for aff in AFFIRMATIVE): phone_to_ring = phone break # Just one elif len(iphones) == 1: phone_to_ring = iphones[0] if not phone_to_ring: mic.say("You didn't select an iPhone") return phone_to_ring.play_sound() mic.say("Sending ring command to the phone now")
devices = api.trusted_devices for i in range(len(devices)): print(str(i) + '. ' + devices[i]['phoneNumber']) deviceId = int(input('Enter the device index #: ')) device = devices[deviceId] if not api.send_verification_code(device): print('Failed to send verification code.') exit(1) while True: code = click.prompt('Enter the verification code') if api.validate_verification_code(device, code): break else: print('Invalid verification code.') from datetime import datetime, timedelta from gspread import authorize from oauth2client.service_account import ServiceAccountCredentials from numpy import array import pytz # how to change day for next month next_day = datetime.now() next_day = datetime.now() + timedelta(days=1) # get all events for the next day from icloud calendar
class Iftp(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) self._sort = True self.prompt = "iftp> " self._api = None self._netrc = netrc.netrc() self.path = [] self.user_path = "/" def do_login(self, args): """Login into iCloud""" try: self.username, _, self.password = self._netrc.authenticators( "icloud") except TypeError: self.username = click.prompt("iCloud Username") self.password = click.prompt("iCloud Password") self._login() def do_ls(self, args): """List files""" if not self.check_login(): print("Not logged in, login first") return if args == '': args = self.user_path else: args = os.path.normpath(os.path.join(self.user_path, args)) path = self._get_handle_for_path(args) if path: for item in path.dir(): drive_file = path[item] if drive_file.type == "file": perms = "-rw-rw-rw-" size = drive_file.size date = drive_file.date_modified if date.year == datetime.datetime.now().year: filedate = date.strftime("%b %d %H:%S") else: filedate = date.strftime("%b %d %Y") elif drive_file.type == "folder": perms = "drwxrwxrwx" size = 0 filedate = "Jan 01 1970" print("%s %-4s %5s %-5s %13s %s %s" % (perms, 0, os.getuid(), os.getgid(), size, filedate, drive_file.name)) return def do_get(self, args): """Download file""" if not self.check_login(): print("Not logged in, login first") return args = os.path.normpath(os.path.join(self.user_path, args)) drive_handle = self._get_handle_for_path(args) if drive_handle and drive_handle.type == 'file': starttime = time.time() with drive_handle.open(stream=True) as response: with open(drive_handle.name, "wb") as file_out: copyfileobj(response.raw, file_out) stoptime = time.time() duration = stoptime - starttime print("%s bytes received in %f secs (%f Kbytes/sec)" % (drive_handle.size, duration, drive_handle.size / duration / 1024)) else: print("550 Failed to open file.") return def do_put(self, args): """Download file""" if not self.check_login(): print("Not logged in, login first") return args = shlex.split(args) if len(args) == 1: sfile = args[0] dfile = os.path.basename(sfile) else: (sfile, dfile) = args head, tail = os.path.split(dfile) if head: drive_handle = self._get_handle_for_path( os.path.normpath(os.path.join(self.user_path, head))) else: drive_handle = self._get_handle_for_path(self.user_path) if drive_handle and drive_handle.type == 'folder': size = os.path.getsize(sfile) starttime = time.time() with open(sfile, 'rb') as file_in: drive_handle.upload(file_in) stoptime = time.time() duration = stoptime - starttime print("%s bytes sent in %f secs (%f Kbytes/sec)" % (size, duration, size / duration / 1024)) return def do_mkdir(self, args): """Create new directory""" if not self.check_login(): print("Not logged in, login first") return head, tail = os.path.split(args) if head: drive_handle = self._get_handle_for_path( os.path.normpath(os.path.join(self.user_path, head))) else: drive_handle = self._get_handle_for_path(self.user_path) if drive_handle and drive_handle.type == 'folder': drive_handle.mkdir(tail) print('257 "%s" created' % os.path.normpath(os.path.join(self.user_path, head, tail))) def do_rmdir(self, args): """Delete a directory""" if not self.check_login(): print("Not logged in, login first") return args = os.path.normpath(os.path.join(self.user_path, args)) drive_handle = self._get_handle_for_path(args) if drive_handle and drive_handle.type == 'folder': drive_handle.delete() print("250 Remove directory operation successful.") else: print("550 Remove directory operation failed.") def do_delete(self, args): """Delete a file""" if not self.check_login(): print("Not logged in, login first") return args = os.path.normpath(os.path.join(self.user_path, args)) drive_handle = self._get_handle_for_path(args) if drive_handle and drive_handle.type == 'file': drive_handle.delete() print("250 Delete operation successful.") else: print("550 Delete operation failed.") def do_rename(self, args): """Rename a file or folder""" if not self.check_login(): print("Not logged in, login first") return sfile, dfile = shlex.split(args) args = os.path.normpath(os.path.join(self.user_path, sfile)) drive_handle = self._get_handle_for_path(args) drive_handle.rename(os.path.basename(dfile)) print("250 Rename successful.") def do_pwd(self, args): """Show current directory""" if not self.check_login(): print("Not logged in, login first") return print('%s "%s"' % (257, self.user_path)) def do_cd(self, args): """Change directory""" if not self.check_login(): print("Not logged in, login first") return path = os.path.normpath(os.path.join(self.user_path, args)) drive_handle = self._get_handle_for_path(path) if drive_handle and drive_handle.type == "folder": print('%s "%s"' % (250, "Directory successfully changed.")) self.user_path = path else: print("%s %s" % (550, "Failed to change directory.")) return def do_quit(self, args): return -1 def do_EOF(self, args): return -1 def check_login(self): if self._api is None: return False return True def _login(self): self._api = PyiCloudService(self.username, self.password) if self._api.requires_2sa: print( "Two-factor authentication required. Your trusted devices are:" ) devices = self._api.trusted_devices for i, device in enumerate(devices): print(" %s: %s" % ( i, device.get("deviceName", "SMS to %s") % device.get("phoneNumber"), )) device = click.prompt("Which device would you like to use?", default=0) device = devices[device] if not self._api.send_verification_code(device): print("Failed to send verification code") sys.exit(1) code = click.prompt("Please enter validation code") if not self._api.validate_verification_code(device, code): print("Failed to verify verification code") sys.exit(1) print("Logged into iCloud Drive as %s" % self.username) def _verify_path(self, path): pass def _get_handle_for_path(self, args): """Get a drive API handle for a path""" drive_handle = self._api.drive # Handle '/' and just return the base drive_handle if args.strip() == '/': return drive_handle tmppath = args.split("/") # Cut leading / if tmppath[0] == "" and len(tmppath) > 1: tmppath.pop(0) try: for element in tmppath: drive_handle = drive_handle[element] return drive_handle except KeyError: return False