wi_status wi_parse_plist(wi_t self, const char *from_buf, size_t length, plist_t *to_rpc_dict, bool *to_is_partial) { wi_private_t my = self->private_state; *to_is_partial = false; *to_rpc_dict = NULL; if (my->is_sim) { plist_from_bin(from_buf, length, to_rpc_dict); } else { plist_t wi_dict = NULL; plist_from_bin(from_buf, length, &wi_dict); if (!wi_dict) { return WI_ERROR; } plist_t wi_rpc = plist_dict_get_item(wi_dict, "WIRFinalMessageKey"); if (!wi_rpc) { wi_rpc = plist_dict_get_item(wi_dict, "WIRPartialMessageKey"); if (!wi_rpc) { return WI_ERROR; } *to_is_partial = true; } uint64_t rpc_len = 0; char *rpc_bin = NULL; plist_get_data_val(wi_rpc, &rpc_bin, &rpc_len); plist_free(wi_dict); // also frees wi_rpc if (!rpc_bin) { return WI_ERROR; } // assert rpc_len < MAX_RPC_LEN? size_t p_length = my->partial->tail - my->partial->head; if (*to_is_partial || p_length) { if (cb_ensure_capacity(my->partial, rpc_len)) { return self->on_error(self, "Out of memory"); } memcpy(my->partial->tail, rpc_bin, rpc_len); my->partial->tail += rpc_len; p_length += rpc_len; free(rpc_bin); if (*to_is_partial) { return WI_SUCCESS; } } if (p_length) { plist_from_bin(my->partial->head, (uint32_t)p_length, to_rpc_dict); cb_clear(my->partial); } else { plist_from_bin(rpc_bin, (uint32_t)rpc_len, to_rpc_dict); free(rpc_bin); } } return (*to_rpc_dict ? WI_SUCCESS : WI_ERROR); }
// // If plist_from_bin will fail, xmlBuff_ will remain unchanged, pointing to PREVIOUS-SAVED xml buffer. // const CharVt& PlistEntry::GetXML(CharVt&& BplistBuff) { GuardedPlist plist; try { plist_from_bin(BplistBuff.data(), BplistBuff.size(), plist.get_ptr()); } catch (...) { // catch SEH exceptions (/withSEH) throw std::system_error(std::error_code(EFAULT, std::generic_category()), "Invalid bplist file! Cant parse it"); } uint32_t cbXML = 0; char* pXML_{}; plist_to_xml( plist, &pXML_, &cbXML ); if ( cbXML > 0 ) { xmlBuff_.assign( pXML_, pXML_ + cbXML ); // free mem allocated by plist_to_xml free( pXML_ ); } if ( xmlBuff_.empty() ) { contentType_ = ContentType::corrupted; throw std::runtime_error( "error converting bplist to xml" ); } contentType_ = ContentType::xml; return xmlBuff_; }
/** Polls the iPhone for MobileSync data. * * @param client The MobileSync client * @param plist A pointer to the location where the plist should be stored * * @return an error code */ mobilesync_error_t mobilesync_recv(mobilesync_client_t client, plist_t * plist) { if (!client || !plist || (plist && *plist)) return MOBILESYNC_E_INVALID_ARG; mobilesync_error_t ret = MOBILESYNC_E_UNKNOWN_ERROR; char *receive = NULL; uint32_t datalen = 0, bytes = 0, received_bytes = 0; ret = iphone_device_recv(client->connection, (char *) &datalen, sizeof(datalen), &bytes); datalen = ntohl(datalen); receive = (char *) malloc(sizeof(char) * datalen); /* fill buffer and request more packets if needed */ while ((received_bytes < datalen) && (ret == MOBILESYNC_E_SUCCESS)) { ret = iphone_device_recv(client->connection, receive + received_bytes, datalen - received_bytes, &bytes); received_bytes += bytes; } if (ret != MOBILESYNC_E_SUCCESS) { free(receive); return MOBILESYNC_E_MUX_ERROR; } plist_from_bin(receive, received_bytes, plist); free(receive); char *XMLContent = NULL; uint32_t length = 0; plist_to_xml(*plist, &XMLContent, &length); log_dbg_msg(DBGMASK_MOBILESYNC, "%s: plist size: %i\nbuffer :\n%s\n", __func__, length, XMLContent); free(XMLContent); return ret; }
/** * Receives a plist using the given property list service client. * Internally used generic plist receive function. * * @param client The property list service client to use for receiving * @param plist pointer to a plist_t that will point to the received plist * upon successful return * @param timeout Maximum time in milliseconds to wait for data. * * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL, * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR * when an unspecified error occurs. */ static property_list_service_error_t internal_plist_receive_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) { property_list_service_error_t res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; uint32_t pktlen = 0; uint32_t bytes = 0; if (!client || (client && !client->connection) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; } idevice_connection_receive_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); debug_info("initial read=%i", bytes); if (bytes < 4) { debug_info("initial read failed!"); return PROPERTY_LIST_SERVICE_E_MUX_ERROR; } else { pktlen = be32toh(pktlen); if (pktlen < (1 << 24)) { /* prevent huge buffers */ uint32_t curlen = 0; char *content = NULL; debug_info("%d bytes following", pktlen); content = (char*)malloc(pktlen); while (curlen < pktlen) { idevice_connection_receive(client->connection, content+curlen, pktlen-curlen, &bytes); if (bytes <= 0) { res = PROPERTY_LIST_SERVICE_E_MUX_ERROR; break; } debug_info("received %d bytes", bytes); curlen += bytes; } if (!memcmp(content, "bplist00", 8)) { plist_from_bin(content, pktlen, plist); } else { /* iOS 4.3+ hack: plist data might contain invalid characters, thus we convert those to spaces */ for (bytes = 0; bytes < pktlen-1; bytes++) { if ((content[bytes] >= 0) && (content[bytes] < 0x20) && (content[bytes] != 0x09) && (content[bytes] != 0x0a) && (content[bytes] != 0x0d)) content[bytes] = 0x20; } plist_from_xml(content, pktlen, plist); } if (*plist) { debug_plist(*plist); res = PROPERTY_LIST_SERVICE_E_SUCCESS; } else { res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR; } free(content); content = NULL; } else { res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; } } return res; }
PLIST_API void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist) { if (length < 8) { *plist = NULL; return; } if (plist_is_binary(plist_data, length)) { plist_from_bin(plist_data, length, plist); } else { plist_from_xml(plist_data, length, plist); } }
static int rproxy_recv_dict(idevice_connection_t connection, plist_t * dict, unsigned int timeout) { int res = -1; char * content = NULL; uint32_t dict_length = 0; /* Read the size of the plist buffer */ if (IDEVICE_E_SUCCESS != idevice_connection_receive_all(connection, (char *)&dict_length, sizeof(dict_length), timeout)) { error("ERROR: Failed to receive plist size\n"); goto cleanup; } /* Allocate a buffer for the plist */ content = (char*)malloc(dict_length); if (NULL == content) { error("ERROR: Out of memory\n"); goto cleanup; } /* Read the plist */ if (IDEVICE_E_SUCCESS != idevice_connection_receive_all(connection, content, dict_length, timeout)) { error("ERROR: Failed to receive plist data\n"); goto cleanup; } /* Finally, parse the plist */ plist_from_bin(content, dict_length, dict); if (*dict) { res = 0; } else { error("ERROR: Failed to parse the recieved plist\n"); } cleanup: if (content) { free(content); } return res; }
int plist_read_from_filename(plist_t *plist, const char *filename) { char *buffer = NULL; uint64_t length; if (!filename) return 0; buffer_read_from_filename(filename, &buffer, &length); if (!buffer) { return 0; } if ((length > 8) && (memcmp(buffer, "bplist00", 8) == 0)) { plist_from_bin(buffer, length, plist); } else { plist_from_xml(buffer, length, plist); } free(buffer); return 1; }
int main(int argc, char *argv[]) { FILE *iplist1 = NULL; FILE *iplist2 = NULL; plist_t root_node1 = NULL; plist_t root_node2 = NULL; char *plist_1 = NULL; char *plist_2 = NULL; int size_in1 = 0; int size_in2 = 0; char *file_in1 = NULL; char *file_in2 = NULL; int res = 0; struct stat *filestats1 = (struct stat *) malloc(sizeof(struct stat)); struct stat *filestats2 = (struct stat *) malloc(sizeof(struct stat)); if (argc!= 3) { printf("Wrong input\n"); return 1; } file_in1 = argv[1]; file_in2 = argv[2]; //read input file iplist1 = fopen(file_in1, "rb"); iplist2 = fopen(file_in2, "rb"); if (!iplist1 || !iplist2) { printf("File does not exists\n"); return 2; } stat(file_in1, filestats1); stat(file_in2, filestats2); size_in1 = filestats1->st_size; size_in2 = filestats2->st_size; plist_1 = (char *) malloc(sizeof(char) * (size_in1 + 1)); plist_2 = (char *) malloc(sizeof(char) * (size_in2 + 1)); fread(plist_1, sizeof(char), size_in1, iplist1); fread(plist_2, sizeof(char), size_in2, iplist2); fclose(iplist1); fclose(iplist2); if (memcmp(plist_1, "bplist00", 8) == 0) plist_from_bin(plist_1, size_in1, &root_node1); else plist_from_xml(plist_1, size_in1, &root_node1); if (memcmp(plist_2, "bplist00", 8) == 0) plist_from_bin(plist_2, size_in2, &root_node2); else plist_from_xml(plist_2, size_in2, &root_node2); if (!root_node1 || !root_node2) { printf("PList parsing failed\n"); return 3; } else printf("PList parsing succeeded\n"); res = compare_plist(root_node1, root_node2); plist_free(root_node1); plist_free(root_node2); free(plist_1); free(plist_2); free(filestats1); free(filestats2); return !res; }
/** * Receives a plist using the given property list service client. * Internally used generic plist receive function. * * @param client The property list service client to use for receiving * @param plist pointer to a plist_t that will point to the received plist * upon successful return * @param timeout Maximum time in milliseconds to wait for data. * * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL, * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR * when an unspecified error occurs. */ static property_list_service_error_t internal_plist_receive_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) { property_list_service_error_t res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; uint32_t pktlen = 0; uint32_t bytes = 0; if (!client || (client && !client->parent) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; } *plist = NULL; service_error_t serr = service_receive_with_timeout(client->parent, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); if ((serr == SERVICE_E_SUCCESS) && (bytes == 0)) { return PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT; } debug_info("initial read=%i", bytes); if (bytes < 4) { debug_info("initial read failed!"); return PROPERTY_LIST_SERVICE_E_MUX_ERROR; } else { uint32_t curlen = 0; char *content = NULL; pktlen = be32toh(pktlen); debug_info("%d bytes following", pktlen); content = (char*)malloc(pktlen); if (!content) { debug_info("out of memory when allocating %d bytes", pktlen); return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; } while (curlen < pktlen) { service_receive(client->parent, content+curlen, pktlen-curlen, &bytes); if (bytes <= 0) { res = PROPERTY_LIST_SERVICE_E_MUX_ERROR; break; } debug_info("received %d bytes", bytes); curlen += bytes; } if (curlen < pktlen) { debug_info("received incomplete packet (%d of %d bytes)", curlen, pktlen); if (curlen > 0) { debug_info("incomplete packet following:"); debug_buffer(content, curlen); } free(content); return res; } if ((pktlen > 8) && !memcmp(content, "bplist00", 8)) { plist_from_bin(content, pktlen, plist); } else if ((pktlen > 5) && !memcmp(content, "<?xml", 5)) { /* iOS 4.3+ hack: plist data might contain invalid characters, thus we convert those to spaces */ for (bytes = 0; bytes < pktlen-1; bytes++) { if ((content[bytes] >= 0) && (content[bytes] < 0x20) && (content[bytes] != 0x09) && (content[bytes] != 0x0a) && (content[bytes] != 0x0d)) content[bytes] = 0x20; } plist_from_xml(content, pktlen, plist); } else { debug_info("WARNING: received unexpected non-plist content"); debug_buffer(content, pktlen); } if (*plist) { debug_plist(*plist); res = PROPERTY_LIST_SERVICE_E_SUCCESS; } else { res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR; } free(content); content = NULL; } return res; }
int main(int argc, char *argv[]) { FILE *iplist = NULL; plist_t root_node1 = NULL; plist_t root_node2 = NULL; char *plist_xml = NULL; char *plist_xml2 = NULL; char *plist_bin = NULL; int size_in = 0; uint32_t size_out = 0; uint32_t size_out2 = 0; char *file_in = NULL; char *file_out = NULL; struct stat *filestats = (struct stat *) malloc(sizeof(struct stat)); if (argc != 3) { printf("Wrong input\n"); return 1; } file_in = argv[1]; file_out = argv[2]; //read input file iplist = fopen(file_in, "rb"); if (!iplist) { printf("File does not exists\n"); return 2; } printf("File %s is open\n", file_in); stat(file_in, filestats); size_in = filestats->st_size; plist_xml = (char *) malloc(sizeof(char) * (size_in + 1)); fread(plist_xml, sizeof(char), size_in, iplist); fclose(iplist); //convert one format to another plist_from_xml(plist_xml, size_in, &root_node1); if (!root_node1) { printf("PList XML parsing failed\n"); return 3; } else printf("PList XML parsing succeeded\n"); plist_to_bin(root_node1, &plist_bin, &size_out); if (!plist_bin) { printf("PList BIN writing failed\n"); return 4; } else printf("PList BIN writing succeeded\n"); plist_from_bin(plist_bin, size_out, &root_node2); if (!root_node2) { printf("PList BIN parsing failed\n"); return 5; } else printf("PList BIN parsing succeeded\n"); plist_to_xml(root_node2, &plist_xml2, &size_out2); if (!plist_xml2) { printf("PList XML writing failed\n"); return 8; } else printf("PList XML writing succeeded\n"); if (plist_xml2) { FILE *oplist = NULL; oplist = fopen(file_out, "wb"); fwrite(plist_xml2, size_out2, sizeof(char), oplist); fclose(oplist); } plist_free(root_node1); plist_free(root_node2); free(plist_bin); free(plist_xml); free(plist_xml2); free(filestats); if ((uint32_t)size_in != size_out2) { printf("Size of input and output is different\n"); printf("Input size : %i\n", size_in); printf("Output size : %i\n", size_out2); } //success return 0; }
webinspector_error_t webinspector_receive_with_timeout(webinspector_client_t client, plist_t * plist, uint32_t timeout_ms) { webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR; plist_t message = NULL; plist_t key = NULL; int is_final_message = 1; char* buffer = NULL; uint64_t length = 0; char* packet = NULL; char* newpacket = NULL; uint64_t packet_length = 0; debug_info("Receiving webinspector message..."); do { /* receive message */ res = webinspector_error(property_list_service_receive_plist_with_timeout(client->parent, &message, timeout_ms)); if (res != WEBINSPECTOR_E_SUCCESS || !message) { debug_info("Could not receive message, error %d", res); plist_free(message); return WEBINSPECTOR_E_MUX_ERROR; } /* get message key */ key = plist_dict_get_item(message, "WIRFinalMessageKey"); if (!key) { key = plist_dict_get_item(message, "WIRPartialMessageKey"); if (!key) { debug_info("ERROR: Unable to read message key."); plist_free(message); return WEBINSPECTOR_E_PLIST_ERROR; } is_final_message = 0; } else { is_final_message = 1; } /* read partial data */ plist_get_data_val(key, &buffer, &length); if (!buffer || length == 0 || length > 0xFFFFFFFF) { debug_info("ERROR: Unable to get the inner plist binary data."); free(packet); free(buffer); return WEBINSPECTOR_E_PLIST_ERROR; } /* (re)allocate packet data */ if (!packet) { packet = (char*)malloc(length * sizeof(char)); } else { newpacket = (char*)realloc(packet, (packet_length + length) * sizeof(char)); packet = newpacket; } /* copy partial data into final packet data */ memcpy(packet + packet_length, buffer, length); /* cleanup buffer */ free(buffer); buffer = NULL; if (message) { plist_free(message); message = NULL; } /* adjust packet length */ packet_length += length; length = 0; } while(!is_final_message); /* read final message */ if (packet_length) { plist_from_bin(packet, (uint32_t)packet_length, plist); if (!*plist) { debug_info("Error restoring the final plist."); free(packet); return WEBINSPECTOR_E_PLIST_ERROR; } debug_plist(*plist); } if (packet) { free(packet); } return res; }
int get_shsh_blobs(struct idevicerestore_client_t* client, uint64_t ecid, unsigned char* nonce, int nonce_size, plist_t build_identity, plist_t* tss) { plist_t request = NULL; plist_t response = NULL; *tss = NULL; if ((client->build_major <= 8) || (client->flags & FLAG_CUSTOM)) { error("checking for local shsh\n"); /* first check for local copy */ char zfn[1024]; if (client->version) { if (client->cache_dir) { sprintf(zfn, "%s/shsh/" FMT_qu "-%s-%s.shsh", client->cache_dir, (long long int)client->ecid, client->device->product_type, client->version); } else { sprintf(zfn, "shsh/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product_type, client->version); } struct stat fst; if (stat(zfn, &fst) == 0) { gzFile zf = gzopen(zfn, "rb"); if (zf) { int blen = 0; int readsize = 16384; int bufsize = readsize; char* bin = (char*)malloc(bufsize); char* p = bin; do { int bytes_read = gzread(zf, p, readsize); if (bytes_read < 0) { fprintf(stderr, "Error reading gz compressed data\n"); exit(EXIT_FAILURE); } blen += bytes_read; if (bytes_read < readsize) { if (gzeof(zf)) { bufsize += bytes_read; break; } } bufsize += readsize; bin = realloc(bin, bufsize); p = bin + blen; } while (!gzeof(zf)); gzclose(zf); if (blen > 0) { if (memcmp(bin, "bplist00", 8) == 0) { plist_from_bin(bin, blen, tss); } else { plist_from_xml(bin, blen, tss); } } free(bin); } } else { error("no local file %s\n", zfn); } } else { error("No version found?!\n"); } } if (*tss) { info("Using cached SHSH\n"); return 0; } else { info("Trying to fetch new SHSH blob\n"); } request = tss_create_request(build_identity, ecid, nonce, nonce_size); if (request == NULL) { error("ERROR: Unable to create TSS request\n"); return -1; } response = tss_send_request(request, client->tss_url); if (response == NULL) { info("ERROR: Unable to send TSS request\n"); plist_free(request); return -1; } info("Received SHSH blobs\n"); plist_free(request); *tss = response; return 0; }
int get_shsh_blobs(struct idevicerestore_client_t* client, uint64_t ecid, unsigned char* nonce, int nonce_size, plist_t build_identity, plist_t* tss) { plist_t request = NULL; plist_t response = NULL; *tss = NULL; if ((client->build[0] <= '8') || (client->flags & FLAG_CUSTOM)) { error("checking for local shsh\n"); /* first check for local copy */ char zfn[512]; if (client->version) { sprintf(zfn, "shsh/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product, client->version); struct stat fst; if (stat(zfn, &fst) == 0) { gzFile zf = gzopen(zfn, "rb"); if (zf) { unsigned char bin[65536]; int blen = gzread(zf, bin, sizeof(bin)); if (blen > 0) { if (memcmp(bin, "bplist00", 8) == 0) { plist_from_bin(bin, blen, tss); } else { plist_from_xml(bin, blen, tss); } } gzclose(zf); } } else { error("no local file %s\n", zfn); } } else { error("No version found?!\n"); } } if (*tss) { info("Using cached SHSH\n"); return 0; } else { info("Trying to fetch new SHSH blob\n"); } request = tss_create_request(build_identity, ecid, nonce, nonce_size); if (request == NULL) { error("ERROR: Unable to create TSS request\n"); return -1; } info("Sending TSS request... "); response = tss_send_request(request); if (response == NULL) { info("ERROR: Unable to send TSS request\n"); plist_free(request); return -1; } info("received SHSH blobs\n"); plist_free(request); *tss = response; 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; }