def create_info_plist(self):
        root_node =  self.lockdown.allValues
        info = {"BuildVersion": root_node.get("BuildVersion") or "",
                "DeviceName":  root_node.get("DeviceName") or "",
                "Display Name": root_node.get("DeviceName") or "",
                "GUID": "---",
                "ProductType": root_node.get("ProductType") or "",
                "ProductVersion": root_node.get("ProductVersion") or "",
                "Serial Number": root_node.get("SerialNumber") or "",
                "Unique Identifier": self.udid.upper(),
                "Target Identifier": self.udid,
                "Target Type": "Device",
                "iTunes Version": "10.0.1"
                }
        info["ICCID"] = root_node.get("IntegratedCircuitCardIdentity") or ""
        info["IMEI"] = root_node.get("InternationalMobileEquipmentIdentity") or ""
        info["Last Backup Date"] = datetime.datetime.now()

        afc = AFCClient(self.lockdown)
        iTunesFilesDict = {}
        iTunesFiles = afc.read_directory("/iTunes_Control/iTunes/")

        for i in iTunesFiles:
            data = afc.get_file_contents("/iTunes_Control/iTunes/"  + i)
            if data:
                iTunesFilesDict[i] = plistlib.Data(data)
        info["iTunesFiles"] = iTunesFilesDict

        iBooksData2 = afc.get_file_contents("/Books/iBooksData2.plist")
        if iBooksData2:
            info["iBooks Data 2"] = plistlib.Data(iBooksData2)

        info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes")
        self.logger.info("Creating: %s", os.path.join(self.udid,"Info.plist"))
        self.write_file(os.path.join(self.udid,"Info.plist"), plistlib.writePlistToString(info))
Example #2
0
    def start(self):
        self.udid = lockdown.getValue("", "UniqueDeviceID")
        self.willEncrypt = lockdown.getValue("com.apple.mobile.backup",
                                             "WillEncrypt")
        self.escrowBag = lockdown.getValue('', 'EscrowBag')
        # We need this to create lock files
        self.afc = AFCClient(self.lockdown)
        self.service = self.lockdown.startServiceWithEscrowBag(
            "com.apple.mobilebackup2", self.escrowBag)
        if not self.service:
            raise Exception(
                "MobileBackup2 init error : Could not start com.apple.mobilebackup2"
            )

        if not os.path.isdir(self.backupPath):
            os.makedirs(self.backupPath, 0o0755)

        self.logger.info(
            "Starting new com.apple.mobilebackup2 service with working dir: %s",
            self.backupPath)

        DLMessageVersionExchange = self.service.recvPlist()
        version_major = DLMessageVersionExchange[1]
        self.service.sendPlist(
            ["DLMessageVersionExchange", "DLVersionsOk", version_major])
        DLMessageDeviceReady = self.service.recvPlist()
        if DLMessageDeviceReady and DLMessageDeviceReady[
                0] == "DLMessageDeviceReady":
            res = self.version_exchange()
            protocol_version = res.get('ProtocolVersion')
            self.logger.info("Negotiated Protocol Version %s",
                             protocol_version[1])
        else:
            raise Exception("MobileBackup2 init error %s",
                            DLMessageDeviceReady)
Example #3
0
 def install(self, ipa_path, options=None, handler=None, *args):
     '''安装应用程序
     
     :param ipa_path: 安装包的路径
     :type ipa_path: str
     :return: boolean - 安装是否成功
     '''
     print "上传安装包..."
     afc_client = AFCClient(self.lockdown)
     tmp_ipa = "t%d.ipa" % time.time()
     with open(ipa_path, "rb") as f:
         ipa_content = f.read()
         afc_client.set_file_contents("/" + tmp_ipa, ipa_content)
         print "上传完毕"
     print "开始安装"
     cmd = {"Command": "Install", "PackagePath": tmp_ipa}
     if options:
         cmd.update(options)
     self.lockdown = LockdownClient(self.udid)
     self.service = self.lockdown.startService(
         "com.apple.mobile.installation_proxy")
     self.service.sendPlist(cmd)
     ret = self.wait_completion(handler, args)
     if ret[0]:
         print "安装成功"
     else:
         print "安装失败:%s" % ret[1]
     return ret
    def install_or_upgrade(self, ipaPath, cmd="Install", options={}, handler=None, *args):
        afc = AFCClient(self.lockdown)
        afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath,"rb").read())
        cmd = { "Command": cmd,
                "ClientOptions": options,
                "PackagePath": os.path.basename(ipaPath)}

        self.service.sendPlist(cmd)
        self.watch_completion(handler, args)
    def install_or_upgrade(self, ipaPath, cmd="Install", options=None, handler=None, *args):
        afc = AFCClient(self.lockdown)
        afc.set_file_contents("/" + os.path.basename(ipaPath), open(ipaPath,"rb").read())
        cmd = {"Command":cmd, "PackagePath": os.path.basename(ipaPath)}
        if options:
            cmd.update(options)
        self.service.sendPlist(cmd)
#         print "%s : " % (cmd, bundleID)
        print "%s : %s\n" % (cmd, self.watch_completion(handler, args))
Example #6
0
def install_ipa(uuid, ipa_path):
    """
    docstring for install_ipa
    """
    from pymobiledevice.afc import AFCClient
    lockdown, service = get_lockdown_and_service(uuid)
    afc = AFCClient(lockdown=lockdown)
    afc.set_file_contents(path.basename(ipa_path), open(ipa_path, "rb").read())
    cmd = {"Command": "Install", "PackagePath": path.basename(ipa_path)}
    return run_command(service, uuid, cmd)
