Guest User

icloud_contacts.py

a guest
Feb 12th, 2018
224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.91 KB | None | 0 0
  1. import base64
  2. import plistlib
  3. import urllib2
  4. import sys
  5. from xml.etree import ElementTree as ET
  6.  
  7.  
  8. def dsid_factory(username, password):
  9.     credentials = base64.b64encode("{0}:{1}".format(username, password))
  10.     url = "https://setup.icloud.com/setup/authenticate/{0}".format(username)
  11.     headers = {
  12.         "Authorization": "Basic {0}".format(credentials),
  13.         "Content-Type": "application/xml",
  14.     }
  15.     request = urllib2.Request(url, None, headers)
  16.  
  17.     try:
  18.         response = urllib2.urlopen(request)
  19.     except urllib2.HTTPError as e:
  20.         if e.code != 200:
  21.             if e.code == 401:
  22.                 return "Error 401: Unauthorized, are you sure the credentials are correct?"
  23.             elif e.code == 409:
  24.                 return "Error 409: 2FA enabled, MMeAuthToken required."
  25.             elif e.code == 404:
  26.                 return "Error 404: URL not found, did you enter a username?"
  27.             else:
  28.                 return "Error %s." % e.code
  29.         else:
  30.             print e
  31.             raise urllib2.HTTPError
  32.  
  33.     content = response.read()
  34.  
  35.     dsid = int(plistlib.readPlistFromString(content)["appleAccountInfo"]["dsPrsID"])
  36.     token = plistlib.readPlistFromString(content)["tokens"]["mmeAuthToken"]
  37.     return dsid, token
  38.  
  39.  
  40. def get_card_links(dsid, token):
  41.     url = "https://p04-contacts.icloud.com/{0}/carddavhome/card".format(dsid)
  42.     headers = {
  43.         "Depth": "1",
  44.         "Authorization": "X-MobileMe-AuthToken {0}".format(base64.b64encode("{0}:{1}".format(dsid, token))),
  45.         "Content-Type": "text/xml",
  46.     }
  47.     data = """<?xml version="1.0" encoding="UTF-8"?>
  48.    <A:propfind xmlns:A="DAV:">
  49.      <A:prop>
  50.        <A:getetag/>
  51.      </A:prop>
  52.    </A:propfind>
  53.    """
  54.  
  55.     request = urllib2.Request(url, data, headers)
  56.     request.get_method = lambda: 'PROPFIND'
  57.  
  58.     response = urllib2.urlopen(request)
  59.     zebra = ET.fromstring(response.read())
  60.     returned_data = """<?xml version="1.0" encoding="UTF-8"?>
  61.    <F:addressbook-multiget xmlns:F="urn:ietf:params:xml:ns:carddav">
  62.      <A:prop xmlns:A="DAV:">
  63.        <A:getetag/>
  64.        <F:address-data/>
  65.      </A:prop>\n"""
  66.  
  67.     for response in zebra:
  68.         for link in response:
  69.             href = response.find('{DAV:}href').text  # Get each link in the tree
  70.         returned_data += "<A:href xmlns:A=\"DAV:\">{0}</A:href>\n".format(href)
  71.     return "{0}</F:addressbook-multiget>".format(str(returned_data))
  72.  
  73.  
  74. def get_contacts(dsid, token):
  75.     url = "https://p04-contacts.icloud.com/{0}/carddavhome/card".format(dsid)
  76.     headers = {
  77.         "Content-Type": "text/xml",
  78.         "Authorization": "X-MobileMe-AuthToken {0}".format(base64.b64encode("{0}:{1}".format(dsid, token))),
  79.     }
  80.     data = get_card_links(dsid, token)
  81.  
  82.     request = urllib2.Request(url, data, headers)
  83.     request.get_method = lambda: "REPORT"
  84.  
  85.     response = urllib2.urlopen(request)
  86.     zebra = ET.fromstring(response.read())
  87.  
  88.     cards = []
  89.     for response in zebra:
  90.         tel, email = [], []
  91.         name = ""
  92.         vcard = response.find('{DAV:}propstat').find('{DAV:}prop').find('{urn:ietf:params:xml:ns:carddav}address-data').text
  93.         if vcard:
  94.             # print "-------------------------"
  95.  
  96.             for y in vcard.splitlines():
  97.                 # print y
  98.  
  99.                 if y.startswith("FN:"):
  100.                     name = y[3:]
  101.                 if y.startswith("TEL;"):
  102.                     tel.append((y.split("type")[-1].split(":")[-1].replace("(", "").replace(")", "").replace(" ", "").replace("-", "").encode("ascii", "ignore")))
  103.                 if y.startswith("EMAIL;") or y.startswith("item1.EMAIL;"):
  104.                     email.append(y.split(":")[-1])
  105.             cards.append(([name], tel, email))
  106.     return sorted(cards)
  107.  
  108. if __name__ == '__main__':
  109.     username = sys.argv[1]
  110.     password = sys.argv[2]
  111.  
  112.     token = dsid_factory(username, password)
  113.  
  114.     try:
  115.         (dsid, token) = dsid_factory(username, password)
  116.     except ValueError:
  117.         # Error, print error message
  118.         print token
  119.         sys.exit()
  120.  
  121.     for contact in get_contacts(dsid, token):
  122.         data = "\"{0}\": ".format(contact[0][0])
  123.         phone_numbers = contact[1]
  124.         emails = contact[2]
  125.  
  126.         if phone_numbers:
  127.             data += "["
  128.             current_count = 1
  129.  
  130.             for number in phone_numbers:
  131.                 data += number
  132.  
  133.                 if current_count != len(phone_numbers):
  134.                     data += ", "
  135.                 current_count += 1
  136.             data += "]"
  137.  
  138.         if emails:
  139.             if phone_numbers:
  140.                 data += ", ["
  141.             else:
  142.                 data += "["
  143.  
  144.             current_count = 1
  145.  
  146.             for email in emails:
  147.                 data += email
  148.  
  149.                 if current_count != len(emails):
  150.                     data += ", "
  151.                 current_count += 1
  152.             data += "]"
  153.  
  154.         print data
Add Comment
Please, Sign In to add comment