/** * Checks if a notification has been sent by the device. * * @param client NP to get a notification from * @param notification Pointer to a buffer that will be allocated and filled * with the notification that has been received. * * @return 0 if a notification has been received or nothing has been received, * or a negative value if an error occured. * * @note You probably want to check out np_set_notify_callback * @see np_set_notify_callback */ static int np_get_notification(np_client_t client, char **notification) { int res = 0; plist_t dict = NULL; if (!client || !client->parent || *notification) return -1; np_lock(client); property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) { debug_info("NotificationProxy: no notification received!"); res = 0; } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) { debug_info("NotificationProxy: error %d occured!", perr); res = perr; } if (dict) { char *cmd_value = NULL; plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); if (plist_get_node_type(cmd_value_node) == PLIST_STRING) { plist_get_string_val(cmd_value_node, &cmd_value); } if (cmd_value && !strcmp(cmd_value, "RelayNotification")) { char *name_value = NULL; plist_t name_value_node = plist_dict_get_item(dict, "Name"); if (plist_get_node_type(name_value_node) == PLIST_STRING) { plist_get_string_val(name_value_node, &name_value); } res = -2; if (name_value_node && name_value) { *notification = name_value; debug_info("got notification %s", __func__, name_value); res = 0; } } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { debug_info("NotificationProxy died!"); res = -1; } else if (cmd_value) { debug_info("unknown NotificationProxy command '%s' received!", cmd_value); res = -1; } else { res = -2; } if (cmd_value) { free(cmd_value); } plist_free(dict); dict = NULL; } np_unlock(client); return res; }
/* Generic device link service receive function, with a custom timeout. * * @param client The device link service client to use for sending * @param plist Pointer that will point to the property list received upon * successful return. * @param timeout Maximum time in milliseconds to wait for data. * * @return DEVICE_LINK_SERVICE_E_SUCCESS on success, * DEVICE_LINK_SERVICE_E_INVALID_ARG when client or plist is NULL, * or DEVICE_LINK_SERVICE_E_MUX_ERROR when no property list could be * received. */ device_link_service_error_t device_link_service_receive_with_timeout(device_link_service_client_t client, plist_t *plist, unsigned int timeout) { if (!client || !plist || (plist && *plist)) { return DEVICE_LINK_SERVICE_E_INVALID_ARG; } if (property_list_service_receive_plist_with_timeout(client->parent, plist, timeout) != PROPERTY_LIST_SERVICE_E_SUCCESS) { return DEVICE_LINK_SERVICE_E_MUX_ERROR; } return DEVICE_LINK_SERVICE_E_SUCCESS; }
heartbeat_error_t heartbeat_receive_with_timeout(heartbeat_client_t client, plist_t * plist, uint32_t timeout_ms) { heartbeat_error_t res = HEARTBEAT_E_UNKNOWN_ERROR; plist_t outplist = NULL; res = heartbeat_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, timeout_ms)); if (res != HEARTBEAT_E_SUCCESS || !outplist) { debug_info("Could not receive plist, error %d", res); plist_free(outplist); return HEARTBEAT_E_MUX_ERROR; } *plist = outplist; debug_plist(*plist); return res; }
/** * Internally used function that will synchronously receive messages from * the specified installation_proxy until it completes or an error occurs. * * If status_cb is not NULL, the callback function will be called each time * a status update or error message is received. * * @param client The connected installation proxy client * @param status_cb Pointer to a callback function or NULL * @param operation Operation name. Will be passed to the callback function * in async mode or shown in debug messages in sync mode. * @param user_data Callback data passed to status_cb. */ static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; int ok = 1; plist_t dict = NULL; do { instproxy_lock(client); res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 30000)); instproxy_unlock(client); if (res != INSTPROXY_E_SUCCESS) { debug_info("could not receive plist, error %d", res); break; } if (dict) { /* invoke callback function */ if (status_cb) { status_cb(operation, dict, user_data); } /* check for 'Error', so we can abort cleanly */ plist_t err = plist_dict_get_item(dict, "Error"); if (err) { #ifndef STRIP_DEBUG_CODE char *err_msg = NULL; plist_get_string_val(err, &err_msg); if (err_msg) { debug_info("(%s): ERROR: %s", operation, err_msg); free(err_msg); } #endif ok = 0; res = INSTPROXY_E_OP_FAILED; } /* get 'Status' */ plist_t status = plist_dict_get_item(dict, "Status"); if (status) { char *status_msg = NULL; plist_get_string_val(status, &status_msg); if (status_msg) { if (!strcmp(status_msg, "Complete")) { ok = 0; res = INSTPROXY_E_SUCCESS; } #ifndef STRIP_DEBUG_CODE plist_t npercent = plist_dict_get_item(dict, "PercentComplete"); if (npercent) { uint64_t val = 0; int percent; plist_get_uint_val(npercent, &val); percent = val; debug_info("(%s): %s (%d%%)", operation, status_msg, percent); } else { debug_info("(%s): %s", operation, status_msg); } #endif free(status_msg); } } plist_free(dict); dict = NULL; } } while (ok && client->parent); return res; }
/** * Request data for the given sources. * * @param client The connected file_relay client. * @param sources A NULL-terminated list of sources to retrieve. * Valid sources are: * - AppleSupport * - Network * - VPN * - WiFi * - UserDatabases * - CrashReporter * - tmp * - SystemConfiguration * @param connection The connection that has to be used for receiving the * data using idevice_connection_receive(). The connection will be closed * automatically by the device, but use file_relay_client_free() to clean * up properly. * * @note WARNING: Don't call this function without reading the data afterwards. * A directory mobile_file_relay.XXXX used for creating the archive will * remain in the /tmp directory otherwise. * * @return FILE_RELAY_E_SUCCESS on succes, FILE_RELAY_E_INVALID_ARG when one or * more parameters are invalid, FILE_RELAY_E_MUX_ERROR if a communication * error occurs, FILE_RELAY_E_PLIST_ERROR when the received result is NULL * or is not a valid plist, FILE_RELAY_E_INVALID_SOURCE if one or more * sources are invalid, FILE_RELAY_E_STAGING_EMPTY if no data is available * for the given sources, or FILE_RELAY_E_UNKNOWN_ERROR otherwise. */ file_relay_error_t file_relay_request_sources(file_relay_client_t client, const char **sources, idevice_connection_t *connection) { if (!client || !client->parent || !sources || !sources[0]) { return FILE_RELAY_E_INVALID_ARG; } *connection = NULL; file_relay_error_t err = FILE_RELAY_E_UNKNOWN_ERROR; /* set up request plist */ plist_t array = plist_new_array(); int i = 0; while (sources[i]) { plist_array_append_item(array, plist_new_string(sources[i])); i++; } plist_t dict = plist_new_dict(); plist_dict_insert_item(dict, "Sources", array); if (property_list_service_send_xml_plist(client->parent, dict) != PROPERTY_LIST_SERVICE_E_SUCCESS) { debug_info("ERROR: Could not send request to device!"); err = FILE_RELAY_E_MUX_ERROR; goto leave; } plist_free(dict); dict = NULL; if (property_list_service_receive_plist_with_timeout(client->parent, &dict, 60000) != PROPERTY_LIST_SERVICE_E_SUCCESS) { debug_info("ERROR: Could not receive answer from device!"); err = FILE_RELAY_E_MUX_ERROR; goto leave; } if (!dict) { debug_info("ERROR: Did not receive any plist!"); err = FILE_RELAY_E_PLIST_ERROR; goto leave; } plist_t error = plist_dict_get_item(dict, "Error"); if (error) { char *errmsg = NULL; plist_get_string_val(error, &errmsg); if (errmsg) { if (!strcmp(errmsg, "InvalidSource")) { debug_info("ERROR: One or more given sources are invalid!"); err = FILE_RELAY_E_INVALID_SOURCE; } else if (!strcmp(errmsg, "StagingEmpty")) { debug_info("ERROR: StagingEmpty - No data available!"); err = FILE_RELAY_E_STAGING_EMPTY; } else { debug_info("ERROR: Unknown error '%s'", errmsg); } free(errmsg); } else { debug_info("ERROR: Could not get error message!"); } goto leave; } plist_t status = plist_dict_get_item(dict, "Status"); if (!status) { debug_info("ERROR: Unexpected plist received!"); debug_plist(dict); err = FILE_RELAY_E_PLIST_ERROR; goto leave; } char *ack = NULL; plist_get_string_val(status, &ack); if (!ack) { debug_info("ERROR: Could not get 'Acknowledged' string!"); goto leave; } if (strcmp(ack, "Acknowledged")) { debug_info("ERROR: Did not receive 'Acknowledged' but '%s'", ack); goto leave; } free(ack); err = FILE_RELAY_E_SUCCESS; *connection = client->parent->connection; leave: if (dict) { plist_free(dict); } return err; }
/** * Internally used function that will synchronously receive messages from * the specified installation_proxy until it completes or an error occurs. * * If status_cb is not NULL, the callback function will be called each time * a status update or error message is received. * * @param client The connected installation proxy client * @param status_cb Pointer to a callback function or NULL * @param command Operation specificiation in plist. Will be passed to the * status_cb callback. * @param user_data Callback data passed to status_cb. */ static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client, plist_t command, instproxy_status_cb_t status_cb, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; int complete = 0; plist_t node = NULL; char* command_name = NULL; char* status_name = NULL; char* error_name = NULL; char* error_description = NULL; uint64_t error_code = 0; #ifndef STRIP_DEBUG_CODE int percent_complete = 0; #endif instproxy_command_get_name(command, &command_name); do { /* receive status response */ instproxy_lock(client); res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000)); instproxy_unlock(client); /* break out if we have a communication problem */ if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) { debug_info("could not receive plist, error %d", res); break; } /* parse status response */ if (node) { /* check status for possible error to allow reporting it and aborting it gracefully */ res = instproxy_status_get_error(node, &error_name, &error_description, &error_code); if (res != INSTPROXY_E_SUCCESS) { debug_info("command: %s, error %d, code 0x%08"PRIx64", name: %s, description: \"%s\"", command_name, res, error_code, error_name, error_description ? error_description: "N/A"); complete = 1; } if (error_name) { free(error_name); error_name = NULL; } if (error_description) { free(error_description); error_description = NULL; } /* check status from response */ instproxy_status_get_name(node, &status_name); if (!status_name) { debug_info("failed to retrieve name from status response with error %d.", res); complete = 1; } if (status_name) { if (!strcmp(status_name, "Complete")) { complete = 1; } else { res = INSTPROXY_E_OP_IN_PROGRESS; } #ifndef STRIP_DEBUG_CODE percent_complete = -1; instproxy_status_get_percent_complete(node, &percent_complete); if (percent_complete >= 0) { debug_info("command: %s, status: %s, percent (%d%%)", command_name, status_name, percent_complete); } else { debug_info("command: %s, status: %s", command_name, status_name); } #endif free(status_name); status_name = NULL; } /* invoke status callback function */ if (status_cb) { status_cb(command, node, user_data); } plist_free(node); node = NULL; } } while (!complete && client->parent); if (command_name) free(command_name); return res; }
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; }