Example #7
0
 def install_or_upgrade(self,
                        ipaPath,
                        cmd="Install",
                        options=None,
                        handler=None,
                        *args):
     afc = AFCClient(self.lockdown)
     afc.set_file_contents("/" + os.path.basename(ipaPath),
                           open(ipaPath, "rb").read())
     cmd = {"Command": cmd, "PackagePath": os.path.basename(ipaPath)}
     if options:
         cmd.update(options)
     self.service.sendPlist(cmd)
     #         print "%s : " % (cmd, bundleID)
     print "%s : %s\n" % (cmd, self.watch_completion(handler, args))
    def start(self):
        self.udid = lockdown.getValue("", "UniqueDeviceID")
        self.willEncrypt = lockdown.getValue("com.apple.mobile.backup", "WillEncrypt")
        self.escrowBag = lockdown.getValue('', 'EscrowBag')
        self.afc = AFCClient(self.lockdown) #We need this to create lock files
        self.service = self.lockdown.startService("com.apple.mobilebackup2")
        #self.service = self.lockdown.startServiceWithEscrowBag("com.apple.mobilebackup2", self.escrowBag)
        if not self.service:
            raise Exception("MobileBackup2 init error : Could not start com.apple.mobilebackup2")

        if not os.path.isdir(self.backupPath):
            os.makedirs(self.backupPath,0o0755)

        self.logger.info("Starting new com.apple.mobilebackup2 service with working dir: %s", self.backupPath)

        DLMessageVersionExchange = self.service.recvPlist()
        version_major = DLMessageVersionExchange[1]
        self.service.sendPlist(["DLMessageVersionExchange", "DLVersionsOk", version_major])
        DLMessageDeviceReady = self.service.recvPlist()
        if DLMessageDeviceReady and DLMessageDeviceReady[0] == "DLMessageDeviceReady":
            res = self.version_exchange()
            protocol_version = res.get('ProtocolVersion')
            self.logger.info("Negotiated Protocol Version %s", protocol_version)
        else:
            raise Exception("MobileBackup2 init error %s", DLMessageDeviceReady)
    def test_get_crash_log(self):
        mux = USBMux()
        if not mux.devices:
            mux.process(0.1)
        if len(mux.devices) == 0:
            print("no real device found")
            self.no_device = True
            return
        udid = mux.devices[0].serial

        procname = "QQ"
        lockdown = LockdownClient(udid)
        self.service = lockdown.startService("com.apple.crashreportcopymobile")
        client = AFCClient(lockdown, service=self.service)
        afc_shell = AFCShell(client=client)
        remote_crash_path = '/'
        dest_path = '/tmp'
        local_crashes = []
        print('udid:', udid)
        for _dirname, _dirs, files in afc_shell.afc.dir_walk(
                remote_crash_path):

            for filename in files:
                if procname in filename:
                    remote_crash_file = os.path.join(remote_crash_path,
                                                     filename)
                    data = afc_shell.afc.get_file_contents(remote_crash_file)
                    local_crash_file = os.path.join(dest_path, filename)
                    local_crashes.append(local_crash_file)
                    with open(local_crash_file, 'wb') as fp:
                        fp.write(data)
        print(local_crashes)
 def test_install_app(self):
     if self.no_device:
         return
     ipa_path = os.path.join(os.path.expanduser("~"), "Downloads/app/DemoApp.ipa")
     tmp_ipa = "/t%d.ipa" % time.time()
     with open(ipa_path, "rb") as f:
         ipa_content = f.read()
         afc = AFCClient(self.lockdownclient)
         afc.set_file_contents(tmp_ipa, ipa_content)
         print("Upload completed")
     print("Starting installation")
     cmd = {"Command":"Install", "PackagePath": tmp_ipa}
     self.lockdownclient = LockdownClient(self.udid)
     self.service = self.lockdownclient.startService("com.apple.mobile.installation_proxy")
     self.service.sendPlist(cmd)
     result, err = self.wait_completion()
     self.assertTrue(result, 'install_app failed: %s' % err)
    def install_or_upgrade(self,
                           ipaPath,
                           cmd="Install",
                           options={},
                           handler=None,
                           *args):
        afc = AFCClient(self.lockdown)
        afc.set_file_contents("/" + os.path.basename(ipaPath),
                              open(ipaPath, "rb").read())
        cmd = {
            "Command": cmd,
            "ClientOptions": options,
            "PackagePath": os.path.basename(ipaPath)
        }

        self.service.sendPlist(cmd)
        self.watch_completion(handler, args)
Example #12
0
 def install(self, ipa_path, options=None, handler=None, *args):
     '''安装应用程序
     
     :param ipa_path: 安装包的路径
     :type ipa_path: str
     :return: boolean - 安装是否成功
     '''
     print "上传安装包..."
     afc_client = AFCClient()
     tmp_ipa = "t%d.ipa" % time.time()
     with open(ipa_path, "rb") as f:
         ipa_content = f.read()
         afc_client.set_file_contents("/" + tmp_ipa, ipa_content)
         print "上传完毕"
     print "开始安装"
     cmd = {"Command": "Install", "PackagePath": tmp_ipa}
     if options:
         cmd.update(options)
     self.service.sendPlist(cmd)
     return self.wait_completion(handler, args)
Example #13
0
def mobile_install(lockdown, ipaPath):
    # Start afc service & upload ipa
    afc = AFCClient(lockdown)
    afc.set_file_contents("/" + os.path.basename(ipaPath),
                          open(ipaPath, 'rb').read())
    mci = lockdown.startService("com.apple.mobile.installation_proxy")
    #print mci.sendPlist({"Command":"Archive","ApplicationIdentifier": "com.joystickgenerals.STActionPig"})
    mci.sendPlist({"Command": "Install",
                   # "ApplicationIdentifier": "com.gotohack.JBChecker",
                   "PackagePath": os.path.basename(ipaPath)})
    while True:
        z = mci.recvPlist()
        if not z:
            break
        completion = z.get('PercentComplete')
        if completion:
            print('Installing, %s: %s %% Complete' % (
                ipaPath, z['PercentComplete']))
        if z.get('Status') == 'Complete':
            print("Installation %s\n" % z['Status'])
            break
Example #14
0
def mobile_install(lockdown, ipaPath):
    # Start afc service & upload ipa
    afc = AFCClient(lockdown)
    afc.set_file_contents("/" + os.path.basename(ipaPath),
                          open(ipaPath, 'rb').read())
    mci = lockdown.startService("com.apple.mobile.installation_proxy")
    # print mci.sendPlist({"Command":"Archive","ApplicationIdentifier":
    # "com.joystickgenerals.STActionPig"})
    mci.sendPlist({"Command": "Install",
                   #"ApplicationIdentifier": "com.gotohack.JBChecker",
                   "PackagePath": os.path.basename(ipaPath)})
    while True:
        z = mci.recvPlist()
        if not z:
            break
        completion = z.get('PercentComplete')
        if completion:
            print('Installing, %s: %s %% Complete' %
                  (ipaPath, z['PercentComplete']))
        if z.get('Status') == 'Complete':
            print("Installation %s\n" % z['Status'])
            break
