summaryrefslogtreecommitdiffstats
path: root/src/notification_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/notification_proxy.c')
-rw-r--r--src/notification_proxy.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/notification_proxy.c b/src/notification_proxy.c
new file mode 100644
index 0000000..0969985
--- /dev/null
+++ b/src/notification_proxy.c
@@ -0,0 +1,392 @@
1/*
2 * notification_proxy.c
3 * Notification Proxy implementation.
4 *
5 * Copyright (c) 2009 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <string.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <arpa/inet.h>
27#include <plist/plist.h>
28
29#include "notification_proxy.h"
30#include "property_list_service.h"
31#include "debug.h"
32
33struct np_thread {
34 np_client_t client;
35 np_notify_cb_t cbfunc;
36};
37
38/** Locks an NP client, done for thread safety stuff.
39 *
40 * @param client The NP
41 */
42static void np_lock(np_client_t client)
43{
44 debug_info("NP: Locked");
45 g_mutex_lock(client->mutex);
46}
47
48/** Unlocks an NP client, done for thread safety stuff.
49 *
50 * @param client The NP
51 */
52static void np_unlock(np_client_t client)
53{
54 debug_info("NP: Unlocked");
55 g_mutex_unlock(client->mutex);
56}
57
58/**
59 * Convert a property_list_service_error_t value to an np_error_t value.
60 * Used internally to get correct error codes.
61 *
62 * @param err A property_list_service_error_t error code
63 *
64 * @return A matching np_error_t error code,
65 * NP_E_UNKNOWN_ERROR otherwise.
66 */
67static np_error_t np_error(property_list_service_error_t err)
68{
69 switch (err) {
70 case PROPERTY_LIST_SERVICE_E_SUCCESS:
71 return NP_E_SUCCESS;
72 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
73 return NP_E_INVALID_ARG;
74 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
75 return NP_E_PLIST_ERROR;
76 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
77 return NP_E_CONN_FAILED;
78 default:
79 break;
80 }
81 return NP_E_UNKNOWN_ERROR;
82}
83
84/** Makes a connection to the NP service on the phone.
85 *
86 * @param device The device to connect to.
87 * @param port Destination port (usually given by lockdownd_start_service).
88 * @param client Pointer that will be set to a newly allocated np_client_t
89 * upon successful return.
90 *
91 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when device is NULL,
92 * or NP_E_CONN_FAILED when the connection to the device could not be
93 * established.
94 */
95np_error_t np_client_new(iphone_device_t device, uint16_t port, np_client_t *client)
96{
97 /* makes sure thread environment is available */
98 if (!g_thread_supported())
99 g_thread_init(NULL);
100
101 if (!device)
102 return NP_E_INVALID_ARG;
103
104 property_list_service_client_t plistclient = NULL;
105 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
106 return NP_E_CONN_FAILED;
107 }
108
109 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_int));
110 client_loc->parent = plistclient;
111
112 client_loc->mutex = g_mutex_new();
113
114 client_loc->notifier = NULL;
115
116 *client = client_loc;
117 return NP_E_SUCCESS;
118}
119
120/** Disconnects an NP client from the device.
121 *
122 * @param client The client to disconnect.
123 *
124 * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL.
125 */
126np_error_t np_client_free(np_client_t client)
127{
128 if (!client)
129 return NP_E_INVALID_ARG;
130
131 property_list_service_client_free(client->parent);
132 client->parent = NULL;
133 if (client->notifier) {
134 debug_info("joining np callback");
135 g_thread_join(client->notifier);
136 }
137 if (client->mutex) {
138 g_mutex_free(client->mutex);
139 }
140 free(client);
141
142 return NP_E_SUCCESS;
143}
144
145/** Sends a notification to the device's Notification Proxy.
146 *
147 * @param client The client to send to
148 * @param notification The notification message to send
149 *
150 * @return NP_E_SUCCESS on success, or an error returned by np_plist_send
151 */
152np_error_t np_post_notification(np_client_t client, const char *notification)
153{
154 if (!client || !notification) {
155 return NP_E_INVALID_ARG;
156 }
157 np_lock(client);
158
159 plist_t dict = plist_new_dict();
160 plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification"));
161 plist_dict_insert_item(dict,"Name", plist_new_string(notification));
162
163 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
164 plist_free(dict);
165
166 dict = plist_new_dict();
167 plist_dict_insert_item(dict,"Command", plist_new_string("Shutdown"));
168
169 res = np_error(property_list_service_send_xml_plist(client->parent, dict));
170 plist_free(dict);
171
172 if (res != NP_E_SUCCESS) {
173 debug_info("Error sending XML plist to device!");
174 }
175
176 np_unlock(client);
177 return res;
178}
179
180/** Notifies the iphone to send a notification on the specified event.
181 *
182 * @param client The client to send to
183 * @param notification The notifications that should be observed.
184 *
185 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client or
186 * notification are NULL, or an error returned by np_plist_send.
187 */
188np_error_t np_observe_notification( np_client_t client, const char *notification )
189{
190 if (!client || !notification) {
191 return NP_E_INVALID_ARG;
192 }
193 np_lock(client);
194
195 plist_t dict = plist_new_dict();
196 plist_dict_insert_item(dict,"Command", plist_new_string("ObserveNotification"));
197 plist_dict_insert_item(dict,"Name", plist_new_string(notification));
198
199 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
200 if (res != NP_E_SUCCESS) {
201 debug_info("Error sending XML plist to device!");
202 }
203 plist_free(dict);
204
205 np_unlock(client);
206 return res;
207}
208
209/** Notifies the iphone to send a notification on specified events.
210 *
211 * @param client The client to send to
212 * @param notification_spec Specification of the notifications that should be
213 * observed. This is expected to be an array of const char* that MUST have a
214 * terminating NULL entry.
215 *
216 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client is null,
217 * or an error returned by np_observe_notification.
218 */
219np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)
220{
221 int i = 0;
222 np_error_t res = NP_E_UNKNOWN_ERROR;
223 const char **notifications = notification_spec;
224
225 if (!client) {
226 return NP_E_INVALID_ARG;
227 }
228
229 if (!notifications) {
230 return NP_E_INVALID_ARG;
231 }
232
233 while (notifications[i]) {
234 res = np_observe_notification(client, notifications[i]);
235 if (res != NP_E_SUCCESS) {
236 break;
237 }
238 i++;
239 }
240
241 return res;
242}
243
244/**
245 * Checks if a notification has been sent.
246 *
247 * @param client NP to get a notification from
248 * @param notification Pointer to a buffer that will be allocated and filled
249 * with the notification that has been received.
250 *
251 * @return 0 if a notification has been received or nothing has been received,
252 * or a negative value if an error occured.
253 *
254 * @note You probably want to check out np_set_notify_callback
255 * @see np_set_notify_callback
256 */
257static int np_get_notification(np_client_t client, char **notification)
258{
259 int res = 0;
260 plist_t dict = NULL;
261
262 if (!client || !client->parent || *notification)
263 return -1;
264
265 np_lock(client);
266
267 property_list_service_receive_plist_with_timeout(client->parent, &dict, 500);
268 if (!dict) {
269 debug_info("NotificationProxy: no notification received!");
270 res = 0;
271 } else {
272 char *cmd_value = NULL;
273 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
274
275 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
276 plist_get_string_val(cmd_value_node, &cmd_value);
277 }
278
279 if (cmd_value && !strcmp(cmd_value, "RelayNotification")) {
280 char *name_value = NULL;
281 plist_t name_value_node = plist_dict_get_item(dict, "Name");
282
283 if (plist_get_node_type(name_value_node) == PLIST_STRING) {
284 plist_get_string_val(name_value_node, &name_value);
285 }
286
287 res = -2;
288 if (name_value_node && name_value) {
289 *notification = name_value;
290 debug_info("got notification %s\n", __func__, name_value);
291 res = 0;
292 }
293 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
294 debug_info("ERROR: NotificationProxy died!");
295 res = -1;
296 } else if (cmd_value) {
297 debug_info("unknown NotificationProxy command '%s' received!", cmd_value);
298 res = -1;
299 } else {
300 res = -2;
301 }
302 if (cmd_value) {
303 free(cmd_value);
304 }
305 plist_free(dict);
306 dict = NULL;
307 }
308
309 np_unlock(client);
310
311 return res;
312}
313
314/**
315 * Internally used thread function.
316 */
317gpointer np_notifier( gpointer arg )
318{
319 char *notification = NULL;
320 struct np_thread *npt = (struct np_thread*)arg;
321
322 if (!npt) return NULL;
323
324 debug_info("starting callback.");
325 while (npt->client->parent) {
326 np_get_notification(npt->client, &notification);
327 if (notification) {
328 npt->cbfunc(notification);
329 free(notification);
330 notification = NULL;
331 }
332 sleep(1);
333 }
334 if (npt) {
335 free(npt);
336 }
337
338 return NULL;
339}
340
341/**
342 * This function allows an application to define a callback function that will
343 * be called when a notification has been received.
344 * It will start a thread that polls for notifications and calls the callback
345 * function if a notification has been received.
346 *
347 * @param client the NP client
348 * @param notify_cb pointer to a callback function or NULL to de-register a
349 * previously set callback function.
350 *
351 * @note Only one callback function can be registered at the same time;
352 * any previously set callback function will be removed automatically.
353 *
354 * @return NP_E_SUCCESS when the callback was successfully registered,
355 * NP_E_INVALID_ARG when client is NULL, or NP_E_UNKNOWN_ERROR when
356 * the callback thread could no be created.
357 */
358np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb )
359{
360 if (!client)
361 return NP_E_INVALID_ARG;
362
363 np_error_t res = NP_E_UNKNOWN_ERROR;
364
365 np_lock(client);
366 if (client->notifier) {
367 debug_info("callback already set, removing\n");
368 property_list_service_client_t parent = client->parent;
369 client->parent = NULL;
370 g_thread_join(client->notifier);
371 client->notifier = NULL;
372 client->parent = parent;
373 }
374
375 if (notify_cb) {
376 struct np_thread *npt = (struct np_thread*)malloc(sizeof(struct np_thread));
377 if (npt) {
378 npt->client = client;
379 npt->cbfunc = notify_cb;
380
381 client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL);
382 if (client->notifier) {
383 res = NP_E_SUCCESS;
384 }
385 }
386 } else {
387 debug_info("no callback set");
388 }
389 np_unlock(client);
390
391 return res;
392}