/** * This function allows an application to define a callback function that will * be called when a notification has been received. * It will start a thread that polls for notifications and calls the callback * function if a notification has been received. * * @param client the NP client * @param notify_cb pointer to a callback function or NULL to de-register a * previously set callback function * * @return NP_E_SUCCESS when the callback was successfully registered, * or an error value when an error occured. */ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb ) { if (!client) return NP_E_INVALID_ARG; np_error_t res = NP_E_UNKNOWN_ERROR; np_lock(client); if (client->notifier) { log_debug_msg("%s: callback already set, removing\n"); iphone_connection_t conn = client->connection; client->connection = NULL; g_thread_join(client->notifier); client->notifier = NULL; client->connection = conn; } if (notify_cb) { struct np_thread *npt = (struct np_thread*)malloc(sizeof(struct np_thread)); if (npt) { npt->client = client; npt->cbfunc = notify_cb; client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL); if (client->notifier) { res = NP_E_SUCCESS; } } } else { log_debug_msg("%s: no callback set\n", __func__); } np_unlock(client); return res; }
/** Opens a file on the phone. * * @param client The client to use to open the file. * @param filename The file to open. (must be a fully-qualified path) * @param file_mode The mode to use to open the file. Can be AFC_FILE_READ or * AFC_FILE_WRITE; the former lets you read and write, * however, and the second one will *create* the file, * destroying anything previously there. * @param handle Pointer to a uint64_t that will hold the handle of the file * * @return AFC_E_SUCCESS on success or an AFC_E_* error on failure. */ iphone_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) { uint32_t ag = 0; int bytes = 0; char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1)); afc_error_t ret = AFC_E_UNKNOWN_ERROR; // set handle to 0 so in case an error occurs, the handle is invalid *handle = 0; if (!client || !client->connection || !client->afc_packet) return AFC_E_INVALID_ARGUMENT; afc_lock(client); // Send command memcpy(data, &file_mode, 4); memcpy(data + 4, &ag, 4); memcpy(data + 8, filename, strlen(filename)); data[8 + strlen(filename)] = '\0'; client->afc_packet->operation = AFC_OP_FILE_OPEN; client->afc_packet->entire_length = client->afc_packet->this_length = 0; bytes = afc_dispatch_packet(client, data, 8 + strlen(filename) + 1); free(data); if (bytes <= 0) { log_debug_msg("%s: Didn't receive a response to the command\n", __func__); afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } // Receive the data ret = afc_receive_data(client, &data, &bytes); if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { afc_unlock(client); // Get the file handle memcpy(handle, data, sizeof(uint64_t)); free(data); return ret; } log_debug_msg("%s: Didn't get any further data\n", __func__); afc_unlock(client); return ret; }
/** Reads the HostID from a previously generated configuration file. * * @note It is the responsibility of the calling function to free the returned host_id * * @return The string containing the HostID or NULL */ void userpref_get_host_id(char **host_id) { gchar *config_file; GKeyFile *key_file; gchar *loc_host_id; config_file = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_CONF_FILE, NULL); /* now parse file to get the HostID */ key_file = g_key_file_new(); if (g_key_file_load_from_file(key_file, config_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) { loc_host_id = g_key_file_get_value(key_file, "Global", "HostID", NULL); if (loc_host_id) *host_id = strdup((char *) loc_host_id); g_free(loc_host_id); } g_key_file_free(key_file); g_free(config_file); if (!*host_id) { /* no config, generate host_id */ *host_id = userpref_generate_host_id(); userpref_set_host_id(*host_id); } log_debug_msg("%s: Using %s as HostID\n", __func__, *host_id); }
/** Sends a notification to the device's Notification Proxy. * * notification messages seen so far: * com.apple.itunes-mobdev.syncWillStart * com.apple.itunes-mobdev.syncDidStart * * @param client The client to send to * @param notification The notification message to send */ np_error_t np_post_notification(np_client_t client, const char *notification) { if (!client || !notification) { return NP_E_INVALID_ARG; } np_lock(client); plist_t dict = plist_new_dict(); plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification")); plist_dict_insert_item(dict,"Name", plist_new_string(notification)); np_error_t res = np_plist_send(client, dict); plist_free(dict); dict = plist_new_dict(); plist_dict_insert_item(dict,"Command", plist_new_string("Shutdown")); res = np_plist_send(client, dict); plist_free(dict); if (res != NP_E_SUCCESS) { log_debug_msg("%s: Error sending XML plist to device!\n", __func__); } np_unlock(client); return res; }
/** Store HostID in config file. * * @param host_id A null terminated string containing a valid HostID. */ static int userpref_set_host_id(const char *host_id) { GKeyFile *key_file; gsize length; gchar *buf, *config_file; GIOChannel *file; if (!host_id) return 0; /* Make sure config directory exists */ userpref_create_config_dir(); /* Now parse file to get the HostID */ key_file = g_key_file_new(); /* Store in config file */ log_debug_msg("%s: setting hostID to %s\n", __func__, host_id); g_key_file_set_value(key_file, "Global", "HostID", host_id); /* Write config file on disk */ buf = g_key_file_to_data(key_file, &length, NULL); config_file = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_CONF_FILE, NULL); file = g_io_channel_new_file(config_file, "w", NULL); g_free(config_file); g_io_channel_write_chars(file, buf, length, NULL, NULL); g_io_channel_shutdown(file, TRUE, NULL); g_io_channel_unref(file); g_key_file_free(key_file); return 1; }
/** * Get a list of currently available devices. * * @param devices List of uuids of devices that are currently available. * This list is terminated by a NULL pointer. * @param count Number of devices found. * * @return IPHONE_E_SUCCESS on success or an error value when an error occured. */ iphone_error_t iphone_get_device_list(char ***devices, int *count) { usbmuxd_device_info_t *dev_list; *devices = NULL; *count = 0; if (usbmuxd_get_device_list(&dev_list) < 0) { log_debug_msg("%s: ERROR: usbmuxd is not running!\n", __func__); return IPHONE_E_NO_DEVICE; } char **newlist = NULL; int i, newcount = 0; for (i = 0; dev_list[i].handle > 0; i++) { newlist = realloc(*devices, sizeof(char*) * (newcount+1)); newlist[newcount++] = strdup(dev_list[i].uuid); *devices = newlist; } usbmuxd_device_list_free(&dev_list); *count = newcount; newlist = realloc(*devices, sizeof(char*) * (newcount+1)); newlist[newcount] = NULL; *devices = newlist; return IPHONE_E_SUCCESS; }
/** Closes a file on the phone. * * @param client The client to close the file with. * @param handle File handle of a previously opened file. */ afc_error_t afc_file_close(afc_client_t client, uint64_t handle) { char *buffer = malloc(sizeof(char) * 8); int bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || (handle == 0)) return AFC_E_INVALID_ARGUMENT; afc_lock(client); log_debug_msg("%s: File handle %i\n", __func__, handle); // Send command memcpy(buffer, &handle, sizeof(uint64_t)); client->afc_packet->operation = AFC_OP_FILE_CLOSE; client->afc_packet->entire_length = client->afc_packet->this_length = 0; bytes = afc_dispatch_packet(client, buffer, 8); free(buffer); buffer = NULL; if (bytes <= 0) { afc_unlock(client); return AFC_E_UNKNOWN_ERROR; } // Receive the response ret = afc_receive_data(client, &buffer, &bytes); if (buffer) free(buffer); afc_unlock(client); return ret; }
/** * Receive data from a device via the given connection. * This function will return after the given timeout even if no data has been * received. * * @param connection The connection to receive data from. * @param data Buffer that will be filled with the received data. * This buffer has to be large enough to hold len bytes. * @param len Buffer size or number of bytes to receive. * @param recv_bytes Number of bytes actually received. * @param timeout Timeout in milliseconds after which this function should * return even if no data has been received. * * @return IPHONE_E_SUCCESS if ok, otherwise an error code. */ iphone_error_t iphone_device_recv_timeout(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) { if (!connection) { return IPHONE_E_INVALID_ARG; } if (connection->type == CONNECTION_USBMUXD) { int res = usbmuxd_recv_timeout((int)(connection->data), data, len, recv_bytes, timeout); if (res < 0) { log_debug_msg("%s: ERROR: usbmuxd_recv_timeout returned %d (%s)\n", __func__, res, strerror(-res)); return IPHONE_E_UNKNOWN_ERROR; } return IPHONE_E_SUCCESS; } else { log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); } return IPHONE_E_UNKNOWN_ERROR; }
/** * Release the event callback function that has been registered with * iphone_event_subscribe(). * * @return IPHONE_E_SUCCESS on success or an error value when an error occured. */ iphone_error_t iphone_event_unsubscribe() { event_cb = NULL; int res = usbmuxd_unsubscribe(); if (res != 0) { log_debug_msg("%s: Error %d when unsubscribing usbmux event callback!\n", __func__, res); return IPHONE_E_UNKNOWN_ERROR; } return IPHONE_E_SUCCESS; }
/** * Register a callback function that will be called when device add/remove * events occur. * * @param callback Callback function to call. * @param user_data Application-specific data passed as parameter * to the registered callback function. * * @return IPHONE_E_SUCCESS on success or an error value when an error occured. */ iphone_error_t iphone_event_subscribe(iphone_event_cb_t callback, void *user_data) { event_cb = callback; int res = usbmuxd_subscribe(usbmux_event_cb, user_data); if (res != 0) { event_cb = NULL; log_debug_msg("%s: Error %d when subscribing usbmux event callback!\n", __func__, res); return IPHONE_E_UNKNOWN_ERROR; } return IPHONE_E_SUCCESS; }
/** * Retrieves a list of connected devices from usbmuxd and matches their * UUID with the given UUID. If the given UUID is NULL then the first * device reported by usbmuxd is used. * * @param device Upon calling this function, a pointer to a location of type * iphone_device_t, which must have the value NULL. On return, this location * will be filled with a handle to the device. * @param uuid The UUID to match. * * @return IPHONE_E_SUCCESS if ok, otherwise an error code. */ iphone_error_t iphone_get_device_by_uuid(iphone_device_t * device, const char *uuid) { iphone_device_t phone; uint32_t handle = 0; char *serial_number = malloc(41); usbmuxd_scan_result *dev_list = NULL; int i; if (usbmuxd_scan(&dev_list) < 0) { log_debug_msg("%s: usbmuxd_scan returned an error, is usbmuxd running?\n", __func__); } if (dev_list && dev_list[0].handle > 0) { if (!uuid) { // select first device found if no UUID specified handle = dev_list[0].handle; strcpy(serial_number, dev_list[0].serial_number); } else { // otherwise walk through the list for (i = 0; dev_list[i].handle > 0; i++) { log_debug_msg("%s: device handle=%d, uuid=%s\n", __func__, dev_list[i].handle, dev_list[i].serial_number); if (strcasecmp(uuid, dev_list[i].serial_number) == 0) { handle = dev_list[i].handle; strcpy(serial_number, dev_list[i].serial_number); break; } } } free(dev_list); if (handle > 0) { phone = (iphone_device_t) malloc(sizeof(struct iphone_device_int)); phone->handle = handle; phone->serial_number = serial_number; *device = phone; return IPHONE_E_SUCCESS; } } return IPHONE_E_NO_DEVICE; }
iphone_error_t iphone_device_get_handle(iphone_device_t device, uint32_t *handle) { if (!device) return IPHONE_E_INVALID_ARG; if (device->conn_type == CONNECTION_USBMUXD) { *handle = (uint32_t)device->conn_data; return IPHONE_E_SUCCESS; } else { log_debug_msg("%s: Unknown connection type %d\n", __func__, device->conn_type); } return IPHONE_E_UNKNOWN_ERROR; }
/** Locks or unlocks a file on the phone. * * makes use of flock on the device, see * http://developer.apple.com/documentation/Darwin/Reference/ManPages/man2/flock.2.html * * @param client The client to lock the file with. * @param handle File handle of a previously opened file. * @param operation the lock or unlock operation to perform, this is one of * AFC_LOCK_SH (shared lock), AFC_LOCK_EX (exclusive lock), * or AFC_LOCK_UN (unlock). */ afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) { char *buffer = malloc(16); int bytes = 0; uint64_t op = operation; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || (handle == 0)) return AFC_E_INVALID_ARGUMENT; afc_lock(client); log_debug_msg("%s: file handle %i\n", __func__, handle); // Send command memcpy(buffer, &handle, sizeof(uint64_t)); memcpy(buffer + 8, &op, 8); client->afc_packet->operation = AFC_OP_FILE_LOCK; client->afc_packet->entire_length = client->afc_packet->this_length = 0; bytes = afc_dispatch_packet(client, buffer, 16); free(buffer); buffer = NULL; if (bytes <= 0) { afc_unlock(client); log_debug_msg("%s: could not send lock command\n", __func__); return AFC_E_UNKNOWN_ERROR; } // Receive the response ret = afc_receive_data(client, &buffer, &bytes); if (buffer) { log_debug_buffer(buffer, bytes); free(buffer); } afc_unlock(client); return ret; }
/** * Sends an xml plist to the device using the connection specified in client. * This function is only used internally. * * @param client NP to send data to * @param dict plist to send * * @return NP_E_SUCCESS or an error code. */ static np_error_t np_plist_send(np_client_t client, plist_t dict) { char *XML_content = NULL; uint32_t length = 0; uint32_t nlen = 0; int bytes = 0; np_error_t res = NP_E_UNKNOWN_ERROR; if (!client || !dict) { return NP_E_INVALID_ARG; } plist_to_xml(dict, &XML_content, &length); if (!XML_content || length == 0) { return NP_E_PLIST_ERROR; } nlen = htonl(length); iphone_device_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes); if (bytes == sizeof(nlen)) { iphone_device_send(client->connection, XML_content, length, (uint32_t*)&bytes); if (bytes > 0) { if ((uint32_t)bytes == length) { res = NP_E_SUCCESS; } else { log_debug_msg("%s: ERROR: Could not send all data (%d of %d)!\n", __func__, bytes, length); } } } if (bytes <= 0) { log_debug_msg("%s: ERROR: sending to device failed.\n", __func__); } free(XML_content); return res; }
/** Creates a hard link or symbolic link on the device. * * @param client The client to use for making a link * @param type 1 = hard link, 2 = symlink * @param target The file to be linked. * @param linkname The name of link. * * @return AFC_E_SUCCESS if everything went well, AFC_E_INVALID_ARGUMENT * if arguments are NULL or invalid, AFC_E_NOT_ENOUGH_DATA otherwise. */ afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) { char *response = NULL; char *send = (char *) malloc(sizeof(char) * (strlen(target)+1 + strlen(linkname)+1 + 8)); int bytes = 0; uint64_t type = linktype; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || !target || !linkname || !client->afc_packet || !client->connection) return AFC_E_INVALID_ARGUMENT; afc_lock(client); log_debug_msg("%s: link type: %lld\n", __func__, type); log_debug_msg("%s: target: %s, length:%d\n", __func__, target, strlen(target)); log_debug_msg("%s: linkname: %s, length:%d\n", __func__, linkname, strlen(linkname)); // Send command memcpy(send, &type, 8); memcpy(send + 8, target, strlen(target) + 1); memcpy(send + 8 + strlen(target) + 1, linkname, strlen(linkname) + 1); client->afc_packet->entire_length = client->afc_packet->this_length = 0; client->afc_packet->operation = AFC_OP_MAKE_LINK; bytes = afc_dispatch_packet(client, send, 8 + strlen(linkname) + 1 + strlen(target) + 1); free(send); if (bytes <= 0) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } // Receive response ret = afc_receive_data(client, &response, &bytes); if (response) free(response); afc_unlock(client); return ret; }
/** * Set up a connection to the given device. * * @param device The device to connect to. * @param dst_port The destination port to connect to. * @param connection Pointer to an iphone_connection_t that will be filled * with the necessary data of the connection. * * @return IPHONE_E_SUCCESS if ok, otherwise an error code. */ iphone_error_t iphone_device_connect(iphone_device_t device, uint16_t dst_port, iphone_connection_t *connection) { if (!device) { return IPHONE_E_INVALID_ARG; } if (device->conn_type == CONNECTION_USBMUXD) { int sfd = usbmuxd_connect((uint32_t)(device->conn_data), dst_port); if (sfd < 0) { log_debug_msg("%s: ERROR: Connecting to usbmuxd failed: %d (%s)\n", __func__, sfd, strerror(-sfd)); return IPHONE_E_UNKNOWN_ERROR; } iphone_connection_t new_connection = (iphone_connection_t)malloc(sizeof(struct iphone_connection_int)); new_connection->type = CONNECTION_USBMUXD; new_connection->data = (void*)sfd; *connection = new_connection; return IPHONE_E_SUCCESS; } else { log_debug_msg("%s: Unknown connection type %d\n", __func__, device->conn_type); } return IPHONE_E_UNKNOWN_ERROR; }
/** * Disconnect from the device and clean up the connection structure. * * @param connection The connection to close. * * @return IPHONE_E_SUCCESS if ok, otherwise an error code. */ iphone_error_t iphone_device_disconnect(iphone_connection_t connection) { if (!connection) { return IPHONE_E_INVALID_ARG; } iphone_error_t result = IPHONE_E_UNKNOWN_ERROR; if (connection->type == CONNECTION_USBMUXD) { usbmuxd_disconnect((int)(connection->data)); result = IPHONE_E_SUCCESS; } else { log_debug_msg("%s: Unknown connection type %d\n", __func__, connection->type); } free(connection); return result; }
/** Disconnects an NP client from the phone. * * @param client The client to disconnect. */ np_error_t np_client_free(np_client_t client) { if (!client) return NP_E_INVALID_ARG; iphone_device_disconnect(client->connection); client->connection = NULL; if (client->notifier) { log_debug_msg("joining np callback\n"); g_thread_join(client->notifier); } if (client->mutex) { g_mutex_free(client->mutex); } free(client); return NP_E_SUCCESS; }
/** * Internally used thread function. */ gpointer np_notifier( gpointer arg ) { char *notification = NULL; struct np_thread *npt = (struct np_thread*)arg; if (!npt) return NULL; log_debug_msg("%s: starting callback.\n", __func__); while (npt->client->connection) { np_get_notification(npt->client, ¬ification); if (notification) { npt->cbfunc(notification); free(notification); notification = NULL; } sleep(1); } if (npt) { free(npt); } return NULL; }
/** Unlocks an NP client, done for thread safety stuff. * * @param client The NP */ static void np_unlock(np_client_t client) { log_debug_msg("NP: Unlocked\n"); g_mutex_unlock(client->mutex); }
/** Writes a given number of bytes to a file. * * @param client The client to use to write to the file. * @param handle File handle of previously opened file. * @param data The data to write to the file. * @param length How much data to write. * * @return The number of bytes written to the file, or a value less than 0 if * none were written... */ iphone_error_t afc_file_write(afc_client_t client, uint64_t handle, const char *data, int length, uint32_t * bytes) { char *acknowledgement = NULL; const int MAXIMUM_WRITE_SIZE = 1 << 15; uint32_t zero = 0, current_count = 0, i = 0; uint32_t segments = (length / MAXIMUM_WRITE_SIZE); int bytes_loc = 0; char *out_buffer = NULL; afc_error_t ret = AFC_E_SUCCESS; if (!client || !client->afc_packet || !client->connection || !bytes || (handle == 0)) return AFC_E_INVALID_ARGUMENT; afc_lock(client); log_debug_msg("%s: Write length: %i\n", __func__, length); // Divide the file into segments. for (i = 0; i < segments; i++) { // Send the segment client->afc_packet->this_length = sizeof(AFCPacket) + 8; client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE; client->afc_packet->operation = AFC_OP_WRITE; out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); memcpy(out_buffer, (char *)&handle, sizeof(uint64_t)); memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE); bytes_loc = afc_dispatch_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8); if (bytes_loc < 0) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } free(out_buffer); out_buffer = NULL; current_count += bytes_loc; ret = afc_receive_data(client, &acknowledgement, &bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } else { free(acknowledgement); } } // By this point, we should be at the end. i.e. the last segment that // didn't get sent in the for loop // this length is fine because it's always sizeof(AFCPacket) + 8, but // to be sure we do it again if (current_count == (uint32_t)length) { afc_unlock(client); *bytes = current_count; return ret; } client->afc_packet->this_length = sizeof(AFCPacket) + 8; client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count); client->afc_packet->operation = AFC_OP_WRITE; out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); memcpy(out_buffer, (char *) &handle, sizeof(uint64_t)); memcpy(out_buffer + 8, data + current_count, (length - current_count)); bytes_loc = afc_dispatch_packet(client, out_buffer, (length - current_count) + 8); free(out_buffer); out_buffer = NULL; current_count += bytes_loc; if (bytes_loc <= 0) { afc_unlock(client); *bytes = current_count; return AFC_E_SUCCESS; } zero = bytes_loc; ret = afc_receive_data(client, &acknowledgement, &bytes_loc); afc_unlock(client); if (ret != AFC_E_SUCCESS) { log_debug_msg("%s: uh oh?\n", __func__); } else { free(acknowledgement); } *bytes = current_count; return ret; }
/** Attempts to the read the given number of bytes from the given file. * * @param client The relevant AFC client * @param handle File handle of a previously opened file * @param data The pointer to the memory region to store the read data * @param length The number of bytes to read * * @return The number of bytes read if successful. If there was an error -1. */ iphone_error_t afc_file_read(afc_client_t client, uint64_t handle, char *data, int length, uint32_t * bytes) { char *input = NULL; int current_count = 0, bytes_loc = 0; const int MAXIMUM_READ_SIZE = 1 << 16; afc_error_t ret = AFC_E_SUCCESS; if (!client || !client->afc_packet || !client->connection || handle == 0) return AFC_E_INVALID_ARGUMENT; log_debug_msg("%s: called for length %i\n", __func__, length); afc_lock(client); // Looping here to get around the maximum amount of data that // afc_receive_data can handle while (current_count < length) { log_debug_msg("%s: current count is %i but length is %i\n", __func__, current_count, length); // Send the read command AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); packet->filehandle = handle; packet->size = ((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE; client->afc_packet->operation = AFC_OP_READ; client->afc_packet->entire_length = client->afc_packet->this_length = 0; bytes_loc = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket)); free(packet); if (bytes_loc <= 0) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } // Receive the data ret = afc_receive_data(client, &input, &bytes_loc); log_debug_msg("%s: afc_receive_data returned error: %d\n", __func__, ret); log_debug_msg("%s: bytes returned: %i\n", __func__, bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } else if (bytes_loc == 0) { if (input) free(input); afc_unlock(client); *bytes = current_count; /* FIXME: check that's actually a success */ return ret; } else { if (input) { log_debug_msg("%s: %d\n", __func__, bytes_loc); memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); free(input); input = NULL; current_count += (bytes_loc > length) ? length : bytes_loc; } } } log_debug_msg("%s: returning current_count as %i\n", __func__, current_count); afc_unlock(client); *bytes = current_count; return ret; }
/** * Checks if a notification has been sent. * * @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 an error 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) { uint32_t bytes = 0; int res = 0; uint32_t pktlen = 0; char *XML_content = NULL; plist_t dict = NULL; if (!client || !client->connection || *notification) return -1; np_lock(client); iphone_device_recv_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, 500); log_debug_msg("NotificationProxy: initial read=%i\n", bytes); if (bytes < 4) { log_debug_msg("NotificationProxy: no notification received!\n"); res = 0; } else { if ((char)pktlen == 0) { pktlen = ntohl(pktlen); log_debug_msg("NotificationProxy: %d bytes following\n", pktlen); XML_content = (char*)malloc(pktlen); log_debug_msg("pointer %p\n", XML_content); iphone_device_recv_timeout(client->connection, XML_content, pktlen, &bytes, 1000); if (bytes <= 0) { res = -1; } else { log_debug_msg("NotificationProxy: received data:\n"); log_debug_buffer(XML_content, pktlen); plist_from_xml(XML_content, bytes, &dict); if (!dict) { np_unlock(client); return -2; } plist_t cmd_key_node = plist_find_node_by_key(dict, "Command"); plist_t cmd_value_node = plist_get_next_sibling(cmd_key_node); char *cmd_value = NULL; 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")) { plist_t name_key_node = plist_get_next_sibling(cmd_value_node); plist_t name_value_node = plist_get_next_sibling(name_key_node); char *name_key = NULL; char *name_value = NULL; if (plist_get_node_type(name_key_node) == PLIST_KEY) { plist_get_key_val(name_key_node, &name_key); } if (plist_get_node_type(name_value_node) == PLIST_STRING) { plist_get_string_val(name_value_node, &name_value); } res = -2; if (name_key && name_value && !strcmp(name_key, "Name")) { *notification = name_value; log_debug_msg("%s: got notification %s\n", __func__, name_value); res = 0; } free(name_key); } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { log_debug_msg("%s: ERROR: NotificationProxy died!\n", __func__); res = -1; } else if (cmd_value) { log_debug_msg("%d: unknown NotificationProxy command '%s' received!\n", __func__); res = -1; } else { res = -2; } if (cmd_value) { free(cmd_value); } plist_free(dict); dict = NULL; free(XML_content); XML_content = NULL; } } else { res = -1; } } np_unlock(client); return res; }
/** Unlocks an AFC client, done for thread safety stuff. * * @param client The AFC */ static void afc_unlock(afc_client_t client) { log_debug_msg("%s: Unlocked\n", __func__); g_mutex_unlock(client->mutex); }
/** Receives data through an AFC client and sets a variable to the received data. * * @param client The client to receive data on. * @param dump_here The char* to point to the newly-received data. * * @return How much data was received, 0 on successful receive with no errors, * -1 if there was an error involved with receiving or if the packet * received raised a non-trivial error condition (i.e. non-zero with * AFC_ERROR operation) */ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, int *bytes) { AFCPacket header; uint32_t entire_len = 0; uint32_t this_len = 0; uint32_t current_count = 0; uint64_t param1 = -1; *bytes = 0; /* first, read the AFC header */ iphone_device_recv(client->connection, (char*)&header, sizeof(AFCPacket), (uint32_t*)bytes); if (*bytes <= 0) { log_debug_msg("%s: Just didn't get enough.\n", __func__); *dump_here = NULL; return AFC_E_MUX_ERROR; } else if ((uint32_t)*bytes < sizeof(AFCPacket)) { log_debug_msg("%s: Did not even get the AFCPacket header\n", __func__); *dump_here = NULL; return AFC_E_MUX_ERROR; } /* check if it's a valid AFC header */ if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN)) { log_debug_msg("%s: Invalid AFC packet received (magic != " AFC_MAGIC ")!\n", __func__); } /* check if it has the correct packet number */ if (header.packet_num != client->afc_packet->packet_num) { /* otherwise print a warning but do not abort */ log_debug_msg("%s: ERROR: Unexpected packet number (%lld != %lld) aborting.\n", __func__, header.packet_num, client->afc_packet->packet_num); *dump_here = NULL; return AFC_E_OP_HEADER_INVALID; } /* then, read the attached packet */ if (header.this_length < sizeof(AFCPacket)) { log_debug_msg("%s: Invalid AFCPacket header received!\n", __func__); *dump_here = NULL; return AFC_E_OP_HEADER_INVALID; } else if ((header.this_length == header.entire_length) && header.entire_length == sizeof(AFCPacket)) { log_debug_msg("%s: Empty AFCPacket received!\n", __func__); *dump_here = NULL; *bytes = 0; if (header.operation == AFC_OP_DATA) { return AFC_E_SUCCESS; } else { return AFC_E_IO_ERROR; } } log_debug_msg("%s: received AFC packet, full len=%lld, this len=%lld, operation=0x%llx\n", __func__, header.entire_length, header.this_length, header.operation); entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); this_len = (uint32_t)header.this_length - sizeof(AFCPacket); /* this is here as a check (perhaps a different upper limit is good?) */ if (entire_len > (uint32_t)MAXIMUM_PACKET_SIZE) { fprintf(stderr, "%s: entire_len is larger than MAXIMUM_PACKET_SIZE, (%d > %d)!\n", __func__, entire_len, MAXIMUM_PACKET_SIZE); } *dump_here = (char*)malloc(entire_len); if (this_len > 0) { iphone_device_recv(client->connection, *dump_here, this_len, (uint32_t*)bytes); if (*bytes <= 0) { free(*dump_here); *dump_here = NULL; log_debug_msg("%s: Did not get packet contents!\n", __func__); return AFC_E_NOT_ENOUGH_DATA; } else if ((uint32_t)*bytes < this_len) { free(*dump_here); *dump_here = NULL; log_debug_msg("%s: Could not receive this_len=%d bytes\n", __func__, this_len); return AFC_E_NOT_ENOUGH_DATA; } } current_count = this_len; if (entire_len > this_len) { while (current_count < entire_len) { iphone_device_recv(client->connection, (*dump_here)+current_count, entire_len - current_count, (uint32_t*)bytes); if (*bytes <= 0) { log_debug_msg("%s: Error receiving data (recv returned %d)\n", __func__, *bytes); break; } current_count += *bytes; } if (current_count < entire_len) { log_debug_msg("%s: WARNING: could not receive full packet (read %s, size %d)\n", __func__, current_count, entire_len); } } if (current_count >= sizeof(uint64_t)) { param1 = *(uint64_t*)(*dump_here); } log_debug_msg("%s: packet data size = %i\n", __func__, current_count); log_debug_msg("%s: packet data follows\n", __func__); log_debug_buffer(*dump_here, current_count); /* check operation types */ if (header.operation == AFC_OP_STATUS) { /* status response */ log_debug_msg("%s: got a status response, code=%lld\n", __func__, param1); if (param1 != AFC_E_SUCCESS) { /* error status */ /* free buffer */ free(*dump_here); *dump_here = NULL; return (afc_error_t)param1; } } else if (header.operation == AFC_OP_DATA) { /* data response */ log_debug_msg("%s: got a data response\n", __func__); } else if (header.operation == AFC_OP_FILE_OPEN_RES) { /* file handle response */ log_debug_msg("%s: got a file handle response, handle=%lld\n", __func__, param1); } else if (header.operation == AFC_OP_FILE_TELL_RES) { /* tell response */ log_debug_msg("%s: got a tell response, position=%lld\n", __func__, param1); } else { /* unknown operation code received */ free(*dump_here); *dump_here = NULL; *bytes = 0; log_debug_msg("%s: WARNING: Unknown operation code received 0x%llx param1=%lld\n", __func__, header.operation, param1); fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld\n", __func__, (long long)header.operation, (long long)param1); return AFC_E_OP_NOT_SUPPORTED; } *bytes = current_count; return AFC_E_SUCCESS; }
/** Dispatches an AFC packet over a client. * * @param client The client to send data through. * @param data The data to send. * @param length The length to send. * * @return The number of bytes actually sent, or -1 on error. * * @warning set client->afc_packet->this_length and * client->afc_packet->entire_length to 0 before calling this. The * reason is that if you set them to different values, it indicates * you want to send the data as two packets. */ static int afc_dispatch_packet(afc_client_t client, const char *data, uint64_t length) { int bytes = 0, offset = 0; char *buffer; if (!client || !client->connection || !client->afc_packet) return 0; if (!data || !length) length = 0; client->afc_packet->packet_num++; if (!client->afc_packet->entire_length) { client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length : sizeof(AFCPacket); client->afc_packet->this_length = client->afc_packet->entire_length; } if (!client->afc_packet->this_length) { client->afc_packet->this_length = sizeof(AFCPacket); } // We want to send two segments; buffer+sizeof(AFCPacket) to // this_length is the parameters // And everything beyond that is the next packet. (for writing) if (client->afc_packet->this_length != client->afc_packet->entire_length) { buffer = (char *) malloc(client->afc_packet->this_length); memcpy(buffer, (char *) client->afc_packet, sizeof(AFCPacket)); offset = client->afc_packet->this_length - sizeof(AFCPacket); log_debug_msg("%s: Offset: %i\n", __func__, offset); if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) { log_debug_msg("%s: Length did not resemble what it was supposed", __func__); log_debug_msg("to based on the packet.\n"); log_debug_msg("%s: length minus offset: %i\n", __func__, length - offset); log_debug_msg("%s: rest of packet: %i\n", __func__, client->afc_packet->entire_length - client->afc_packet->this_length); free(buffer); return -1; } memcpy(buffer + sizeof(AFCPacket), data, offset); iphone_device_send(client->connection, buffer, client->afc_packet->this_length, (uint32_t*)&bytes); free(buffer); if (bytes <= 0) { return bytes; } log_debug_msg("%s: sent the first now go with the second\n", __func__); log_debug_msg("%s: Length: %i\n", __func__, length - offset); log_debug_msg("%s: Buffer: \n", __func__); log_debug_buffer(data + offset, length - offset); iphone_device_send(client->connection, data + offset, length - offset, (uint32_t*)&bytes); return bytes; } else { log_debug_msg("%s: doin things the old way\n", __func__); buffer = (char *) malloc(sizeof(char) * client->afc_packet->this_length); log_debug_msg("%s: packet length = %i\n", __func__, client->afc_packet->this_length); memcpy(buffer, (char *) client->afc_packet, sizeof(AFCPacket)); log_debug_msg("%s: packet data follows\n", __func__); if (length > 0) { memcpy(buffer + sizeof(AFCPacket), data, length); } log_debug_buffer(buffer, client->afc_packet->this_length); log_debug_msg("\n"); iphone_device_send(client->connection, buffer, client->afc_packet->this_length, (uint32_t*)&bytes); if (buffer) { free(buffer); buffer = NULL; } return bytes; } return -1; }