Example #15
0
    def create_info_plist(self):
        root_node = self.lockdown.allValues
        # print pprint(root_node)
        info = {"BuildVersion": root_node.get("BuildVersion") or "",
                "DeviceName":  root_node.get("DeviceName") or "",
                "Display Name": root_node.get("DeviceName") or "",
                "GUID": "---",
                "ProductType": root_node.get("ProductType") or "",
                "ProductVersion": root_node.get("ProductVersion") or "",
                "Serial Number": root_node.get("SerialNumber") or "",
                "Unique Identifier": self.udid.upper(),
                "Target Identifier": self.udid,
                "Target Type": "Device",
                "iTunes Version": "10.0.1"
                }
        info["ICCID"] = root_node.get("IntegratedCircuitCardIdentity") or ""
        info["IMEI"] = root_node.get(
            "InternationalMobileEquipmentIdentity") or ""
        info["Last Backup Date"] = datetime.datetime.now()

        afc = AFCClient(self.lockdown)
        iTunesFilesDict = {}
        iTunesFiles = afc.read_directory("/iTunes_Control/iTunes/")

        for i in iTunesFiles:
            data = afc.get_file_contents("/iTunes_Control/iTunes/" + i)
            if data:
                iTunesFilesDict[i] = plistlib.Data(data)
        info["iTunesFiles"] = iTunesFilesDict

        iBooksData2 = afc.get_file_contents("/Books/iBooksData2.plist")
        if iBooksData2:
            info["iBooks Data 2"] = plistlib.Data(iBooksData2)

        info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes")
        print("Creating %s" % os.path.join(self.udid, "Info.plist"))
        self.write_file(os.path.join(self.udid, "Info.plist"),
                        plistlib.dumps(info))
 def setUp(self):
     mux = USBMux()
     if not mux.devices:
         mux.process(0.1)
     if len(mux.devices) == 0:
         print("no real device found")
         self.no_device = True
         return
     udid = mux.devices[0].serial
     lockdown_client = LockdownClient(udid)
     self.service = lockdown_client.startService(
         "com.apple.mobile.house_arrest")
     self.service.sendPlist({
         "Command": "VendContainer",
         "Identifier": "com.gotohack.testapp"
     })
     status = self.service.recvPlist()
     if 'Error' in status and status['Error'] == "ApplicationLookupFailed":
         raise RuntimeWarning('ApplicationLookupFailed')
     if 'Status' in status and status['Status'] != 'Complete':
         raise RuntimeWarning('House arrest service launch failed')
     self.afc = AFCClient(lockdown_client, service=self.service)
     self.afc_shell = AFCShell(client=self.afc)
Example #17
0
def house_arrest(lockdown, applicationId):
    try:
        mis = lockdown.startService("com.apple.mobile.house_arrest")
    except:
        lockdown = LockdownClient()
        mis = lockdown.startService("com.apple.mobile.house_arrest")

    if mis == None:
        return
    mis.sendPlist({"Command": "VendDocuments", "Identifier": applicationId})
    res = mis.recvPlist()
    if res.get("Error"):
        print("Unable to Lookup the selected application: You probably trying to access to a system app...")
        return None
    return AFCClient(lockdown, service=mis)
class HouseArrestTest(unittest.TestCase):
    def setUp(self):
        mux = USBMux()
        if not mux.devices:
            mux.process(0.1)
        if len(mux.devices) == 0:
            print("no real device found")
            self.no_device = True
            return
        udid = mux.devices[0].serial
        lockdown_client = LockdownClient(udid)
        self.service = lockdown_client.startService(
            "com.apple.mobile.house_arrest")
        self.service.sendPlist({
            "Command": "VendContainer",
            "Identifier": "com.gotohack.testapp"
        })
        status = self.service.recvPlist()
        if 'Error' in status and status['Error'] == "ApplicationLookupFailed":
            raise RuntimeWarning('ApplicationLookupFailed')
        if 'Status' in status and status['Status'] != 'Complete':
            raise RuntimeWarning('House arrest service launch failed')
        self.afc = AFCClient(lockdown_client, service=self.service)
        self.afc_shell = AFCShell(client=self.afc)

    def test_list_files_in_sandbox(self):
        if self.no_device:
            return
        sandbox_tree = []
        file_path = '/Documents'
        for l in self.afc.read_directory(file_path):
            if l not in ('.', '..'):
                tmp_dict = {}
                tmp_dict['path'] = os.path.join(file_path, l)
                info = self.afc.get_file_info(tmp_dict['path'])
                tmp_dict['is_dir'] = (info is not None
                                      and info['st_ifmt'] == 'S_IFDIR')
                sandbox_tree.append(tmp_dict)
        pprint.pprint(sandbox_tree)

    def test_push_file_to_sandbox(self):
        if self.no_device:
            return
        data = b"hello sandbox!"
        self.afc.set_file_contents('/Documents/test.log', data)

    def test_pull_file_from_sandbox(self):
        if self.no_device:
            return
        data = b"hello sandbox!"
        content = self.afc.get_file_contents('/Documents/test.log')
        print(content)

    def test_remove_file_in_sandbox(self):
        if self.no_device:
            return
        self.afc_shell.do_rm('/Documents/test.log')

    def tearDown(self):
        if not self.no_device and self.service:
            self.service.close()
