/**
 * Uninstall an application from the device.
 *
 * @param client The connected installation proxy client
 * @param appid ApplicationIdentifier of the app to uninstall
 * @param client_options The client options to use, as PLIST_DICT, or NULL.
 *        Currently there are no known client options, so pass NULL here.
 * @param status_cb Callback function for progress and status information. If
 *        NULL is passed, this function will run synchronously.
 * @param user_data Callback data passed to status_cb.
 *
 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
 *     an error occured.
 *
 * @note If a callback function is given (async mode), this function returns
 *     INSTPROXY_E_SUCCESS immediately if the status updater thread has been
 *     created successfully; any error occuring during the operation has to be
 *     handled inside the specified callback function.
 */
instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
{
	if (!client || !client->parent || !appid) {
		return INSTPROXY_E_INVALID_ARG;
	}

	if (client->status_updater) {
		return INSTPROXY_E_OP_IN_PROGRESS;
	}

	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
	plist_t dict = plist_new_dict();
	plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
	plist_dict_insert_item(dict, "Command", plist_new_string("Uninstall"));

	instproxy_lock(client);
	res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL);
	instproxy_unlock(client);

	plist_free(dict);

	if (res != INSTPROXY_E_SUCCESS) {
		debug_info("could not send plist, error %d", res);
		return res;
	}

	return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data);
}
/**
 * Removes a previously archived application from the device.
 * This function removes the ZIP archive from the 'ApplicationArchives'
 * directory.
 *
 * @param client The connected installation proxy client
 * @param appid ApplicationIdentifier of the archived app to remove.
 * @param client_options The client options to use, as PLIST_DICT, or NULL.
 *        Currently there are no known client options, so passing NULL is fine.
 * @param status_cb Callback function for progress and status information. If
 *        NULL is passed, this function will run synchronously.
 * @param user_data Callback data passed to status_cb.
 *
 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
 *     an error occured.
 *
 * @note If a callback function is given (async mode), this function returns
 *     INSTPROXY_E_SUCCESS immediately if the status updater thread has been
 *     created successfully; any error occuring during the operation has to be
 *     handled inside the specified callback function.
 */
instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
{
	if (!client || !client->parent || !appid)
		return INSTPROXY_E_INVALID_ARG;

	if (client->status_updater) {
		return INSTPROXY_E_OP_IN_PROGRESS;
	}

	instproxy_lock(client);
	instproxy_error_t res = instproxy_send_command(client, "RemoveArchive", client_options, appid, NULL);
	instproxy_unlock(client);

	if (res != INSTPROXY_E_SUCCESS) {
		debug_info("could not send plist, error %d", res);
		return res;
	}
	return instproxy_create_status_updater(client, status_cb, "RemoveArchive", user_data);
}
/**
 * Internal function used by instproxy_install and instproxy_upgrade.
 *
 * @param client The connected installation_proxy client
 * @param pkg_path Path of the installation package (inside the AFC jail)
 * @param client_options The client options to use, as PLIST_DICT, or NULL.
 * @param status_cb Callback function for progress and status information. If
 *        NULL is passed, this function will run synchronously.
 * @param command The command to execute.
 * @param user_data Callback data passed to status_cb.
 *
 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
 *     an error occured. 
 */
static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, const char *command, void *user_data)
{
	if (!client || !client->parent || !pkg_path) {
		return INSTPROXY_E_INVALID_ARG;
	}
	if (client->status_updater) {
		return INSTPROXY_E_OP_IN_PROGRESS;
	}

	instproxy_lock(client);
	instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path);
	instproxy_unlock(client);

	if (res != INSTPROXY_E_SUCCESS) {
		debug_info("could not send plist, error %d", res);
		return res;
	}

	return instproxy_create_status_updater(client, status_cb, command, user_data);
}
LIBIMOBILEDEVICE_API instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
{
	if (!client || !client->parent || !appid) {
		return INSTPROXY_E_INVALID_ARG;
	}

	if (client->status_updater) {
		return INSTPROXY_E_OP_IN_PROGRESS;
	}

	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;

	instproxy_lock(client);
	res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL);
	instproxy_unlock(client);

	if (res != INSTPROXY_E_SUCCESS) {
		debug_info("could not send plist, error %d", res);
		return res;
	}

	return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data);
}