NotificationProxy support added.

[#27 state:resolved]

Signed-off-by: Matt Colyer <matt@colyer.name>
diff --git a/dev/main.c b/dev/main.c
index e4eb686..510c75f 100644
--- a/dev/main.c
+++ b/dev/main.c
@@ -27,26 +27,14 @@
 #include <libiphone/libiphone.h>
 #include "../src/utils.h"
 
-void perform_syncWillStart(iphone_device_t phone, iphone_lckd_client_t control)
+void notifier(const char *notification)
 {
-	int nport = 0;
-	iphone_np_client_t np;
-
-	iphone_lckd_start_service(control, "com.apple.mobile.notification_proxy", &nport);
-	if (nport) {
-		printf("::::::::::::::: np was started ::::::::::::\n");
-		iphone_np_new_client(phone, 3555, nport, &np);
-		if (np) {
-			printf("::::::::: PostNotification com.apple.itunes-mobdev.syncWillStart\n");
-			iphone_np_post_notification(np, "com.apple.itunes-mobdev.syncWillStart");
-			iphone_np_free_client(np);
-		}
-	} else {
-		printf("::::::::::::::: np was NOT started ::::::::::::\n");
-	}
+	printf("---------------------------------------------------------\n");
+	printf("------> Notification received: %s\n", notification);
+	printf("---------------------------------------------------------\n");
 }
 
-void perform_syncDidStart(iphone_device_t phone, iphone_lckd_client_t control)
+void perform_notification(iphone_device_t phone, iphone_lckd_client_t control, const char *notification)
 {
 	int nport = 0;
 	iphone_np_client_t np;
@@ -54,11 +42,10 @@
 	iphone_lckd_start_service(control, "com.apple.mobile.notification_proxy", &nport);
 	if (nport) {
 		printf("::::::::::::::: np was started ::::::::::::\n");
-		sleep(1);
 		iphone_np_new_client(phone, 3555, nport, &np);
 		if (np) {
-			printf("::::::::: PostNotification com.apple.itunes-mobdev.syncDidStart\n");
-			iphone_np_post_notification(np, "com.apple.itunes-mobdev.syncDidStart");
+			printf("::::::::: PostNotification %s\n", notification);
+			iphone_np_post_notification(np, notification);
 			iphone_np_free_client(np);
 		}
 	} else {
@@ -69,9 +56,11 @@
 int main(int argc, char *argv[])
 {
 	int bytes = 0, port = 0, i = 0;
+	int npp;
 	iphone_lckd_client_t control = NULL;
 	iphone_device_t phone = NULL;
 	iphone_afc_file_t lockfile = NULL;
+	iphone_np_client_t gnp = NULL;
 
 	if (argc > 1 && !strcasecmp(argv[1], "--debug")) {
 		iphone_set_debug(1);
@@ -88,6 +77,7 @@
 
 	if (IPHONE_E_SUCCESS != iphone_lckd_new_client(phone, &control)) {
 		iphone_free_device(phone);
+		printf("Exiting.\n");
 		return -1;
 	}
 
@@ -97,20 +87,45 @@
 		free(uid);
 	}
 
+
+	char *nnn = NULL;
+	if (IPHONE_E_SUCCESS == lockdownd_get_device_name(control, &nnn)) {
+		printf("DeviceName : %s\n", nnn);
+		free(nnn);
+	}
+
 	iphone_lckd_start_service(control, "com.apple.afc", &port);
 
 	if (port) {
 		iphone_afc_client_t afc = NULL;
 		iphone_afc_new_client(phone, 3432, port, &afc);
 		if (afc) {
-			perform_syncWillStart(phone, control);
+			iphone_lckd_start_service(control, "com.apple.mobile.notification_proxy", &npp);
+			if (npp) {
+				printf("Notification Proxy started.\n");
+				iphone_np_new_client(phone, 3756, npp, &gnp);
+			} else {
+				printf("ERROR: Notification proxy could not be started.\n");
+			}
+			if (gnp) {
+				const char *nspec[4] = {
+					NP_SYNC_CANCEL_REQUEST,
+					NP_SYNC_SUSPEND_REQUEST,
+					NP_SYNC_RESUME_REQUEST,
+					NULL
+				};
+				iphone_np_observe_notifications(gnp, nspec);
+				//iphone_np_set_notify_callback(gnp, notifier);
+			}
+
+			perform_notification(phone, control, NP_SYNC_WILL_START);
 
 			iphone_afc_open_file(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
 			if (lockfile) {
 				printf("locking file\n");
 				iphone_afc_lock_file(afc, lockfile, 2 | 4);
 
-				perform_syncDidStart(phone, control);
+				perform_notification(phone, control, NP_SYNC_DID_START);
 			}
 
 			char **dirs = NULL;
@@ -123,6 +138,8 @@
 			}
 
 			g_strfreev(dirs);
+
+			dirs = NULL;
 			iphone_afc_get_devinfo(afc, &dirs);
 			if (dirs) {
 				for (i = 0; dirs[i]; i += 2) {
@@ -189,9 +206,31 @@
 			iphone_afc_close_file(afc, my_file);
 		}
 
-		if (lockfile) {
-			printf("XXX sleeping 2 seconds\n");
-			sleep(2);
+		if (gnp && lockfile) {
+			char *noti;
+
+			noti = NULL;
+			iphone_np_get_notification(gnp, &noti);
+			if (noti) {
+				printf("------> received notification '%s'\n", noti);
+				free(noti);
+			}
+
+			printf("XXX sleeping\n");
+			for (i = 0; i < 5; i++) {
+				noti = NULL;
+				printf("--- getting notification\n");
+				iphone_np_get_notification(gnp, &noti);
+				if (noti) {
+					printf("------> received notification '%s'\n", noti);
+					free(noti);
+				} else {
+					printf("---- no notification\n");
+				}
+				sleep(1);
+			}
+
+			//perform_notification(phone, control, NP_SYNC_DID_FINISH);
 
 			printf("XXX unlocking file\n");
 			iphone_afc_lock_file(afc, lockfile, 8 | 4);
@@ -199,6 +238,12 @@
 			printf("XXX closing file\n");
 			iphone_afc_close_file(afc, lockfile);
 		}
+
+		if (gnp) {
+			iphone_np_free_client(gnp);
+			gnp = NULL;
+		}
+
 		iphone_afc_free_client(afc);
 	} else {
 		printf("Start service failure.\n");
diff --git a/include/libiphone/libiphone.h b/include/libiphone/libiphone.h
index a45319f..dedc78f 100644
--- a/include/libiphone/libiphone.h
+++ b/include/libiphone/libiphone.h
@@ -101,6 +101,7 @@
 
 //lockdownd related functions
 iphone_error_t lockdownd_get_device_uid(iphone_lckd_client_t control, char **uid);
+iphone_error_t lockdownd_get_device_name ( iphone_lckd_client_t client, char **device_name );
 iphone_error_t iphone_lckd_new_client ( iphone_device_t device, iphone_lckd_client_t *client );
 iphone_error_t iphone_lckd_free_client( iphone_lckd_client_t client );
 
@@ -115,6 +116,7 @@
 
 iphone_error_t iphone_mux_send ( iphone_umux_client_t client, const char *data, uint32_t datalen, uint32_t *sent_bytes );
 iphone_error_t iphone_mux_recv ( iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t *recv_bytes  );
+iphone_error_t iphone_mux_recv_timeout ( iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t *recv_bytes, int timeout);
 
 
 //afc related functions
@@ -148,6 +150,36 @@
 iphone_error_t iphone_msync_recv(iphone_msync_client_t client, plist_t * plist);
 iphone_error_t iphone_msync_send(iphone_msync_client_t client, plist_t plist);
 
+// NotificationProxy related
+// notifications for use with post_notification (client --> device)
+#define NP_SYNC_WILL_START      "com.apple.itunes-mobdev.syncWillStart"
+#define NP_SYNC_DID_START       "com.apple.itunes-mobdev.syncDidStart"
+#define NP_SYNC_DID_FINISH      "com.apple.itunes-mobdev.syncDidFinish"
+
+// notifications for use with observe_notification (device --> client)
+#define NP_SYNC_CANCEL_REQUEST  "com.apple.itunes-client.syncCancelRequest"
+#define NP_SYNC_SUSPEND_REQUEST "com.apple.itunes-client.syncSuspendRequest"
+#define NP_SYNC_RESUME_REQUEST  "com.apple.itunes-client.syncResumeRequest"
+#define NP_PHONE_NUMBER_CHANGED "com.apple.mobile.lockdown.phone_number_changed"
+#define NP_DEVICE_NAME_CHANGED  "com.apple.mobile.lockdown.device_name_changed"
+#define NP_ATTEMPTACTIVATION    "com.apple.springboard.attemptactivation"
+#define NP_DS_DOMAIN_CHANGED    "com.apple.mobile.data_sync.domain_changed"
+#define NP_APP_INSTALLED        "com.apple.mobile.application_installed"
+#define NP_APP_UNINSTALLED      "com.apple.mobile.application_uninstalled"
+
+iphone_error_t iphone_np_new_client ( iphone_device_t device, int src_port, int dst_port, iphone_np_client_t *client );
+iphone_error_t iphone_np_free_client ( iphone_np_client_t client );
+
+iphone_error_t iphone_np_post_notification ( iphone_np_client_t client, const char *notification );
+
+iphone_error_t iphone_np_observe_notification ( iphone_np_client_t client, const char *notification );
+iphone_error_t iphone_np_observe_notifications ( iphone_np_client_t client, const char **notification_spec );
+iphone_error_t iphone_np_get_notification ( iphone_np_client_t client, char **notification );
+
+typedef void (*iphone_np_notify_cb_t) ( const char *notification );
+
+iphone_error_t iphone_np_set_notify_callback ( iphone_np_client_t client, iphone_np_notify_cb_t notify_cb );
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/NotificationProxy.c b/src/NotificationProxy.c
index bf837bf..d8bcc34 100644
--- a/src/NotificationProxy.c
+++ b/src/NotificationProxy.c
@@ -21,10 +21,16 @@
 
 #include <string.h>
 #include <stdio.h>
+#include <arpa/inet.h>
 #include <plist/plist.h>
 #include "NotificationProxy.h"
 #include "utils.h"
 
+struct np_thread {
+	iphone_np_client_t client;
+	iphone_np_notify_cb_t cbfunc;
+};
+
 /** Locks an NP client, done for thread safety stuff.
  *
  * @param client The NP
@@ -45,6 +51,54 @@
 	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 NP to send data to
+ * @param dict plist to send
+ *
+ * @return IPHONE_E_SUCCESS or an error code.
+ */
+static iphone_error_t np_plist_send(iphone_np_client_t client, plist_t dict)
+{
+	char *XML_content = NULL;
+	uint32_t length = 0;
+	uint32_t nlen = 0;
+	int bytes = 0;
+	iphone_error_t res = IPHONE_E_UNKNOWN_ERROR;
+
+	if (!client || !dict) {
+		return IPHONE_E_INVALID_ARG;
+	}
+
+	plist_to_xml(dict, &XML_content, &length);
+
+	if (!XML_content || length == 0) {
+		return IPHONE_E_PLIST_ERROR;
+	}
+
+	nlen = htonl(length);
+	iphone_mux_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes);
+	if (bytes == sizeof(nlen)) {
+		iphone_mux_send(client->connection, XML_content, length, (uint32_t*)&bytes);
+		if (bytes > 0) {
+			if ((uint32_t)bytes == length) {
+				res = IPHONE_E_SUCCESS;
+			} else {
+				log_debug_msg("%s: ERROR: Could not send all data (%d of %d)!\n", __func__, bytes, length);
+			}
+		}
+	}
+	if (bytes <= 0) {
+		log_debug_msg("%s: ERROR: sending to device failed.\n", __func__);
+	}
+
+	free(XML_content);
+
+	return res;
+}
+
 /** Makes a connection to the NP service on the phone. 
  * 
  * @param phone The iPhone to connect on.
@@ -53,7 +107,7 @@
  * 
  * @return A handle to the newly-connected client or NULL upon error.
  */
-iphone_error_t iphone_np_new_client(iphone_device_t device, int src_port, int dst_port, iphone_np_client_t * client)
+iphone_error_t iphone_np_new_client ( iphone_device_t device, int src_port, int dst_port, iphone_np_client_t *client )
 {
 	int ret = IPHONE_E_SUCCESS;
 
@@ -75,6 +129,8 @@
 
 	client_loc->mutex = g_mutex_new();
 
+	client_loc->notifier = NULL;
+
 	*client = client_loc;
 	return IPHONE_E_SUCCESS;
 }
@@ -83,91 +139,97 @@
  * 
  * @param client The client to disconnect.
  */
-iphone_error_t iphone_np_free_client(iphone_np_client_t client)
+iphone_error_t iphone_np_free_client ( iphone_np_client_t client )
 {
-	if (!client || !client->connection)
+	if (!client)
 		return IPHONE_E_INVALID_ARG;
 
-	iphone_mux_free_client(client->connection);
+	if (client->connection) {
+		iphone_mux_free_client(client->connection);
+		client->connection = NULL;
+		if (client->notifier) {
+			log_debug_msg("joining np callback\n");
+			g_thread_join(client->notifier);
+		}
+	}
+	if (client->mutex) {
+		g_mutex_free(client->mutex);
+	}
 	free(client);
+
 	return IPHONE_E_SUCCESS;
 }
 
-/** Sends a notification to the NP client.
+/** Sends a notification to the device's Notification Proxy.
  *
  * notification messages seen so far:
  *   com.apple.itunes-mobdev.syncWillStart
  *   com.apple.itunes-mobdev.syncDidStart
  *
  * @param client The client to send to
- * @param notification The notification Message
+ * @param notification The notification message to send
  */
-iphone_error_t iphone_np_post_notification(iphone_np_client_t client, const char *notification)
+iphone_error_t iphone_np_post_notification( iphone_np_client_t client, const char *notification )
 {
-	char *XML_content = NULL;
-	uint32_t length = 0;
-	int bytes = 0;
-	iphone_error_t ret;
-	unsigned char sndbuf[4096];
-	int sndlen = 0;
-	int nlen = 0;
-	plist_t dict = NULL;
-
 	if (!client || !notification) {
 		return IPHONE_E_INVALID_ARG;
 	}
 	np_lock(client);
 
-	dict = plist_new_dict();
+	plist_t dict = plist_new_dict();
 	plist_add_sub_key_el(dict, "Command");
 	plist_add_sub_string_el(dict, "PostNotification");
 	plist_add_sub_key_el(dict, "Name");
 	plist_add_sub_string_el(dict, notification);
-	plist_to_xml(dict, &XML_content, &length);
 
-	nlen = htonl(length);
-
-	memcpy(sndbuf + sndlen, &nlen, 4);
-	sndlen += 4;
-	memcpy(sndbuf + sndlen, XML_content, length);
-	sndlen += length;
-
+	iphone_error_t res = np_plist_send(client, dict);
 	plist_free(dict);
-	dict = NULL;
-	free(XML_content);
-	XML_content = NULL;
 
 	dict = plist_new_dict();
 	plist_add_sub_key_el(dict, "Command");
 	plist_add_sub_string_el(dict, "Shutdown");
-	plist_to_xml(dict, &XML_content, &length);
 
-	nlen = htonl(length);
-
-	memcpy(sndbuf + sndlen, &nlen, 4);
-	sndlen += 4;
-
-	memcpy(sndbuf + sndlen, XML_content, length);
-	sndlen += length;
-
+	res = np_plist_send(client, dict);
 	plist_free(dict);
-	dict = NULL;
-	free(XML_content);
-	XML_content = NULL;
 
-	log_debug_buffer(sndbuf, sndlen);
-
-	iphone_mux_send(client->connection, sndbuf, sndlen, &bytes);
-	if (bytes <= 0) {
-		np_unlock(client);
-		return bytes;
+	if (res != IPHONE_E_SUCCESS) {
+		log_debug_msg("%s: Error sending XML plist to device!\n", __func__);
 	}
 
 	np_unlock(client);
-	return bytes;
+	return res;
 }
 
-/** Notifies the iphone to send a notification on certain events.
+/** Notifies the iphone to send a notification on the specified event.
+ *
+ * @param client The client to send to
+ * @param notification The notifications that should be observed.
+ */
+iphone_error_t iphone_np_observe_notification( iphone_np_client_t client, const char *notification )
+{
+	if (!client || !notification) {
+		return IPHONE_E_INVALID_ARG;
+	}
+	np_lock(client);
+
+	plist_t dict = plist_new_dict();
+	plist_add_sub_key_el(dict, "Command");
+	plist_add_sub_string_el(dict, "ObserveNotification");
+	plist_add_sub_key_el(dict, "Name");
+	plist_add_sub_string_el(dict, notification);
+
+	iphone_error_t res = np_plist_send(client, dict);
+	if (res != IPHONE_E_SUCCESS) {
+		log_debug_msg("%s: Error sending XML plist to device!\n", __func__);
+	}
+	plist_free(dict);
+
+	np_unlock(client);
+	return res;
+}
+
+
+/** Notifies the iphone to send a notification on specified events.
  *
  * observation messages seen so far:
  *   com.apple.itunes-client.syncCancelRequest
@@ -181,85 +243,217 @@
  *   com.apple.mobile.application_uninstalled
  *
  * @param client The client to send to
+ * @param notification_spec Specification of the notifications that should be
+ *  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.
  */
-iphone_error_t iphone_np_observe_notification(iphone_np_client_t client)
+iphone_error_t iphone_np_observe_notifications( iphone_np_client_t client, const char **notification_spec )
 {
-	plist_t dict = NULL;
-	char *XML_content = NULL;
-	uint32_t length = 0;
-	int bytes = 0;
-	iphone_error_t ret;
-	unsigned char sndbuf[4096];
-	int sndlen = 0;
-	int nlen = 0;
 	int i = 0;
-	const char *notifications[10] = {
-		"com.apple.itunes-client.syncCancelRequest",
-		"com.apple.itunes-client.syncSuspendRequest",
-		"com.apple.itunes-client.syncResumeRequest",
-		"com.apple.mobile.lockdown.phone_number_changed",
-		"com.apple.mobile.lockdown.device_name_changed",
-		"com.apple.springboard.attemptactivation",
-		"com.apple.mobile.data_sync.domain_changed",
-		"com.apple.mobile.application_installed",
-		"com.apple.mobile.application_uninstalled",
-		NULL
-	};
-
-	sndlen = 0;
+	iphone_error_t res = IPHONE_E_UNKNOWN_ERROR;
+	const char **notifications = notification_spec;
 
 	if (!client) {
 		return IPHONE_E_INVALID_ARG;
 	}
-	np_lock(client);
 
-	while (notifications[i]) {
-
-		dict = plist_new_dict();
-		plist_add_sub_key_el(dict, "Command");
-		plist_add_sub_string_el(dict, "ObserveNotification");
-		plist_add_sub_key_el(dict, "Name");
-		plist_add_sub_string_el(dict, notifications[i++]);
-		plist_to_xml(dict, &XML_content, &length);
-
-		nlen = htonl(length);
-		memcpy(sndbuf + sndlen, &nlen, 4);
-		sndlen += 4;
-		memcpy(sndbuf + sndlen, XML_content, length);
-		sndlen += length;
-
-		plist_free(dict);
-		dict = NULL;
-		free(XML_content);
-		XML_content = NULL;
+	if (!notifications) {
+		notifications = np_default_notifications;
 	}
 
-	dict = plist_new_dict();
-	plist_add_sub_key_el(dict, "Command");
-	plist_add_sub_string_el(dict, "Shutdown");
-	plist_to_xml(dict, &XML_content, &length);
+	while (notifications[i]) {
+		res = iphone_np_observe_notification(client, notifications[i]);
+		if (res != IPHONE_E_SUCCESS) {
+			break;
+		}
+		i++;
+	}
 
-	nlen = htonl(length);
+	return res;
+}
 
-	memcpy(sndbuf + sndlen, &nlen, 4);
-	sndlen += 4;
+/**
+ * Checks if a notification has been sent.
+ *
+ * @param client NP to get a notification from
+ * @param notification Pointer to a buffer that will be allocated and filled
+ *  with the notification that has been received.
+ *
+ * @return IPHONE_E_SUCCESS if a notification has been received,
+ *         IPHONE_E_TIMEOUT if nothing has been received,
+ *         or an error value if an error occured.
+ *
+ * @note You probably want to check out iphone_np_set_notify_callback
+ * @see iphone_np_set_notify_callback
+ */
+iphone_error_t iphone_np_get_notification( iphone_np_client_t client, char **notification )
+{
+	uint32_t bytes = 0;
+	iphone_error_t res;
+	uint32_t pktlen = 0;
+	char *XML_content = NULL;
+	plist_t dict = NULL;
 
-	memcpy(sndbuf + sndlen, XML_content, length);
-	sndlen += length;
+	if (!client || !client->connection || *notification) {
+		return IPHONE_E_INVALID_ARG;
+	}
 
-	plist_free(dict);
-	dict = NULL;
-	free(XML_content);
-	XML_content = NULL;
+	np_lock(client);
 
-	log_debug_buffer(sndbuf, sndlen);
+	iphone_mux_recv_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, 500);
+	log_debug_msg("NotificationProxy: initial read=%i\n", bytes);
+	if (bytes < 4) {
+		log_debug_msg("NotificationProxy: no notification received!\n");
+		res = IPHONE_E_TIMEOUT;
+	} else {
+		if ((char)pktlen == 0) {
+			pktlen = ntohl(pktlen);
+			log_debug_msg("NotificationProxy: %d bytes following\n", pktlen);
+			XML_content = (char*)malloc(pktlen);
+			log_debug_msg("pointer %p\n", XML_content);
 
-	iphone_mux_send(client->connection, sndbuf, sndlen, &bytes);
-	if (bytes <= 0) {
-		np_unlock(client);
-		return bytes;
+			iphone_mux_recv_timeout(client->connection, XML_content, pktlen, &bytes, 1000);
+			if (bytes <= 0) {
+				res = IPHONE_E_UNKNOWN_ERROR;
+			} else {
+				log_debug_msg("NotificationProxy: received data:\n");
+				log_debug_buffer(XML_content, pktlen);
+
+				plist_from_xml(XML_content, bytes, &dict);
+				if (!dict) {
+					np_unlock(client);
+					return IPHONE_E_PLIST_ERROR;
+				}
+
+				plist_t cmd_key_node = plist_find_node_by_key(dict, "Command");
+				plist_t cmd_value_node = plist_get_next_sibling(cmd_key_node);
+				char *cmd_value = NULL;
+
+				if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
+					plist_get_string_val(cmd_value_node, &cmd_value);
+				}
+
+				if (cmd_value && !strcmp(cmd_value, "RelayNotification")) {
+					plist_t name_key_node = plist_get_next_sibling(cmd_value_node);
+					plist_t name_value_node = plist_get_next_sibling(name_key_node);
+
+					char *name_key = NULL;
+					char *name_value = NULL;
+
+					if (plist_get_node_type(name_key_node) == PLIST_KEY) {
+						plist_get_key_val(name_key_node, &name_key);
+					}
+					if (plist_get_node_type(name_value_node) == PLIST_STRING) {
+						plist_get_string_val(name_value_node, &name_value);
+					}
+
+					res = IPHONE_E_PLIST_ERROR;
+					if (name_key && name_value && !strcmp(name_key, "Name")) {
+						*notification = name_value;
+						log_debug_msg("%s: got notification %s\n", __func__, name_value);
+						res = IPHONE_E_SUCCESS;
+					}
+					free(name_key);
+				} else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
+					log_debug_msg("%s: ERROR: NotificationProxy died!\n", __func__);
+					res = IPHONE_E_UNKNOWN_ERROR;
+				} else if (cmd_value) {
+					log_debug_msg("%d: unknown NotificationProxy command '%s' received!\n", __func__);
+					res = IPHONE_E_UNKNOWN_ERROR;
+				} else {
+					res = IPHONE_E_PLIST_ERROR;
+				}
+				if (cmd_value) {
+					free(cmd_value);
+				}
+				plist_free(dict);
+				dict = NULL;
+				free(XML_content);
+				XML_content = NULL;
+			}
+		} else {
+			res = IPHONE_E_UNKNOWN_ERROR;
+		}
 	}
 
 	np_unlock(client);
-	return bytes;
+
+	return res;
+}
+
+/**
+ * Internally used thread function.
+ */
+gpointer iphone_np_notifier( gpointer arg )
+{
+	char *notification = NULL;
+	struct np_thread *npt = (struct np_thread*)arg;
+
+	if (!npt) return NULL;
+
+	log_debug_msg("%s: starting callback.\n", __func__);
+	while (npt->client->connection) {
+		iphone_np_get_notification(npt->client, &notification);
+		if (notification) {
+			npt->cbfunc(notification);
+			free(notification);
+			notification = NULL;
+		}
+		sleep(1);
+	}
+	if (npt) {
+		free(npt);
+	}
+
+	return NULL;
+}
+
+/**
+ * This function allows an application to define a callback function that will
+ * be called when a notification has been received.
+ * It will start a thread that polls for notifications and calls the callback
+ * function if a notification has been received.
+ *
+ * @param client the NP client
+ * @param notify_cb pointer to a callback function or NULL to de-register a
+ *        previously set callback function
+ *
+ * @return IPHONE_E_SUCCESS when the callback was successfully registered,
+ *         or an error value when an error occured.
+ */
+iphone_error_t iphone_np_set_notify_callback( iphone_np_client_t client, iphone_np_notify_cb_t notify_cb )
+{
+	if (!client) {
+		return IPHONE_E_INVALID_ARG;
+	}
+	iphone_error_t res = IPHONE_E_UNKNOWN_ERROR;
+
+	np_lock(client);
+	if (client->notifier) {
+		log_debug_msg("%s: callback already set, removing\n");
+		iphone_umux_client_t conn = client->connection;
+		client->connection = NULL;
+		g_thread_join(client->notifier);
+		client->notifier = NULL;
+		client->connection = conn;
+	}
+
+	if (notify_cb) {
+		struct np_thread *npt = (struct np_thread*)malloc(sizeof(struct np_thread));
+		if (npt) {
+			npt->client = client;
+			npt->cbfunc = notify_cb;
+
+			client->notifier = g_thread_create(iphone_np_notifier, npt, TRUE, NULL);
+			if (client->notifier) {
+				res = IPHONE_E_SUCCESS;
+			}
+		}
+	} else {
+		log_debug_msg("%s: no callback set\n", __func__);
+	}
+	np_unlock(client);
+
+	return res;
 }
diff --git a/src/NotificationProxy.h b/src/NotificationProxy.h
index 7b4b48d..3552b79 100644
--- a/src/NotificationProxy.h
+++ b/src/NotificationProxy.h
@@ -27,4 +27,20 @@
 struct iphone_np_client_int {
 	iphone_umux_client_t connection;
 	GMutex *mutex;
+	GThread *notifier;
 };
+
+static const char *np_default_notifications[10] = {
+	NP_SYNC_SUSPEND_REQUEST,
+	NP_SYNC_RESUME_REQUEST,
+	NP_PHONE_NUMBER_CHANGED,
+	NP_SYNC_CANCEL_REQUEST,
+	NP_DEVICE_NAME_CHANGED,
+	NP_ATTEMPTACTIVATION,
+	NP_DS_DOMAIN_CHANGED,
+	NP_APP_INSTALLED,
+	NP_APP_UNINSTALLED,
+	NULL
+};
+
+gpointer iphone_np_notifier( gpointer arg );
diff --git a/src/iphone.c b/src/iphone.c
index 3c3034e..9dd3c07 100644
--- a/src/iphone.c
+++ b/src/iphone.c
@@ -288,10 +288,11 @@
  * @param phone The iPhone to receive data from
  * @param data Where to put data read
  * @param datalen How much data to read in
+ * @param timeout How many milliseconds to wait for data
  * 
  * @return How many bytes were read in, or -1 on error.
  */
-int recv_from_phone(iphone_device_t phone, char *data, int datalen)
+int recv_from_phone(iphone_device_t phone, char *data, int datalen, int timeout)
 {
 	if (!phone)
 		return -1;
@@ -301,7 +302,7 @@
 		return -1;
 	log_debug_msg("recv_from_phone(): attempting to receive %i bytes\n", datalen);
 
-	bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, 3500);
+	bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, timeout);
 	if (bytes < 0) {
 		log_debug_msg("recv_from_phone(): libusb gave me the error %d: %s (%s)\n", bytes, usb_strerror(),
 					  strerror(-bytes));
diff --git a/src/iphone.h b/src/iphone.h
index 222a1be..15515e3 100644
--- a/src/iphone.h
+++ b/src/iphone.h
@@ -41,5 +41,5 @@
 
 // Function definitions
 int send_to_phone(iphone_device_t phone, char *data, int datalen);
-int recv_from_phone(iphone_device_t phone, char *data, int datalen);
+int recv_from_phone(iphone_device_t phone, char *data, int datalen, int timeout);
 #endif
diff --git a/src/lockdown.c b/src/lockdown.c
index c017cdf..5ade79a 100644
--- a/src/lockdown.c
+++ b/src/lockdown.c
@@ -472,6 +472,19 @@
 	return lockdownd_generic_get_value(control, "Key", "DevicePublicKey", public_key);
 }
 
+/** Askes for the device's name.
+ *
+ * @return IPHONE_E_SUCCESS on succes or an error value < 0 on failure.
+ */
+iphone_error_t lockdownd_get_device_name(iphone_lckd_client_t control, char **device_name)
+{
+	gnutls_datum_t temp = { NULL, 0 };
+	iphone_error_t res = lockdownd_generic_get_value(control, "Key", "DeviceName", &temp);
+	log_debug_msg("%s: %s\n", __func__, temp.data);
+	*device_name = (char*)temp.data;
+	return res;
+}
+
 /** Completes the entire lockdownd handshake.
  *
  * @param phone The iPhone
diff --git a/src/usbmux.c b/src/usbmux.c
index 22ce588..7d74b4b 100644
--- a/src/usbmux.c
+++ b/src/usbmux.c
@@ -143,7 +143,7 @@
 		if (send_to_phone(device, (char *) new_connection->header, sizeof(usbmux_tcp_header)) >= 0) {
 			usbmux_tcp_header *response;
 			response = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header));
-			bytes = recv_from_phone(device, (char *) response, sizeof(*response));
+			bytes = recv_from_phone(device, (char *) response, sizeof(*response), 3500);
 			if (response->tcp_flags != 0x12) {
 				free(response);
 				return IPHONE_E_UNKNOWN_ERROR;
@@ -268,10 +268,13 @@
  * @param connection The connection to receive data on.
  * @param data Where to put the data we receive. 
  * @param datalen How much data to read.
+ * @param recv_bytes Pointer to a uint32_t that will be set
+ *        to the number of bytes received.
+ * @param timeout How many milliseconds to wait for data.
  *
- * @return How many bytes were read, or -1 if something bad happens.
+ * @return IPHONE_E_SUCCESS on success, or and error value.
  */
-iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes)
+iphone_error_t iphone_mux_recv_timeout(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout)
 {
 
 	if (!client || !data || datalen == 0 || !recv_bytes)
@@ -323,7 +326,7 @@
 	buffer = (char *) malloc(sizeof(char) * 131072);	// make sure we get enough ;)
 
 	// See #3.
-	bytes = recv_from_phone(client->phone, buffer, 131072);
+	bytes = recv_from_phone(client->phone, buffer, 131072, timeout);
 	if (bytes < 28) {
 		free(buffer);
 		log_debug_msg("mux_recv: Did not even get the header.\n");
@@ -385,3 +388,23 @@
 	log_debug_msg("mux_recv: Heisenbug: bytes and datalen not matching up\n");
 	return IPHONE_E_UNKNOWN_ERROR;
 }
+
+/**
+ * This function is just like 'iphone_mux_recv_timeout' but you do not need
+ * to specify a timeout. It simply calls iphone_mux_recv_timeout with a
+ * timeout value of 3500 milliseconds.
+ *
+ * @param connection The connection to receive data on.
+ * @param data Where to put the data we receive.
+ * @param datalen How much data to read.
+ * @param recv_bytes Pointer to a uint32_t that will be set
+ *        to the number of bytes received.
+ *
+ * @return The return value of iphone_mux_recv_timeout.
+ *
+ * @see iphone_mux_recv_timeout
+ */
+iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes)
+{
+	return iphone_mux_recv_timeout(client, data, datalen, recv_bytes, 3500);
+}