diff options
| -rw-r--r-- | include/Makefile.am | 2 | ||||
| -rw-r--r-- | include/libiphone/installation_proxy.h | 76 | ||||
| -rw-r--r-- | include/libiphone/libiphone.h | 1 | ||||
| -rw-r--r-- | include/libiphone/notification_proxy.h | 1 | ||||
| -rw-r--r-- | include/libiphone/sbservices.h | 56 | ||||
| -rw-r--r-- | src/InstallationProxy.c | 824 | ||||
| -rw-r--r-- | src/InstallationProxy.h | 34 | ||||
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/NotificationProxy.c | 39 | ||||
| -rw-r--r-- | src/SBServices.c | 291 | ||||
| -rw-r--r-- | src/SBServices.h | 33 | 
11 files changed, 1349 insertions, 10 deletions
| diff --git a/include/Makefile.am b/include/Makefile.am index df7b823..54dae0e 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,4 +2,6 @@ nobase_include_HEADERS = libiphone/libiphone.h \  			 libiphone/lockdown.h \  			 libiphone/afc.h \  			 libiphone/notification_proxy.h \ +			 libiphone/installation_proxy.h \ +			 libiphone/sbservices.h \  			 libiphone/mobilesync.h diff --git a/include/libiphone/installation_proxy.h b/include/libiphone/installation_proxy.h new file mode 100644 index 0000000..f432e8a --- /dev/null +++ b/include/libiphone/installation_proxy.h @@ -0,0 +1,76 @@ +/** + * @file libiphone/installation_proxy.h + * @brief Implementation to talk to the installation proxy on a device + * \internal + * + * Copyright (c) 2009 Nikias Bassen All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#ifndef INSTALLATION_PROXY_H +#define INSTALLATION_PROXY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libiphone/libiphone.h> + +/* Error Codes */ +#define INSTPROXY_E_SUCCESS                0 +#define INSTPROXY_E_INVALID_ARG           -1 +#define INSTPROXY_E_PLIST_ERROR           -2 +#define INSTPROXY_E_CONN_FAILED           -3 +#define INSTPROXY_E_OP_IN_PROGRESS        -4 +#define INSTPROXY_E_OP_FAILED             -5 + +#define INSTPROXY_E_UNKNOWN_ERROR       -256 + +typedef int16_t instproxy_error_t; + +typedef enum { +    INSTPROXY_APPTYPE_ALL = 0, +    INSTPROXY_APPTYPE_SYSTEM = 1, +    INSTPROXY_APPTYPE_USER = 2 +} instproxy_apptype_t; + +#define INSTPROXY_ARCHIVE_APP_ONLY (1 << 0) +#define INSTPROXY_ARCHIVE_SKIP_UNINSTALL (1 << 1) + +struct instproxy_client_int; +typedef struct instproxy_client_int *instproxy_client_t; + +typedef void (*instproxy_status_cb_t) (const char *operation, plist_t status); + +/* Interface */ +instproxy_error_t instproxy_client_new(iphone_device_t device, int dst_port, instproxy_client_t *client); +instproxy_error_t instproxy_client_free(instproxy_client_t client); + +instproxy_error_t instproxy_browse(instproxy_client_t client, instproxy_apptype_t apptype, plist_t *result); +instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb); +instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb); +instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb); + +instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t *result); +instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, uint32_t options, instproxy_status_cb_t status_cb); +instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb); +instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libiphone/libiphone.h b/include/libiphone/libiphone.h index 6b95edc..923bc58 100644 --- a/include/libiphone/libiphone.h +++ b/include/libiphone/libiphone.h @@ -53,6 +53,7 @@ typedef struct iphone_connection_int *iphone_connection_t;  #define DBGMASK_NONE       0x0000  #define DBGMASK_LOCKDOWND  (1 << 1)  #define DBGMASK_MOBILESYNC (1 << 2) +#define DBGMASK_INSTPROXY  (1 << 3)  /* generic */  void iphone_set_debug_mask(uint16_t mask); diff --git a/include/libiphone/notification_proxy.h b/include/libiphone/notification_proxy.h index 66b7a80..96af5fd 100644 --- a/include/libiphone/notification_proxy.h +++ b/include/libiphone/notification_proxy.h @@ -33,6 +33,7 @@ extern "C" {  #define NP_E_SUCCESS                0  #define NP_E_INVALID_ARG           -1  #define NP_E_PLIST_ERROR           -2 +#define NP_E_CONN_FAILED           -3  #define NP_E_UNKNOWN_ERROR       -256 diff --git a/include/libiphone/sbservices.h b/include/libiphone/sbservices.h new file mode 100644 index 0000000..6ea8189 --- /dev/null +++ b/include/libiphone/sbservices.h @@ -0,0 +1,56 @@ +/** + * @file libiphone/sbservices.h + * @brief Implementation to talk to com.apple.springboardservices on a device + * \internal + * + * Copyright (c) 2009 Nikias Bassen All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#ifndef SB_SERVICES_H +#define SB_SERVICES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libiphone/libiphone.h> + +/* Error Codes */ +#define SBSERVICES_E_SUCCESS                0 +#define SBSERVICES_E_INVALID_ARG           -1 +#define SBSERVICES_E_PLIST_ERROR           -2 +#define SBSERVICES_E_CONN_FAILED           -3 + +#define SBSERVICES_E_UNKNOWN_ERROR       -256 + +typedef int16_t sbservices_error_t; + +struct sbservices_client_int; +typedef struct sbservices_client_int *sbservices_client_t; + +/* Interface */ +sbservices_error_t sbservices_client_new(iphone_device_t device, int dst_port, sbservices_client_t *client); +sbservices_error_t sbservices_client_free(sbservices_client_t client); +sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state); +sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate); +sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/InstallationProxy.c b/src/InstallationProxy.c new file mode 100644 index 0000000..917886d --- /dev/null +++ b/src/InstallationProxy.c @@ -0,0 +1,824 @@ +/* + * InstallationProxy.c + * Installation Proxy implementation. + * + * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  + */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <plist/plist.h> + +#include "InstallationProxy.h" +#include "iphone.h" +#include "utils.h" + +struct instproxy_status_data { +	instproxy_client_t client; +	instproxy_status_cb_t cbfunc; +	char *operation; +}; + +/** Locks an installation_proxy client, done for thread safety stuff. + * + * @param client The installation_proxy client to lock + */ +static void instproxy_lock(instproxy_client_t client) +{ +	log_debug_msg("InstallationProxy: Locked\n"); +	g_mutex_lock(client->mutex); +} + +/** Unlocks an installation_proxy client, done for thread safety stuff. + *  + * @param client The installation_proxy client to lock + */ +static void instproxy_unlock(instproxy_client_t client) +{ +	log_debug_msg("InstallationProxy: Unlocked\n"); +	g_mutex_unlock(client->mutex); +} + +/** + * Sends an xml plist to the device using the connection specified in client. + * This function is only used internally. + * + * @param client The installation_proxy to send data to + * @param plist plist to send + * + * @return INSTPROXY_E_SUCCESS on success, INSTPROXY_E_INVALID_ARG when client + *      or plist are NULL, INSTPROXY_E_PLIST_ERROR when dict is not a valid + *      plist, or INSTPROXY_E_UNKNOWN_ERROR when an unspecified error occurs. + */ +static instproxy_error_t instproxy_plist_send(instproxy_client_t client, plist_t plist) +{ +	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; +	char *XML_content = NULL; +	uint32_t length = 0; +	uint32_t nlen = 0; +	int bytes = 0; + +	if (!client || !plist) { +		return INSTPROXY_E_INVALID_ARG; +	} + +	plist_to_xml(plist, &XML_content, &length); + +	if (!XML_content || length == 0) { +		return INSTPROXY_E_PLIST_ERROR; +	} + +	nlen = htonl(length); +	log_debug_msg("%s: sending %d bytes\n", __func__, 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) { +			log_debug_msg("%s: received %d bytes\n", __func__, bytes); +			log_debug_buffer(XML_content, bytes); +			if ((uint32_t)bytes == length) { +				res = INSTPROXY_E_SUCCESS; +			} else { +				log_debug_msg("%s: ERROR: Could not send all data (%d of %d)!\n", __func__, bytes, length); +			} +		} +	} +	if (bytes <= 0) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: ERROR: sending to device failed.\n", __func__); +	} + +	free(XML_content); + +	return res; +} + +/** + * Receives an xml plist from the device using the connection specified in + *  client. + * This function is only used internally. + * + * @param client The installation_proxy to receive data from + * @param plist pointer to a plist_t that will point to the received plist + *              upon successful return + * + * @return INSTPROXY_E_SUCCESS on success, INSTPROXY_E_INVALID_ARG when client + *      or *plist are NULL, INSTPROXY_E_PLIST_ERROR when dict is not a valid + *      plist, or INSTPROXY_E_UNKNOWN_ERROR when an unspecified error occurs. + */ +static instproxy_error_t instproxy_plist_recv(instproxy_client_t client, plist_t *plist) +{ +	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; +	char *XML_content = NULL; +	uint32_t pktlen = 0; +	uint32_t bytes = 0; + +	if (!client || !plist) { +		return INSTPROXY_E_INVALID_ARG; +	} + +	iphone_device_recv_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, 300000); /* 5 minute timeout should be enough */ +	log_debug_msg("%s: initial read=%i\n", __func__, bytes); +	if (bytes < 4) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: initial read failed!\n"); +	} else { +		if ((char)pktlen == 0) { +			uint32_t curlen = 0; +			pktlen = ntohl(pktlen); +			log_debug_msg("%s: %d bytes following\n", __func__, pktlen); +			XML_content = (char*)malloc(pktlen); + +			while (curlen < pktlen) { +				iphone_device_recv(client->connection, XML_content+curlen, pktlen-curlen, &bytes); +				if (bytes <= 0) { +					res = INSTPROXY_E_UNKNOWN_ERROR; +					break; +				} +				log_debug_msg("%s: received %d bytes\n", __func__, bytes); +				curlen += bytes; +			} +			log_debug_buffer(XML_content, pktlen); +			plist_from_xml(XML_content, pktlen, plist); +			res = INSTPROXY_E_SUCCESS; +			free(XML_content); +			XML_content = NULL; +		} else { +			res = INSTPROXY_E_UNKNOWN_ERROR; +		} +	} +	return res; +} + +/** + * Creates a new installation_proxy client + * + * @param device The device to connect to + * @param dst_port Destination port (usually given by lockdownd_start_service). + * @param client Pointer that will be set to a newly allocated + *     instproxy_client_t upon successful return. + * + * @return INSTPROXY_E_SUCCESS on success, or an INSTPROXY_E_* error value + *     when an error occured. + */ +instproxy_error_t instproxy_client_new(iphone_device_t device, int dst_port, instproxy_client_t *client) +{ +	/* makes sure thread environment is available */ +	if (!g_thread_supported()) +		g_thread_init(NULL); + +	if (!device) +		return INSTPROXY_E_INVALID_ARG; + +	/* Attempt connection */ +	iphone_connection_t connection = NULL; +	if (iphone_device_connect(device, dst_port, &connection) != IPHONE_E_SUCCESS) { +		return INSTPROXY_E_CONN_FAILED; +	} + +	instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_int)); +	client_loc->connection = connection; +	client_loc->mutex = g_mutex_new(); +	client_loc->status_updater = NULL; + +	*client = client_loc; +	return INSTPROXY_E_SUCCESS; +} + +/** + * Frees an installation_proxy client. + * + * @param client The installation_proxy client to free. + * + * @return INSTPROXY_E_SUCCESS on success + *      or INSTPROXY_E_INVALID_ARG if client is NULL. + */ +instproxy_error_t instproxy_client_free(instproxy_client_t client) +{ +	if (!client) +		return INSTPROXY_E_INVALID_ARG; + +	iphone_device_disconnect(client->connection); +	client->connection = NULL; +	if (client->status_updater) { +		log_dbg_msg(DBGMASK_INSTPROXY, "joining status_updater"); +		g_thread_join(client->status_updater); +	} +	if (client->mutex) { +		g_mutex_free(client->mutex); +	} +	free(client); + +	return INSTPROXY_E_SUCCESS; +} + +/** + * List installed applications. This function runs synchronously. + * + * @param client The connected installation_proxy client + * @param apptype The type of applications to list. + * @param result Pointer that will be set to a plist that will hold an array + *        of PLIST_DICT holding information about the applications found. + * + * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if + *     an error occured. + */ +instproxy_error_t instproxy_browse(instproxy_client_t client, instproxy_apptype_t apptype, plist_t *result) +{ +	if (!client || !client->connection || !result) +		return INSTPROXY_E_INVALID_ARG; + +	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; +	int browsing = 0; +	plist_t apps_array = NULL; + +	plist_t dict = plist_new_dict(); +	if (apptype != INSTPROXY_APPTYPE_ALL) { +		plist_t client_opts = plist_new_dict(); +		plist_t p_apptype = NULL; +		switch (apptype) { +			case INSTPROXY_APPTYPE_SYSTEM: +				p_apptype = plist_new_string("System"); +				break; +			case INSTPROXY_APPTYPE_USER: +				p_apptype = plist_new_string("User"); +				break; +			default: +				log_dbg_msg(DBGMASK_INSTPROXY, "%s: unknown apptype %d given, using INSTPROXY_APPTYPE_USER instead\n", __func__, apptype); +				p_apptype = plist_new_string("User"); +				break; +		} +		plist_dict_insert_item(client_opts, "ApplicationType", p_apptype); +		plist_dict_insert_item(dict, "ClientOptions", client_opts); +	} +	plist_dict_insert_item(dict, "Command", plist_new_string("Browse")); + +	instproxy_lock(client); +	res = instproxy_plist_send(client, dict); +	plist_free(dict); +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist\n", __func__); +		goto leave_unlock; +	} + +	apps_array = plist_new_array(); + +	do { +		browsing = 0; +		dict = NULL; +		res = instproxy_plist_recv(client, &dict); +		if (res != INSTPROXY_E_SUCCESS) { +			break; +		} +		if (dict) { +			uint64_t i; +			uint64_t current_amount = 0; +			char *status = NULL; +			plist_t camount = plist_dict_get_item(dict, "CurrentAmount"); +			plist_t pstatus = plist_dict_get_item(dict, "Status"); +			if (camount) { +				plist_get_uint_val(camount, ¤t_amount); +			} +			if (current_amount > 0) { +				plist_t current_list = plist_dict_get_item(dict, "CurrentList"); +				for (i = 0; current_list && (i < current_amount); i++) { +					plist_t item = plist_array_get_item(current_list, i); +					plist_array_append_item(apps_array, plist_copy(item)); +				} +			} +			if (pstatus) { +				plist_get_string_val(pstatus, &status); +			} +			if (status) { +				if (!strcmp(status, "BrowsingApplications")) { +					browsing = 1; +				} else if (!strcmp(status, "Complete")) { +					log_dbg_msg(DBGMASK_INSTPROXY, "%s: Browsing applications completed\n"); +					res = INSTPROXY_E_SUCCESS; +				} +				free(status); +			} +			plist_free(dict); +		} +	} while (browsing); + +	if (res == INSTPROXY_E_SUCCESS) { +		*result = apps_array; +	} + +leave_unlock: +	instproxy_unlock(client); +	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.  + */ +static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation) +{ +	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; +	int ok = 1; +	plist_t dict = NULL; + +	do { +		instproxy_lock(client); +		res = instproxy_plist_recv(client, &dict); +		instproxy_unlock(client); +		if (res != INSTPROXY_E_SUCCESS) { +			log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not receive plist, error %d\n", __func__, res); +			break; +		} +		if (dict) { +			/* invoke callback function */ +			if (status_cb) { +				status_cb(operation, dict); +			} +			/* 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) { +					log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): ERROR: %s\n", __func__, 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; +						log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): %s (%d%%)\n", __func__, operation, status_msg, percent); +					} else { +						log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): %s\n", __func__, operation, status_msg); +					} +#endif +					free(status_msg); +				} +			} +			plist_free(dict); +			dict = NULL; +		} +	} while (ok && client->connection); + +	return res; +} + +/** + * Internally used status updater thread function that will call the specified + * callback function when status update messages (or error messages) are + * received. + * + * @param arg Pointer to an allocated struct instproxy_status_data that holds + *     the required data about the connected client and the callback function. + * + * @return Always NULL. + */ +static gpointer instproxy_status_updater(gpointer arg) +{	 +	struct instproxy_status_data *data = (struct instproxy_status_data*)arg; + +	/* run until the operation is complete or an error occurs */ +	(void)instproxy_perform_operation(data->client, data->cbfunc, data->operation); + +	/* cleanup */ +	instproxy_lock(data->client); +	log_dbg_msg(DBGMASK_INSTPROXY, "%s: done, cleaning up.\n", __func__); +	if (data->operation) { +	    free(data->operation); +	} +	data->client->status_updater = NULL; +	instproxy_unlock(data->client); +	free(data); + +	return NULL; +} + +/** + * Internally used helper function that creates a status updater thread which + * will call the passed callback function when status updates occur. + * If status_cb is NULL no thread will be created, but the operation will + * run synchronously until it completes or an error occurs. + * + * @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. + * + * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or + *         when the operation completed successfully (sync). + *         An INSTPROXY_E_* error value is returned if an error occured. + */ +static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation) +{ +	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; +	if (status_cb) { +		/* async mode */ +		struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); +		if (data) { +			data->client = client; +			data->cbfunc = status_cb; +			data->operation = strdup(operation); + +			client->status_updater = g_thread_create(instproxy_status_updater, data, TRUE, NULL); +			if (client->status_updater) { +				res = INSTPROXY_E_SUCCESS; +			} +		} +	} else { +		/* sync mode */ +		res = instproxy_perform_operation(client, NULL, operation); +	} +	return res; +} + + +/** + * 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 sinf PLIST_DATA node holding the package's SINF data or NULL. + * @param metadata PLIST_DATA node holding the packages's Metadata 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. + * + * @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 sinf, plist_t metadata, instproxy_status_cb_t status_cb, const char *command) +{ +	if (!client || !client->connection || !pkg_path) { +		return INSTPROXY_E_INVALID_ARG; +	} +	if (sinf && (plist_get_node_type(sinf) != PLIST_DATA)) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): ERROR: sinf data is not a PLIST_DATA node!\n", __func__, command); +		return INSTPROXY_E_INVALID_ARG; +	} +	if (metadata && (plist_get_node_type(metadata) != PLIST_DATA)) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): ERROR: metadata is not a PLIST_DATA node!\n", __func__, command); +		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(); +	if (sinf && metadata) { +		plist_t client_opts = plist_new_dict(); +		plist_dict_insert_item(client_opts, "ApplicationSINF", plist_copy(sinf)); +		plist_dict_insert_item(client_opts, "iTunesMetadata", plist_copy(metadata)); +		plist_dict_insert_item(dict, "ClientOptions", client_opts); +	} +	plist_dict_insert_item(dict, "Command", plist_new_string(command)); +	plist_dict_insert_item(dict, "PackagePath", plist_new_string(pkg_path)); + +	instproxy_lock(client); +	res = instproxy_plist_send(client, dict); +	instproxy_unlock(client); + +	plist_free(dict); + +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res); +		return res; +	} + +	return instproxy_create_status_updater(client, status_cb, command); +} + +/** + * Install an application on the device. + * + * @param client The connected installation_proxy client + * @param pkg_path Path of the installation package (inside the AFC jail) + * @param sinf PLIST_DATA node holding the package's SINF data or NULL. + * @param metadata PLIST_DATA node holding the packages's Metadata or NULL. + * @param status_cb Callback function for progress and status information. If + *        NULL is passed, this function will run synchronously. + * + * @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_install(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb) +{ +	return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Install"); +} + +/** + * Upgrade an application on the device. This function is nearly the same as + * instproxy_install; the difference is that the installation progress on the + * device is faster if the application is already installed. + * + * @param client The connected installation_proxy client + * @param pkg_path Path of the installation package (inside the AFC jail) + * @param sinf PLIST_DATA node holding the package's SINF data or NULL. + * @param metadata PLIST_DATA node holding the packages's Metadata or NULL. + * @param status_cb Callback function for progress and status information. If + *        NULL is passed, this function will run synchronously. + * + * @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_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb) +{ +	return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Upgrade"); +} + +/** + * Uninstall an application from the device. + * + * @param client The connected installation proxy client + * @param appid ApplicationIdentifier of the app to uninstall + * @param status_cb Callback function for progress and status information. If + *        NULL is passed, this function will run synchronously. + * + * @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, instproxy_status_cb_t status_cb) +{ +	if (!client || !client->connection || !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_plist_send(client, dict); +	instproxy_unlock(client); + +	plist_free(dict); + +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res); +		return res; +	} + +	return instproxy_create_status_updater(client, status_cb, "Uninstall"); +} + +/** + * List archived applications. This function runs synchronously. + * + * @see instproxy_archive + * + * @param client The connected installation_proxy client + * @param result Pointer that will be set to a plist containing a PLIST_DICT + *        holding information about the archived applications found. + * + * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if + *     an error occured. + */ +instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t *result) +{ +	if (!client || !client->connection || !result) +		return INSTPROXY_E_INVALID_ARG; + +	instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; + +	plist_t dict = plist_new_dict(); +	plist_dict_insert_item(dict, "Command", plist_new_string("LookupArchives")); + +	instproxy_lock(client); + +	res = instproxy_plist_send(client, dict); +	plist_free(dict); + +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res); +		goto leave_unlock; +	} + +	res = instproxy_plist_recv(client, result); +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not receive plist, error %d\n", __func__, res); +		goto leave_unlock; +	} + +	res = INSTPROXY_E_SUCCESS; + +leave_unlock: +	instproxy_unlock(client); +	return res; +} + +/** + * Archive an application on the device. + * This function tells the device to make an archive of the specified + * application. This results in the device creating a ZIP archive in the + * 'ApplicationArchives' directory and uninstalling the application. + * + * @param client The connected installation proxy client + * @param appid ApplicationIdentifier of the app to archive. + * @param options This is either 0 for default behaviour (make an archive + *        including app/user settings etc. AND uninstall the application), + *        or one or a combination of the following options: + *        INSTPROXY_ARCHIVE_APP_ONLY (1) + *        INSTPROXY_ARCHIVE_SKIP_UNINSTALL (2) + * @param status_cb Callback function for progress and status information. If + *        NULL is passed, this function will run synchronously. + * + * @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_archive(instproxy_client_t client, const char *appid, uint32_t options, instproxy_status_cb_t status_cb) +{ +	if (!client || !client->connection || !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)); +	if (options > 0) { +		plist_t client_opts = plist_new_dict(); +		if (options & INSTPROXY_ARCHIVE_APP_ONLY) { +			plist_dict_insert_item(client_opts, "ArchiveType", plist_new_string("ApplicationOnly")); +		} +		if (options & INSTPROXY_ARCHIVE_SKIP_UNINSTALL) { +			plist_dict_insert_item(client_opts, "SkipUninstall", plist_new_bool(1)); +		} +		plist_dict_insert_item(dict, "ClientOptions", client_opts); +	} +	plist_dict_insert_item(dict, "Command", plist_new_string("Archive")); + +	instproxy_lock(client); +	res = instproxy_plist_send(client, dict); +	instproxy_unlock(client); + +	plist_free(dict); + +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res); +		return res; +	} +	return instproxy_create_status_updater(client, status_cb, "Archive"); +} + +/** + * Restore a previously archived application on the device. + * This function is the counterpart to instproxy_archive. + * @see instproxy_archive + * + * @param client The connected installation proxy client + * @param appid ApplicationIdentifier of the app to restore. + * @param status_cb Callback function for progress and status information. If + *        NULL is passed, this function will run synchronously. + * + * @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_restore(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb) +{ +	if (!client || !client->connection || !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("Restore")); + +	instproxy_lock(client); +	res = instproxy_plist_send(client, dict); +	instproxy_unlock(client); + +	plist_free(dict); + +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res); +		return res; +	} +	return instproxy_create_status_updater(client, status_cb, "Restore"); +} + +/** + * 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 status_cb Callback function for progress and status information. If + *        NULL is passed, this function will run synchronously. + * + * @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, instproxy_status_cb_t status_cb) +{ +	if (!client || !client->connection || !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("RemoveArchive")); + +	instproxy_lock(client); +	res = instproxy_plist_send(client, dict); +	instproxy_unlock(client); + +	plist_free(dict); + +	if (res != INSTPROXY_E_SUCCESS) { +		log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res); +		return res; +	} +	return instproxy_create_status_updater(client, status_cb, "RemoveArchive"); +} + diff --git a/src/InstallationProxy.h b/src/InstallationProxy.h new file mode 100644 index 0000000..c8c5ef1 --- /dev/null +++ b/src/InstallationProxy.h @@ -0,0 +1,34 @@ +/* + * InstallationProxy.h + * Installation Proxy header file. + * + * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  + */ +#ifndef IINSTALLATION_PROXY_H +#define IINSTALLATION_PROXY_H + +#include <glib.h> + +#include "libiphone/installation_proxy.h" + +struct instproxy_client_int { +	iphone_connection_t connection; +	GMutex *mutex; +	GThread *status_updater; +}; + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index ce1d237..9b42f1c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,8 @@ libiphone_la_SOURCES = iphone.c iphone.h \  		       lockdown.c lockdown.h\  		       AFC.c AFC.h\  		       NotificationProxy.c NotificationProxy.h\ +		       InstallationProxy.c InstallationProxy.h\ +		       SBServices.c SBServices.h\  		       userpref.c userpref.h\  		       utils.c utils.h\  		       MobileSync.c MobileSync.h diff --git a/src/NotificationProxy.c b/src/NotificationProxy.c index 884be5f..160ac4a 100644 --- a/src/NotificationProxy.c +++ b/src/NotificationProxy.c @@ -62,7 +62,9 @@ static void np_unlock(np_client_t client)   * @param client NP to send data to   * @param dict plist to send   * - * @return NP_E_SUCCESS or an error code. + * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client or dict + *      are NULL, NP_E_PLIST_ERROR when dict is not a valid plist, + *      or NP_E_UNKNOWN_ERROR when an unspecified error occurs.   */  static np_error_t np_plist_send(np_client_t client, plist_t dict)  { @@ -105,11 +107,14 @@ static np_error_t np_plist_send(np_client_t client, plist_t dict)  /** Makes a connection to the NP service on the phone.    *  - * @param phone The iPhone to connect on. - * @param s_port The source port.  - * @param d_port The destination port.  + * @param device The device to connect to. + * @param dst_port Destination port (usually given by lockdownd_start_service). + * @param client Pointer that will be set to a newly allocated np_client_t + *    upon successful return.   *  - * @return A handle to the newly-connected client or NULL upon error. + * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when device is NULL, + *   or NP_E_CONN_FAILED when the connection to the device could not be + *   established.   */  np_error_t np_client_new(iphone_device_t device, int dst_port, np_client_t *client)  { @@ -123,7 +128,7 @@ np_error_t np_client_new(iphone_device_t device, int dst_port, np_client_t *clie  	/* Attempt connection */  	iphone_connection_t connection = NULL;  	if (iphone_device_connect(device, dst_port, &connection) != IPHONE_E_SUCCESS) { -		return NP_E_UNKNOWN_ERROR; +		return NP_E_CONN_FAILED;  	}  	np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_int)); @@ -137,9 +142,11 @@ np_error_t np_client_new(iphone_device_t device, int dst_port, np_client_t *clie  	return NP_E_SUCCESS;  } -/** Disconnects an NP client from the phone. +/** Disconnects an NP client from the device.   *    * @param client The client to disconnect. + * + * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL.   */  np_error_t np_client_free(np_client_t client)  { @@ -168,6 +175,8 @@ np_error_t np_client_free(np_client_t client)   *   * @param client The client to send to   * @param notification The notification message to send + * + * @return NP_E_SUCCESS on success, or an error returned by np_plist_send   */  np_error_t np_post_notification(np_client_t client, const char *notification)  { @@ -201,6 +210,9 @@ np_error_t np_post_notification(np_client_t client, const char *notification)   *   * @param client The client to send to   * @param notification The notifications that should be observed. + * + * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client or + *    notification are NULL, or an error returned by np_plist_send.   */  np_error_t np_observe_notification( np_client_t client, const char *notification )  { @@ -241,6 +253,9 @@ np_error_t np_observe_notification( np_client_t client, const char *notification   *  observed. This is expected to be an array of const char* that MUST have a   *  terminating NULL entry. However this parameter can be NULL; in this case,   *  the default set of notifications will be used. + * + * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client is null, + *   or an error returned by np_observe_notification.   */  np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)  { @@ -275,7 +290,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio   *  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. + *         or a negative value if an error occured.   *   * @note You probably want to check out np_set_notify_callback   * @see np_set_notify_callback @@ -401,10 +416,14 @@ gpointer np_notifier( gpointer arg )   *   * @param client the NP client   * @param notify_cb pointer to a callback function or NULL to de-register a - *        previously set callback function + *        previously set callback function. + * + * @note Only one callback function can be registered at the same time; + *       any previously set callback function will be removed automatically.   *   * @return NP_E_SUCCESS when the callback was successfully registered, - *         or an error value when an error occured. + *         NP_E_INVALID_ARG when client is NULL, or NP_E_UNKNOWN_ERROR when + *         the callback thread could no be created.   */  np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb )  { diff --git a/src/SBServices.c b/src/SBServices.c new file mode 100644 index 0000000..9849415 --- /dev/null +++ b/src/SBServices.c @@ -0,0 +1,291 @@ +/* + * SBServices.c + * SpringBoard Services implementation. + * + * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  + */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <plist/plist.h> + +#include "SBServices.h" +#include "iphone.h" +#include "utils.h" + +/** Locks an sbservices client, done for thread safety stuff. + * + * @param client The sbservices client to lock. + */ +static void sbs_lock(sbservices_client_t client) +{ +	log_debug_msg("SBServices: Locked\n"); +	g_mutex_lock(client->mutex); +} + +/** Unlocks an sbservices client, done for thread safety stuff. + *  + * @param client The sbservices client to unlock + */ +static void sbs_unlock(sbservices_client_t client) +{ +	log_debug_msg("SBServices: Unlocked\n"); +	g_mutex_unlock(client->mutex); +} + +sbservices_error_t sbservices_client_new(iphone_device_t device, int dst_port, sbservices_client_t *client) +{ +	/* makes sure thread environment is available */ +	if (!g_thread_supported()) +		g_thread_init(NULL); + +	if (!device) +		return SBSERVICES_E_INVALID_ARG; + +	/* Attempt connection */ +	iphone_connection_t connection = NULL; +	if (iphone_device_connect(device, dst_port, &connection) != IPHONE_E_SUCCESS) { +		return SBSERVICES_E_CONN_FAILED; +	} + +	sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_int)); +	client_loc->connection = connection; +	client_loc->mutex = g_mutex_new(); + +	*client = client_loc; +	return SBSERVICES_E_SUCCESS; +} + +sbservices_error_t sbservices_client_free(sbservices_client_t client) +{ +	if (!client) +		return SBSERVICES_E_INVALID_ARG; + +	iphone_device_disconnect(client->connection); +	client->connection = NULL; +	if (client->mutex) { +		g_mutex_free(client->mutex); +	} +	free(client); + +	return SBSERVICES_E_SUCCESS; +} + +/** + * Sends a binary plist to the device using the connection specified in client. + * This function is only used internally. + * + * @param client InstallationProxy to send data to + * @param dict plist to send + * + * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when + *     client or dict are NULL, SBSERVICES_E_PLIST_ERROR when dict is not a + *     valid plist, or SBSERVICES_E_UNKNOWN_ERROR when an unspecified error + *     occurs. + */ +static sbservices_error_t sbservices_plist_send(sbservices_client_t client, plist_t dict) +{ +	char *content = NULL; +	uint32_t length = 0; +	uint32_t nlen = 0; +	int bytes = 0; +	sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; + +	if (!client || !dict) { +		return SBSERVICES_E_INVALID_ARG; +	} + +	plist_to_bin(dict, &content, &length); + +	if (!content || length == 0) { +		return SBSERVICES_E_PLIST_ERROR; +	} + +	nlen = htonl(length); +	log_debug_msg("%s: sending %d bytes\n", __func__, length); +	iphone_device_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes); +	if (bytes == sizeof(nlen)) { +		iphone_device_send(client->connection, content, length, (uint32_t*)&bytes); +		if (bytes > 0) { +			if ((uint32_t)bytes == length) { +				res = SBSERVICES_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(content); + +	return res; +} + +sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state) +{ +	if (!client || !client->connection || !state) +		return SBSERVICES_E_INVALID_ARG; + +	sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; +	uint32_t pktlen = 0; +	uint32_t bytes = 0; + +	plist_t dict = plist_new_dict(); +	plist_dict_insert_item(dict, "command", plist_new_string("getIconState")); + +	sbs_lock(client); + +	res = sbservices_plist_send(client, dict); +	plist_free(dict); +	if (res != SBSERVICES_E_SUCCESS) { +		log_debug_msg("%s: could not send plist\n", __func__); +		goto leave_unlock; +	} + +	iphone_device_recv(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes); +	log_debug_msg("%s: initial read=%i\n", __func__, bytes); +	if (bytes < 4) { +		log_debug_msg("%s: initial read failed!\n"); +		res = 0; +	} else { +		if ((char)pktlen == 0) { +			char *content = NULL; +			uint32_t curlen = 0; +			pktlen = ntohl(pktlen); +			log_debug_msg("%s: %d bytes following\n", __func__, pktlen); +			content = (char*)malloc(pktlen); +			log_debug_msg("pointer %p\n", content); + +			while (curlen < pktlen) { +				iphone_device_recv(client->connection, content+curlen, pktlen-curlen, &bytes); +				if (bytes <= 0) { +					res = SBSERVICES_E_UNKNOWN_ERROR; +					break; +				} +				log_debug_msg("%s: received %d bytes\n", __func__, bytes); +				curlen += bytes; +			} +			log_debug_buffer(content, pktlen); +			plist_from_bin(content, pktlen, state); +			res = SBSERVICES_E_SUCCESS; +			free(content); +		} else { +			res = SBSERVICES_E_UNKNOWN_ERROR; +		} +	} + +leave_unlock: +	sbs_unlock(client); +	return res; +} + +sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) +{ +	if (!client || !client->connection || !newstate) +		return SBSERVICES_E_INVALID_ARG; + +	sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; + +	plist_t dict = plist_new_dict(); +	plist_dict_insert_item(dict, "command", plist_new_string("setIconState")); +	plist_dict_insert_item(dict, "iconState", plist_copy(newstate)); + +	sbs_lock(client); + +	res = sbservices_plist_send(client, dict); +	plist_free(dict); +	if (res != SBSERVICES_E_SUCCESS) { +		log_debug_msg("%s: could not send plist\n", __func__); +		goto leave_unlock; +	} +	// NO RESPONSE + +leave_unlock: +	sbs_unlock(client); +	return res; +} + +sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) +{ +	if (!client || !client->connection || !pngdata) +		return SBSERVICES_E_INVALID_ARG; + +	sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; +	uint32_t pktlen = 0; +	uint32_t bytes = 0; + +	plist_t dict = plist_new_dict(); +	plist_dict_insert_item(dict, "command", plist_new_string("getIconPNGData")); +	plist_dict_insert_item(dict, "bundleId", plist_new_string(bundleId)); + +	sbs_lock(client); + +	res = sbservices_plist_send(client, dict); +	plist_free(dict); +	if (res != SBSERVICES_E_SUCCESS) { +		log_debug_msg("%s: could not send plist\n", __func__); +		goto leave_unlock; +	} + +	iphone_device_recv(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes); +	log_debug_msg("%s: initial read=%i\n", __func__, bytes); +	if (bytes < 4) { +		log_debug_msg("%s: initial read failed!\n"); +		res = 0; +	} else { +		if ((char)pktlen == 0) { +			char *content = NULL; +			uint32_t curlen = 0; +			pktlen = ntohl(pktlen); +			log_debug_msg("%s: %d bytes following\n", __func__, pktlen); +			content = (char*)malloc(pktlen); +			log_debug_msg("pointer %p\n", content); + +			while (curlen < pktlen) { +				iphone_device_recv(client->connection, content+curlen, pktlen-curlen, &bytes); +				if (bytes <= 0) { +					res = SBSERVICES_E_UNKNOWN_ERROR; +					break; +				} +				log_debug_msg("%s: received %d bytes\n", __func__, bytes); +				curlen += bytes; +			} +			log_debug_buffer(content, pktlen); +			plist_t pngdict = NULL; +			plist_from_bin(content, pktlen, &pngdict); +			plist_t node = plist_dict_get_item(pngdict, "pngData"); +			if (node) { +				plist_get_data_val(node, pngdata, pngsize); +			} +			plist_free(pngdict); +			res = SBSERVICES_E_SUCCESS; +			free(content); +		} else { +			res = SBSERVICES_E_UNKNOWN_ERROR; +		} +	} + +leave_unlock: +	sbs_unlock(client); +	return res; + +} + diff --git a/src/SBServices.h b/src/SBServices.h new file mode 100644 index 0000000..8f923b9 --- /dev/null +++ b/src/SBServices.h @@ -0,0 +1,33 @@ +/* + * SBServices.h + * SpringBoard Services header file. + * + * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  + */ +#ifndef ISBSERVICES_H +#define ISBSERVICES_H + +#include <glib.h> + +#include "libiphone/sbservices.h" + +struct sbservices_client_int { +	iphone_connection_t connection; +	GMutex *mutex; +}; + +#endif | 
