summaryrefslogtreecommitdiffstats
path: root/src/NotificationProxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/NotificationProxy.c')
-rw-r--r--src/NotificationProxy.c404
1 files changed, 299 insertions, 105 deletions
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 @@ static void np_unlock(iphone_np_client_t client)
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 @@ static void np_unlock(iphone_np_client_t client)
*
* @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 @@ iphone_error_t iphone_np_new_client(iphone_device_t device, int src_port, int ds
client_loc->mutex = g_mutex_new();
+ client_loc->notifier = NULL;
+
*client = client_loc;
return IPHONE_E_SUCCESS;
}
@@ -83,91 +139,97 @@ iphone_error_t iphone_np_new_client(iphone_device_t device, int src_port, int ds
*
* @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);
+ res = np_plist_send(client, dict);
+ plist_free(dict);
- memcpy(sndbuf + sndlen, &nlen, 4);
- sndlen += 4;
+ if (res != IPHONE_E_SUCCESS) {
+ log_debug_msg("%s: Error sending XML plist to device!\n", __func__);
+ }
- memcpy(sndbuf + sndlen, XML_content, length);
- sndlen += length;
+ np_unlock(client);
+ return res;
+}
- plist_free(dict);
- dict = NULL;
- free(XML_content);
- XML_content = NULL;
+/** 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);
- log_debug_buffer(sndbuf, sndlen);
+ 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_mux_send(client->connection, sndbuf, sndlen, &bytes);
- if (bytes <= 0) {
- np_unlock(client);
- return bytes;
+ 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 bytes;
+ return res;
}
-/** Notifies the iphone to send a notification on certain events.
+
+/** Notifies the iphone to send a notification on specified events.
*
* observation messages seen so far:
* com.apple.itunes-client.syncCancelRequest
@@ -181,85 +243,217 @@ iphone_error_t iphone_np_post_notification(iphone_np_client_t client, const char
* 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);
+
+ if (!notifications) {
+ notifications = np_default_notifications;
+ }
while (notifications[i]) {
+ res = iphone_np_observe_notification(client, notifications[i]);
+ if (res != IPHONE_E_SUCCESS) {
+ break;
+ }
+ i++;
+ }
+
+ return res;
+}
- 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;
+/**
+ * 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;
+
+ if (!client || !client->connection || *notification) {
+ return IPHONE_E_INVALID_ARG;
}
- 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);
+ np_lock(client);
- nlen = htonl(length);
+ 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_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;
+ }
+ }
- memcpy(sndbuf + sndlen, &nlen, 4);
- sndlen += 4;
+ np_unlock(client);
- memcpy(sndbuf + sndlen, XML_content, length);
- sndlen += length;
+ return res;
+}
- plist_free(dict);
- dict = NULL;
- free(XML_content);
- XML_content = NULL;
+/**
+ * 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);
+ }
- log_debug_buffer(sndbuf, sndlen);
+ return NULL;
+}
- iphone_mux_send(client->connection, sndbuf, sndlen, &bytes);
- if (bytes <= 0) {
- np_unlock(client);
- return bytes;
+/**
+ * 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 bytes;
+
+ return res;
}