1 /*
2 * notification_proxy.c
3 * com.apple.mobile.notification_proxy service 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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <plist/plist.h>
29
30 #include "notification_proxy.h"
31 #include "property_list_service.h"
32 #include "common/debug.h"
33
34 #ifdef WIN32
35 #define sleep(x) Sleep(x*1000)
36 #endif
37
38 struct np_thread {
39 np_client_t client;
40 np_notify_cb_t cbfunc;
41 void *user_data;
42 };
43
44 /**
45 * Locks a notification_proxy client, used for thread safety.
46 *
47 * @param client notification_proxy client to lock
48 */
np_lock(np_client_t client)49 static void np_lock(np_client_t client)
50 {
51 debug_info("Locked");
52 mutex_lock(&client->mutex);
53 }
54
55 /**
56 * Unlocks a notification_proxy client, used for thread safety.
57 *
58 * @param client notification_proxy client to unlock
59 */
np_unlock(np_client_t client)60 static void np_unlock(np_client_t client)
61 {
62 debug_info("Unlocked");
63 mutex_unlock(&client->mutex);
64 }
65
66 /**
67 * Convert a property_list_service_error_t value to an np_error_t value.
68 * Used internally to get correct error codes.
69 *
70 * @param err A property_list_service_error_t error code
71 *
72 * @return A matching np_error_t error code,
73 * NP_E_UNKNOWN_ERROR otherwise.
74 */
np_error(property_list_service_error_t err)75 static np_error_t np_error(property_list_service_error_t err)
76 {
77 switch (err) {
78 case PROPERTY_LIST_SERVICE_E_SUCCESS:
79 return NP_E_SUCCESS;
80 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
81 return NP_E_INVALID_ARG;
82 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
83 return NP_E_PLIST_ERROR;
84 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
85 return NP_E_CONN_FAILED;
86 default:
87 break;
88 }
89 return NP_E_UNKNOWN_ERROR;
90 }
91
np_client_new(idevice_t device,lockdownd_service_descriptor_t service,np_client_t * client)92 LIBIMOBILEDEVICE_API np_error_t np_client_new(idevice_t device, lockdownd_service_descriptor_t service, np_client_t *client)
93 {
94 property_list_service_client_t plistclient = NULL;
95 np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient));
96 if (err != NP_E_SUCCESS) {
97 return err;
98 }
99
100 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private));
101 client_loc->parent = plistclient;
102
103 mutex_init(&client_loc->mutex);
104 client_loc->notifier = THREAD_T_NULL;
105
106 *client = client_loc;
107 return NP_E_SUCCESS;
108 }
109
np_client_start_service(idevice_t device,np_client_t * client,const char * label)110 LIBIMOBILEDEVICE_API np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label)
111 {
112 np_error_t err = NP_E_UNKNOWN_ERROR;
113 service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err);
114 return err;
115 }
116
np_client_free(np_client_t client)117 LIBIMOBILEDEVICE_API np_error_t np_client_free(np_client_t client)
118 {
119 plist_t dict;
120 property_list_service_client_t parent;
121
122 if (!client)
123 return NP_E_INVALID_ARG;
124
125 dict = plist_new_dict();
126 plist_dict_set_item(dict,"Command", plist_new_string("Shutdown"));
127 property_list_service_send_xml_plist(client->parent, dict);
128 plist_free(dict);
129
130 parent = client->parent;
131 /* notifies the client->notifier thread that it should terminate */
132 client->parent = NULL;
133
134 if (client->notifier) {
135 debug_info("joining np callback");
136 thread_join(client->notifier);
137 thread_free(client->notifier);
138 client->notifier = THREAD_T_NULL;
139 } else {
140 dict = NULL;
141 property_list_service_receive_plist(parent, &dict);
142 if (dict) {
143 #ifndef STRIP_DEBUG_CODE
144 char *cmd_value = NULL;
145 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
146 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
147 plist_get_string_val(cmd_value_node, &cmd_value);
148 }
149 if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
150 // this is the expected answer
151 } else {
152 debug_info("Did not get ProxyDeath but:");
153 debug_plist(dict);
154 }
155 if (cmd_value) {
156 free(cmd_value);
157 }
158 #endif
159 plist_free(dict);
160 }
161 }
162
163 property_list_service_client_free(parent);
164
165 mutex_destroy(&client->mutex);
166 free(client);
167
168 return NP_E_SUCCESS;
169 }
170
np_post_notification(np_client_t client,const char * notification)171 LIBIMOBILEDEVICE_API np_error_t np_post_notification(np_client_t client, const char *notification)
172 {
173 if (!client || !notification) {
174 return NP_E_INVALID_ARG;
175 }
176 np_lock(client);
177
178 plist_t dict = plist_new_dict();
179 plist_dict_set_item(dict,"Command", plist_new_string("PostNotification"));
180 plist_dict_set_item(dict,"Name", plist_new_string(notification));
181
182 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
183 plist_free(dict);
184
185 if (res != NP_E_SUCCESS) {
186 debug_info("Error sending XML plist to device!");
187 }
188 np_unlock(client);
189 return res;
190 }
191
internal_np_observe_notification(np_client_t client,const char * notification)192 static np_error_t internal_np_observe_notification(np_client_t client, const char *notification)
193 {
194 plist_t dict = plist_new_dict();
195 plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification"));
196 plist_dict_set_item(dict,"Name", plist_new_string(notification));
197
198 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
199 if (res != NP_E_SUCCESS) {
200 debug_info("Error sending XML plist to device!");
201 }
202 plist_free(dict);
203
204 return res;
205 }
206
np_observe_notification(np_client_t client,const char * notification)207 LIBIMOBILEDEVICE_API np_error_t np_observe_notification( np_client_t client, const char *notification )
208 {
209 if (!client || !notification) {
210 return NP_E_INVALID_ARG;
211 }
212 np_lock(client);
213 np_error_t res = internal_np_observe_notification(client, notification);
214 np_unlock(client);
215 return res;
216 }
217
np_observe_notifications(np_client_t client,const char ** notification_spec)218 LIBIMOBILEDEVICE_API np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)
219 {
220 int i = 0;
221 np_error_t res = NP_E_UNKNOWN_ERROR;
222 const char **notifications = notification_spec;
223
224 if (!client) {
225 return NP_E_INVALID_ARG;
226 }
227
228 if (!notifications) {
229 return NP_E_INVALID_ARG;
230 }
231
232 np_lock(client);
233 while (notifications[i]) {
234 res = internal_np_observe_notification(client, notifications[i]);
235 if (res != NP_E_SUCCESS) {
236 break;
237 }
238 i++;
239 }
240 np_unlock(client);
241
242 return res;
243 }
244
245 /**
246 * Checks if a notification has been sent by the device.
247 *
248 * @param client NP to get a notification from
249 * @param notification Pointer to a buffer that will be allocated and filled
250 * with the notification that has been received.
251 *
252 * @return 0 if a notification has been received or nothing has been received,
253 * or a negative value if an error occurred.
254 *
255 * @note You probably want to check out np_set_notify_callback
256 * @see np_set_notify_callback
257 */
np_get_notification(np_client_t client,char ** notification)258 static int np_get_notification(np_client_t client, char **notification)
259 {
260 int res = 0;
261 plist_t dict = NULL;
262
263 if (!client || !client->parent || *notification)
264 return -1;
265
266 np_lock(client);
267
268 property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500);
269 if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) {
270 debug_info("NotificationProxy: no notification received!");
271 res = 0;
272 } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
273 debug_info("NotificationProxy: error %d occurred!", perr);
274 res = perr;
275 }
276 if (dict) {
277 char *cmd_value = NULL;
278 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
279
280 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
281 plist_get_string_val(cmd_value_node, &cmd_value);
282 }
283
284 if (cmd_value && !strcmp(cmd_value, "RelayNotification")) {
285 char *name_value = NULL;
286 plist_t name_value_node = plist_dict_get_item(dict, "Name");
287
288 if (plist_get_node_type(name_value_node) == PLIST_STRING) {
289 plist_get_string_val(name_value_node, &name_value);
290 }
291
292 res = -2;
293 if (name_value_node && name_value) {
294 *notification = name_value;
295 debug_info("got notification %s", __func__, name_value);
296 res = 0;
297 }
298 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
299 debug_info("NotificationProxy died!");
300 res = -1;
301 } else if (cmd_value) {
302 debug_info("unknown NotificationProxy command '%s' received!", cmd_value);
303 res = -1;
304 } else {
305 res = -2;
306 }
307 if (cmd_value) {
308 free(cmd_value);
309 }
310 plist_free(dict);
311 dict = NULL;
312 }
313
314 np_unlock(client);
315
316 return res;
317 }
318
319 /**
320 * Internally used thread function.
321 */
np_notifier(void * arg)322 void* np_notifier( void* arg )
323 {
324 char *notification = NULL;
325 struct np_thread *npt = (struct np_thread*)arg;
326
327 if (!npt) return NULL;
328
329 debug_info("starting callback.");
330 while (npt->client->parent) {
331 if (np_get_notification(npt->client, ¬ification) < 0) {
332 npt->cbfunc("", npt->user_data);
333 break;
334 }
335 if (notification) {
336 npt->cbfunc(notification, npt->user_data);
337 free(notification);
338 notification = NULL;
339 }
340 sleep(1);
341 }
342 if (npt) {
343 free(npt);
344 }
345
346 return NULL;
347 }
348
np_set_notify_callback(np_client_t client,np_notify_cb_t notify_cb,void * user_data)349 LIBIMOBILEDEVICE_API np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data )
350 {
351 if (!client)
352 return NP_E_INVALID_ARG;
353
354 np_error_t res = NP_E_UNKNOWN_ERROR;
355
356 np_lock(client);
357 if (client->notifier) {
358 debug_info("callback already set, removing");
359 property_list_service_client_t parent = client->parent;
360 client->parent = NULL;
361 thread_join(client->notifier);
362 thread_free(client->notifier);
363 client->notifier = THREAD_T_NULL;
364 client->parent = parent;
365 }
366
367 if (notify_cb) {
368 struct np_thread *npt = (struct np_thread*)malloc(sizeof(struct np_thread));
369 if (npt) {
370 npt->client = client;
371 npt->cbfunc = notify_cb;
372 npt->user_data = user_data;
373
374 if (thread_new(&client->notifier, np_notifier, npt) == 0) {
375 res = NP_E_SUCCESS;
376 }
377 }
378 } else {
379 debug_info("no callback set");
380 }
381 np_unlock(client);
382
383 return res;
384 }
385