class MobileBackup2(MobileBackup):
    service = None
    def __init__(self, lockdown = None,backupPath = None, password="", udid=None, logger=None):
        self.logger = logger or logging.getLogger(__name__)
        self.backupPath = backupPath if backupPath else "backups"
        self.password = password
        self.lockdown = lockdown if lockdown else LockdownClient(udid=udid)
        if not self.lockdown:
            raise Exception("Unable to start lockdown")

        ProductVersion = self.lockdown.getValue("", "ProductVersion")
        if ProductVersion and int(ProductVersion[:ProductVersion.find('.')]) < 5:
            raise DeviceVersionNotSupported
        self.start()


    def start(self):
        self.udid = lockdown.getValue("", "UniqueDeviceID")
        self.willEncrypt = lockdown.getValue("com.apple.mobile.backup", "WillEncrypt")
        self.escrowBag = lockdown.getValue('', 'EscrowBag')
        self.afc = AFCClient(self.lockdown) #We need this to create lock files
        self.service = self.lockdown.startService("com.apple.mobilebackup2")
        #self.service = self.lockdown.startServiceWithEscrowBag("com.apple.mobilebackup2", self.escrowBag)
        if not self.service:
            raise Exception("MobileBackup2 init error : Could not start com.apple.mobilebackup2")

        if not os.path.isdir(self.backupPath):
            os.makedirs(self.backupPath,0o0755)

        self.logger.info("Starting new com.apple.mobilebackup2 service with working dir: %s", self.backupPath)

        DLMessageVersionExchange = self.service.recvPlist()
        version_major = DLMessageVersionExchange[1]
        self.service.sendPlist(["DLMessageVersionExchange", "DLVersionsOk", version_major])
        DLMessageDeviceReady = self.service.recvPlist()
        if DLMessageDeviceReady and DLMessageDeviceReady[0] == "DLMessageDeviceReady":
            res = self.version_exchange()
            protocol_version = res.get('ProtocolVersion')
            self.logger.info("Negotiated Protocol Version %s", protocol_version)
        else:
            raise Exception("MobileBackup2 init error %s", DLMessageDeviceReady)

    def __del__(self):
        if self.service:
            self.service.sendPlist(["DLMessageDisconnect", "___EmptyParameterString___"])

    def internal_mobilebackup2_send_message(self, name, data):
        data["MessageName"] = name
        self.device_link_service_send_process_message(data)

    def internal_mobilebackup2_receive_message(self, name=None):
        res = self.device_link_service_receive_process_message()
        if res:
            if name and res["MessageName"] != name:
                self.logger.error("MessageName does not match %s %s", name, str(res))
            return res

    def version_exchange(self):
        self.internal_mobilebackup2_send_message("Hello",
                                                {"SupportedProtocolVersions": [2.0,2.1]})

        return self.internal_mobilebackup2_receive_message("Response")

    def mobilebackup2_send_request(self, request, target, source, options={}):
        d = {"TargetIdentifier": target,
             "SourceIdentifier": source,
             "Options": options}

        self.internal_mobilebackup2_send_message(request, d)

    def mobilebackup2_receive_message(self):
        return self.service.recvPlist()

    def mobilebackup2_send_status_response(self, status_code,
                                                 status1="___EmptyParameterString___",
                                                 status2={}):
        a = ["DLMessageStatusResponse", status_code, status1, status2]
        self.service.sendPlist(a)

    def mb2_handle_free_disk_space(self,msg):
        s = os.statvfs(self.backupPath)
        freeSpace = s.f_bsize * s.f_bavail
        res = ["DLMessageStatusResponse", 0, "___EmptyParameterString___", freeSpace]
        self.service.sendPlist(res)

    def mb2_multi_status_add_file_error(self, errplist, path, error_code, error_message):
        errplist[path] = {"DLFileErrorCode": error_code, "DLFileErrorString": error_message}

    def mb2_handle_copy_item(self, msg):
        src = self.check_filename(msg[1])
        dst = self.check_filename(msg[2])
        if os.path.isfile(src):
            data = self.read_file(src)
            self.write_file(dst, data)
        else:
            os.makedirs(dst)
        self.mobilebackup2_send_status_response(0)

    def mb2_handle_send_file(self, filename, errplist):
        self.service.send_raw(filename)
        if not filename.startswith(self.udid):
            filename = self.udid + "/" + filename

        data = self.read_file(self.check_filename(filename))
        if data != None:
            self.logger.info("Sending %s to device", filename)
            if PY3: # FIXME
                msg = b"".join([(CODE_FILE_DATA).to_bytes(1,'little'), data])
            else:
                msg = chr(CODE_FILE_DATA) + data
            self.service.send_raw(msg)
            self.service.send_raw(chr(CODE_SUCCESS))
        else:
            self.logger.warn("File %s requested from device not found", filename)
            self.service.send_raw(chr(CODE_ERROR_LOCAL))
            self.mb2_multi_status_add_file_error(errplist, filename,
                ERROR_ENOENT, "Could not find the droid you were looking for ;)")

    def mb2_handle_send_files(self, msg):
        errplist = {}
        for f in msg[1]:
            self.mb2_handle_send_file(f, errplist)
        msg = "\x00\x00\x00\x00"
        if PY3:
            msg = b"\x00\x00\x00\x00"
        self.service.send(msg)
        if len(errplist):
            self.mobilebackup2_send_status_response(-13, "Multi status", errplist)
        else:
            self.mobilebackup2_send_status_response(0)

    def mb2_handle_list_directory(self, msg):
        path = msg[1]
        self.logger.info("List directory: %s" % path)
        dirlist = {}
        if path.find("../") != -1:
            raise Exception("HAX, sneaky dots in path %s" % name)
        for root, dirs, files in os.walk(os.path.join(self.backupPath, path)):
            for fname in files:
                fpath = os.path.join(root, fname)
                finfo = {}
                st = os.stat(fpath)
                ftype = "DLFileTypeUnknown"
                if S_ISDIR(st.st_mode):
                    ftype = "DLFileTypeDirectory"
                elif S_ISREG(st.st_mode):
                    ftype = "DLFileTypeRegular"
                finfo["DLFileType"]  = ftype
                finfo["DLFileSize"]  = st.st_size
                finfo["DLFileModificationDate"] = st.st_mtime
                dirlist[fname] = finfo
        self.mobilebackup2_send_status_response(0, status2=dirlist);

    def mb2_handle_make_directory(self, msg):
        dirname = self.check_filename(msg[1])
        self.logger.info("Creating directory %s", dirname)
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        self.mobilebackup2_send_status_response(0, "")

    def mb2_handle_receive_files(self, msg):
        done = 0
        while not done:
            device_filename = self.service.recv_raw()
            if device_filename == "":
                break
            backup_filename = self.service.recv_raw()
            self.logger.debug("Downloading: %s to %s", device_filename, backup_filename)
            filedata = ""
            if PY3:
                filedata = bytearray(b"")
            last_code = 0x00
            while True:
                stuff = self.service.recv_raw()
                if PY3:
                    code = stuff[0]
                else:
                    code = ord(stuff[0])
                if code == CODE_FILE_DATA:
                    filedata = stuff[1:]
                elif code == CODE_SUCCESS:
                    self.write_file(self.check_filename(backup_filename), filedata)
                    break
                elif code == CODE_ERROR_REMOTE:
                      if last_code != CODE_FILE_DATA:
                        self.logger.warn("Received an error message from device: %s for:\n\t%s\n\t[%s]",
                                ord(code), device_filename, backup_filename)
                else:
                    self.logger.warn("Unknown code: %s for:\n\t%s\n\t[%s]",
                                        code, device_filename, backup_filename)
                    self.logger.warn(msg)
                    #break
            last_code = code
        self.mobilebackup2_send_status_response(0)

    def mb2_handle_move_files(self, msg):
        self.logger.info("Moving %d files", len(msg[1]) )
        for k,v in msg[1].items():
            self.logger.info("Renaming:\n\t%s \n\tto %s", self.check_filename(k), self.check_filename(v))
            os.rename(self.check_filename(k),self.check_filename(v))
        self.mobilebackup2_send_status_response(0)

    def mb2_handle_remove_files(self, msg):
        self.logger.info("Removing %d files", len(msg[1]) )
        for filename in msg[1]:
            self.logger.info("Removing %s", self.check_filename(filename))
            try:
                filename = self.check_filename(filename)
                if os.path.isfile(filename):
                    os.unlink(filename)
            except Exception as e:
                self.logger.error(e)
        self.mobilebackup2_send_status_response(0)

    def work_loop(self):
        while True:
            msg = self.mobilebackup2_receive_message()
            if not msg:
                break

            assert(msg[0] in ["DLMessageDownloadFiles",
                    "DLContentsOfDirectory",
                    "DLMessageCreateDirectory",
                    "DLMessageUploadFiles",
                    "DLMessageMoveFiles","DLMessageMoveItems",
                    "DLMessageRemoveFiles", "DLMessageRemoveItems",
                    "DLMessageCopyItem",
                    "DLMessageProcessMessage",
                    "DLMessageGetFreeDiskSpace",
                    "DLMessageDisconnect"])

            if msg[0] == "DLMessageDownloadFiles":
                self.mb2_handle_send_files(msg)
            elif msg[0] == "DLContentsOfDirectory":
                self.mb2_handle_list_directory(msg)
            elif msg[0] == "DLMessageCreateDirectory":
                self.mb2_handle_make_directory(msg)
            elif msg[0] == "DLMessageUploadFiles":
                self.mb2_handle_receive_files(msg)
            elif msg[0] in ["DLMessageMoveFiles","DLMessageMoveItems"]:
                self.mb2_handle_move_files(msg)
            elif msg[0] in ["DLMessageRemoveFiles", "DLMessageRemoveItems"]:
                self.mb2_handle_remove_files(msg)
            elif msg[0] == "DLMessageCopyItem":
                self.mb2_handle_copy_item(msg)
            elif msg[0] == "DLMessageProcessMessage":
                errcode = msg[1].get("ErrorCode")
                if errcode == 0:
                    m =  msg[1].get("MessageName")
                    if m != "Response":
                        self.logger.warn(m)
                    break
                if errcode == 1:
                    self.logger.info("Please unlock your device and retry...")
                    raise Exception("Please unlock your device and retry...")
                if errcode == 211:
                    self.logger.info("Please go to Settings->iClould->Find My iPhone and disable it")
                    raise Exception('Please go to Settings->iClould->Find My iPhone and disable it')
                if errcode == 105:
                    self.logger.info("Not enough free space on device for restore")
                    raise Exception('Not enough free space on device for restore')
                if errcode == 17:
                    self.logger.info("please press 'trust this computer' in your device")
                    raise Exception('please press \'trust this computer\' in your device')
                if errcode == 102:
                    self.logger.info("Please reboot your device and try again")
                    raise Exception('Please reboot your device and try again')
                self.logger.error("Unknown error: %d : %s", errcode, msg[1].get("ErrorDescription", ""))
                raise Exception('Unknown error ' + str(errcode) + msg[1].get("ErrorDescription", ""))
            elif msg[0] == "DLMessageGetFreeDiskSpace":
                self.mb2_handle_free_disk_space(msg)
            elif msg[0] == "DLMessageDisconnect":
                break

    def create_status_plist(self,fullBackup=True):
        #Creating Status file for backup
        statusDict = { 'UUID': str(uuid4()).upper(),
                   'BackupState': 'new',
                   'IsFullBackup': fullBackup,
                   'Version': '2.4',
                   'Date': datetime.datetime.fromtimestamp(mktime(gmtime())),
                   'SnapshotState': 'finished'
                 }
        writePlist(statusDict,self.check_filename("Status.plist"))

