예제 #1
0
    def start(self):
        self.udid = self.lockdown.allValues["UniqueDeviceID"]
        self.willEncrypt = self.lockdown.getValue("com.apple.mobile.backup",
                                                  "WillEncrypt")
        self.escrowBag = self.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)
예제 #2
0
    def startAcquisition(self):

        sn = get_serial(self.logging)
        lockdown = LockdownClient(sn)

        device_found_mes = f"""\n
Device Found!
Device Name: {lockdown.allValues['DeviceName']}
Device Model: {lockdown.allValues['ProductType']}
iOS Version: {lockdown.allValues['ProductVersion']}
Device Build: {lockdown.allValues['BuildVersion']}
WiFi Address: {lockdown.allValues['WiFiAddress']}
Hardware Model: {lockdown.allValues['HardwareModel']}
                """

        self.logging.info(device_found_mes)

        afc_service = lockdown.startService("com.apple.afc")
        afc = AFCClient(lockdown=lockdown)
        afc.pull_directory('/', self.output)
        if self.md5 or self.sha1:
            with open(self.csv_path, "w", newline='') as csvfile:
                csvfile_obj = csv.writer(csvfile)
                if self.md5 and self.sha1:
                    csvfile_obj.writerow(
                        ["File Name", "Full Path", "MD5", "SHA-1"])
                elif self.md5:
                    csvfile_obj.writerow(["File Name", "Full Path", "MD5"])
                elif self.sha1:
                    csvfile_obj.writerow(["File Name", "Full Path", "SHA-1"])
                hasher(self.output, self.md5, self.sha1, csvfile_obj,
                       self.logging)
예제 #3
0
    def startAcquisition(self):

        sn = get_serial(self.logging)
        lockdown = LockdownClient(sn)

        device_found_mes = f"""
                Device Found!
                Device Name: {lockdown.allValues['DeviceName']}
                Device Model: {lockdown.allValues['ProductType']}
                iOS Version: {lockdown.allValues['ProductVersion']}
                Device Build: {lockdown.allValues['BuildVersion']}
                WiFi Address: {lockdown.allValues['WiFiAddress']}
                Hardware Model: {lockdown.allValues['HardwareModel']}
                """

        print(device_found_mes)


        afc_service = lockdown.startService("com.apple.afc")
        afc = AFCClient(lockdown=lockdown)
        afc.pull_directory('/', self.output)
        if self.md5 or self.sha1:
            with open(self.csv_path, "ab") as csvfile:
                csvfile_obj = csv.writer(csvfile)
                hasher(self.output, self.md5, self.sha1, csvfile_obj, self.logging)
예제 #4
0
def install_ipa(uuid, ipa_path):
    """
    docstring for install_ipa
    """
    from pymobiledevice2.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)
예제 #5
0
    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)
예제 #6
0
    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))
예제 #7
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
예제 #8
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)
예제 #9
0
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.allValues['ProductVersion']
        #ProductVersion = self.lockdown.getValue("", "ProductVersion")
        if ProductVersion and int(
                ProductVersion[:ProductVersion.find('.')]) < 5:
            raise DeviceVersionNotSupported
        self.start()

    def start(self):
        self.udid = self.lockdown.allValues["UniqueDeviceID"]
        self.willEncrypt = self.lockdown.getValue("com.apple.mobile.backup",
                                                  "WillEncrypt")
        self.escrowBag = self.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 = disk_usage(self.backupPath).free
        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 == "" or device_filename == b'':
                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 convert_prodtype_to_model(self, ProductType):
        if ProductType == "iPhone125":
            return "iPhone 11 Pro Max"
        if ProductType == "iPhone123":
            return "iPhone 11 Pro"
        if ProductType == "iPhone121":
            return "iPhone 11"
        if ProductType == "iPhone114" or ProductType == "iPhone116":
            return "iPhone XS Max"
        if ProductType == "iPhone112":
            return "iPhone XS"
        if ProductType == "iPhone118":
            return "iPhone XR"
        if ProductType == "iPhone103" or ProductType == "iPhone106":
            return "iPhone X"
        if ProductType == "iPhone102" or ProductType == "iPhone105":
            return "iPhone 8 Plus"
        if ProductType == "iPhone101" or ProductType == "iPhone104":
            return "iPhone 8"
        if ProductType == "iPhone92" or ProductType == "iPhone94":
            return "iPhone 7 Plus"
        if ProductType == "iPhone91" or ProductType == "iPhone93":
            return "iPhone 7"
        if ProductType == "iPhone84":
            return "iPhone SE"
        if ProductType == "iPhone82":
            return "iPhone 6s Plus"

    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.plist
        info = {
            "BuildVersion":
            device_info.get("BuildVersion") or "",
            "DeviceName":
            device_info.get("DeviceName") or "",
            "Display Name":
            device_info.get("DeviceName") or "",
            "GUID":
            "---",
            "Product Name":
            self.convert_prodtype_to_model(device_info.get("ProductType"
                                                           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"))

        for applications in info["Applications"]:
            if info["Applications"][applications]["PlaceholderIcon"] == None:
                info["Applications"][applications]["PlaceholderIcon"] = ""
            if info["Applications"][applications]["iTunesMetadata"] == None:
                info["Applications"][applications]["iTunesMetadata"] = b""
            if info["Applications"][applications]["ApplicationSINF"] == None:
                info["Applications"][applications]["ApplicationSINF"] = b""

        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)