From 897f9058554678008fc751e71b24e082b61fdb12 Mon Sep 17 00:00:00 2001 From: FiZiX Date: Wed, 13 May 2015 13:01:08 -0400 Subject: [PATCH 1/7] Add X-APPLE-WEB-KB cookie handling Add X-APPLE-WEB-KB cookie handling to prevent e-mail from Apple every time script is run. --- pyicloud/base.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pyicloud/base.py b/pyicloud/base.py index 739e7bc..bc83cc7 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -3,6 +3,9 @@ import hashlib import json import requests import sys +import pickle +import os +from re import match from pyicloud.exceptions import PyiCloudFailedLoginException from pyicloud.services import ( @@ -37,6 +40,8 @@ class PyiCloudService(object): self._base_system_url = '%s/system/version.json' % self._home_endpoint self._base_webauth_url = '%s/refreshWebAuth' % self._push_endpoint + self._cookie_directory = 'cookies' + self.session = requests.Session() self.session.verify = False self.session.headers.update({ @@ -86,6 +91,24 @@ class PyiCloudService(object): """ self.refresh_validate() + # Check if cookies directory exists + if not os.path.exists(self._cookie_directory): + # If not, create it + os.mkdir(self._cookie_directory) + + # Set path for cookie file + cookiefile = self.user.get('apple_id') + cookiefile = os.path.join(self._cookie_directory, ''.join([c for c in cookiefile if match(r'\w', c)])) + + # Check if cookie file already exists + if os.path.isfile(cookiefile): + # Get cookie data from file + with open(cookiefile, 'rb') as f: + webKBCookie = pickle.load(f) + self.session.cookies = requests.utils.cookiejar_from_dict(webKBCookie) + else: + webKBCookie = None + data = dict(self.user) data.update({'id': self.params['id'], 'extended_login': False}) req = self.session.post( @@ -98,6 +121,14 @@ class PyiCloudService(object): msg = 'Invalid email/password combination.' raise PyiCloudFailedLoginException(msg) + # Pull X-APPLE-WEB-KB cookie from cookies + NewWebKBCookie = next(({key:val} for key, val in req.cookies.items() if 'X-APPLE-WEB-KB' in key), None) + # Check if cookie changed + if NewWebKBCookie and NewWebKBCookie != webKBCookie: + # Save the cookie in a pickle file + with open(cookiefile, 'wb') as f: + pickle.dump(NewWebKBCookie, f) + self.refresh_validate() self.discovery = req.json() From 4daa34f310c41d110f274ff2f4aaa7ca56676645 Mon Sep 17 00:00:00 2001 From: Adam Coddington Date: Wed, 13 May 2015 21:59:44 -0700 Subject: [PATCH 2/7] Cleaning up cookie handling to use a system-level temporary directory. Adding logger. --- pyicloud/base.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index bc83cc7..5b09657 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -1,9 +1,11 @@ import uuid import hashlib import json +import logging +import pickle import requests import sys -import pickle +import tempfile import os from re import match @@ -16,6 +18,9 @@ from pyicloud.services import ( ) +logger = logging.getLogger(__name__) + + class PyiCloudService(object): """ A base authentication class for the iCloud service. Handles the @@ -40,7 +45,10 @@ class PyiCloudService(object): self._base_system_url = '%s/system/version.json' % self._home_endpoint self._base_webauth_url = '%s/refreshWebAuth' % self._push_endpoint - self._cookie_directory = 'cookies' + self._cookie_directory = os.path.join( + tempfile.gettempdir(), + 'pyicloud', + ) self.session = requests.Session() self.session.verify = False From 0419012e15fc2f68d90fcaaefda4cee8ea3ef8df Mon Sep 17 00:00:00 2001 From: Adam Coddington Date: Wed, 13 May 2015 22:00:47 -0700 Subject: [PATCH 3/7] Make cookie loading a little more pythonic. --- pyicloud/base.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index 5b09657..89625aa 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -105,17 +105,27 @@ class PyiCloudService(object): os.mkdir(self._cookie_directory) # Set path for cookie file - cookiefile = self.user.get('apple_id') - cookiefile = os.path.join(self._cookie_directory, ''.join([c for c in cookiefile if match(r'\w', c)])) + cookiefile = os.path.join( + self._cookie_directory, + ''.join([c for c in self.user.get('apple_id') if match(r'\w', c)]) + ) + webKBCookie = None # Check if cookie file already exists - if os.path.isfile(cookiefile): + try: # Get cookie data from file with open(cookiefile, 'rb') as f: webKBCookie = pickle.load(f) - self.session.cookies = requests.utils.cookiejar_from_dict(webKBCookie) - else: - webKBCookie = None + self.session.cookies = requests.utils.cookiejar_from_dict( + webKBCookie + ) + except IOError: + # This just means that the file doesn't exist; that's OK! + pass + except Exception as e: + logger.exception( + "Unexpected error occurred while loading cookies: %s" % (e, ) + ) data = dict(self.user) data.update({'id': self.params['id'], 'extended_login': False}) From 1a3a2bd6164dfb82cdee6b67da59c7d5f0144dad Mon Sep 17 00:00:00 2001 From: Adam Coddington Date: Wed, 13 May 2015 22:01:05 -0700 Subject: [PATCH 4/7] PEP-8 naming for 'newKBCookie.' --- pyicloud/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index 89625aa..d010fd0 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -140,12 +140,12 @@ class PyiCloudService(object): raise PyiCloudFailedLoginException(msg) # Pull X-APPLE-WEB-KB cookie from cookies - NewWebKBCookie = next(({key:val} for key, val in req.cookies.items() if 'X-APPLE-WEB-KB' in key), None) + newWebKBCookie = next(({key:val} for key, val in req.cookies.items() if 'X-APPLE-WEB-KB' in key), None) # Check if cookie changed - if NewWebKBCookie and NewWebKBCookie != webKBCookie: + if newWebKBCookie and newWebKBCookie != webKBCookie: # Save the cookie in a pickle file with open(cookiefile, 'wb') as f: - pickle.dump(NewWebKBCookie, f) + pickle.dump(newWebKBCookie, f) self.refresh_validate() From 67030b0ebae413eaea3d7b74d6f272afa4c6b32e Mon Sep 17 00:00:00 2001 From: Adam Coddington Date: Sun, 17 May 2015 22:48:43 -0700 Subject: [PATCH 5/7] Allow user to specify cookie directory. --- pyicloud/base.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index d010fd0..87b3338 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -31,7 +31,7 @@ class PyiCloudService(object): pyicloud = PyiCloudService('username@apple.com', 'password') pyicloud.iphone.location() """ - def __init__(self, apple_id, password): + def __init__(self, apple_id, password, cookie_directory=None): self.discovery = None self.client_id = str(uuid.uuid1()).upper() self.user = {'apple_id': apple_id, 'password': password} @@ -45,10 +45,15 @@ class PyiCloudService(object): self._base_system_url = '%s/system/version.json' % self._home_endpoint self._base_webauth_url = '%s/refreshWebAuth' % self._push_endpoint - self._cookie_directory = os.path.join( - tempfile.gettempdir(), - 'pyicloud', - ) + if cookie_directory: + self._cookie_directory = os.path.expanduser( + os.path.normpath(cookie_directory) + ) + else: + self._cookie_directory = os.path.join( + tempfile.gettempdir(), + 'pyicloud', + ) self.session = requests.Session() self.session.verify = False From d056c9b7080cc43c9f818defdd0a28d149f8cdd0 Mon Sep 17 00:00:00 2001 From: Adam Coddington Date: Sun, 17 May 2015 23:11:27 -0700 Subject: [PATCH 6/7] Updating cookie handling to serialize all cookies following the request. --- pyicloud/base.py | 70 ++++++++++++++++++++++++++------------------- pyicloud/cmdline.py | 10 +++++++ 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index 87b3338..ba9eae8 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -109,28 +109,9 @@ class PyiCloudService(object): # If not, create it os.mkdir(self._cookie_directory) - # Set path for cookie file - cookiefile = os.path.join( - self._cookie_directory, - ''.join([c for c in self.user.get('apple_id') if match(r'\w', c)]) - ) - - webKBCookie = None - # Check if cookie file already exists - try: - # Get cookie data from file - with open(cookiefile, 'rb') as f: - webKBCookie = pickle.load(f) - self.session.cookies = requests.utils.cookiejar_from_dict( - webKBCookie - ) - except IOError: - # This just means that the file doesn't exist; that's OK! - pass - except Exception as e: - logger.exception( - "Unexpected error occurred while loading cookies: %s" % (e, ) - ) + cookie = self._get_cookie() + if cookie: + self.session.cookies = cookie data = dict(self.user) data.update({'id': self.params['id'], 'extended_login': False}) @@ -144,19 +125,50 @@ class PyiCloudService(object): msg = 'Invalid email/password combination.' raise PyiCloudFailedLoginException(msg) - # Pull X-APPLE-WEB-KB cookie from cookies - newWebKBCookie = next(({key:val} for key, val in req.cookies.items() if 'X-APPLE-WEB-KB' in key), None) - # Check if cookie changed - if newWebKBCookie and newWebKBCookie != webKBCookie: - # Save the cookie in a pickle file - with open(cookiefile, 'wb') as f: - pickle.dump(newWebKBCookie, f) + self._update_cookie(req) self.refresh_validate() self.discovery = req.json() self.webservices = self.discovery['webservices'] + def _get_cookie_path(self): + # Set path for cookie file + return os.path.join( + self._cookie_directory, + ''.join([c for c in self.user.get('apple_id') if match(r'\w', c)]) + ) + + def _get_cookie(self): + if hasattr(self, '_cookies'): + return self._cookies + + cookiefile = self._get_cookie_path() + + # Check if cookie file already exists + try: + # Get cookie data from file + with open(cookiefile, 'rb') as f: + return pickle.load(f) + except IOError: + # This just means that the file doesn't exist; that's OK! + pass + except Exception as e: + logger.exception( + "Unexpected error occurred while loading cookies: %s" % (e, ) + ) + + return None + + def _update_cookie(self, request): + cookiefile = self._get_cookie_path() + + # Save the cookie in a pickle file + with open(cookiefile, 'wb') as f: + pickle.dump(request.cookies, f) + + self._cookies = request.cookies + @property def devices(self): """ Return all devices.""" diff --git a/pyicloud/cmdline.py b/pyicloud/cmdline.py index e7a1f70..baff280 100644 --- a/pyicloud/cmdline.py +++ b/pyicloud/cmdline.py @@ -6,6 +6,7 @@ command line scripts, and related. """ from __future__ import print_function import argparse +import logging import pickle import sys @@ -150,11 +151,20 @@ def main(args=None): default="", help="Save device data to a file in the current directory.", ) + parser.add_argument( + '--loglevel', + default='INFO', + help='Increase logging verbosity by specifying DEBUG' + ) command_line = parser.parse_args(args) if not command_line.username or not command_line.password: parser.error('No username or password supplied') + logging.basicConfig( + level=logging.getLevelName(command_line.loglevel) + ) + from pyicloud import PyiCloudService try: api = PyiCloudService( From 3d795fab1b11aee3778c6dfb19c530f6af8778b6 Mon Sep 17 00:00:00 2001 From: Adam Coddington Date: Sun, 17 May 2015 23:14:11 -0700 Subject: [PATCH 7/7] Remove logging configuration. --- pyicloud/cmdline.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pyicloud/cmdline.py b/pyicloud/cmdline.py index baff280..e7a1f70 100644 --- a/pyicloud/cmdline.py +++ b/pyicloud/cmdline.py @@ -6,7 +6,6 @@ command line scripts, and related. """ from __future__ import print_function import argparse -import logging import pickle import sys @@ -151,20 +150,11 @@ def main(args=None): default="", help="Save device data to a file in the current directory.", ) - parser.add_argument( - '--loglevel', - default='INFO', - help='Increase logging verbosity by specifying DEBUG' - ) command_line = parser.parse_args(args) if not command_line.username or not command_line.password: parser.error('No username or password supplied') - logging.basicConfig( - level=logging.getLevelName(command_line.loglevel) - ) - from pyicloud import PyiCloudService try: api = PyiCloudService(