From 603b1d8f57ba23546b8e445027fd570fad263333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 7 Jan 2016 21:47:30 +0100 Subject: [PATCH 1/5] Normalize HTTP header names to title case The spec says they are case insensitive, so this is just for consistency (with eg. Chrome). --- pyicloud/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index 1b03a22..f7e43c4 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -60,9 +60,9 @@ class PyiCloudService(object): self.session = requests.Session() self.session.verify = verify self.session.headers.update({ - 'host': 'setup.icloud.com', - 'origin': self._home_endpoint, - 'referer': '%s/' % self._home_endpoint, + 'Host': 'setup.icloud.com', + 'Origin': self._home_endpoint, + 'Referer': '%s/' % self._home_endpoint, 'User-Agent': 'Opera/9.52 (X11; Linux i686; U; en)' }) From 2e836b91855cf688ddef5cc9ac2db4f7caadcc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 7 Jan 2016 21:49:18 +0100 Subject: [PATCH 2/5] Remove three unused endpoint definitions from PyiCloudService base --- pyicloud/base.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index f7e43c4..d57465f 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -40,12 +40,9 @@ class PyiCloudService(object): self._home_endpoint = 'https://www.icloud.com' self._setup_endpoint = 'https://p12-setup.icloud.com/setup/ws/1' - self._push_endpoint = 'https://p12-pushws.icloud.com' self._base_login_url = '%s/login' % self._setup_endpoint self._base_validate_url = '%s/validate' % self._setup_endpoint - self._base_system_url = '%s/system/version.json' % self._home_endpoint - self._base_webauth_url = '%s/refreshWebAuth' % self._push_endpoint if cookie_directory: self._cookie_directory = os.path.expanduser( From 35f7955228b92e3696b00d3050a1ff412ebc5592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 7 Jan 2016 21:51:56 +0100 Subject: [PATCH 3/5] Use same setup/login endpoint as iCloud webapp --- pyicloud/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index d57465f..ba47524 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -39,7 +39,7 @@ class PyiCloudService(object): self.user = {'apple_id': apple_id, 'password': password} self._home_endpoint = 'https://www.icloud.com' - self._setup_endpoint = 'https://p12-setup.icloud.com/setup/ws/1' + self._setup_endpoint = 'https://setup.icloud.com/setup/ws/1' self._base_login_url = '%s/login' % self._setup_endpoint self._base_validate_url = '%s/validate' % self._setup_endpoint From 5d241fd1b5d708abe96a93b73971ffc47f1de236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 7 Jan 2016 22:13:08 +0100 Subject: [PATCH 4/5] Don't set Host header manually It's taken care of by the requests module, based on the url we're posting to, so no need to juggle the host header ourselves. --- pyicloud/base.py | 1 - pyicloud/services/calendar.py | 4 ---- pyicloud/services/contacts.py | 2 -- pyicloud/services/findmyiphone.py | 2 -- pyicloud/services/ubiquity.py | 3 --- 5 files changed, 12 deletions(-) diff --git a/pyicloud/base.py b/pyicloud/base.py index ba47524..e59003d 100644 --- a/pyicloud/base.py +++ b/pyicloud/base.py @@ -57,7 +57,6 @@ class PyiCloudService(object): self.session = requests.Session() self.session.verify = verify self.session.headers.update({ - 'Host': 'setup.icloud.com', 'Origin': self._home_endpoint, 'Referer': '%s/' % self._home_endpoint, 'User-Agent': 'Opera/9.52 (X11; Linux i686; U; en)' diff --git a/pyicloud/services/calendar.py b/pyicloud/services/calendar.py index bab3ebf..1ed03f6 100644 --- a/pyicloud/services/calendar.py +++ b/pyicloud/services/calendar.py @@ -57,8 +57,6 @@ class CalendarService(object): Fetches a single event's details by specifying a pguid (a calendar) and a guid (an event's ID). """ - host = self._service_root.split('//')[1].split(':')[0] - self.session.headers.update({'host': host}) params = dict(self.params) params.update({'lang': 'en-us', 'usertz': self.get_system_tz()}) url = '%s/%s/%s' % (self._calendar_event_detail_url, pguid, guid) @@ -78,8 +76,6 @@ class CalendarService(object): from_dt = datetime(today.year, today.month, first_day) if not to_dt: to_dt = datetime(today.year, today.month, last_day) - host = self._service_root.split('//')[1].split(':')[0] - self.session.headers.update({'host': host}) params = dict(self.params) params.update({ 'lang': 'en-us', diff --git a/pyicloud/services/contacts.py b/pyicloud/services/contacts.py index 27338d4..a5a79e0 100644 --- a/pyicloud/services/contacts.py +++ b/pyicloud/services/contacts.py @@ -22,8 +22,6 @@ class ContactsService(object): Refreshes the ContactsService endpoint, ensuring that the contacts data is up-to-date. """ - host = self._service_root.split('//')[1].split(':')[0] - self.session.headers.update({'host': host}) params_contacts = dict(self.params) params_contacts.update({ 'clientVersion': '2.1', diff --git a/pyicloud/services/findmyiphone.py b/pyicloud/services/findmyiphone.py index 1d9971c..7cce1bc 100644 --- a/pyicloud/services/findmyiphone.py +++ b/pyicloud/services/findmyiphone.py @@ -33,8 +33,6 @@ class FindMyiPhoneServiceManager(object): This ensures that the location data is up-to-date. """ - host = self._service_root.split('//')[1].split(':')[0] - self.session.headers.update({'host': host}) req = self.session.post( self._fmip_refresh_url, params=self.params, diff --git a/pyicloud/services/ubiquity.py b/pyicloud/services/ubiquity.py index 86878e2..30c7ec5 100644 --- a/pyicloud/services/ubiquity.py +++ b/pyicloud/services/ubiquity.py @@ -13,9 +13,6 @@ class UbiquityService(object): self._service_root = service_root self._node_url = '/ws/%s/%s/%s' - host = self._service_root.split('//')[1].split(':')[0] - self.session.headers.update({'host': host}) - def get_node_url(self, id, variant='item'): return self._service_root + self._node_url % ( self.params['dsid'], From 07aaaeb67af656f82337472a14de7f328c2ccee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 13 Jan 2016 21:05:21 +0100 Subject: [PATCH 5/5] Fix reStructuredText inline literal formatting in README --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 5f052e2..a7dc333 100644 --- a/README.rst +++ b/README.rst @@ -15,18 +15,18 @@ At its core, PyiCloud connects to iCloud using your username and password, then Authentication ============== -Authentication is as simple as passing your username and password to the `PyiCloudService` class: +Authentication is as simple as passing your username and password to the ``PyiCloudService`` class: >>> from pyicloud import PyiCloudService >>> api = PyiCloudService('jappleseed@apple.com', 'password') -In the event that the username/password combination is invalid, a `PyiCloudFailedLoginException` exception is thrown. +In the event that the username/password combination is invalid, a ``PyiCloudFailedLoginException`` exception is thrown. ======= Devices ======= -You can list which devices associated with your account by using the `devices` property: +You can list which devices associated with your account by using the ``devices`` property: >>> api.devices { @@ -41,7 +41,7 @@ and you can access individual devices by either their index, or their ID: >>> api.devices['i9vbKRGIcLYqJnXMd1b257kUWnoyEBcEh6yM+IfmiMLh7BmOpALS+w=='] -or, as a shorthand if you have only one associated apple device, you can simply use the `iphone` property to access the first device associated with your account: +or, as a shorthand if you have only one associated apple device, you can simply use the ``iphone`` property to access the first device associated with your account: >>> api.iphone @@ -122,7 +122,7 @@ Alternatively, you may fetch a single event's details, like so: File Storage (Ubiquity) ======================= -You can access documents stored in your iCloud account by using the `files` property's `dir` method: +You can access documents stored in your iCloud account by using the ``files`` property's ``dir`` method: >>> api.files.dir() [u'.do-not-delete', @@ -153,12 +153,12 @@ datetime.datetime(2012, 9, 13, 2, 26, 17) >>> api.files['com~apple~Notes']['Documents']['Some Document'].type u'file' -And when you have a file that you'd like to download, the `open` method will return a response object from which you can read the `content`. +And when you have a file that you'd like to download, the ``open`` method will return a response object from which you can read the ``content``. >>> api.files['com~apple~Notes']['Documents']['Some Document'].open().content 'Hello, these are the file contents' -Note: the object returned from the above `open` method is a `response object `_ and the `open` method can accept any parameters you might normally use in a request using `requests `_. +Note: the object returned from the above ``open`` method is a `response object `_ and the ``open`` method can accept any parameters you might normally use in a request using `requests `_. For example, if you know that the file you're opening has JSON content: @@ -167,7 +167,7 @@ For example, if you know that the file you're opening has JSON content: >>> api.files['com~apple~Notes']['Documents']['information.json'].open().json()['How much we love you'] 'lots' -Or, if you're downloading a particularly large file, you may want to use the `stream` keyword argument, and read directly from the raw response object: +Or, if you're downloading a particularly large file, you may want to use the ``stream`` keyword argument, and read directly from the raw response object: >>> download = api.files['com~apple~Notes']['Documents']['big_file.zip'].open(stream=True) >>> with open('downloaded_file.zip', 'wb') as opened_file: