summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/InstallationProxy.c824
-rw-r--r--src/InstallationProxy.h34
-rw-r--r--src/Makefile.am2
-rw-r--r--src/NotificationProxy.c39
-rw-r--r--src/SBServices.c291
-rw-r--r--src/SBServices.h33
6 files changed, 1213 insertions, 10 deletions
diff --git a/src/InstallationProxy.c b/src/InstallationProxy.c
new file mode 100644
index 0000000..917886d
--- /dev/null
+++ b/src/InstallationProxy.c
@@ -0,0 +1,824 @@
1/*
2 * InstallationProxy.c
3 * Installation 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 <stdlib.h>
24#include <unistd.h>
25#include <arpa/inet.h>
26#include <plist/plist.h>
27
28#include "InstallationProxy.h"
29#include "iphone.h"
30#include "utils.h"
31
32struct instproxy_status_data {
33 instproxy_client_t client;
34 instproxy_status_cb_t cbfunc;
35 char *operation;
36};
37
38/** Locks an installation_proxy client, done for thread safety stuff.
39 *
40 * @param client The installation_proxy client to lock
41 */
42static void instproxy_lock(instproxy_client_t client)
43{
44 log_debug_msg("InstallationProxy: Locked\n");
45 g_mutex_lock(client->mutex);
46}
47
48/** Unlocks an installation_proxy client, done for thread safety stuff.
49 *
50 * @param client The installation_proxy client to lock
51 */
52static void instproxy_unlock(instproxy_client_t client)
53{
54 log_debug_msg("InstallationProxy: Unlocked\n");
55 g_mutex_unlock(client->mutex);
56}
57
58/**
59 * Sends an xml plist to the device using the connection specified in client.
60 * This function is only used internally.
61 *
62 * @param client The installation_proxy to send data to
63 * @param plist plist to send
64 *
65 * @return INSTPROXY_E_SUCCESS on success, INSTPROXY_E_INVALID_ARG when client
66 * or plist are NULL, INSTPROXY_E_PLIST_ERROR when dict is not a valid
67 * plist, or INSTPROXY_E_UNKNOWN_ERROR when an unspecified error occurs.
68 */
69static instproxy_error_t instproxy_plist_send(instproxy_client_t client, plist_t plist)
70{
71 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
72 char *XML_content = NULL;
73 uint32_t length = 0;
74 uint32_t nlen = 0;
75 int bytes = 0;
76
77 if (!client || !plist) {
78 return INSTPROXY_E_INVALID_ARG;
79 }
80
81 plist_to_xml(plist, &XML_content, &length);
82
83 if (!XML_content || length == 0) {
84 return INSTPROXY_E_PLIST_ERROR;
85 }
86
87 nlen = htonl(length);
88 log_debug_msg("%s: sending %d bytes\n", __func__, length);
89 iphone_device_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes);
90 if (bytes == sizeof(nlen)) {
91 iphone_device_send(client->connection, XML_content, length, (uint32_t*)&bytes);
92 if (bytes > 0) {
93 log_debug_msg("%s: received %d bytes\n", __func__, bytes);
94 log_debug_buffer(XML_content, bytes);
95 if ((uint32_t)bytes == length) {
96 res = INSTPROXY_E_SUCCESS;
97 } else {
98 log_debug_msg("%s: ERROR: Could not send all data (%d of %d)!\n", __func__, bytes, length);
99 }
100 }
101 }
102 if (bytes <= 0) {
103 log_dbg_msg(DBGMASK_INSTPROXY, "%s: ERROR: sending to device failed.\n", __func__);
104 }
105
106 free(XML_content);
107
108 return res;
109}
110
111/**
112 * Receives an xml plist from the device using the connection specified in
113 * client.
114 * This function is only used internally.
115 *
116 * @param client The installation_proxy to receive data from
117 * @param plist pointer to a plist_t that will point to the received plist
118 * upon successful return
119 *
120 * @return INSTPROXY_E_SUCCESS on success, INSTPROXY_E_INVALID_ARG when client
121 * or *plist are NULL, INSTPROXY_E_PLIST_ERROR when dict is not a valid
122 * plist, or INSTPROXY_E_UNKNOWN_ERROR when an unspecified error occurs.
123 */
124static instproxy_error_t instproxy_plist_recv(instproxy_client_t client, plist_t *plist)
125{
126 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
127 char *XML_content = NULL;
128 uint32_t pktlen = 0;
129 uint32_t bytes = 0;
130
131 if (!client || !plist) {
132 return INSTPROXY_E_INVALID_ARG;
133 }
134
135 iphone_device_recv_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, 300000); /* 5 minute timeout should be enough */
136 log_debug_msg("%s: initial read=%i\n", __func__, bytes);
137 if (bytes < 4) {
138 log_dbg_msg(DBGMASK_INSTPROXY, "%s: initial read failed!\n");
139 } else {
140 if ((char)pktlen == 0) {
141 uint32_t curlen = 0;
142 pktlen = ntohl(pktlen);
143 log_debug_msg("%s: %d bytes following\n", __func__, pktlen);
144 XML_content = (char*)malloc(pktlen);
145
146 while (curlen < pktlen) {
147 iphone_device_recv(client->connection, XML_content+curlen, pktlen-curlen, &bytes);
148 if (bytes <= 0) {
149 res = INSTPROXY_E_UNKNOWN_ERROR;
150 break;
151 }
152 log_debug_msg("%s: received %d bytes\n", __func__, bytes);
153 curlen += bytes;
154 }
155 log_debug_buffer(XML_content, pktlen);
156 plist_from_xml(XML_content, pktlen, plist);
157 res = INSTPROXY_E_SUCCESS;
158 free(XML_content);
159 XML_content = NULL;
160 } else {
161 res = INSTPROXY_E_UNKNOWN_ERROR;
162 }
163 }
164 return res;
165}
166
167/**
168 * Creates a new installation_proxy client
169 *
170 * @param device The device to connect to
171 * @param dst_port Destination port (usually given by lockdownd_start_service).
172 * @param client Pointer that will be set to a newly allocated
173 * instproxy_client_t upon successful return.
174 *
175 * @return INSTPROXY_E_SUCCESS on success, or an INSTPROXY_E_* error value
176 * when an error occured.
177 */
178instproxy_error_t instproxy_client_new(iphone_device_t device, int dst_port, instproxy_client_t *client)
179{
180 /* makes sure thread environment is available */
181 if (!g_thread_supported())
182 g_thread_init(NULL);
183
184 if (!device)
185 return INSTPROXY_E_INVALID_ARG;
186
187 /* Attempt connection */
188 iphone_connection_t connection = NULL;
189 if (iphone_device_connect(device, dst_port, &connection) != IPHONE_E_SUCCESS) {
190 return INSTPROXY_E_CONN_FAILED;
191 }
192
193 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_int));
194 client_loc->connection = connection;
195 client_loc->mutex = g_mutex_new();
196 client_loc->status_updater = NULL;
197
198 *client = client_loc;
199 return INSTPROXY_E_SUCCESS;
200}
201
202/**
203 * Frees an installation_proxy client.
204 *
205 * @param client The installation_proxy client to free.
206 *
207 * @return INSTPROXY_E_SUCCESS on success
208 * or INSTPROXY_E_INVALID_ARG if client is NULL.
209 */
210instproxy_error_t instproxy_client_free(instproxy_client_t client)
211{
212 if (!client)
213 return INSTPROXY_E_INVALID_ARG;
214
215 iphone_device_disconnect(client->connection);
216 client->connection = NULL;
217 if (client->status_updater) {
218 log_dbg_msg(DBGMASK_INSTPROXY, "joining status_updater");
219 g_thread_join(client->status_updater);
220 }
221 if (client->mutex) {
222 g_mutex_free(client->mutex);
223 }
224 free(client);
225
226 return INSTPROXY_E_SUCCESS;
227}
228
229/**
230 * List installed applications. This function runs synchronously.
231 *
232 * @param client The connected installation_proxy client
233 * @param apptype The type of applications to list.
234 * @param result Pointer that will be set to a plist that will hold an array
235 * of PLIST_DICT holding information about the applications found.
236 *
237 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
238 * an error occured.
239 */
240instproxy_error_t instproxy_browse(instproxy_client_t client, instproxy_apptype_t apptype, plist_t *result)
241{
242 if (!client || !client->connection || !result)
243 return INSTPROXY_E_INVALID_ARG;
244
245 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
246 int browsing = 0;
247 plist_t apps_array = NULL;
248
249 plist_t dict = plist_new_dict();
250 if (apptype != INSTPROXY_APPTYPE_ALL) {
251 plist_t client_opts = plist_new_dict();
252 plist_t p_apptype = NULL;
253 switch (apptype) {
254 case INSTPROXY_APPTYPE_SYSTEM:
255 p_apptype = plist_new_string("System");
256 break;
257 case INSTPROXY_APPTYPE_USER:
258 p_apptype = plist_new_string("User");
259 break;
260 default:
261 log_dbg_msg(DBGMASK_INSTPROXY, "%s: unknown apptype %d given, using INSTPROXY_APPTYPE_USER instead\n", __func__, apptype);
262 p_apptype = plist_new_string("User");
263 break;
264 }
265 plist_dict_insert_item(client_opts, "ApplicationType", p_apptype);
266 plist_dict_insert_item(dict, "ClientOptions", client_opts);
267 }
268 plist_dict_insert_item(dict, "Command", plist_new_string("Browse"));
269
270 instproxy_lock(client);
271 res = instproxy_plist_send(client, dict);
272 plist_free(dict);
273 if (res != INSTPROXY_E_SUCCESS) {
274 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist\n", __func__);
275 goto leave_unlock;
276 }
277
278 apps_array = plist_new_array();
279
280 do {
281 browsing = 0;
282 dict = NULL;
283 res = instproxy_plist_recv(client, &dict);
284 if (res != INSTPROXY_E_SUCCESS) {
285 break;
286 }
287 if (dict) {
288 uint64_t i;
289 uint64_t current_amount = 0;
290 char *status = NULL;
291 plist_t camount = plist_dict_get_item(dict, "CurrentAmount");
292 plist_t pstatus = plist_dict_get_item(dict, "Status");
293 if (camount) {
294 plist_get_uint_val(camount, &current_amount);
295 }
296 if (current_amount > 0) {
297 plist_t current_list = plist_dict_get_item(dict, "CurrentList");
298 for (i = 0; current_list && (i < current_amount); i++) {
299 plist_t item = plist_array_get_item(current_list, i);
300 plist_array_append_item(apps_array, plist_copy(item));
301 }
302 }
303 if (pstatus) {
304 plist_get_string_val(pstatus, &status);
305 }
306 if (status) {
307 if (!strcmp(status, "BrowsingApplications")) {
308 browsing = 1;
309 } else if (!strcmp(status, "Complete")) {
310 log_dbg_msg(DBGMASK_INSTPROXY, "%s: Browsing applications completed\n");
311 res = INSTPROXY_E_SUCCESS;
312 }
313 free(status);
314 }
315 plist_free(dict);
316 }
317 } while (browsing);
318
319 if (res == INSTPROXY_E_SUCCESS) {
320 *result = apps_array;
321 }
322
323leave_unlock:
324 instproxy_unlock(client);
325 return res;
326}
327
328/**
329 * Internally used function that will synchronously receive messages from
330 * the specified installation_proxy until it completes or an error occurs.
331 *
332 * If status_cb is not NULL, the callback function will be called each time
333 * a status update or error message is received.
334 *
335 * @param client The connected installation proxy client
336 * @param status_cb Pointer to a callback function or NULL
337 * @param operation Operation name. Will be passed to the callback function
338 * in async mode or shown in debug messages in sync mode.
339 */
340static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation)
341{
342 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
343 int ok = 1;
344 plist_t dict = NULL;
345
346 do {
347 instproxy_lock(client);
348 res = instproxy_plist_recv(client, &dict);
349 instproxy_unlock(client);
350 if (res != INSTPROXY_E_SUCCESS) {
351 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not receive plist, error %d\n", __func__, res);
352 break;
353 }
354 if (dict) {
355 /* invoke callback function */
356 if (status_cb) {
357 status_cb(operation, dict);
358 }
359 /* check for 'Error', so we can abort cleanly */
360 plist_t err = plist_dict_get_item(dict, "Error");
361 if (err) {
362#ifndef STRIP_DEBUG_CODE
363 char *err_msg = NULL;
364 plist_get_string_val(err, &err_msg);
365 if (err_msg) {
366 log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): ERROR: %s\n", __func__, operation, err_msg);
367 free(err_msg);
368 }
369#endif
370 ok = 0;
371 res = INSTPROXY_E_OP_FAILED;
372 }
373 /* get 'Status' */
374 plist_t status = plist_dict_get_item(dict, "Status");
375 if (status) {
376 char *status_msg = NULL;
377 plist_get_string_val(status, &status_msg);
378 if (status_msg) {
379 if (!strcmp(status_msg, "Complete")) {
380 ok = 0;
381 res = INSTPROXY_E_SUCCESS;
382 }
383#ifndef STRIP_DEBUG_CODE
384 plist_t npercent = plist_dict_get_item(dict, "PercentComplete");
385 if (npercent) {
386 uint64_t val = 0;
387 int percent;
388 plist_get_uint_val(npercent, &val);
389 percent = val;
390 log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): %s (%d%%)\n", __func__, operation, status_msg, percent);
391 } else {
392 log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): %s\n", __func__, operation, status_msg);
393 }
394#endif
395 free(status_msg);
396 }
397 }
398 plist_free(dict);
399 dict = NULL;
400 }
401 } while (ok && client->connection);
402
403 return res;
404}
405
406/**
407 * Internally used status updater thread function that will call the specified
408 * callback function when status update messages (or error messages) are
409 * received.
410 *
411 * @param arg Pointer to an allocated struct instproxy_status_data that holds
412 * the required data about the connected client and the callback function.
413 *
414 * @return Always NULL.
415 */
416static gpointer instproxy_status_updater(gpointer arg)
417{
418 struct instproxy_status_data *data = (struct instproxy_status_data*)arg;
419
420 /* run until the operation is complete or an error occurs */
421 (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation);
422
423 /* cleanup */
424 instproxy_lock(data->client);
425 log_dbg_msg(DBGMASK_INSTPROXY, "%s: done, cleaning up.\n", __func__);
426 if (data->operation) {
427 free(data->operation);
428 }
429 data->client->status_updater = NULL;
430 instproxy_unlock(data->client);
431 free(data);
432
433 return NULL;
434}
435
436/**
437 * Internally used helper function that creates a status updater thread which
438 * will call the passed callback function when status updates occur.
439 * If status_cb is NULL no thread will be created, but the operation will
440 * run synchronously until it completes or an error occurs.
441 *
442 * @param client The connected installation proxy client
443 * @param status_cb Pointer to a callback function or NULL
444 * @param operation Operation name. Will be passed to the callback function
445 * in async mode or shown in debug messages in sync mode.
446 *
447 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or
448 * when the operation completed successfully (sync).
449 * An INSTPROXY_E_* error value is returned if an error occured.
450 */
451static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation)
452{
453 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
454 if (status_cb) {
455 /* async mode */
456 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data));
457 if (data) {
458 data->client = client;
459 data->cbfunc = status_cb;
460 data->operation = strdup(operation);
461
462 client->status_updater = g_thread_create(instproxy_status_updater, data, TRUE, NULL);
463 if (client->status_updater) {
464 res = INSTPROXY_E_SUCCESS;
465 }
466 }
467 } else {
468 /* sync mode */
469 res = instproxy_perform_operation(client, NULL, operation);
470 }
471 return res;
472}
473
474
475/**
476 * Internal function used by instproxy_install and instproxy_upgrade.
477 *
478 * @param client The connected installation_proxy client
479 * @param pkg_path Path of the installation package (inside the AFC jail)
480 * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
481 * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
482 * @param status_cb Callback function for progress and status information. If
483 * NULL is passed, this function will run synchronously.
484 * @param command The command to execute.
485 *
486 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
487 * an error occured.
488 */
489static 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)
490{
491 if (!client || !client->connection || !pkg_path) {
492 return INSTPROXY_E_INVALID_ARG;
493 }
494 if (sinf && (plist_get_node_type(sinf) != PLIST_DATA)) {
495 log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): ERROR: sinf data is not a PLIST_DATA node!\n", __func__, command);
496 return INSTPROXY_E_INVALID_ARG;
497 }
498 if (metadata && (plist_get_node_type(metadata) != PLIST_DATA)) {
499 log_dbg_msg(DBGMASK_INSTPROXY, "%s(%s): ERROR: metadata is not a PLIST_DATA node!\n", __func__, command);
500 return INSTPROXY_E_INVALID_ARG;
501 }
502
503 if (client->status_updater) {
504 return INSTPROXY_E_OP_IN_PROGRESS;
505 }
506
507 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
508
509 plist_t dict = plist_new_dict();
510 if (sinf && metadata) {
511 plist_t client_opts = plist_new_dict();
512 plist_dict_insert_item(client_opts, "ApplicationSINF", plist_copy(sinf));
513 plist_dict_insert_item(client_opts, "iTunesMetadata", plist_copy(metadata));
514 plist_dict_insert_item(dict, "ClientOptions", client_opts);
515 }
516 plist_dict_insert_item(dict, "Command", plist_new_string(command));
517 plist_dict_insert_item(dict, "PackagePath", plist_new_string(pkg_path));
518
519 instproxy_lock(client);
520 res = instproxy_plist_send(client, dict);
521 instproxy_unlock(client);
522
523 plist_free(dict);
524
525 if (res != INSTPROXY_E_SUCCESS) {
526 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res);
527 return res;
528 }
529
530 return instproxy_create_status_updater(client, status_cb, command);
531}
532
533/**
534 * Install an application on the device.
535 *
536 * @param client The connected installation_proxy client
537 * @param pkg_path Path of the installation package (inside the AFC jail)
538 * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
539 * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
540 * @param status_cb Callback function for progress and status information. If
541 * NULL is passed, this function will run synchronously.
542 *
543 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
544 * an error occured.
545 *
546 * @note If a callback function is given (async mode), this function returns
547 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
548 * created successfully; any error occuring during the operation has to be
549 * handled inside the specified callback function.
550 */
551instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb)
552{
553 return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Install");
554}
555
556/**
557 * Upgrade an application on the device. This function is nearly the same as
558 * instproxy_install; the difference is that the installation progress on the
559 * device is faster if the application is already installed.
560 *
561 * @param client The connected installation_proxy client
562 * @param pkg_path Path of the installation package (inside the AFC jail)
563 * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
564 * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
565 * @param status_cb Callback function for progress and status information. If
566 * NULL is passed, this function will run synchronously.
567 *
568 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
569 * an error occured.
570 *
571 * @note If a callback function is given (async mode), this function returns
572 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
573 * created successfully; any error occuring during the operation has to be
574 * handled inside the specified callback function.
575 */
576instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb)
577{
578 return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Upgrade");
579}
580
581/**
582 * Uninstall an application from the device.
583 *
584 * @param client The connected installation proxy client
585 * @param appid ApplicationIdentifier of the app to uninstall
586 * @param status_cb Callback function for progress and status information. If
587 * NULL is passed, this function will run synchronously.
588 *
589 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
590 * an error occured.
591 *
592 * @note If a callback function is given (async mode), this function returns
593 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
594 * created successfully; any error occuring during the operation has to be
595 * handled inside the specified callback function.
596 */
597instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
598{
599 if (!client || !client->connection || !appid) {
600 return INSTPROXY_E_INVALID_ARG;
601 }
602
603 if (client->status_updater) {
604 return INSTPROXY_E_OP_IN_PROGRESS;
605 }
606
607 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
608 plist_t dict = plist_new_dict();
609 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
610 plist_dict_insert_item(dict, "Command", plist_new_string("Uninstall"));
611
612 instproxy_lock(client);
613 res = instproxy_plist_send(client, dict);
614 instproxy_unlock(client);
615
616 plist_free(dict);
617
618 if (res != INSTPROXY_E_SUCCESS) {
619 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res);
620 return res;
621 }
622
623 return instproxy_create_status_updater(client, status_cb, "Uninstall");
624}
625
626/**
627 * List archived applications. This function runs synchronously.
628 *
629 * @see instproxy_archive
630 *
631 * @param client The connected installation_proxy client
632 * @param result Pointer that will be set to a plist containing a PLIST_DICT
633 * holding information about the archived applications found.
634 *
635 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
636 * an error occured.
637 */
638instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t *result)
639{
640 if (!client || !client->connection || !result)
641 return INSTPROXY_E_INVALID_ARG;
642
643 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
644
645 plist_t dict = plist_new_dict();
646 plist_dict_insert_item(dict, "Command", plist_new_string("LookupArchives"));
647
648 instproxy_lock(client);
649
650 res = instproxy_plist_send(client, dict);
651 plist_free(dict);
652
653 if (res != INSTPROXY_E_SUCCESS) {
654 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res);
655 goto leave_unlock;
656 }
657
658 res = instproxy_plist_recv(client, result);
659 if (res != INSTPROXY_E_SUCCESS) {
660 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not receive plist, error %d\n", __func__, res);
661 goto leave_unlock;
662 }
663
664 res = INSTPROXY_E_SUCCESS;
665
666leave_unlock:
667 instproxy_unlock(client);
668 return res;
669}
670
671/**
672 * Archive an application on the device.
673 * This function tells the device to make an archive of the specified
674 * application. This results in the device creating a ZIP archive in the
675 * 'ApplicationArchives' directory and uninstalling the application.
676 *
677 * @param client The connected installation proxy client
678 * @param appid ApplicationIdentifier of the app to archive.
679 * @param options This is either 0 for default behaviour (make an archive
680 * including app/user settings etc. AND uninstall the application),
681 * or one or a combination of the following options:
682 * INSTPROXY_ARCHIVE_APP_ONLY (1)
683 * INSTPROXY_ARCHIVE_SKIP_UNINSTALL (2)
684 * @param status_cb Callback function for progress and status information. If
685 * NULL is passed, this function will run synchronously.
686 *
687 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
688 * an error occured.
689 *
690 * @note If a callback function is given (async mode), this function returns
691 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
692 * created successfully; any error occuring during the operation has to be
693 * handled inside the specified callback function.
694 */
695instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, uint32_t options, instproxy_status_cb_t status_cb)
696{
697 if (!client || !client->connection || !appid)
698 return INSTPROXY_E_INVALID_ARG;
699
700 if (client->status_updater) {
701 return INSTPROXY_E_OP_IN_PROGRESS;
702 }
703
704 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
705
706 plist_t dict = plist_new_dict();
707 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
708 if (options > 0) {
709 plist_t client_opts = plist_new_dict();
710 if (options & INSTPROXY_ARCHIVE_APP_ONLY) {
711 plist_dict_insert_item(client_opts, "ArchiveType", plist_new_string("ApplicationOnly"));
712 }
713 if (options & INSTPROXY_ARCHIVE_SKIP_UNINSTALL) {
714 plist_dict_insert_item(client_opts, "SkipUninstall", plist_new_bool(1));
715 }
716 plist_dict_insert_item(dict, "ClientOptions", client_opts);
717 }
718 plist_dict_insert_item(dict, "Command", plist_new_string("Archive"));
719
720 instproxy_lock(client);
721 res = instproxy_plist_send(client, dict);
722 instproxy_unlock(client);
723
724 plist_free(dict);
725
726 if (res != INSTPROXY_E_SUCCESS) {
727 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res);
728 return res;
729 }
730 return instproxy_create_status_updater(client, status_cb, "Archive");
731}
732
733/**
734 * Restore a previously archived application on the device.
735 * This function is the counterpart to instproxy_archive.
736 * @see instproxy_archive
737 *
738 * @param client The connected installation proxy client
739 * @param appid ApplicationIdentifier of the app to restore.
740 * @param status_cb Callback function for progress and status information. If
741 * NULL is passed, this function will run synchronously.
742 *
743 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
744 * an error occured.
745 *
746 * @note If a callback function is given (async mode), this function returns
747 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
748 * created successfully; any error occuring during the operation has to be
749 * handled inside the specified callback function.
750 */
751instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
752{
753 if (!client || !client->connection || !appid)
754 return INSTPROXY_E_INVALID_ARG;
755
756 if (client->status_updater) {
757 return INSTPROXY_E_OP_IN_PROGRESS;
758 }
759
760 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
761
762 plist_t dict = plist_new_dict();
763 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
764 plist_dict_insert_item(dict, "Command", plist_new_string("Restore"));
765
766 instproxy_lock(client);
767 res = instproxy_plist_send(client, dict);
768 instproxy_unlock(client);
769
770 plist_free(dict);
771
772 if (res != INSTPROXY_E_SUCCESS) {
773 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res);
774 return res;
775 }
776 return instproxy_create_status_updater(client, status_cb, "Restore");
777}
778
779/**
780 * Removes a previously archived application from the device.
781 * This function removes the ZIP archive from the 'ApplicationArchives'
782 * directory.
783 *
784 * @param client The connected installation proxy client
785 * @param appid ApplicationIdentifier of the archived app to remove.
786 * @param status_cb Callback function for progress and status information. If
787 * NULL is passed, this function will run synchronously.
788 *
789 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
790 * an error occured.
791 *
792 * @note If a callback function is given (async mode), this function returns
793 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
794 * created successfully; any error occuring during the operation has to be
795 * handled inside the specified callback function.
796 */
797instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
798{
799 if (!client || !client->connection || !appid)
800 return INSTPROXY_E_INVALID_ARG;
801
802 if (client->status_updater) {
803 return INSTPROXY_E_OP_IN_PROGRESS;
804 }
805
806 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
807
808 plist_t dict = plist_new_dict();
809 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
810 plist_dict_insert_item(dict, "Command", plist_new_string("RemoveArchive"));
811
812 instproxy_lock(client);
813 res = instproxy_plist_send(client, dict);
814 instproxy_unlock(client);
815
816 plist_free(dict);
817
818 if (res != INSTPROXY_E_SUCCESS) {
819 log_dbg_msg(DBGMASK_INSTPROXY, "%s: could not send plist, error %d\n", __func__, res);
820 return res;
821 }
822 return instproxy_create_status_updater(client, status_cb, "RemoveArchive");
823}
824
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 @@
1/*
2 * InstallationProxy.h
3 * Installation Proxy header file.
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#ifndef IINSTALLATION_PROXY_H
22#define IINSTALLATION_PROXY_H
23
24#include <glib.h>
25
26#include "libiphone/installation_proxy.h"
27
28struct instproxy_client_int {
29 iphone_connection_t connection;
30 GMutex *mutex;
31 GThread *status_updater;
32};
33
34#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index ce1d237..9b42f1c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,8 @@ libiphone_la_SOURCES = iphone.c iphone.h \
8 lockdown.c lockdown.h\ 8 lockdown.c lockdown.h\
9 AFC.c AFC.h\ 9 AFC.c AFC.h\
10 NotificationProxy.c NotificationProxy.h\ 10 NotificationProxy.c NotificationProxy.h\
11 InstallationProxy.c InstallationProxy.h\
12 SBServices.c SBServices.h\
11 userpref.c userpref.h\ 13 userpref.c userpref.h\
12 utils.c utils.h\ 14 utils.c utils.h\
13 MobileSync.c MobileSync.h 15 MobileSync.c MobileSync.h
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)
62 * @param client NP to send data to 62 * @param client NP to send data to
63 * @param dict plist to send 63 * @param dict plist to send
64 * 64 *
65 * @return NP_E_SUCCESS or an error code. 65 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client or dict
66 * are NULL, NP_E_PLIST_ERROR when dict is not a valid plist,
67 * or NP_E_UNKNOWN_ERROR when an unspecified error occurs.
66 */ 68 */
67static np_error_t np_plist_send(np_client_t client, plist_t dict) 69static np_error_t np_plist_send(np_client_t client, plist_t dict)
68{ 70{
@@ -105,11 +107,14 @@ static np_error_t np_plist_send(np_client_t client, plist_t dict)
105 107
106/** Makes a connection to the NP service on the phone. 108/** Makes a connection to the NP service on the phone.
107 * 109 *
108 * @param phone The iPhone to connect on. 110 * @param device The device to connect to.
109 * @param s_port The source port. 111 * @param dst_port Destination port (usually given by lockdownd_start_service).
110 * @param d_port The destination port. 112 * @param client Pointer that will be set to a newly allocated np_client_t
113 * upon successful return.
111 * 114 *
112 * @return A handle to the newly-connected client or NULL upon error. 115 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when device is NULL,
116 * or NP_E_CONN_FAILED when the connection to the device could not be
117 * established.
113 */ 118 */
114np_error_t np_client_new(iphone_device_t device, int dst_port, np_client_t *client) 119np_error_t np_client_new(iphone_device_t device, int dst_port, np_client_t *client)
115{ 120{
@@ -123,7 +128,7 @@ np_error_t np_client_new(iphone_device_t device, int dst_port, np_client_t *clie
123 /* Attempt connection */ 128 /* Attempt connection */
124 iphone_connection_t connection = NULL; 129 iphone_connection_t connection = NULL;
125 if (iphone_device_connect(device, dst_port, &connection) != IPHONE_E_SUCCESS) { 130 if (iphone_device_connect(device, dst_port, &connection) != IPHONE_E_SUCCESS) {
126 return NP_E_UNKNOWN_ERROR; 131 return NP_E_CONN_FAILED;
127 } 132 }
128 133
129 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_int)); 134 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
137 return NP_E_SUCCESS; 142 return NP_E_SUCCESS;
138} 143}
139 144
140/** Disconnects an NP client from the phone. 145/** Disconnects an NP client from the device.
141 * 146 *
142 * @param client The client to disconnect. 147 * @param client The client to disconnect.
148 *
149 * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL.
143 */ 150 */
144np_error_t np_client_free(np_client_t client) 151np_error_t np_client_free(np_client_t client)
145{ 152{
@@ -168,6 +175,8 @@ np_error_t np_client_free(np_client_t client)
168 * 175 *
169 * @param client The client to send to 176 * @param client The client to send to
170 * @param notification The notification message to send 177 * @param notification The notification message to send
178 *
179 * @return NP_E_SUCCESS on success, or an error returned by np_plist_send
171 */ 180 */
172np_error_t np_post_notification(np_client_t client, const char *notification) 181np_error_t np_post_notification(np_client_t client, const char *notification)
173{ 182{
@@ -201,6 +210,9 @@ np_error_t np_post_notification(np_client_t client, const char *notification)
201 * 210 *
202 * @param client The client to send to 211 * @param client The client to send to
203 * @param notification The notifications that should be observed. 212 * @param notification The notifications that should be observed.
213 *
214 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client or
215 * notification are NULL, or an error returned by np_plist_send.
204 */ 216 */
205np_error_t np_observe_notification( np_client_t client, const char *notification ) 217np_error_t np_observe_notification( np_client_t client, const char *notification )
206{ 218{
@@ -241,6 +253,9 @@ np_error_t np_observe_notification( np_client_t client, const char *notification
241 * observed. This is expected to be an array of const char* that MUST have a 253 * observed. This is expected to be an array of const char* that MUST have a
242 * terminating NULL entry. However this parameter can be NULL; in this case, 254 * terminating NULL entry. However this parameter can be NULL; in this case,
243 * the default set of notifications will be used. 255 * the default set of notifications will be used.
256 *
257 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client is null,
258 * or an error returned by np_observe_notification.
244 */ 259 */
245np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) 260np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)
246{ 261{
@@ -275,7 +290,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
275 * with the notification that has been received. 290 * with the notification that has been received.
276 * 291 *
277 * @return 0 if a notification has been received or nothing has been received, 292 * @return 0 if a notification has been received or nothing has been received,
278 * or an error value if an error occured. 293 * or a negative value if an error occured.
279 * 294 *
280 * @note You probably want to check out np_set_notify_callback 295 * @note You probably want to check out np_set_notify_callback
281 * @see np_set_notify_callback 296 * @see np_set_notify_callback
@@ -401,10 +416,14 @@ gpointer np_notifier( gpointer arg )
401 * 416 *
402 * @param client the NP client 417 * @param client the NP client
403 * @param notify_cb pointer to a callback function or NULL to de-register a 418 * @param notify_cb pointer to a callback function or NULL to de-register a
404 * previously set callback function 419 * previously set callback function.
420 *
421 * @note Only one callback function can be registered at the same time;
422 * any previously set callback function will be removed automatically.
405 * 423 *
406 * @return NP_E_SUCCESS when the callback was successfully registered, 424 * @return NP_E_SUCCESS when the callback was successfully registered,
407 * or an error value when an error occured. 425 * NP_E_INVALID_ARG when client is NULL, or NP_E_UNKNOWN_ERROR when
426 * the callback thread could no be created.
408 */ 427 */
409np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb ) 428np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb )
410{ 429{
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 @@
1/*
2 * SBServices.c
3 * SpringBoard Services 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 <stdlib.h>
24#include <unistd.h>
25#include <arpa/inet.h>
26#include <plist/plist.h>
27
28#include "SBServices.h"
29#include "iphone.h"
30#include "utils.h"
31
32/** Locks an sbservices client, done for thread safety stuff.
33 *
34 * @param client The sbservices client to lock.
35 */
36static void sbs_lock(sbservices_client_t client)
37{
38 log_debug_msg("SBServices: Locked\n");
39 g_mutex_lock(client->mutex);
40}
41
42/** Unlocks an sbservices client, done for thread safety stuff.
43 *
44 * @param client The sbservices client to unlock
45 */
46static void sbs_unlock(sbservices_client_t client)
47{
48 log_debug_msg("SBServices: Unlocked\n");
49 g_mutex_unlock(client->mutex);
50}
51
52sbservices_error_t sbservices_client_new(iphone_device_t device, int dst_port, sbservices_client_t *client)
53{
54 /* makes sure thread environment is available */
55 if (!g_thread_supported())
56 g_thread_init(NULL);
57
58 if (!device)
59 return SBSERVICES_E_INVALID_ARG;
60
61 /* Attempt connection */
62 iphone_connection_t connection = NULL;
63 if (iphone_device_connect(device, dst_port, &connection) != IPHONE_E_SUCCESS) {
64 return SBSERVICES_E_CONN_FAILED;
65 }
66
67 sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_int));
68 client_loc->connection = connection;
69 client_loc->mutex = g_mutex_new();
70
71 *client = client_loc;
72 return SBSERVICES_E_SUCCESS;
73}
74
75sbservices_error_t sbservices_client_free(sbservices_client_t client)
76{
77 if (!client)
78 return SBSERVICES_E_INVALID_ARG;
79
80 iphone_device_disconnect(client->connection);
81 client->connection = NULL;
82 if (client->mutex) {
83 g_mutex_free(client->mutex);
84 }
85 free(client);
86
87 return SBSERVICES_E_SUCCESS;
88}
89
90/**
91 * Sends a binary plist to the device using the connection specified in client.
92 * This function is only used internally.
93 *
94 * @param client InstallationProxy to send data to
95 * @param dict plist to send
96 *
97 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when
98 * client or dict are NULL, SBSERVICES_E_PLIST_ERROR when dict is not a
99 * valid plist, or SBSERVICES_E_UNKNOWN_ERROR when an unspecified error
100 * occurs.
101 */
102static sbservices_error_t sbservices_plist_send(sbservices_client_t client, plist_t dict)
103{
104 char *content = NULL;
105 uint32_t length = 0;
106 uint32_t nlen = 0;
107 int bytes = 0;
108 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
109
110 if (!client || !dict) {
111 return SBSERVICES_E_INVALID_ARG;
112 }
113
114 plist_to_bin(dict, &content, &length);
115
116 if (!content || length == 0) {
117 return SBSERVICES_E_PLIST_ERROR;
118 }
119
120 nlen = htonl(length);
121 log_debug_msg("%s: sending %d bytes\n", __func__, length);
122 iphone_device_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes);
123 if (bytes == sizeof(nlen)) {
124 iphone_device_send(client->connection, content, length, (uint32_t*)&bytes);
125 if (bytes > 0) {
126 if ((uint32_t)bytes == length) {
127 res = SBSERVICES_E_SUCCESS;
128 } else {
129 log_debug_msg("%s: ERROR: Could not send all data (%d of %d)!\n", __func__, bytes, length);
130 }
131 }
132 }
133 if (bytes <= 0) {
134 log_debug_msg("%s: ERROR: sending to device failed.\n", __func__);
135 }
136
137 free(content);
138
139 return res;
140}
141
142sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state)
143{
144 if (!client || !client->connection || !state)
145 return SBSERVICES_E_INVALID_ARG;
146
147 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
148 uint32_t pktlen = 0;
149 uint32_t bytes = 0;
150
151 plist_t dict = plist_new_dict();
152 plist_dict_insert_item(dict, "command", plist_new_string("getIconState"));
153
154 sbs_lock(client);
155
156 res = sbservices_plist_send(client, dict);
157 plist_free(dict);
158 if (res != SBSERVICES_E_SUCCESS) {
159 log_debug_msg("%s: could not send plist\n", __func__);
160 goto leave_unlock;
161 }
162
163 iphone_device_recv(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes);
164 log_debug_msg("%s: initial read=%i\n", __func__, bytes);
165 if (bytes < 4) {
166 log_debug_msg("%s: initial read failed!\n");
167 res = 0;
168 } else {
169 if ((char)pktlen == 0) {
170 char *content = NULL;
171 uint32_t curlen = 0;
172 pktlen = ntohl(pktlen);
173 log_debug_msg("%s: %d bytes following\n", __func__, pktlen);
174 content = (char*)malloc(pktlen);
175 log_debug_msg("pointer %p\n", content);
176
177 while (curlen < pktlen) {
178 iphone_device_recv(client->connection, content+curlen, pktlen-curlen, &bytes);
179 if (bytes <= 0) {
180 res = SBSERVICES_E_UNKNOWN_ERROR;
181 break;
182 }
183 log_debug_msg("%s: received %d bytes\n", __func__, bytes);
184 curlen += bytes;
185 }
186 log_debug_buffer(content, pktlen);
187 plist_from_bin(content, pktlen, state);
188 res = SBSERVICES_E_SUCCESS;
189 free(content);
190 } else {
191 res = SBSERVICES_E_UNKNOWN_ERROR;
192 }
193 }
194
195leave_unlock:
196 sbs_unlock(client);
197 return res;
198}
199
200sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate)
201{
202 if (!client || !client->connection || !newstate)
203 return SBSERVICES_E_INVALID_ARG;
204
205 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
206
207 plist_t dict = plist_new_dict();
208 plist_dict_insert_item(dict, "command", plist_new_string("setIconState"));
209 plist_dict_insert_item(dict, "iconState", plist_copy(newstate));
210
211 sbs_lock(client);
212
213 res = sbservices_plist_send(client, dict);
214 plist_free(dict);
215 if (res != SBSERVICES_E_SUCCESS) {
216 log_debug_msg("%s: could not send plist\n", __func__);
217 goto leave_unlock;
218 }
219 // NO RESPONSE
220
221leave_unlock:
222 sbs_unlock(client);
223 return res;
224}
225
226sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize)
227{
228 if (!client || !client->connection || !pngdata)
229 return SBSERVICES_E_INVALID_ARG;
230
231 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
232 uint32_t pktlen = 0;
233 uint32_t bytes = 0;
234
235 plist_t dict = plist_new_dict();
236 plist_dict_insert_item(dict, "command", plist_new_string("getIconPNGData"));
237 plist_dict_insert_item(dict, "bundleId", plist_new_string(bundleId));
238
239 sbs_lock(client);
240
241 res = sbservices_plist_send(client, dict);
242 plist_free(dict);
243 if (res != SBSERVICES_E_SUCCESS) {
244 log_debug_msg("%s: could not send plist\n", __func__);
245 goto leave_unlock;
246 }
247
248 iphone_device_recv(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes);
249 log_debug_msg("%s: initial read=%i\n", __func__, bytes);
250 if (bytes < 4) {
251 log_debug_msg("%s: initial read failed!\n");
252 res = 0;
253 } else {
254 if ((char)pktlen == 0) {
255 char *content = NULL;
256 uint32_t curlen = 0;
257 pktlen = ntohl(pktlen);
258 log_debug_msg("%s: %d bytes following\n", __func__, pktlen);
259 content = (char*)malloc(pktlen);
260 log_debug_msg("pointer %p\n", content);
261
262 while (curlen < pktlen) {
263 iphone_device_recv(client->connection, content+curlen, pktlen-curlen, &bytes);
264 if (bytes <= 0) {
265 res = SBSERVICES_E_UNKNOWN_ERROR;
266 break;
267 }
268 log_debug_msg("%s: received %d bytes\n", __func__, bytes);
269 curlen += bytes;
270 }
271 log_debug_buffer(content, pktlen);
272 plist_t pngdict = NULL;
273 plist_from_bin(content, pktlen, &pngdict);
274 plist_t node = plist_dict_get_item(pngdict, "pngData");
275 if (node) {
276 plist_get_data_val(node, pngdata, pngsize);
277 }
278 plist_free(pngdict);
279 res = SBSERVICES_E_SUCCESS;
280 free(content);
281 } else {
282 res = SBSERVICES_E_UNKNOWN_ERROR;
283 }
284 }
285
286leave_unlock:
287 sbs_unlock(client);
288 return res;
289
290}
291
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 @@
1/*
2 * SBServices.h
3 * SpringBoard Services header file.
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#ifndef ISBSERVICES_H
22#define ISBSERVICES_H
23
24#include <glib.h>
25
26#include "libiphone/sbservices.h"
27
28struct sbservices_client_int {
29 iphone_connection_t connection;
30 GMutex *mutex;
31};
32
33#endif