void plist_dict_merge(plist_t *target, plist_t source) { if (!target || !*target || (plist_get_node_type(*target) != PLIST_DICT) || !source || (plist_get_node_type(source) != PLIST_DICT)) return; char* key = NULL; plist_dict_iter it = NULL; plist_t subnode = NULL; plist_dict_new_iter(source, &it); if (!it) return; do { plist_dict_next_item(source, it, &key, &subnode); if (!key) break; if (plist_dict_get_item(*target, key) != NULL) plist_dict_remove_item(*target, key); plist_dict_insert_item(*target, key, plist_copy(subnode)); free(key); key = NULL; } while (1); free(it); }
Dictionary& Dictionary::operator=(PList::Dictionary& d) { for (Dictionary::iterator it = _map.begin(); it != _map.end(); it++) { plist_free(it->second->GetPlist()); delete it->second; } _map.clear(); _node = plist_copy(d.GetPlist()); plist_dict_iter it = NULL; char* key = NULL; plist_t subnode = NULL; plist_dict_new_iter(_node, &it); plist_dict_next_item(_node, it, &key, &subnode); while (subnode) { _map[std::string(key)] = Node::FromPlist(subnode, this); subnode = NULL; free(key); key = NULL; plist_dict_next_item(_node, it, NULL, &subnode); } free(it); return *this; }
int tss_response_get_blob_by_path(plist_t tss, const char* path, unsigned char** blob) { uint32_t i = 0; uint32_t tss_size = 0; uint64_t blob_size = 0; char* entry_key = NULL; char* blob_data = NULL; char* entry_path = NULL; plist_t tss_entry = NULL; plist_t blob_node = NULL; plist_t path_node = NULL; plist_dict_iter iter = NULL; *blob = NULL; plist_dict_new_iter(tss, &iter); tss_size = plist_dict_get_size(tss); for (i = 0; i < tss_size; i++) { plist_dict_next_item(tss, iter, &entry_key, &tss_entry); if (entry_key == NULL) break; if (!tss_entry || plist_get_node_type(tss_entry) != PLIST_DICT) { continue; } path_node = plist_dict_get_item(tss_entry, "Path"); if (!path_node || plist_get_node_type(path_node) != PLIST_STRING) { error("ERROR: Unable to find TSS path node in entry %s\n", entry_key); free(iter); return -1; } plist_get_string_val(path_node, &entry_path); if (strcmp(path, entry_path) == 0) { blob_node = plist_dict_get_item(tss_entry, "Blob"); if (!blob_node || plist_get_node_type(blob_node) != PLIST_DATA) { error("ERROR: Unable to find TSS blob node in entry %s\n", entry_key); free(iter); return -1; } plist_get_data_val(blob_node, &blob_data, &blob_size); break; } free(entry_key); } free(iter); if (blob_data == NULL || blob_size <= 0) { return -1; } *blob = (unsigned char*)blob_data; return 0; }
void ibrowserAPI::installCallback(const char *operation, plist_t status, void *user_data) { char *xml_doc=NULL; uint32_t xml_length; Callback *cb = (Callback *)user_data; FB::JSObjectPtr pcb = cb->get("pcb"); FB::JSObjectPtr scb = cb->get("scb"); FB::JSObjectPtr ecb = cb->get("ecb"); if(pcb) { plist_to_xml(status, &xml_doc, &xml_length); pcb->InvokeAsync("", FB::variant_list_of(xml_doc)); free(xml_doc); } plist_dict_iter it = NULL; char* key = NULL; char* s = NULL; plist_t subnode = NULL; plist_dict_new_iter(status, &it); plist_dict_next_item(status, it, &key, &subnode); while (subnode) { if(strcmp(key, "Error") == 0) { plist_get_string_val(subnode, &s); if(ecb) ecb->InvokeAsync("", FB::variant_list_of(s)); }else if(strcmp(key, "Status") == 0){ plist_get_string_val(subnode, &s); if(strcmp(s, "Complete") == 0){ if(scb) scb->InvokeAsync("", FB::variant_list_of(NULL)); } } plist_dict_next_item(status, it, &key, &subnode); } if(key) free(key); if(s) free(s); free(it); return; }
Dictionary::Dictionary(plist_t node, Node* parent) : Structure(parent) { _node = node; plist_dict_iter it = NULL; char* key = NULL; plist_t subnode = NULL; plist_dict_new_iter(_node, &it); plist_dict_next_item(_node, it, &key, &subnode); while (subnode) { _map[std::string(key)] = Node::FromPlist(subnode, this); subnode = NULL; free(key); key = NULL; plist_dict_next_item(_node, it, &key, &subnode); } free(it); }
static void plist_dict_to_string(plist_t node) { plist_dict_iter it = NULL; char* key = NULL; plist_t subnode = NULL; plist_dict_new_iter(node, &it); plist_dict_next_item(node, it, &key, &subnode); while (subnode) { printf("%*s", indent_level, ""); printf("%s", key); if (plist_get_node_type(subnode) == PLIST_ARRAY) printf("[%d]: ", plist_array_get_size(subnode)); else printf(": "); free(key); key = NULL; plist_node_to_string(subnode); plist_dict_next_item(node, it, &key, &subnode); } free(it); }
plist_t tss_create_request(plist_t build_identity, uint64_t ecid, unsigned char* nonce, int nonce_size) { uint64_t unique_build_size = 0; char* unique_build_data = NULL; plist_t unique_build_node = plist_dict_get_item(build_identity, "UniqueBuildID"); if (!unique_build_node || plist_get_node_type(unique_build_node) != PLIST_DATA) { error("ERROR: Unable to find UniqueBuildID node\n"); return NULL; } plist_get_data_val(unique_build_node, &unique_build_data, &unique_build_size); int chip_id = 0; char* chip_id_string = NULL; plist_t chip_id_node = plist_dict_get_item(build_identity, "ApChipID"); if (!chip_id_node || plist_get_node_type(chip_id_node) != PLIST_STRING) { error("ERROR: Unable to find ApChipID node\n"); return NULL; } plist_get_string_val(chip_id_node, &chip_id_string); sscanf(chip_id_string, "%x", &chip_id); int board_id = 0; char* board_id_string = NULL; plist_t board_id_node = plist_dict_get_item(build_identity, "ApBoardID"); if (!board_id_node || plist_get_node_type(board_id_node) != PLIST_STRING) { error("ERROR: Unable to find ApBoardID node\n"); return NULL; } plist_get_string_val(board_id_node, &board_id_string); sscanf(board_id_string, "%x", &board_id); int security_domain = 0; char* security_domain_string = NULL; plist_t security_domain_node = plist_dict_get_item(build_identity, "ApSecurityDomain"); if (!security_domain_node || plist_get_node_type(security_domain_node) != PLIST_STRING) { error("ERROR: Unable to find ApSecurityDomain node\n"); return NULL; } plist_get_string_val(security_domain_node, &security_domain_string); sscanf(security_domain_string, "%x", &security_domain); char ecid_string[ECID_STRSIZE]; memset(ecid_string, '\0', ECID_STRSIZE); if (ecid == 0) { error("ERROR: Unable to get ECID\n"); return NULL; } snprintf(ecid_string, ECID_STRSIZE, FMT_qu, (long long unsigned int)ecid); // Add build information to TSS request plist_t tss_request = plist_new_dict(); plist_dict_insert_item(tss_request, "@APTicket", plist_new_bool(1)); plist_dict_insert_item(tss_request, "@BBTicket", plist_new_bool(1)); plist_dict_insert_item(tss_request, "@HostIpAddress", plist_new_string("192.168.0.1")); plist_dict_insert_item(tss_request, "@HostPlatformInfo", plist_new_string("mac")); plist_dict_insert_item(tss_request, "@Locality", plist_new_string("en_US")); char* guid = generate_guid(); if (guid) { plist_dict_insert_item(tss_request, "@UUID", plist_new_string(guid)); free(guid); } plist_dict_insert_item(tss_request, "@VersionInfo", plist_new_string("libauthinstall-107.3")); plist_dict_insert_item(tss_request, "ApBoardID", plist_new_uint(board_id)); plist_dict_insert_item(tss_request, "ApChipID", plist_new_uint(chip_id)); plist_dict_insert_item(tss_request, "ApECID", plist_new_string(ecid_string)); if (nonce && (nonce_size > 0)) { plist_dict_insert_item(tss_request, "ApNonce", plist_new_data(nonce, nonce_size)); } plist_dict_insert_item(tss_request, "ApProductionMode", plist_new_bool(1)); plist_dict_insert_item(tss_request, "ApSecurityDomain", plist_new_uint(security_domain)); plist_dict_insert_item(tss_request, "UniqueBuildID", plist_new_data(unique_build_data, unique_build_size)); free(unique_build_data); // Add all firmware files to TSS request plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find restore manifest\n"); plist_free(tss_request); return NULL; } char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { error("ERROR: Unable to fetch BuildManifest entry\n"); free(tss_request); return NULL; } if (strcmp(key, "BasebandFirmware") == 0) { free(key); continue; } plist_t tss_entry = plist_copy(manifest_entry); plist_dict_insert_item(tss_request, key, tss_entry); free(key); } if (idevicerestore_debug) { debug_plist(tss_request); } return tss_request; }
int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the SE,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@SE,Ticket", plist_new_bool(1)); /* add SE,ChipID */ node = plist_dict_get_item(parameters, "SE,ChipID"); if (!node) { error("ERROR: %s: Unable to find required SE,ChipID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,ChipID", plist_copy(node)); node = NULL; /* add SE,ID */ node = plist_dict_get_item(parameters, "SE,ID"); if (!node) { error("ERROR: %s: Unable to find required SE,ID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,ID", plist_copy(node)); node = NULL; /* add SE,Nonce */ node = plist_dict_get_item(parameters, "SE,Nonce"); if (!node) { error("ERROR: %s: Unable to find required SE,Nonce in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,Nonce", plist_copy(node)); node = NULL; /* add SE,RootKeyIdentifier */ node = plist_dict_get_item(parameters, "SE,RootKeyIdentifier"); if (!node) { error("ERROR: %s: Unable to find required SE,RootKeyIdentifier in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,RootKeyIdentifier", plist_copy(node)); node = NULL; /* 'IsDev' determines whether we have Production or Development */ const char *removing_cmac_key = "DevelopmentCMAC"; node = plist_dict_get_item(parameters, "SE,IsDev"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t is_dev = 0; plist_get_bool_val(node, &is_dev); removing_cmac_key = (is_dev) ? "ProductionCMAC" : "DevelopmentCMAC"; } /* add SE,* components from build manifest to request */ char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { key = NULL; plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { free(key); error("ERROR: Unable to fetch BuildManifest entry\n"); return -1; } if (strncmp(key, "SE,", 3)) { free(key); continue; } /* copy this entry */ plist_t tss_entry = plist_copy(manifest_entry); /* remove Info node */ plist_dict_remove_item(tss_entry, "Info"); /* remove 'DevelopmentCMAC' (or 'ProductionCMAC') node */ if (plist_dict_get_item(tss_entry, removing_cmac_key)) { plist_dict_remove_item(tss_entry, removing_cmac_key); } /* add entry to request */ plist_dict_set_item(request, key, tss_entry); free(key); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides) { /* loop over components from build manifest */ plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find restore manifest\n"); return -1; } /* add components to request */ char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { error("ERROR: Unable to fetch BuildManifest entry\n"); return -1; } /* do not populate BaseBandFirmware, only in basebaseband request */ if ((strcmp(key, "BasebandFirmware") == 0)) { free(key); continue; } /* FIXME: only used with diagnostics firmware */ if (strcmp(key, "Diags") == 0) { free(key); continue; } /* copy this entry */ plist_t tss_entry = plist_copy(manifest_entry); /* remove obsolete Info node */ plist_dict_remove_item(tss_entry, "Info"); /* handle RestoreRequestRules */ plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); if (rules) { debug("DEBUG: Applying restore request rules for entry %s\n", key); tss_entry_apply_restore_request_rules(tss_entry, parameters, rules); } /* Make sure we have a Digest key for Trusted items even if empty */ plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t trusted; plist_get_bool_val(node, &trusted); if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { debug("DEBUG: No Digest data, using empty value for entry %s\n", key); plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); } } /* finally add entry to request */ plist_dict_set_item(request, key, tss_entry); free(key); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
static void tss_entry_apply_restore_request_rules(plist_t tss_entry, plist_t parameters, plist_t rules) { if (!tss_entry || !rules) { return; } if (plist_get_node_type(tss_entry) != PLIST_DICT) { return; } if (plist_get_node_type(rules) != PLIST_ARRAY) { return; } uint32_t i; for (i = 0; i < plist_array_get_size(rules); i++) { plist_t rule = plist_array_get_item(rules, i); plist_t conditions = plist_dict_get_item(rule, "Conditions"); plist_dict_iter iter = NULL; plist_dict_new_iter(conditions, &iter); char* key = NULL; plist_t value = NULL; plist_t value2 = NULL; int conditions_fulfilled = 1; while (conditions_fulfilled) { plist_dict_next_item(conditions, iter, &key, &value); if (key == NULL) break; if (!strcmp(key, "ApRawProductionMode")) { value2 = plist_dict_get_item(parameters, "ApProductionMode"); } else if (!strcmp(key, "ApCurrentProductionMode")) { value2 = plist_dict_get_item(parameters, "ApProductionMode"); } else if (!strcmp(key, "ApRawSecurityMode")) { value2 = plist_dict_get_item(parameters, "ApSecurityMode"); } else if (!strcmp(key, "ApRequiresImage4")) { value2 = plist_dict_get_item(parameters, "ApSupportsImg4"); } else if (!strcmp(key, "ApDemotionPolicyOverride")) { value2 = plist_dict_get_item(parameters, "DemotionPolicy"); } else if (!strcmp(key, "ApInRomDFU")) { value2 = plist_dict_get_item(parameters, "ApInRomDFU"); } else { error("WARNING: Unhandled condition '%s' while parsing RestoreRequestRules\n", key); value2 = NULL; } if (value2) { conditions_fulfilled = plist_compare_node_value(value, value2); } else { conditions_fulfilled = 0; } free(key); } free(iter); iter = NULL; if (!conditions_fulfilled) { continue; } plist_t actions = plist_dict_get_item(rule, "Actions"); plist_dict_new_iter(actions, &iter); while (1) { plist_dict_next_item(actions, iter, &key, &value); if (key == NULL) break; uint8_t bv = 0; plist_get_bool_val(value, &bv); if (bv) { value2 = plist_dict_get_item(tss_entry, key); if (value2) { plist_dict_remove_item(tss_entry, key); } debug("DEBUG: Adding action %s to TSS entry\n", key); plist_dict_set_item(tss_entry, key, plist_new_bool(1)); } free(key); } } }
static int process_tracks(plist_t tracks) { plist_t trk; plist_dict_iter iter; char *str; uint64_t trk_id; uint8_t disabled; int ntracks; int nloaded; int mfi_id; int ret; if (plist_dict_get_size(tracks) == 0) { DPRINTF(E_WARN, L_SCAN, "No tracks in iTunes library\n"); return 0; } db_transaction_begin(); ntracks = 0; nloaded = 0; iter = NULL; plist_dict_new_iter(tracks, &iter); plist_dict_next_item(tracks, iter, NULL, &trk); while (trk) { if (plist_get_node_type(trk) != PLIST_DICT) { plist_dict_next_item(tracks, iter, NULL, &trk); continue; } ret = get_dictval_int_from_key(trk, "Track ID", &trk_id); if (ret < 0) { DPRINTF(E_WARN, L_SCAN, "Track ID not found!\n"); plist_dict_next_item(tracks, iter, NULL, &trk); continue; } ret = get_dictval_bool_from_key(trk, "Disabled", &disabled); if (ret < 0) { DPRINTF(E_WARN, L_SCAN, "Malformed track record (id %" PRIu64 ")\n", trk_id); plist_dict_next_item(tracks, iter, NULL, &trk); continue; } if (disabled) { DPRINTF(E_INFO, L_SCAN, "Track %" PRIu64 " disabled; skipping\n", trk_id); plist_dict_next_item(tracks, iter, NULL, &trk); continue; } ret = get_dictval_string_from_key(trk, "Track Type", &str); if (ret < 0) { DPRINTF(E_WARN, L_SCAN, "Track %" PRIu64 " has no track type\n", trk_id); plist_dict_next_item(tracks, iter, NULL, &trk); continue; } if (strcmp(str, "URL") == 0) mfi_id = process_track_stream(trk); else if (strcmp(str, "File") == 0) mfi_id = process_track_file(trk); else { DPRINTF(E_LOG, L_SCAN, "Unknown track type: '%s'\n", str); free(str); plist_dict_next_item(tracks, iter, NULL, &trk); continue; } free(str); ntracks++; if (ntracks % 200 == 0) { DPRINTF(E_LOG, L_SCAN, "Processed %d tracks...\n", ntracks); db_transaction_end(); db_transaction_begin(); } if (mfi_id <= 0) { plist_dict_next_item(tracks, iter, NULL, &trk); continue; } ret = id_map_add(trk_id, mfi_id); if (ret < 0) DPRINTF(E_LOG, L_SCAN, "Out of memory for itml -> db mapping\n"); nloaded++; plist_dict_next_item(tracks, iter, NULL, &trk); } free(iter); db_transaction_end(); return nloaded; }
int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the Savage,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Yonkers,Ticket", plist_new_bool(1)); /* add SEP */ node = plist_access_path(manifest_node, 2, "SEP", "Digest"); if (!node) { error("ERROR: Unable to get SEP digest from manifest\n"); return -1; } plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "Digest", plist_copy(node)); plist_dict_set_item(request, "SEP", dict); { static const char *keys[] = {"Yonkers,AllowOfflineBoot", "Yonkers,BoardID", "Yonkers,ChipID", "Yonkers,ECID", "Yonkers,Nonce", "Yonkers,PatchEpoch", "Yonkers,ProductionMode", "Yonkers,ReadECKey", "Yonkers,ReadFWKey", }; int i; for (i = 0; i < (int)(sizeof(keys) / sizeof(keys[0])); ++i) { node = plist_dict_get_item(parameters, keys[i]); if (!node) { error("ERROR: %s: Unable to find required %s in parameters\n", __func__, keys[i]); } plist_dict_set_item(request, keys[i], plist_copy(node)); node = NULL; } } char *comp_name = NULL; plist_t comp_node = NULL; uint8_t isprod = 1; uint64_t fabrevision = (uint64_t)-1; node = plist_dict_get_item(parameters, "Yonkers,ProductionMode"); if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { plist_get_bool_val(node, &isprod); } node = plist_dict_get_item(parameters, "Yonkers,FabRevision"); if (node && (plist_get_node_type(node) == PLIST_UINT)) { plist_get_uint_val(node, &fabrevision); } plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (iter) { node = NULL; comp_name = NULL; plist_dict_next_item(manifest_node, iter, &comp_name, &node); if (comp_name == NULL) { node = NULL; break; } if (strncmp(comp_name, "Yonkers,", 8) == 0) { int target_node = 1; plist_t sub_node; if ((sub_node = plist_dict_get_item(node, "EPRO")) != NULL && plist_get_node_type(sub_node) == PLIST_BOOLEAN) { uint8_t b = 0; plist_get_bool_val(sub_node, &b); target_node &= ((isprod) ? b : !b); } if ((sub_node = plist_dict_get_item(node, "FabRevision")) != NULL && plist_get_node_type(sub_node) == PLIST_UINT) { uint64_t v = 0; plist_get_uint_val(sub_node, &v); target_node &= (v == fabrevision); } if (target_node) { comp_node = node; break; } } free(comp_name); } free(iter); if (comp_name == NULL) { error("ERROR: No Yonkers node for %s/%lu\n", (isprod) ? "Production" : "Development", (unsigned long)fabrevision); return -1; } /* add Yonkers,SysTopPatch* */ if (comp_node != NULL) { plist_t comp_dict = plist_copy(comp_node); plist_dict_remove_item(comp_dict, "Info"); plist_dict_set_item(request, comp_name, comp_dict); } if (component_name) { *component_name = comp_name; } else { free(comp_name); } /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, unsigned char* sha1buf) { *fwurl = NULL; if (sha1buf != NULL) { memset(sha1buf, '\0', 20); } plist_t n1 = plist_dict_get_item(version_data, "MobileDeviceSoftwareVersionsByVersion"); if (!n1) { error("%s: ERROR: Can't find MobileDeviceSoftwareVersionsByVersion dict in version data\n", __func__); return -1; } plist_dict_iter iter = NULL; plist_dict_new_iter(n1, &iter); if (!iter) { error("%s: ERROR: Can't get dict iter\n", __func__); return -1; } char* key = NULL; long long unsigned int major = 0; plist_t val = NULL; do { plist_dict_next_item(n1, iter, &key, &val); if (key) { plist_t pr = plist_access_path(n1, 3, key, "MobileDeviceSoftwareVersions", product); if (pr) { long long unsigned int v = strtoull(key, NULL, 10); if (v > major) major = v; } free(key); } } while (val); free(iter); if (major == 0) { error("%s: ERROR: Can't find major version?!\n", __func__); return -1; } char majstr[32]; // should be enough for a uint64_t value sprintf(majstr, FMT_qu, (long long unsigned int)major); n1 = plist_access_path(version_data, 7, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, "Unknown", "Universal", "Restore"); if (!n1) { error("%s: ERROR: Can't get Unknown/Universal/Restore node?!\n", __func__); return -1; } plist_t n2 = plist_dict_get_item(n1, "BuildVersion"); if (!n2 || (plist_get_node_type(n2) != PLIST_STRING)) { error("%s: ERROR: Can't get build version node?!\n", __func__); return -1; } char* strval = NULL; plist_get_string_val(n2, &strval); n1 = plist_access_path(version_data, 5, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, strval); if (!n1) { error("%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s node?!\n", __func__, strval); free(strval); return -1; } free(strval); strval = NULL; n2 = plist_dict_get_item(n1, "SameAs"); if (n2) { plist_get_string_val(n2, &strval); } if (strval) { n1 = plist_access_path(version_data, 5, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, strval); free(strval); strval = NULL; if (!n1 || (plist_dict_get_size(n1) == 0)) { error("%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s dict\n", __func__, product); return -1; } } n2 = plist_access_path(n1, 2, "Update", "BuildVersion"); if (n2) { strval = NULL; plist_get_string_val(n2, &strval); if (strval) { n1 = plist_access_path(version_data, 5, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, strval); free(strval); strval = NULL; } } n2 = plist_access_path(n1, 2, "Restore", "FirmwareURL"); if (!n2 || (plist_get_node_type(n2) != PLIST_STRING)) { error("%s: ERROR: Can't get FirmwareURL node\n", __func__); return -1; } plist_get_string_val(n2, fwurl); if (sha1buf != NULL) { n2 = plist_access_path(n1, 2, "Restore", "FirmwareSHA1"); if (n2 && plist_get_node_type(n2) == PLIST_STRING) { strval = NULL; plist_get_string_val(n2, &strval); if (strval) { if (strlen(strval) == 40) { int i; int v; for (i = 0; i < 40; i+=2) { v = 0; sscanf(strval+i, "%02x", &v); sha1buf[i/2] = (unsigned char)v; } } free(strval); } } } return 0; }
int main(int argc, char **argv) { idevice_t phone = NULL; lockdownd_client_t client = NULL; instproxy_client_t ipc = NULL; instproxy_error_t err; np_client_t np = NULL; afc_client_t afc = NULL; #ifdef HAVE_LIBIMOBILEDEVICE_1_1_5 lockdownd_service_descriptor_t service = NULL; #else uint16_t service = 0; #endif int res = 0; char *bundleidentifier = NULL; parse_opts(argc, argv); argc -= optind; argv += optind; if (IDEVICE_E_SUCCESS != idevice_new(&phone, udid)) { fprintf(stderr, "No iOS device found, is it plugged in?\n"); return -1; } if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "ideviceinstaller")) { fprintf(stderr, "Could not connect to lockdownd. Exiting.\n"); goto leave_cleanup; } if ((lockdownd_start_service (client, "com.apple.mobile.notification_proxy", &service) != LOCKDOWN_E_SUCCESS) || !service) { fprintf(stderr, "Could not start com.apple.mobile.notification_proxy!\n"); goto leave_cleanup; } np_error_t nperr = np_client_new(phone, service, &np); #ifdef HAVE_LIBIMOBILEDEVICE_1_1_5 if (service) { lockdownd_service_descriptor_free(service); } service = NULL; #else service = 0; #endif if (nperr != NP_E_SUCCESS) { fprintf(stderr, "Could not connect to notification_proxy!\n"); goto leave_cleanup; } #ifdef HAVE_LIBIMOBILEDEVICE_1_1 np_set_notify_callback(np, notifier, NULL); #else np_set_notify_callback(np, notifier); #endif const char *noties[3] = { NP_APP_INSTALLED, NP_APP_UNINSTALLED, NULL }; np_observe_notifications(np, noties); run_again: #ifdef HAVE_LIBIMOBILEDEVICE_1_1_5 if (service) { lockdownd_service_descriptor_free(service); } service = NULL; #else service = 0; #endif if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy", &service) != LOCKDOWN_E_SUCCESS) || !service) { fprintf(stderr, "Could not start com.apple.mobile.installation_proxy!\n"); goto leave_cleanup; } err = instproxy_client_new(phone, service, &ipc); #ifdef HAVE_LIBIMOBILEDEVICE_1_1_5 if (service) { lockdownd_service_descriptor_free(service); } service = NULL; #else service = 0; #endif if (err != INSTPROXY_E_SUCCESS) { fprintf(stderr, "Could not connect to installation_proxy!\n"); goto leave_cleanup; } setbuf(stdout, NULL); if (last_status) { free(last_status); last_status = NULL; } notification_expected = 0; if (cmd == CMD_LIST_APPS) { int xml_mode = 0; plist_t client_opts = instproxy_client_options_new(); instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL); plist_t apps = NULL; /* look for options */ if (options) { char *opts = strdup(options); char *elem = strtok(opts, ","); while (elem) { if (!strcmp(elem, "list_system")) { if (!client_opts) { client_opts = instproxy_client_options_new(); } instproxy_client_options_add(client_opts, "ApplicationType", "System", NULL); } else if (!strcmp(elem, "list_all")) { instproxy_client_options_free(client_opts); client_opts = NULL; } else if (!strcmp(elem, "list_user")) { /* do nothing, we're already set */ } else if (!strcmp(elem, "xml")) { xml_mode = 1; } elem = strtok(NULL, ","); } free(opts); } err = instproxy_browse(ipc, client_opts, &apps); instproxy_client_options_free(client_opts); if (err != INSTPROXY_E_SUCCESS) { fprintf(stderr, "ERROR: instproxy_browse returned %d\n", err); goto leave_cleanup; } if (!apps || (plist_get_node_type(apps) != PLIST_ARRAY)) { fprintf(stderr, "ERROR: instproxy_browse returnd an invalid plist!\n"); goto leave_cleanup; } if (xml_mode) { char *xml = NULL; uint32_t len = 0; plist_to_xml(apps, &xml, &len); if (xml) { puts(xml); free(xml); } plist_free(apps); goto leave_cleanup; } printf("Total: %d apps\n", plist_array_get_size(apps)); uint32_t i = 0; for (i = 0; i < plist_array_get_size(apps); i++) { plist_t app = plist_array_get_item(apps, i); plist_t p_appid = plist_dict_get_item(app, "CFBundleIdentifier"); char *s_appid = NULL; char *s_dispName = NULL; char *s_version = NULL; plist_t dispName = plist_dict_get_item(app, "CFBundleDisplayName"); plist_t version = plist_dict_get_item(app, "CFBundleVersion"); if (p_appid) { plist_get_string_val(p_appid, &s_appid); } if (!s_appid) { fprintf(stderr, "ERROR: Failed to get APPID!\n"); break; } if (dispName) { plist_get_string_val(dispName, &s_dispName); } if (version) { plist_get_string_val(version, &s_version); } if (!s_dispName) { s_dispName = strdup(s_appid); } if (s_version) { printf("%s - %s %s\n", s_appid, s_dispName, s_version); free(s_version); } else { printf("%s - %s\n", s_appid, s_dispName); } free(s_dispName); free(s_appid); } plist_free(apps); } else if (cmd == CMD_INSTALL || cmd == CMD_UPGRADE) { plist_t sinf = NULL; plist_t meta = NULL; char *pkgname = NULL; struct stat fst; uint64_t af = 0; char buf[8192]; #ifdef HAVE_LIBIMOBILEDEVICE_1_1_5 if (service) { lockdownd_service_descriptor_free(service); } service = NULL; #else service = 0; #endif if ((lockdownd_start_service(client, "com.apple.afc", &service) != LOCKDOWN_E_SUCCESS) || !service) { fprintf(stderr, "Could not start com.apple.afc!\n"); goto leave_cleanup; } lockdownd_client_free(client); client = NULL; if (afc_client_new(phone, service, &afc) != INSTPROXY_E_SUCCESS) { fprintf(stderr, "Could not connect to AFC!\n"); goto leave_cleanup; } if (stat(appid, &fst) != 0) { fprintf(stderr, "ERROR: stat: %s: %s\n", appid, strerror(errno)); goto leave_cleanup; } char **strs = NULL; if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) { if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH); } } if (strs) { int i = 0; while (strs[i]) { free(strs[i]); i++; } free(strs); } plist_t client_opts = instproxy_client_options_new(); /* open install package */ int errp = 0; struct zip *zf = NULL; if ((strlen(appid) > 5) && (strcmp(&appid[strlen(appid)-5], ".ipcc") == 0)) { zf = zip_open(appid, 0, &errp); if (!zf) { fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp); goto leave_cleanup; } char* ipcc = strdup(appid); if ((asprintf(&pkgname, "%s/%s", PKG_PATH, basename(ipcc)) > 0) && pkgname) { afc_make_directory(afc, pkgname); } printf("Uploading %s package contents... ", basename(ipcc)); /* extract the contents of the .ipcc file to PublicStaging/<name>.ipcc directory */ zip_uint64_t numzf = zip_get_num_entries(zf, 0); zip_uint64_t i = 0; for (i = 0; numzf > 0 && i < numzf; i++) { const char* zname = zip_get_name(zf, i, 0); char* dstpath = NULL; if (!zname) continue; if (zname[strlen(zname)-1] == '/') { // directory if ((asprintf(&dstpath, "%s/%s/%s", PKG_PATH, basename(ipcc), zname) > 0) && dstpath) { afc_make_directory(afc, dstpath); } free(dstpath); dstpath = NULL; } else { // file struct zip_file* zfile = zip_fopen_index(zf, i, 0); if (!zfile) continue; if ((asprintf(&dstpath, "%s/%s/%s", PKG_PATH, basename(ipcc), zname) <= 0) || !dstpath || (afc_file_open(afc, dstpath, AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS)) { fprintf(stderr, "ERROR: can't open afc://%s for writing\n", dstpath); free(dstpath); dstpath = NULL; zip_fclose(zfile); continue; } struct zip_stat zs; zip_stat_init(&zs); if (zip_stat_index(zf, i, 0, &zs) != 0) { fprintf(stderr, "ERROR: zip_stat_index %" PRIu64 " failed!\n", i); free(dstpath); dstpath = NULL; zip_fclose(zfile); continue; } free(dstpath); dstpath = NULL; zip_uint64_t zfsize = 0; while (zfsize < zs.size) { zip_int64_t amount = zip_fread(zfile, buf, sizeof(buf)); if (amount == 0) { break; } if (amount > 0) { uint32_t written, total = 0; while (total < amount) { written = 0; if (afc_file_write(afc, af, buf, amount, &written) != AFC_E_SUCCESS) { fprintf(stderr, "AFC Write error!\n"); break; } total += written; } if (total != amount) { fprintf(stderr, "Error: wrote only %d of %" PRIi64 "\n", total, amount); afc_file_close(afc, af); zip_fclose(zfile); free(dstpath); goto leave_cleanup; } } zfsize += amount; } afc_file_close(afc, af); af = 0; zip_fclose(zfile); } } free(ipcc); printf("DONE.\n"); instproxy_client_options_add(client_opts, "PackageType", "CarrierBundle", NULL); } else if (S_ISDIR(fst.st_mode)) { /* upload developer app directory */ instproxy_client_options_add(client_opts, "PackageType", "Developer", NULL); if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(appid)) < 0) { fprintf(stderr, "ERROR: Out of memory allocating pkgname!?\n"); goto leave_cleanup; } printf("Uploading %s package contents... ", basename(appid)); afc_upload_dir(afc, appid, pkgname); printf("DONE.\n"); } else { zf = zip_open(appid, 0, &errp); if (!zf) { fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp); goto leave_cleanup; } /* extract iTunesMetadata.plist from package */ char *zbuf = NULL; uint32_t len = 0; plist_t meta_dict = NULL; if (zip_get_contents(zf, ITUNES_METADATA_PLIST_FILENAME, 0, &zbuf, &len) == 0) { meta = plist_new_data(zbuf, len); if (memcmp(zbuf, "bplist00", 8) == 0) { plist_from_bin(zbuf, len, &meta_dict); } else { plist_from_xml(zbuf, len, &meta_dict); } } else { fprintf(stderr, "WARNING: could not locate %s in archive!\n", ITUNES_METADATA_PLIST_FILENAME); } if (zbuf) { free(zbuf); } /* determine .app directory in archive */ zbuf = NULL; len = 0; plist_t info = NULL; char* filename = NULL; char* app_directory_name = NULL; if (zip_get_app_directory(zf, &app_directory_name)) { fprintf(stderr, "Unable to locate app directory in archive!\n"); goto leave_cleanup; } /* construct full filename to Info.plist */ filename = (char*)malloc(strlen(app_directory_name)+10+1); strcpy(filename, app_directory_name); free(app_directory_name); app_directory_name = NULL; strcat(filename, "Info.plist"); if (zip_get_contents(zf, filename, 0, &zbuf, &len) < 0) { fprintf(stderr, "WARNING: could not locate %s in archive!\n", filename); free(filename); zip_unchange_all(zf); zip_close(zf); goto leave_cleanup; } free(filename); if (memcmp(zbuf, "bplist00", 8) == 0) { plist_from_bin(zbuf, len, &info); } else { plist_from_xml(zbuf, len, &info); } free(zbuf); if (!info) { fprintf(stderr, "Could not parse Info.plist!\n"); zip_unchange_all(zf); zip_close(zf); goto leave_cleanup; } char *bundleexecutable = NULL; plist_t bname = plist_dict_get_item(info, "CFBundleExecutable"); if (bname) { plist_get_string_val(bname, &bundleexecutable); } bname = plist_dict_get_item(info, "CFBundleIdentifier"); if (bname) { plist_get_string_val(bname, &bundleidentifier); } plist_free(info); info = NULL; if (!bundleexecutable) { fprintf(stderr, "Could not determine value for CFBundleExecutable!\n"); zip_unchange_all(zf); zip_close(zf); goto leave_cleanup; } char *sinfname = NULL; if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundleexecutable, bundleexecutable) < 0) { fprintf(stderr, "Out of memory!?\n"); goto leave_cleanup; } free(bundleexecutable); /* extract .sinf from package */ zbuf = NULL; len = 0; if (zip_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) { sinf = plist_new_data(zbuf, len); } else { fprintf(stderr, "WARNING: could not locate %s in archive!\n", sinfname); } free(sinfname); if (zbuf) { free(zbuf); } /* copy archive to device */ pkgname = NULL; if (asprintf(&pkgname, "%s/%s", PKG_PATH, bundleidentifier) < 0) { fprintf(stderr, "Out of memory!?\n"); goto leave_cleanup; } printf("Copying '%s' to device... ", appid); if (afc_upload_file(afc, appid, pkgname) < 0) { free(pkgname); goto leave_cleanup; } printf("DONE.\n"); if (bundleidentifier) { instproxy_client_options_add(client_opts, "CFBundleIdentifier", bundleidentifier, NULL); } if (sinf) { instproxy_client_options_add(client_opts, "ApplicationSINF", sinf, NULL); } if (meta) { instproxy_client_options_add(client_opts, "iTunesMetadata", meta, NULL); } } if (zf) { zip_unchange_all(zf); zip_close(zf); } /* perform installation or upgrade */ if (cmd == CMD_INSTALL) { printf("Installing '%s'\n", bundleidentifier); #ifdef HAVE_LIBIMOBILEDEVICE_1_1 instproxy_install(ipc, pkgname, client_opts, status_cb, NULL); #else instproxy_install(ipc, pkgname, client_opts, status_cb); #endif } else { printf("Upgrading '%s'\n", bundleidentifier); #ifdef HAVE_LIBIMOBILEDEVICE_1_1 instproxy_upgrade(ipc, pkgname, client_opts, status_cb, NULL); #else instproxy_upgrade(ipc, pkgname, client_opts, status_cb); #endif } instproxy_client_options_free(client_opts); free(pkgname); wait_for_op_complete = 1; notification_expected = 1; } else if (cmd == CMD_UNINSTALL) { printf("Uninstalling '%s'\n", appid); #ifdef HAVE_LIBIMOBILEDEVICE_1_1 instproxy_uninstall(ipc, appid, NULL, status_cb, NULL); #else instproxy_uninstall(ipc, appid, NULL, status_cb); #endif wait_for_op_complete = 1; notification_expected = 0; } else if (cmd == CMD_LIST_ARCHIVES) { int xml_mode = 0; plist_t dict = NULL; plist_t lres = NULL; /* look for options */ if (options) { char *opts = strdup(options); char *elem = strtok(opts, ","); while (elem) { if (!strcmp(elem, "xml")) { xml_mode = 1; } elem = strtok(NULL, ","); } } err = instproxy_lookup_archives(ipc, NULL, &dict); if (err != INSTPROXY_E_SUCCESS) { fprintf(stderr, "ERROR: lookup_archives returned %d\n", err); goto leave_cleanup; } if (!dict) { fprintf(stderr, "ERROR: lookup_archives did not return a plist!?\n"); goto leave_cleanup; } lres = plist_dict_get_item(dict, "LookupResult"); if (!lres || (plist_get_node_type(lres) != PLIST_DICT)) { plist_free(dict); fprintf(stderr, "ERROR: Could not get dict 'LookupResult'\n"); goto leave_cleanup; } if (xml_mode) { char *xml = NULL; uint32_t len = 0; plist_to_xml(lres, &xml, &len); if (xml) { puts(xml); free(xml); } plist_free(dict); goto leave_cleanup; } plist_dict_iter iter = NULL; plist_t node = NULL; char *key = NULL; printf("Total: %d archived apps\n", plist_dict_get_size(lres)); plist_dict_new_iter(lres, &iter); if (!iter) { plist_free(dict); fprintf(stderr, "ERROR: Could not create plist_dict_iter!\n"); goto leave_cleanup; } do { key = NULL; node = NULL; plist_dict_next_item(lres, iter, &key, &node); if (key && (plist_get_node_type(node) == PLIST_DICT)) { char *s_dispName = NULL; char *s_version = NULL; plist_t dispName = plist_dict_get_item(node, "CFBundleDisplayName"); plist_t version = plist_dict_get_item(node, "CFBundleVersion"); if (dispName) { plist_get_string_val(dispName, &s_dispName); } if (version) { plist_get_string_val(version, &s_version); } if (!s_dispName) { s_dispName = strdup(key); } if (s_version) { printf("%s - %s %s\n", key, s_dispName, s_version); free(s_version); } else { printf("%s - %s\n", key, s_dispName); } free(s_dispName); free(key); } } while (node); plist_free(dict); } else if (cmd == CMD_ARCHIVE) { char *copy_path = NULL; int remove_after_copy = 0; int skip_uninstall = 1; int app_only = 0; int docs_only = 0; plist_t client_opts = NULL; /* look for options */ if (options) { char *opts = strdup(options); char *elem = strtok(opts, ","); while (elem) { if (!strcmp(elem, "uninstall")) { skip_uninstall = 0; } else if (!strcmp(elem, "app_only")) { app_only = 1; docs_only = 0; } else if (!strcmp(elem, "docs_only")) { docs_only = 1; app_only = 0; } else if ((strlen(elem) > 5) && !strncmp(elem, "copy=", 5)) { copy_path = strdup(elem+5); } else if (!strcmp(elem, "remove")) { remove_after_copy = 1; } elem = strtok(NULL, ","); } } if (skip_uninstall || app_only || docs_only) { client_opts = instproxy_client_options_new(); if (skip_uninstall) { instproxy_client_options_add(client_opts, "SkipUninstall", 1, NULL); } if (app_only) { instproxy_client_options_add(client_opts, "ArchiveType", "ApplicationOnly", NULL); } else if (docs_only) { instproxy_client_options_add(client_opts, "ArchiveType", "DocumentsOnly", NULL); } } if (copy_path) { struct stat fst; if (stat(copy_path, &fst) != 0) { fprintf(stderr, "ERROR: stat: %s: %s\n", copy_path, strerror(errno)); free(copy_path); goto leave_cleanup; } if (!S_ISDIR(fst.st_mode)) { fprintf(stderr, "ERROR: '%s' is not a directory as expected.\n", copy_path); free(copy_path); goto leave_cleanup; } #ifdef HAVE_LIBIMOBILEDEVICE_1_1_5 if (service) { lockdownd_service_descriptor_free(service); } service = NULL; #else service = 0; #endif if ((lockdownd_start_service(client, "com.apple.afc", &service) != LOCKDOWN_E_SUCCESS) || !service) { fprintf(stderr, "Could not start com.apple.afc!\n"); free(copy_path); goto leave_cleanup; } lockdownd_client_free(client); client = NULL; if (afc_client_new(phone, service, &afc) != INSTPROXY_E_SUCCESS) { fprintf(stderr, "Could not connect to AFC!\n"); goto leave_cleanup; } } #ifdef HAVE_LIBIMOBILEDEVICE_1_1 instproxy_archive(ipc, appid, client_opts, status_cb, NULL); #else instproxy_archive(ipc, appid, client_opts, status_cb); #endif instproxy_client_options_free(client_opts); wait_for_op_complete = 1; if (skip_uninstall) { notification_expected = 0; } else { notification_expected = 1; } idevice_wait_for_operation_to_complete(); if (copy_path) { if (err_occured) { afc_client_free(afc); afc = NULL; goto leave_cleanup; } FILE *f = NULL; uint64_t af = 0; /* local filename */ char *localfile = NULL; if (asprintf(&localfile, "%s/%s.ipa", copy_path, appid) < 0) { fprintf(stderr, "Out of memory!?\n"); goto leave_cleanup; } free(copy_path); f = fopen(localfile, "wb"); if (!f) { fprintf(stderr, "ERROR: fopen: %s: %s\n", localfile, strerror(errno)); free(localfile); goto leave_cleanup; } /* remote filename */ char *remotefile = NULL; if (asprintf(&remotefile, "%s/%s.zip", APPARCH_PATH, appid) < 0) { fprintf(stderr, "Out of memory!?\n"); goto leave_cleanup; } uint32_t fsize = 0; char **fileinfo = NULL; if ((afc_get_file_info(afc, remotefile, &fileinfo) != AFC_E_SUCCESS) || !fileinfo) { fprintf(stderr, "ERROR getting AFC file info for '%s' on device!\n", remotefile); fclose(f); free(remotefile); free(localfile); goto leave_cleanup; } int i; for (i = 0; fileinfo[i]; i+=2) { if (!strcmp(fileinfo[i], "st_size")) { fsize = atoi(fileinfo[i+1]); break; } } i = 0; while (fileinfo[i]) { free(fileinfo[i]); i++; } free(fileinfo); if (fsize == 0) { fprintf(stderr, "Hm... remote file length could not be determined. Cannot copy.\n"); fclose(f); free(remotefile); free(localfile); goto leave_cleanup; } if ((afc_file_open(afc, remotefile, AFC_FOPEN_RDONLY, &af) != AFC_E_SUCCESS) || !af) { fclose(f); fprintf(stderr, "ERROR: could not open '%s' on device for reading!\n", remotefile); free(remotefile); free(localfile); goto leave_cleanup; } /* copy file over */ printf("Copying '%s' --> '%s'... ", remotefile, localfile); free(remotefile); free(localfile); uint32_t amount = 0; uint32_t total = 0; char buf[8192]; do { if (afc_file_read(afc, af, buf, sizeof(buf), &amount) != AFC_E_SUCCESS) { fprintf(stderr, "AFC Read error!\n"); break; } if (amount > 0) { size_t written = fwrite(buf, 1, amount, f); if (written != amount) { fprintf(stderr, "Error when writing %d bytes to local file!\n", amount); break; } total += written; } } while (amount > 0); afc_file_close(afc, af); fclose(f); printf("DONE.\n"); if (total != fsize) { fprintf(stderr, "WARNING: remote and local file sizes don't match (%d != %d)\n", fsize, total); if (remove_after_copy) { fprintf(stderr, "NOTE: archive file will NOT be removed from device\n"); remove_after_copy = 0; } } if (remove_after_copy) { /* remove archive if requested */ printf("Removing '%s'\n", appid); cmd = CMD_REMOVE_ARCHIVE; free(options); options = NULL; if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "ideviceinstaller")) { fprintf(stderr, "Could not connect to lockdownd. Exiting.\n"); goto leave_cleanup; } goto run_again; } } goto leave_cleanup; } else if (cmd == CMD_RESTORE) { #ifdef HAVE_LIBIMOBILEDEVICE_1_1 instproxy_restore(ipc, appid, NULL, status_cb, NULL); #else instproxy_restore(ipc, appid, NULL, status_cb); #endif wait_for_op_complete = 1; notification_expected = 1; } else if (cmd == CMD_REMOVE_ARCHIVE) { #ifdef HAVE_LIBIMOBILEDEVICE_1_1 instproxy_remove_archive(ipc, appid, NULL, status_cb, NULL); #else instproxy_remove_archive(ipc, appid, NULL, status_cb); #endif wait_for_op_complete = 1; } else { printf ("ERROR: no operation selected?! This should not be reached!\n"); res = -2; goto leave_cleanup; } if (client) { /* not needed anymore */ lockdownd_client_free(client); client = NULL; } idevice_wait_for_operation_to_complete(); leave_cleanup: if (bundleidentifier) { free(bundleidentifier); } if (np) { np_client_free(np); } if (ipc) { instproxy_client_free(ipc); } if (afc) { afc_client_free(afc); } if (client) { lockdownd_client_free(client); } idevice_free(phone); if (udid) { free(udid); } if (appid) { free(appid); } if (options) { free(options); } return res; }