#    def set_sync_lock(self):
#        #do_post_notification(device, NP_SYNC_WILL_START);
#        lockfile = self.afc.file_open("/com.apple.itunes.lock_sync")
#        if lockfile:
#            #do_post_notification(device, NP_SYNC_LOCK_REQUEST);
#			while True:
#				res = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
#				if res == AFC_E_SUCCESS:
#					#do_post_notification(device, NP_SYNC_DID_START);
#					break
#				elif res == AFC_E_OP_WOULD_BLOCK):
#					sleep(0.5)
#					continue
#				else:
#					print "ERROR: could not lock file! error: %d\n" % res
#					self.afc.file_close(lockfile);
#					lockfile = 0;
#					cmd = CMD_LEAVE;
#
#		        if i == LOCK_ATTEMPTS:
#			        print "ERROR: timeout while locking for sync"
#			        self.afc.file_close(afc, lockfile)
#			        lockfile = 0
#			        cmd = CMD_LEAVE
#                    break
#
#    def remove_sync_lock(self):
#        pass
    def create_info_plist(self):
        # Get device information
        device_info =  self.lockdown.allValues

        # Get a list of installed user applications
        instpxy = installation_proxy(self.lockdown)
        apps = instpxy.browse({"ApplicationType":"User"},
                              ["CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata"])
        # Create new info.plits
        info = {"BuildVersion": device_info.get("BuildVersion") or "",
                "DeviceName":  device_info.get("DeviceName") or "",
                "Display Name": device_info.get("DeviceName") or "",
                "GUID": "---",
                "Product Name" : device_info.get("ProductName" or ""),
                "ProductType": device_info.get("ProductType") or "",
                "ProductVersion": device_info.get("ProductVersion") or "",
                "Serial Number": device_info.get("SerialNumber") or "",
                "Unique Identifier": self.udid.upper(),
                "Target Identifier": self.udid,
                "Target Type": "Device",
                "iTunes Version": "10.0.1",
                "MEID" : device_info.get("MobileEquipmentIdentifier") or "",
                "Phone Number" : device_info.get("PhoneNumber") or "",
                }

        info["ICCID"] = device_info.get("IntegratedCircuitCardIdentity") or ""
        info["IMEI"] = device_info.get("InternationalMobileEquipmentIdentity") or ""
        info["Last Backup Date"] = datetime.datetime.now()

        # Starting SpringBoard service to retrieve icons position
        self.sbs = SBServiceClient(self.lockdown)
        installed_apps = []
        apps_data = {}
        for app_entry in apps:
            tmp = {}
            bid = app_entry.get("CFBundleIdentifier")
            if bid:
                installed_apps.append(bid)
                pngdata = self.sbs.get_icon_pngdata(bid)
                if pngdata:
                    tmp["PlaceholderIcon"] = pngdata
                tmp["iTunesMetadata"] = app_entry.get("iTunesMetadata")
                tmp["ApplicationSINF"] = app_entry.get("ApplicationSINF")
                apps_data[bid] = tmp

        info["Applications"] = apps_data
        info["Installed Applications"] = installed_apps
        # Handling itunes files
        iTunesFiles = [ "ApertureAlbumPrefs", "IC-Info.sidb", "IC-Info.sidv", "PhotosFolderAlbums",
		                "PhotosFolderName", "PhotosFolderPrefs", "VoiceMemos.plist", "iPhotoAlbumPrefs",
		                "iTunesApplicationIDs", "iTunesPrefs", "iTunesPrefs.plist" ]
        iTunesFilesDict = {}
        for i in iTunesFiles:
            data = self.afc.get_file_contents("/iTunes_Control/iTunes/"  + i)
            if data:
                iTunesFilesDict[i] = plistlib.Data(data)

        info["iTunesFiles"] = iTunesFilesDict
        iBooksData2 = self.afc.get_file_contents("/Books/iBooksData2.plist")
        if iBooksData2:
            info["iBooks Data 2"] = plistlib.Data(iBooksData2)

        info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes")
        self.logger.info("Creating %s", os.path.join(self.udid,"Info.plist"))
        self.write_file(os.path.join(self.udid,"Info.plist"), plistlib.writePlistToString(info))


    def backup(self,fullBackup=True):
        #TODO set_sync_lock
        self.logger.info("Starting %s backup...", ("Encrypted " if self.willEncrypt else ""))
        if not os.path.isdir(os.path.join(self.backupPath,self.udid)):
            os.makedirs(os.path.join(self.backupPath,self.udid))
        self.logger.info("Backup mode: %s", "Full backup" if fullBackup else "Incremental backup")
        self.create_info_plist()
        options = {"ForceFullBackup": fullBackup}
        self.mobilebackup2_send_request("Backup", self.udid, options)
        self.work_loop()


    def restore(self, options = {"RestoreSystemFiles": True,
                                "RestoreShouldReboot": True,
                                "RestorePreserveCameraRoll": True,
                                "RemoveItemsNotRestored": False,
                                "RestoreDontCopyBackup": True,
                                "RestorePreserveSettings": True},
                                password=None):

        self.logger.info("Starting restoration...")
        m = os.path.join(self.backupPath,self.udid,"Manifest.plist")
        try:
            manifest = readPlist(m)
        except IOError:
            self.logger.error("not a valid backup folder")
            return -1
        if manifest.get("IsEncrypted"):
            print("Backup is encrypted, enter password : "******"Password"] = self.password
        self.mobilebackup2_send_request("Restore", self.udid, self.udid, options)
        self.work_loop()


    def info(self, options={}):
        source_udid = self.udid
        self.mobilebackup2_send_request("Info", self.udid, source_udid, options)
        self.work_loop()

    def list(self, options={}):
        source_udid = self.udid
        self.mobilebackup2_send_request("List", self.udid, source_udid, options)
        self.work_loop()

    def changepw(self,oldpw,newpw):
        options = { "OldPassword" : oldpw,
                    "NewPassword" : newpw }

        self.mobilebackup2_send_request("ChangePassword", self.udid, "", options)
        self.work_loop()

    def unback(self, options={"Password": None}):
        source_udid = self.udid
        self.mobilebackup2_send_request("Unback", self.udid, source_udid, options)
        self.work_loop()

    def enableCloudBackup(self,options={"CloudBackupState": False}):
        self.mobilebackup2_send_request("EnableCloudBackup", self.udid, options)
        self.work_loop()

    def mobilebackup2_notify_cb(notification, data=None):
        if notification == NP_SYNC_CANCEL_REQUEST:
            self.logger.info("User has cancelled the backup process on the device.")
        elif notification == NP_BACKUP_DOMAIN_CHANGED:
            backup_domain_changed = 1
        else:
            self.logger.info("Unhandled notification '%s'", notification)
