From 4c00fb43042a1e8d1a4e2d29827d00ed3144575f Mon Sep 17 00:00:00 2001
From: Nikias Bassen
Date: Wed, 9 Dec 2009 20:27:09 +0100
Subject: Documentation cleanup and a new error code

[#96 state:resolved]

Signed-off-by: Matt Colyer <matt@colyer.name>
---
 src/NotificationProxy.c | 39 +++++++++++++++++++++++++++++----------
 1 file changed, 29 insertions(+), 10 deletions(-)

(limited to 'src')

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 )
 {
-- 
cgit v1.1-32-gdbae


From c6982451d82c340ee8a57c76e39db160c625a1a3 Mon Sep 17 00:00:00 2001
From: Nikias Bassen
Date: Tue, 15 Dec 2009 00:02:50 +0100
Subject: Support for new SBServices interface

This lockdown service has been introduced in firmware 3.1 and allows to
re-arrange the Spr*ngboard icons from the computer.

[#99 state:resolved]

Signed-off-by: Matt Colyer <matt@colyer.name>
---
 src/Makefile.am  |   1 +
 src/SBServices.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/SBServices.h |  33 +++++++
 3 files changed, 325 insertions(+)
 create mode 100644 src/SBServices.c
 create mode 100644 src/SBServices.h

(limited to 'src')

diff --git a/src/Makefile.am b/src/Makefile.am
index ce1d237..d351a8a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ libiphone_la_SOURCES = iphone.c iphone.h \
 		       lockdown.c lockdown.h\
 		       AFC.c AFC.h\
 		       NotificationProxy.c NotificationProxy.h\
+		       SBServices.c SBServices.h\
 		       userpref.c userpref.h\
 		       utils.c utils.h\
 		       MobileSync.c MobileSync.h
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
-- 
cgit v1.1-32-gdbae


From dd3d8700dcdc7acc28e2db58405f247bb98228ab Mon Sep 17 00:00:00 2001
From: Nikias Bassen
Date: Thu, 31 Dec 2009 03:19:43 +0100
Subject: New installation_proxy interface.

Allows enumeration, install, uninstall, upgrade, and some
other stuff with apps.
---
 src/InstallationProxy.c | 809 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/InstallationProxy.h |  34 ++
 src/Makefile.am         |   1 +
 3 files changed, 844 insertions(+)
 create mode 100644 src/InstallationProxy.c
 create mode 100644 src/InstallationProxy.h

(limited to 'src')

diff --git a/src/InstallationProxy.c b/src/InstallationProxy.c
new file mode 100644
index 0000000..7a8ef5a
--- /dev/null
+++ b/src/InstallationProxy.c
@@ -0,0 +1,809 @@
+/*
+ * 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, &current_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 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, 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("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 d351a8a..9b42f1c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ 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\
-- 
cgit v1.1-32-gdbae


From daee8be013dae3821463fd85f4076ca2ab3adce6 Mon Sep 17 00:00:00 2001
From: Nikias Bassen
Date: Thu, 31 Dec 2009 14:29:05 +0100
Subject: Add options to instproxy_archive function

This change allows to specify the following options:
INSTPROXY_ARCHIVE_APP_ONLY - Archive only the application data
INSTPROXY_ARCHIVE_SKIP_UNINSTALL - Do not uninstall the application.

Combine these options with logical OR to specify both. These two options
combined are used by iTunes to create app archives of on-device downloaded
apps that are later copied as *.ipa files to the computer.

[#104 state:resolved]

Signed-off-by: Matt Colyer <matt@colyer.name>
---
 src/InstallationProxy.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/InstallationProxy.c b/src/InstallationProxy.c
index 7a8ef5a..917886d 100644
--- a/src/InstallationProxy.c
+++ b/src/InstallationProxy.c
@@ -676,6 +676,11 @@ leave_unlock:
  *
  * @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.
  *
@@ -687,7 +692,7 @@ leave_unlock:
  *     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, instproxy_status_cb_t status_cb)
+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;
@@ -700,6 +705,16 @@ instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid
 
 	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);
-- 
cgit v1.1-32-gdbae