Example #20
0
class MobileBackup2(MobileBackup):
    def __init__(self,
                 lockdown=None,
                 backupPath=None,
                 password="",
                 udid=None,
                 logger=None):
        self.logger = logger or logging.getLogger(__name__)
        self.backupPath = backupPath if backupPath else "backups"
        self.password = password
        self.lockdown = lockdown if lockdown else LockdownClient(udid=udid)
        if not self.lockdown:
            raise Exception("Unable to start lockdown")

        ProductVersion = self.lockdown.getValue("", "ProductVersion")
        if ProductVersion and int(
                ProductVersion[:ProductVersion.find('.')]) < 5:
            raise DeviceVersionNotSupported
        self.start()

    def start(self):
        self.udid = lockdown.getValue("", "UniqueDeviceID")
        self.willEncrypt = lockdown.getValue("com.apple.mobile.backup",
                                             "WillEncrypt")
        self.escrowBag = lockdown.getValue('', 'EscrowBag')
        # We need this to create lock files
        self.afc = AFCClient(self.lockdown)
        self.service = self.lockdown.startServiceWithEscrowBag(
            "com.apple.mobilebackup2", self.escrowBag)
        if not self.service:
            raise Exception(
                "MobileBackup2 init error : Could not start com.apple.mobilebackup2"
            )

        if not os.path.isdir(self.backupPath):
            os.makedirs(self.backupPath, 0o0755)

        self.logger.info(
            "Starting new com.apple.mobilebackup2 service with working dir: %s",
            self.backupPath)

        DLMessageVersionExchange = self.service.recvPlist()
        version_major = DLMessageVersionExchange[1]
        self.service.sendPlist(
            ["DLMessageVersionExchange", "DLVersionsOk", version_major])
        DLMessageDeviceReady = self.service.recvPlist()
        if DLMessageDeviceReady and DLMessageDeviceReady[
                0] == "DLMessageDeviceReady":
            res = self.version_exchange()
            protocol_version = res.get('ProtocolVersion')
            self.logger.info("Negotiated Protocol Version %s",
                             protocol_version[1])
        else:
            raise Exception("MobileBackup2 init error %s",
                            DLMessageDeviceReady)

    def __del__(self):
        if self.service:
            self.service.sendPlist(
                ["DLMessageDisconnect", "___EmptyParameterString___"])

    def internal_mobilebackup2_send_message(self, name, data):
        data["MessageName"] = name
        self.device_link_service_send_process_message(data)

    def internal_mobilebackup2_receive_message(self, name=None):
        res = self.device_link_service_receive_process_message()
        if res:
            if name and res["MessageName"] != name:
                self.logger.error("MessageName does not match %s %s", name,
                                  str(res))
            return res

    def version_exchange(self):
        self.internal_mobilebackup2_send_message(
            "Hello", {"SupportedProtocolVersions": [2.0, 2.1]})

        return self.internal_mobilebackup2_receive_message("Response")

    def mobilebackup2_send_request(self, request, target, source, options={}):
        d = {
            "TargetIdentifier": target,
            "SourceIdentifier": source,
            "Options": options
        }

        self.internal_mobilebackup2_send_message(request, d)

    def mobilebackup2_receive_message(self):
        return self.service.recvPlist()

    def mobilebackup2_send_status_response(
            self,
            status_code,
            status1="___EmptyParameterString___",
            status2={}):
        a = ["DLMessageStatusResponse", status_code, status1, status2]
        self.service.sendPlist(a)

    def mb2_handle_free_disk_space(self, msg):
        s = os.statvfs(self.backupPath)
        freeSpace = s.f_bsize * s.f_bavail
        res = [
            "DLMessageStatusResponse", 0, "___EmptyParameterString___",
            freeSpace
        ]
        self.service.sendPlist(res)

    def mb2_multi_status_add_file_error(self, errplist, path, error_code,
                                        error_message):
        errplist[path] = {
            "DLFileErrorCode": error_code,
            "DLFileErrorString": error_message
        }

    def mb2_handle_copy_item(self, msg):
        src = self.check_filename(msg[1])
        dst = self.check_filename(msg[2])
        if os.path.isfile(src):
            data = self.read_file(src)
            self.write_file(dst, data)
        else:
            os.makedirs(dst)
        self.mobilebackup2_send_status_response(0)

    def mb2_handle_send_file(self, filename, errplist):
        self.service.send_raw(filename)
        if not filename.startswith(self.udid):
            filename = self.udid + "/" + filename

        data = self.read_file(self.check_filename(filename))
        if data != None:
            self.logger.info("Sending %s to device", filename)
            self.service.send_raw(chr(CODE_FILE_DATA) + data)
            self.service.send_raw(chr(CODE_SUCCESS))
        else:
            self.logger.warn("File %s requested from device not found",
                             filename)
            self.service.send_raw(chr(CODE_ERROR_LOCAL))
            self.mb2_multi_status_add_file_error(
                errplist, filename, ERROR_ENOENT,
                "Could not find the droid you were looking for ;)")

    def mb2_handle_send_files(self, msg):
        errplist = {}
        for f in msg[1]:
            self.mb2_handle_send_file(f, errplist)
        self.service.send("\x00\x00\x00\x00")
        if len(errplist):
            self.mobilebackup2_send_status_response(-13, "Multi status",
                                                    errplist)
        else:
            self.mobilebackup2_send_status_response(0)

    def mb2_handle_list_directory(self, msg):
        path = msg[1]
        self.logger.info("List directory: %s" % path)
        dirlist = {}
        if path.find("../") != -1:
            raise Exception("HAX, sneaky dots in path %s" % name)
        for root, dirs, files in os.walk(os.path.join(self.backupPath, path)):
            for fname in files:
                fpath = os.path.join(root, fname)
                finfo = {}
                st = os.stat(fpath)
                ftype = "DLFileTypeUnknown"
                if S_ISDIR(st.st_mode):
                    ftype = "DLFileTypeDirectory"
                elif S_ISREG(st.st_mode):
                    ftype = "DLFileTypeRegular"
                finfo["DLFileType"] = ftype
                finfo["DLFileSize"] = st.st_size
                finfo["DLFileModificationDate"] = st.st_mtime
                dirlist[fname] = finfo
        self.mobilebackup2_send_status_response(0, status2=dirlist)

    def mb2_handle_make_directory(self, msg):
        dirname = self.check_filename(msg[1])
        self.logger.info("Creating directory %s", dirname)
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        self.mobilebackup2_send_status_response(0, "")

    def mb2_handle_receive_files(self, msg):
        done = 0
        while not done:
            device_filename = self.service.recv_raw()
            if device_filename == "":
                break
            backup_filename = self.service.recv_raw()
            self.logger.debug("Downloading: %s to %s", device_filename,
                              backup_filename)
            filedata = ""
            last_code = 0x00
            while True:
                stuff = self.service.recv_raw()
                if ord(stuff[0]) == CODE_FILE_DATA:
                    filedata += stuff[1:]
                elif ord(stuff[0]) == CODE_SUCCESS:
                    self.write_file(self.check_filename(backup_filename),
                                    filedata)
                    break
                elif ord(stuff[0]) == CODE_ERROR_REMOTE:
                    if last_code != CODE_FILE_DATA:
                        self.logger.warn(
                            "Received an error message from device: %s for:\n\t%s\n\t[%s]",
                            ord(stuff[0]), device_filename, backup_filename)
                else:
                    self.logger.warn("Unknown code: %s for:\n\t%s\n\t[%s]",
                                     ord(stuff[0]), device_filename,
                                     backup_filename)
                    self.logger.warn(msg)
                    # break
            last_code = ord(stuff[0])
        self.mobilebackup2_send_status_response(0)

    def mb2_handle_move_files(self, msg):
        self.logger.info("Moving %d files", len(msg[1]))
        for k, v in list(msg[1].items()):
            self.logger.info("Renaming:\n\t%s \n\tto %s",
                             self.check_filename(k), self.check_filename(v))
            os.rename(self.check_filename(k), self.check_filename(v))
        self.mobilebackup2_send_status_response(0)

    def mb2_handle_remove_files(self, msg):
        self.logger.info("Removing %d files", len(msg[1]))
        for filename in msg[1]:
            self.logger.info("Removing %s", self.check_filename(filename))
            try:
                filename = self.check_filename(filename)
                if os.path.isfile(filename):
                    os.unlink(filename)
            except Exception as e:
                self.logger.error(e)
        self.mobilebackup2_send_status_response(0)

    def work_loop(self):
        while True:
            msg = self.mobilebackup2_receive_message()
            if not msg:
                break

            assert (msg[0] in [
                "DLMessageDownloadFiles", "DLContentsOfDirectory",
                "DLMessageCreateDirectory", "DLMessageUploadFiles",
                "DLMessageMoveFiles", "DLMessageMoveItems",
                "DLMessageRemoveFiles", "DLMessageRemoveItems",
                "DLMessageCopyItem", "DLMessageProcessMessage",
                "DLMessageGetFreeDiskSpace", "DLMessageDisconnect"
            ])

            if msg[0] == "DLMessageDownloadFiles":
                self.mb2_handle_send_files(msg)
            elif msg[0] == "DLContentsOfDirectory":
                self.mb2_handle_list_directory(msg)
            elif msg[0] == "DLMessageCreateDirectory":
                self.mb2_handle_make_directory(msg)
            elif msg[0] == "DLMessageUploadFiles":
                self.mb2_handle_receive_files(msg)
            elif msg[0] in ["DLMessageMoveFiles", "DLMessageMoveItems"]:
                self.mb2_handle_move_files(msg)
            elif msg[0] in ["DLMessageRemoveFiles", "DLMessageRemoveItems"]:
                self.mb2_handle_remove_files(msg)
            elif msg[0] == "DLMessageCopyItem":
                self.mb2_handle_copy_item(msg)
            elif msg[0] == "DLMessageProcessMessage":
                errcode = msg[1].get("ErrorCode")
                if errcode == 0:
                    m = msg[1].get("MessageName")
                    if m != "Response":
                        self.logger.warn(m)
                    break
                if errcode == 1:
                    self.logger.info("Please unlock your device and retry...")
                if errcode == 211:
                    self.logger.info(
                        "Please go to Settings->iClould->Find My iPhone and disable it"
                    )
                if errcode == 105:
                    self.logger.info(
                        "Not enough free space on device for restore")
                if errcode == 17:
                    self.logger.info(
                        "please press 'trust this computer' in your device")
                if errcode == 102:
                    self.logger.info("Please reboot your device and try again")
                self.logger.error("Unknown error: %d : %s", errcode,
                                  msg[1].get("ErrorDescription", ""))
                raise Exception(errcode)
            elif msg[0] == "DLMessageGetFreeDiskSpace":
                self.mb2_handle_free_disk_space(msg)
            elif msg[0] == "DLMessageDisconnect":
                break

    def create_status_plist(self, fullBackup=True):
        # Creating Status file for backup
        statusDict = {
            'UUID': str(uuid4()).upper(),
            'BackupState': 'new',
            'IsFullBackup': fullBackup,
            'Version': '2.4',
            'Date': datetime.datetime.fromtimestamp(mktime(gmtime())),
            'SnapshotState': 'finished'
        }
        writePlist(statusDict, self.check_filename("Status.plist"))


#    def set_sync_lock(self):
#        #do_post_notification(device, NP_SYNC_WILL_START);
#        lockfile = self.afc.file_open("/com.apple.itunes.lock_sync")
#        if lockfile:
#            #do_post_notification(device, NP_SYNC_LOCK_REQUEST);
#			while True:
#				res = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
#				if res == AFC_E_SUCCESS:
#					#do_post_notification(device, NP_SYNC_DID_START);
#					break
#				elif res == AFC_E_OP_WOULD_BLOCK):
#					sleep(0.5)
#					continue
#				else:
#					print "ERROR: could not lock file! error: %d\n" % res
#					self.afc.file_close(lockfile);
#					lockfile = 0;
#					cmd = CMD_LEAVE;
#
#		        if i == LOCK_ATTEMPTS:
#			        print "ERROR: timeout while locking for sync"
#			        self.afc.file_close(afc, lockfile)
#			        lockfile = 0
#			        cmd = CMD_LEAVE
#                    break
#
#    def remove_sync_lock(self):
#        pass

    def create_info_plist(self):
        # Get device information
        device_info = self.lockdown.allValues

        # Get a list of installed user applications
        instpxy = installation_proxy(self.lockdown)
        apps = instpxy.browse(
            {"ApplicationType": "User"},
            ["CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata"])
        # Create new info.plits
        info = {
            "BuildVersion": device_info.get("BuildVersion") or "",
            "DeviceName": device_info.get("DeviceName") or "",
            "Display Name": device_info.get("DeviceName") or "",
            "GUID": "---",
            "Product Name": device_info.get("ProductName" or ""),
            "ProductType": device_info.get("ProductType") or "",
            "ProductVersion": device_info.get("ProductVersion") or "",
            "Serial Number": device_info.get("SerialNumber") or "",
            "Unique Identifier": self.udid.upper(),
            "Target Identifier": self.udid,
            "Target Type": "Device",
            "iTunes Version": "10.0.1",
            "MEID": device_info.get("MobileEquipmentIdentifier") or "",
            "Phone Number": device_info.get("PhoneNumber") or "",
        }

        info["ICCID"] = device_info.get("IntegratedCircuitCardIdentity") or ""
        info["IMEI"] = device_info.get(
            "InternationalMobileEquipmentIdentity") or ""
        info["Last Backup Date"] = datetime.datetime.now()

        # Starting SpringBoard service to retrieve icons position
        self.sbs = SBServiceClient(self.lockdown)
        installed_apps = []
        apps_data = {}
        for app_entry in apps:
            tmp = {}
            bid = app_entry.get("CFBundleIdentifier")
            if bid:
                installed_apps.append(bid)
                pngdata = self.sbs.get_icon_pngdata(bid)
                if pngdata:
                    tmp["PlaceholderIcon"] = pngdata
                tmp["iTunesMetadata"] = app_entry.get("iTunesMetadata")
                tmp["ApplicationSINF"] = app_entry.get("ApplicationSINF")
                apps_data[bid] = tmp

        info["Applications"] = apps_data
        info["Installed Applications"] = installed_apps
        # Handling itunes files
        iTunesFiles = [
            "ApertureAlbumPrefs", "IC-Info.sidb", "IC-Info.sidv",
            "PhotosFolderAlbums", "PhotosFolderName", "PhotosFolderPrefs",
            "VoiceMemos.plist", "iPhotoAlbumPrefs", "iTunesApplicationIDs",
            "iTunesPrefs", "iTunesPrefs.plist"
        ]
        iTunesFilesDict = {}
        for i in iTunesFiles:
            data = self.afc.get_file_contents("/iTunes_Control/iTunes/" + i)
            if data:
                iTunesFilesDict[i] = plistlib.Data(data)

        info["iTunesFiles"] = iTunesFilesDict
        iBooksData2 = self.afc.get_file_contents("/Books/iBooksData2.plist")
        if iBooksData2:
            info["iBooks Data 2"] = plistlib.Data(iBooksData2)

        info["iTunes Settings"] = self.lockdown.getValue("com.apple.iTunes")
        self.logger.info("Creating %s", os.path.join(self.udid, "Info.plist"))
        self.write_file(os.path.join(self.udid, "Info.plist"),
                        plistlib.writePlistToString(info))

    def backup(self, fullBackup=True):
        # TODO set_sync_lock
        self.logger.info("Starting %s backup...",
                         ("Encrypted " if self.willEncrypt else ""))
        if not os.path.isdir(os.path.join(self.backupPath, self.udid)):
            os.makedirs(os.path.join(self.backupPath, self.udid))
        self.logger.info("Backup mode: %s",
                         "Full backup" if fullBackup else "Incremental backup")
        self.create_info_plist()
        options = {"ForceFullBackup": fullBackup}
        self.mobilebackup2_send_request("Backup", self.udid, options)
        self.work_loop()

    def restore(
            self,
            options={
                "RestoreSystemFiles": True,
                "RestoreShouldReboot": True,
                "RestorePreserveCameraRoll": True,
                "RemoveItemsNotRestored": False,
                "RestoreDontCopyBackup": True,
                "RestorePreserveSettings": True
            },
            password=None):

        self.logger.info("Starting restoration...")
        m = os.path.join(self.backupPath, self.udid, "Manifest.plist")
        try:
            manifest = readPlist(m)
        except IOError:
            self.logger.error("not a valid backup folder")
            return -1
        if manifest.get("IsEncrypted"):
            print("Backup is encrypted, enter password : "******"Password"] = self.password
        self.mobilebackup2_send_request("Restore", self.udid, self.udid,
                                        options)
        self.work_loop()

    def info(self, options={}):
        source_udid = self.udid
        self.mobilebackup2_send_request("Info", self.udid, source_udid,
                                        options)
        self.work_loop()

    def list(self, options={}):
        source_udid = self.udid
        self.mobilebackup2_send_request("List", self.udid, source_udid,
                                        options)
        self.work_loop()

    def changepw(self, oldpw, newpw):
        options = {"OldPassword": oldpw, "NewPassword": newpw}

        self.mobilebackup2_send_request("ChangePassword", self.udid, "",
                                        options)
        self.work_loop()

    def unback(self, options={"Password": None}):
        source_udid = self.udid
        self.mobilebackup2_send_request("Unback", self.udid, source_udid,
                                        options)
        self.work_loop()

    def enableCloudBackup(self, options={"CloudBackupState": False}):
        self.mobilebackup2_send_request("EnableCloudBackup", self.udid,
                                        options)
        self.work_loop()

    def mobilebackup2_notify_cb(notification, data=None):
        if notification == NP_SYNC_CANCEL_REQUEST:
            self.logger.info(
                "User has cancelled the backup process on the device.")
        elif notification == NP_BACKUP_DOMAIN_CHANGED:
            backup_domain_changed = 1
        else:
            self.logger.info("Unhandled notification '%s'", notification)