summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am139
-rw-r--r--tools/afcclient.c1664
-rw-r--r--tools/idevice_id.c169
-rw-r--r--tools/idevicebackup.c664
-rw-r--r--tools/idevicebackup2.c2684
-rw-r--r--tools/idevicebtlogger.c458
-rw-r--r--tools/idevicecrashreport.c529
-rw-r--r--tools/idevicedate.c265
-rw-r--r--tools/idevicedebug.c625
-rw-r--r--tools/idevicedebugserverproxy.c375
-rw-r--r--tools/idevicedevmodectl.c462
-rw-r--r--tools/idevicediagnostics.c344
-rw-r--r--tools/ideviceenterrecovery.c128
-rw-r--r--tools/ideviceimagemounter.c873
-rw-r--r--tools/ideviceinfo.c363
-rw-r--r--tools/idevicename.c159
-rw-r--r--tools/idevicenotificationproxy.c293
-rw-r--r--tools/idevicepair.c473
-rw-r--r--tools/ideviceprovision.c689
-rw-r--r--tools/idevicescreenshot.c259
-rw-r--r--tools/idevicesetlocation.c192
-rw-r--r--tools/idevicesyslog.c812
22 files changed, 11237 insertions, 1382 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 0a47fdc..b0c2769 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,51 +1,144 @@
1AM_CPPFLAGS = -I$(top_srcdir)/include 1AM_CPPFLAGS = \
2 -I$(top_srcdir)/include \
3 -I$(top_srcdir)
2 4
3AM_CFLAGS = $(GLOBAL_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(LFS_CFLAGS) 5AM_CFLAGS = \
4AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS) 6 $(GLOBAL_CFLAGS) \
7 $(ssl_lib_CFLAGS) \
8 $(libplist_CFLAGS) \
9 $(LFS_CFLAGS)
5 10
6bin_PROGRAMS = idevice_id ideviceinfo idevicepair idevicesyslog idevicebackup ideviceimagemounter idevicescreenshot ideviceenterrecovery idevicedate 11AM_LDFLAGS = \
12 $(libplist_LIBS)
13
14bin_PROGRAMS = \
15 idevicebtlogger\
16 idevice_id \
17 ideviceinfo \
18 idevicename \
19 idevicepair \
20 idevicesyslog \
21 ideviceimagemounter \
22 idevicescreenshot \
23 ideviceenterrecovery \
24 idevicedate \
25 idevicebackup \
26 idevicebackup2 \
27 ideviceprovision \
28 idevicedebugserverproxy \
29 idevicediagnostics \
30 idevicedebug \
31 idevicedevmodectl \
32 idevicenotificationproxy \
33 idevicecrashreport \
34 idevicesetlocation \
35 afcclient
36
37idevicebtlogger_SOURCES = idevicebtlogger.c
38idevicebtlogger_CFLAGS = $(AM_CFLAGS)
39idevicebtlogger_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
40idevicebtlogger_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
7 41
8ideviceinfo_SOURCES = ideviceinfo.c 42ideviceinfo_SOURCES = ideviceinfo.c
9ideviceinfo_CFLAGS = $(AM_CFLAGS) 43ideviceinfo_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
10ideviceinfo_LDFLAGS = $(AM_LDFLAGS) 44ideviceinfo_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
11ideviceinfo_LDADD = ../src/libimobiledevice.la 45ideviceinfo_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
46
47idevicename_SOURCES = idevicename.c
48idevicename_CFLAGS = $(AM_CFLAGS)
49idevicename_LDFLAGS = $(AM_LDFLAGS)
50idevicename_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
12 51
13idevicepair_SOURCES = idevicepair.c 52idevicepair_SOURCES = idevicepair.c
14idevicepair_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/src 53idevicepair_CFLAGS = $(AM_CFLAGS)
15idevicepair_LDFLAGS = $(AM_LDFLAGS) 54idevicepair_LDFLAGS = $(AM_LDFLAGS) $(libusbmuxd_LIBS) $(ssl_lib_LIBS)
16idevicepair_LDADD = ../src/libimobiledevice.la 55idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS)
17 56
18idevicesyslog_SOURCES = idevicesyslog.c 57idevicesyslog_SOURCES = idevicesyslog.c
19idevicesyslog_CFLAGS = $(AM_CFLAGS) 58idevicesyslog_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
20idevicesyslog_LDFLAGS = $(AM_LDFLAGS) 59idevicesyslog_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
21idevicesyslog_LDADD = ../src/libimobiledevice.la 60idevicesyslog_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
22 61
23idevice_id_SOURCES = idevice_id.c 62idevice_id_SOURCES = idevice_id.c
24idevice_id_CFLAGS = $(AM_CFLAGS) 63idevice_id_CFLAGS = $(AM_CFLAGS)
25idevice_id_LDFLAGS = $(AM_LDFLAGS) 64idevice_id_LDFLAGS = $(AM_LDFLAGS)
26idevice_id_LDADD = ../src/libimobiledevice.la 65idevice_id_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
27 66
28idevicebackup_SOURCES = idevicebackup.c 67idevicebackup_SOURCES = idevicebackup.c
29idevicebackup_CFLAGS = $(AM_CFLAGS) 68idevicebackup_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
30idevicebackup_LDFLAGS = $(AM_LDFLAGS) 69idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
31idevicebackup_LDADD = ../src/libimobiledevice.la 70idevicebackup_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
71
72idevicebackup2_SOURCES = idevicebackup2.c
73idevicebackup2_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
74idevicebackup2_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
75idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
32 76
33ideviceimagemounter_SOURCES = ideviceimagemounter.c 77ideviceimagemounter_SOURCES = ideviceimagemounter.c
34ideviceimagemounter_CFLAGS = $(AM_CFLAGS) 78ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) $(libtatsu_CFLAGS)
35ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) 79ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(ssl_lib_LIBS) $(libtatsu_LIBS)
36ideviceimagemounter_LDADD = ../src/libimobiledevice.la 80ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
37 81
38idevicescreenshot_SOURCES = idevicescreenshot.c 82idevicescreenshot_SOURCES = idevicescreenshot.c
39idevicescreenshot_CFLAGS = $(AM_CFLAGS) 83idevicescreenshot_CFLAGS = $(AM_CFLAGS)
40idevicescreenshot_LDFLAGS = $(AM_LDFLAGS) 84idevicescreenshot_LDFLAGS = $(AM_LDFLAGS)
41idevicescreenshot_LDADD = ../src/libimobiledevice.la 85idevicescreenshot_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
42 86
43ideviceenterrecovery_SOURCES = ideviceenterrecovery.c 87ideviceenterrecovery_SOURCES = ideviceenterrecovery.c
44ideviceenterrecovery_CFLAGS = $(AM_CFLAGS) 88ideviceenterrecovery_CFLAGS = $(AM_CFLAGS)
45ideviceenterrecovery_LDFLAGS = $(AM_LDFLAGS) 89ideviceenterrecovery_LDFLAGS = $(AM_LDFLAGS)
46ideviceenterrecovery_LDADD = ../src/libimobiledevice.la 90ideviceenterrecovery_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
47 91
48idevicedate_SOURCES = idevicedate.c 92idevicedate_SOURCES = idevicedate.c
49idevicedate_CFLAGS = $(AM_CFLAGS) 93idevicedate_CFLAGS = $(AM_CFLAGS)
50idevicedate_LDFLAGS = $(AM_LDFLAGS) 94idevicedate_LDFLAGS = $(AM_LDFLAGS)
51idevicedate_LDADD = ../src/libimobiledevice.la 95idevicedate_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
96
97ideviceprovision_SOURCES = ideviceprovision.c
98ideviceprovision_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
99ideviceprovision_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
100ideviceprovision_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
101
102idevicedebugserverproxy_SOURCES = idevicedebugserverproxy.c
103idevicedebugserverproxy_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
104idevicedebugserverproxy_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
105idevicedebugserverproxy_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
106
107idevicediagnostics_SOURCES = idevicediagnostics.c
108idevicediagnostics_CFLAGS = $(AM_CFLAGS)
109idevicediagnostics_LDFLAGS = $(AM_LDFLAGS)
110idevicediagnostics_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
111
112idevicedebug_SOURCES = idevicedebug.c
113idevicedebug_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
114idevicedebug_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
115idevicedebug_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la
116
117idevicedevmodectl_SOURCES = idevicedevmodectl.c
118idevicedevmodectl_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
119idevicedevmodectl_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
120idevicedevmodectl_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la
121
122idevicenotificationproxy_SOURCES = idevicenotificationproxy.c
123idevicenotificationproxy_CFLAGS = $(AM_CFLAGS)
124idevicenotificationproxy_LDFLAGS = $(AM_LDFLAGS)
125idevicenotificationproxy_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
126
127idevicecrashreport_SOURCES = idevicecrashreport.c
128idevicecrashreport_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
129idevicecrashreport_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
130idevicecrashreport_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
131
132idevicesetlocation_SOURCES = idevicesetlocation.c
133idevicesetlocation_CFLAGS = $(AM_CFLAGS)
134idevicesetlocation_LDFLAGS = $(AM_LDFLAGS)
135idevicesetlocation_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
136
137afcclient_SOURCES = afcclient.c
138afcclient_CFLAGS = $(AM_CFLAGS)
139afcclient_LDFLAGS = $(AM_LDFLAGS)
140if HAVE_READLINE
141 afcclient_CFLAGS += $(readline_CFLAGS)
142 afcclient_LDFLAGS += $(readline_LIBS)
143endif
144afcclient_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(limd_glue_LIBS)
diff --git a/tools/afcclient.c b/tools/afcclient.c
new file mode 100644
index 0000000..8f49831
--- /dev/null
+++ b/tools/afcclient.c
@@ -0,0 +1,1664 @@
1/*
2 * afcclient.c
3 * Utility to interact with AFC/HoustArrest service on the device
4 *
5 * Inspired by https://github.com/emonti/afcclient
6 * But entirely rewritten from scratch.
7 *
8 * Copyright (c) 2023 Nikias Bassen, All Rights Reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#define TOOL_NAME "afcclient"
30
31#include <stdio.h>
32#include <string.h>
33#include <errno.h>
34#include <stdlib.h>
35#include <getopt.h>
36#include <signal.h>
37#include <ctype.h>
38#include <unistd.h>
39#include <dirent.h>
40#include <time.h>
41
42#ifdef WIN32
43#include <windows.h>
44#include <sys/time.h>
45#include <conio.h>
46#define sleep(x) Sleep(x*1000)
47#define S_IFMT 0170000 /* [XSI] type of file mask */
48#define S_IFIFO 0010000 /* [XSI] named pipe (fifo) */
49#define S_IFCHR 0020000 /* [XSI] character special */
50#define S_IFBLK 0060000 /* [XSI] block special */
51#define S_IFLNK 0120000 /* [XSI] symbolic link */
52#define S_IFSOCK 0140000 /* [XSI] socket */
53#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* block special */
54#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* char special */
55#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* fifo or socket */
56#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* symbolic link */
57#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* socket */
58#else
59#include <sys/time.h>
60#include <termios.h>
61#endif
62
63#ifdef HAVE_READLINE
64#include <readline/readline.h>
65#include <readline/history.h>
66#endif
67
68#include <libimobiledevice/libimobiledevice.h>
69#include <libimobiledevice/lockdown.h>
70#include <libimobiledevice/house_arrest.h>
71#include <libimobiledevice/afc.h>
72#include <plist/plist.h>
73
74#include <libimobiledevice-glue/termcolors.h>
75#include <libimobiledevice-glue/utils.h>
76
77#undef st_mtime
78#undef st_birthtime
79struct afc_file_stat {
80 uint16_t st_mode;
81 uint16_t st_nlink;
82 uint64_t st_size;
83 uint64_t st_mtime;
84 uint64_t st_birthtime;
85 uint32_t st_blocks;
86};
87
88static char* udid = NULL;
89static int connected = 0;
90static int use_network = 0;
91static idevice_subscription_context_t context = NULL;
92static char* curdir = NULL;
93static size_t curdir_len = 0;
94
95static int file_exists(const char* path)
96{
97 struct stat tst;
98#ifdef WIN32
99 return (stat(path, &tst) == 0);
100#else
101 return (lstat(path, &tst) == 0);
102#endif
103}
104
105static int is_directory(const char* path)
106{
107 struct stat tst;
108#ifdef WIN32
109 return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
110#else
111 return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
112#endif
113}
114
115static void print_usage(int argc, char **argv, int is_error)
116{
117 char *name = strrchr(argv[0], '/');
118 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
119 fprintf(is_error ? stderr : stdout,
120 "\n"
121 "Interact with AFC/HouseArrest service on a connected device.\n"
122 "\n"
123 "OPTIONS:\n"
124 " -u, --udid UDID target specific device by UDID\n"
125 " -n, --network connect to network device (not recommended!)\n"
126 " --container <appid> Access container of given app\n"
127 " --documents <appid> Access Documents directory of given app\n"
128 " -h, --help prints usage information\n" \
129 " -d, --debug enable communication debugging\n" \
130 " -v, --version prints version information\n" \
131 "\n"
132 );
133 fprintf(is_error ? stderr : stdout,
134 "\n" \
135 "Homepage: <" PACKAGE_URL ">\n"
136 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
137 );
138}
139
140#ifndef HAVE_READLINE
141#ifdef WIN32
142#define BS_CC '\b'
143#else
144#define BS_CC 0x7f
145#define getch getchar
146#endif
147static void get_input(char *buf, int maxlen)
148{
149 int len = 0;
150 int c;
151
152 while ((c = getch())) {
153 if ((c == '\r') || (c == '\n')) {
154 break;
155 }
156 if (isprint(c)) {
157 if (len < maxlen-1)
158 buf[len++] = c;
159 } else if (c == BS_CC) {
160 if (len > 0) {
161 fputs("\b \b", stdout);
162 len--;
163 }
164 }
165 }
166 buf[len] = 0;
167}
168#endif
169
170#define OPT_DOCUMENTS 1
171#define OPT_CONTAINER 2
172
173int stop_requested = 0;
174
175static void handle_signal(int sig)
176{
177 stop_requested++;
178#ifdef WIN32
179 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
180#else
181 kill(getpid(), SIGINT);
182#endif
183}
184
185static void handle_help(afc_client_t afc, int argc, char** argv)
186{
187 printf("Available commands:\n");
188 printf("help - print list of available commands\n");
189 printf("devinfo - print device information\n");
190 printf("info PATH - print file attributes of file at PATH\n");
191 printf("ls [-l] PATH - print directory contents of PATH\n");
192 printf("mv OLD NEW - rename file OLD to NEW\n");
193 printf("mkdir PATH - create directory at PATH\n");
194 printf("ln [-s] FILE [LINK] - create a (symbolic) link to file named LINKNAME\n");
195 printf(" NOTE: This feature has been disabled in newer versions of iOS.\n");
196 printf("rm PATH - remove item at PATH\n");
197 printf("get [-rf] PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n");
198 printf("put [-rf] LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n");
199 printf("\n");
200}
201
202static const char* path_get_basename(const char* path)
203{
204 const char *p = strrchr(path, '/');
205 return p ? p + 1 : path;
206}
207
208static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y)
209{
210 /* Perform the carry for the later subtraction by updating y. */
211 if (x->tv_usec < y->tv_usec) {
212 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
213 y->tv_usec -= 1000000 * nsec;
214 y->tv_sec += nsec;
215 }
216 if (x->tv_usec - y->tv_usec > 1000000) {
217 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
218 y->tv_usec += 1000000 * nsec;
219 y->tv_sec -= nsec;
220 }
221 /* Compute the time remaining to wait.
222 tv_usec is certainly positive. */
223 result->tv_sec = x->tv_sec - y->tv_sec;
224 result->tv_usec = x->tv_usec - y->tv_usec;
225 /* Return 1 if result is negative. */
226 return x->tv_sec < y->tv_sec;
227}
228
229struct str_item {
230 size_t len;
231 char* str;
232};
233
234static char* get_absolute_path(const char *path)
235{
236 if (*path == '/') {
237 return strdup(path);
238 } else {
239 size_t len = curdir_len + 1 + strlen(path) + 1;
240 char* result = (char*)malloc(len);
241 if (!strcmp(curdir, "/")) {
242 snprintf(result, len, "/%s", path);
243 } else {
244 snprintf(result, len, "%s/%s", curdir, path);
245 }
246 return result;
247 }
248}
249
250static char* get_realpath(const char* path)
251{
252 if (!path) return NULL;
253
254 int is_absolute = 0;
255 if (*path == '/') {
256 is_absolute = 1;
257 }
258
259 const char* p = path;
260 if (is_absolute) {
261 while (*p == '/') p++;
262 }
263 if (*p == '\0') {
264 return strdup("/");
265 }
266
267 int c_count = 1;
268 const char* start = p;
269 const char* end = p;
270 struct str_item* comps = NULL;
271
272 while (*p) {
273 if (*p == '/') {
274 p++;
275 end = p-1;
276 while (*p == '/') p++;
277 if (*p == '\0') break;
278 struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
279 if (!newcomps) {
280 free(comps);
281 printf("%s: out of memory?!\n", __func__);
282 return NULL;
283 }
284 comps = newcomps;
285 char *comp = (char*)malloc(end-start+1);
286 strncpy(comp, start, end-start);
287 comp[end-start] = '\0';
288 comps[c_count-1].len = end-start;
289 comps[c_count-1].str = comp;
290 c_count++;
291 start = p;
292 end = p;
293 }
294 p++;
295 }
296 if (p > start) {
297 if (start == end) {
298 end = p;
299 }
300 struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
301 if (!newcomps) {
302 free(comps);
303 printf("%s: out of memory?!\n", __func__);
304 return NULL;
305 }
306 comps = newcomps;
307 char *comp = (char*)malloc(end-start+1);
308 strncpy(comp, start, end-start);
309 comp[end-start] = '\0';
310 comps[c_count-1].len = end-start;
311 comps[c_count-1].str = comp;
312 }
313
314 struct str_item* comps_final = (struct str_item*)malloc(sizeof(struct str_item)*(c_count+1));
315 int o = 1;
316 if (is_absolute) {
317 comps_final[0].len = 1;
318 comps_final[0].str = (char*)"/";
319 } else {
320 comps_final[0].len = curdir_len;
321 comps_final[0].str = curdir;
322 }
323 size_t o_len = comps_final[0].len;
324
325 for (int i = 0; i < c_count; i++) {
326 if (!strcmp(comps[i].str, "..")) {
327 o--;
328 continue;
329 } else if (!strcmp(comps[i].str, ".")) {
330 continue;
331 }
332 o_len += comps[i].len;
333 comps_final[o].str = comps[i].str;
334 comps_final[o].len = comps[i].len;
335 o++;
336 }
337
338 o_len += o;
339 char* result = (char*)malloc(o_len);
340 char* presult = result;
341 for (int i = 0; i < o; i++) {
342 if (i > 0 && strcmp(comps_final[i-1].str, "/") != 0) {
343 *presult = '/';
344 presult++;
345 }
346 strncpy(presult, comps_final[i].str, comps_final[i].len);
347 presult+=comps_final[i].len;
348 *presult = '\0';
349 }
350 if (presult == result) {
351 *presult = '/';
352 presult++;
353 *presult = 0;
354 }
355
356 for (int i = 0; i < c_count; i++) {
357 free(comps[i].str);
358 }
359 free(comps);
360 free(comps_final);
361
362 return result;
363}
364
365static void handle_devinfo(afc_client_t afc, int argc, char** argv)
366{
367 char **info = NULL;
368 afc_error_t err = afc_get_device_info(afc, &info);
369 if (err == AFC_E_SUCCESS && info) {
370 int i;
371 for (i = 0; info[i]; i += 2) {
372 printf("%s: %s\n", info[i], info[i+1]);
373 }
374 } else {
375 printf("Error: Failed to get device info: %s (%d)\n", afc_strerror(err), err);
376 }
377 afc_dictionary_free(info);
378}
379
380static int get_file_info_stat(afc_client_t afc, const char* path, struct afc_file_stat *stbuf)
381{
382 char **info = NULL;
383 afc_error_t ret = afc_get_file_info(afc, path, &info);
384 memset(stbuf, 0, sizeof(struct afc_file_stat));
385 if (ret != AFC_E_SUCCESS) {
386 return -1;
387 } else if (!info) {
388 return -1;
389 } else {
390 // get file attributes from info list
391 int i;
392 for (i = 0; info[i]; i += 2) {
393 if (!strcmp(info[i], "st_size")) {
394 stbuf->st_size = atoll(info[i+1]);
395 } else if (!strcmp(info[i], "st_blocks")) {
396 stbuf->st_blocks = atoi(info[i+1]);
397 } else if (!strcmp(info[i], "st_ifmt")) {
398 if (!strcmp(info[i+1], "S_IFREG")) {
399 stbuf->st_mode = S_IFREG;
400 } else if (!strcmp(info[i+1], "S_IFDIR")) {
401 stbuf->st_mode = S_IFDIR;
402 } else if (!strcmp(info[i+1], "S_IFLNK")) {
403 stbuf->st_mode = S_IFLNK;
404 } else if (!strcmp(info[i+1], "S_IFBLK")) {
405 stbuf->st_mode = S_IFBLK;
406 } else if (!strcmp(info[i+1], "S_IFCHR")) {
407 stbuf->st_mode = S_IFCHR;
408 } else if (!strcmp(info[i+1], "S_IFIFO")) {
409 stbuf->st_mode = S_IFIFO;
410 } else if (!strcmp(info[i+1], "S_IFSOCK")) {
411 stbuf->st_mode = S_IFSOCK;
412 }
413 } else if (!strcmp(info[i], "st_nlink")) {
414 stbuf->st_nlink = atoi(info[i+1]);
415 } else if (!strcmp(info[i], "st_mtime")) {
416 stbuf->st_mtime = (time_t)(atoll(info[i+1]) / 1000000000);
417 } else if (!strcmp(info[i], "st_birthtime")) { /* available on iOS 7+ */
418 stbuf->st_birthtime = (time_t)(atoll(info[i+1]) / 1000000000);
419 }
420 }
421 afc_dictionary_free(info);
422 }
423 return 0;
424}
425
426static void handle_file_info(afc_client_t afc, int argc, char** argv)
427{
428 if (argc < 1) {
429 printf("Error: Missing PATH.\n");
430 return;
431 }
432
433 char **info = NULL;
434 char* abspath = get_absolute_path(argv[0]);
435 if (!abspath) {
436 printf("Error: Invalid argument\n");
437 return;
438 }
439 afc_error_t err = afc_get_file_info(afc, abspath, &info);
440 if (err == AFC_E_SUCCESS && info) {
441 int i;
442 for (i = 0; info[i]; i += 2) {
443 printf("%s: %s\n", info[i], info[i+1]);
444 }
445 } else {
446 printf("Error: Failed to get file info for %s: %s (%d)\n", argv[0], afc_strerror(err), err);
447 }
448 afc_dictionary_free(info);
449 free(abspath);
450}
451
452static void print_file_info(afc_client_t afc, const char* path, int list_verbose)
453{
454 struct afc_file_stat st;
455 get_file_info_stat(afc, path, &st);
456 if (list_verbose) {
457 char timebuf[64];
458 time_t t = st.st_mtime;
459 if (S_ISDIR(st.st_mode)) {
460 printf("drwxr-xr-x");
461 } else if (S_ISLNK(st.st_mode)) {
462 printf("lrwxrwxrwx");
463 } else {
464 if (S_ISFIFO(st.st_mode)) {
465 printf("f");
466 } else if (S_ISBLK(st.st_mode)) {
467 printf("b");
468 } else if (S_ISCHR(st.st_mode)) {
469 printf("c");
470 } else if (S_ISSOCK(st.st_mode)) {
471 printf("s");
472 } else {
473 printf("-");
474 }
475 printf("rw-r--r--");
476 }
477 printf(" ");
478 printf("%4d", st.st_nlink);
479 printf(" ");
480 printf("mobile");
481 printf(" ");
482 printf("mobile");
483 printf(" ");
484 printf("%10lld", (long long)st.st_size);
485 printf(" ");
486#ifdef WIN32
487 strftime(timebuf, 64, "%d %b %Y %H:%M:%S", localtime(&t));
488#else
489 strftime(timebuf, 64, "%d %h %Y %H:%M:%S", localtime(&t));
490#endif
491 printf("%s", timebuf);
492 printf(" ");
493 }
494 if (S_ISDIR(st.st_mode)) {
495 cprintf(FG_CYAN);
496 } else if (S_ISLNK(st.st_mode)) {
497 cprintf(FG_MAGENTA);
498 } else if (S_ISREG(st.st_mode)) {
499 cprintf(FG_DEFAULT);
500 } else {
501 cprintf(FG_YELLOW);
502 }
503 cprintf("%s" COLOR_RESET "\n", path_get_basename(path));
504}
505
506static void handle_list(afc_client_t afc, int argc, char** argv)
507{
508 const char* path = NULL;
509 int list_verbose = 0;
510 if (argc < 1) {
511 path = curdir;
512 } else {
513 if (!strcmp(argv[0], "-l")) {
514 list_verbose = 1;
515 if (argc == 2) {
516 path = argv[1];
517 } else {
518 path = curdir;
519 }
520 } else {
521 path = argv[0];
522 }
523 }
524 char* abspath = get_absolute_path(path);
525 if (!abspath) {
526 printf("Error: Invalid argument\n");
527 return;
528 }
529 int abspath_is_root = strcmp(abspath, "/") == 0;
530 size_t abspath_len = (abspath_is_root) ? 0 : strlen(abspath);
531 char** entries = NULL;
532 afc_error_t err = afc_read_directory(afc, abspath, &entries);
533 if (err == AFC_E_READ_ERROR) {
534 print_file_info(afc, abspath, list_verbose);
535 return;
536 } else if (err != AFC_E_SUCCESS) {
537 printf("Error: Failed to list '%s': %s (%d)\n", path, afc_strerror(err), err);
538 free(abspath);
539 return;
540 }
541
542 char** p = entries;
543 while (p && *p) {
544 if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
545 p++;
546 continue;
547 }
548 size_t len = abspath_len + 1 + strlen(*p) + 1;
549 char* testpath = (char*)malloc(len);
550 if (abspath_is_root) {
551 snprintf(testpath, len, "/%s", *p);
552 } else {
553 snprintf(testpath, len, "%s/%s", abspath, *p);
554 }
555 print_file_info(afc, testpath, list_verbose);
556 free(testpath);
557 p++;
558 }
559 afc_dictionary_free(entries);
560 free(abspath);
561}
562
563static void handle_rename(afc_client_t afc, int argc, char** argv)
564{
565 if (argc != 2) {
566 printf("Error: Invalid number of arguments\n");
567 return;
568 }
569 char* srcpath = get_absolute_path(argv[0]);
570 if (!srcpath) {
571 printf("Error: Invalid argument\n");
572 return;
573 }
574 char* dstpath = get_absolute_path(argv[1]);
575 if (!dstpath) {
576 free(srcpath);
577 printf("Error: Invalid argument\n");
578 return;
579 }
580 afc_error_t err = afc_rename_path(afc, srcpath, dstpath);
581 if (err != AFC_E_SUCCESS) {
582 printf("Error: Failed to rename '%s' -> '%s': %s (%d)\n", argv[0], argv[1], afc_strerror(err), err);
583 }
584 free(srcpath);
585 free(dstpath);
586}
587
588static void handle_mkdir(afc_client_t afc, int argc, char** argv)
589{
590 for (int i = 0; i < argc; i++) {
591 char* abspath = get_absolute_path(argv[i]);
592 if (!abspath) {
593 printf("Error: Invalid argument '%s'\n", argv[i]);
594 continue;
595 }
596 afc_error_t err = afc_make_directory(afc, abspath);
597 if (err != AFC_E_SUCCESS) {
598 printf("Error: Failed to create directory '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
599 }
600 free(abspath);
601 }
602}
603
604static void handle_link(afc_client_t afc, int argc, char** argv)
605{
606 if (argc < 2) {
607 printf("Error: Invalid number of arguments\n");
608 return;
609 }
610 afc_link_type_t link_type = AFC_HARDLINK;
611 if (!strcmp(argv[0], "-s")) {
612 argc--;
613 argv++;
614 link_type = AFC_SYMLINK;
615 }
616 if (argc < 1 || argc > 2) {
617 printf("Error: Invalid number of arguments\n");
618 return;
619 }
620 const char *link_name = (argc == 1) ? path_get_basename(argv[0]) : argv[1];
621 char* abs_link_name = get_absolute_path(link_name);
622 if (!abs_link_name) {
623 printf("Error: Invalid argument\n");
624 return;
625 }
626 afc_error_t err = afc_make_link(afc, link_type, argv[0], link_name);
627 if (err != AFC_E_SUCCESS) {
628 printf("Error: Failed to create %s link for '%s' at '%s': %s (%d)\n", (link_type == AFC_HARDLINK) ? "hard" : "symbolic", argv[0], link_name, afc_strerror(err), err);
629 }
630}
631
632static int ask_yesno(const char* prompt)
633{
634 int ret = 0;
635#ifdef HAVE_READLINE
636 char* result = readline(prompt);
637 if (result && result[0] == 'y') {
638 ret = 1;
639 }
640#else
641 char cmdbuf[2] = {0, };
642 printf("%s", prompt);
643 fflush(stdout);
644 get_input(cmdbuf, sizeof(cmdbuf));
645 if (cmdbuf[0] == 'y') {
646 ret = 1;
647 }
648#endif
649#ifdef HAVE_READLINE
650 free(result);
651#endif
652 return ret;
653}
654
655static void handle_remove(afc_client_t afc, int argc, char** argv)
656{
657 int recursive = 0;
658 int force = 0;
659 int i = 0;
660 for (i = 0; i < argc; i++) {
661 if (!strcmp(argv[i], "--")) {
662 i++;
663 break;
664 } else if (!strcmp(argv[i], "-r")) {
665 recursive = 1;
666 } else if (!strcmp(argv[i], "-f")) {
667 force = 1;
668 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
669 recursive = 1;
670 force = 1;
671 } else {
672 break;
673 }
674 }
675 if (recursive && !force) {
676 if (!ask_yesno("WARNING: This operation will remove all contents of the given path(s). Continue? [y/N] ")) {
677 printf("Aborted.\n");
678 return;
679 }
680 }
681 for ( ; i < argc; i++) {
682 char* abspath = get_absolute_path(argv[i]);
683 if (!abspath) {
684 printf("Error: Invalid argument '%s'\n", argv[i]);
685 continue;
686 }
687 afc_error_t err;
688 if (recursive) {
689 err = afc_remove_path_and_contents(afc, abspath);
690 } else {
691 err = afc_remove_path(afc, abspath);
692 }
693 if (err != AFC_E_SUCCESS) {
694 printf("Error: Failed to remove '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
695 }
696 free(abspath);
697 }
698}
699
700static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size, uint8_t force_overwrite)
701{
702 uint64_t fh = 0;
703 afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh);
704 if (err != AFC_E_SUCCESS) {
705 printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
706 return 0;
707 }
708 if (file_exists(dstpath) && !force_overwrite) {
709 printf("Error: Failed to overwrite existing file without '-f' option: %s\n", dstpath);
710 return 0;
711 }
712 FILE *f = fopen(dstpath, "wb");
713 if (!f) {
714 printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno));
715 return 0;
716 }
717 struct timeval t1;
718 struct timeval t2;
719 struct timeval tdiff;
720 size_t bufsize = 0x100000;
721 char *buf = malloc(bufsize);
722 size_t total = 0;
723 int progress = 0;
724 int lastprog = 0;
725 if (file_size > 0x400000) {
726 progress = 1;
727 gettimeofday(&t1, NULL);
728 }
729 uint8_t succeed = 1;
730 while (err == AFC_E_SUCCESS) {
731 uint32_t bytes_read = 0;
732 size_t chunk = 0;
733 err = afc_file_read(afc, fh, buf, bufsize, &bytes_read);
734 if (bytes_read == 0) {
735 break;
736 }
737 while (chunk < bytes_read) {
738 size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f);
739 if (wr == 0) {
740 if (progress) {
741 printf("\n");
742 }
743 printf("Error: Failed to write to local file\n");
744 succeed = 0;
745 break;
746 }
747 chunk += wr;
748 }
749 total += chunk;
750 if (progress) {
751 int prog = (int) ((double) total / (double) file_size * 100.0f);
752 if (prog > lastprog) {
753 gettimeofday(&t2, NULL);
754 timeval_subtract(&tdiff, &t2, &t1);
755 double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
756 printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
757 fflush(stdout);
758 lastprog = prog;
759 }
760 }
761 }
762 if (progress) {
763 printf("\n");
764 }
765 if (err != AFC_E_SUCCESS) {
766 printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
767 succeed = 0;
768 }
769 free(buf);
770 fclose(f);
771 afc_file_close(afc, fh);
772 return succeed;
773}
774
775static int __mkdir(const char* path)
776{
777#ifdef WIN32
778 return mkdir(path);
779#else
780 return mkdir(path, 0755);
781#endif
782}
783
784static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get)
785{
786 char **info = NULL;
787 uint64_t file_size = 0;
788 afc_error_t err = afc_get_file_info(afc, srcpath, &info);
789 if (err == AFC_E_OBJECT_NOT_FOUND) {
790 printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
791 return 0;
792 }
793 uint8_t is_dir = 0;
794 if (info) {
795 char **p = info;
796 while (p && *p) {
797 if (!strcmp(*p, "st_size")) {
798 p++;
799 file_size = (uint64_t) strtoull(*p, NULL, 10);
800 }
801 if (!strcmp(*p, "st_ifmt")) {
802 p++;
803 is_dir = !strcmp(*p, "S_IFDIR");
804 }
805 p++;
806 }
807 afc_dictionary_free(info);
808 }
809 uint8_t succeed = 1;
810 if (is_dir) {
811 if (!recursive_get) {
812 printf("Error: Failed to get a directory without '-r' option: %s\n", srcpath);
813 return 0;
814 }
815 char **entries = NULL;
816 err = afc_read_directory(afc, srcpath, &entries);
817 if (err != AFC_E_SUCCESS) {
818 printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
819 return 0;
820 }
821 char **p = entries;
822 size_t srcpath_len = strlen(srcpath);
823 uint8_t srcpath_is_root = strcmp(srcpath, "/") == 0;
824 // if directory exists, check force_overwrite flag
825 if (is_directory(dstpath)) {
826 if (!force_overwrite) {
827 printf("Error: Failed to write into existing directory without '-f': %s\n", dstpath);
828 return 0;
829 }
830 } else if (__mkdir(dstpath) != 0) {
831 printf("Error: Failed to create directory '%s': %s\n", dstpath, strerror(errno));
832 afc_dictionary_free(entries);
833 return 0;
834 }
835 while (p && *p) {
836 if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
837 p++;
838 continue;
839 }
840 size_t len = srcpath_is_root ? strlen(*p) + 1 : srcpath_len + 1 + strlen(*p) + 1;
841 char *testpath = (char *) malloc(len);
842 if (srcpath_is_root) {
843 snprintf(testpath, len, "/%s", *p);
844 } else {
845 snprintf(testpath, len, "%s/%s", srcpath, *p);
846 }
847 uint8_t dst_is_root = strcmp(srcpath, "/") == 0;
848 size_t dst_len = dst_is_root ? strlen(*p) + 1 : strlen(dstpath) + 1 + strlen(*p) + 1;
849 char *newdst = (char *) malloc(dst_len);
850 if (dst_is_root) {
851 snprintf(newdst, dst_len, "/%s", *p);
852 } else {
853 snprintf(newdst, dst_len, "%s/%s", dstpath, *p);
854 }
855 if (!get_file(afc, testpath, newdst, force_overwrite, recursive_get)) {
856 succeed = 0;
857 break;
858 }
859 free(testpath);
860 free(newdst);
861 p++;
862 }
863 afc_dictionary_free(entries);
864 } else {
865 succeed = get_single_file(afc, srcpath, dstpath, file_size, force_overwrite);
866 }
867 return succeed;
868}
869
870static void handle_get(afc_client_t afc, int argc, char **argv)
871{
872 if (argc < 1) {
873 printf("Error: Invalid number of arguments\n");
874 return;
875 }
876 uint8_t force_overwrite = 0, recursive_get = 0;
877 char *srcpath = NULL;
878 char *dstpath = NULL;
879 int i = 0;
880 for ( ; i < argc; i++) {
881 if (!strcmp(argv[i], "--")) {
882 i++;
883 break;
884 } else if (!strcmp(argv[i], "-r")) {
885 recursive_get = 1;
886 } else if (!strcmp(argv[i], "-f")) {
887 force_overwrite = 1;
888 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
889 recursive_get = 1;
890 force_overwrite = 1;
891 } else {
892 break;
893 }
894 }
895 if (argc - i == 1) {
896 char *tmp = strdup(argv[i]);
897 size_t src_len = strlen(tmp);
898 if (src_len > 1 && tmp[src_len - 1] == '/') {
899 tmp[src_len - 1] = '\0';
900 }
901 srcpath = get_absolute_path(tmp);
902 dstpath = strdup(path_get_basename(tmp));
903 free(tmp);
904 } else if (argc - i == 2) {
905 char *tmp = strdup(argv[i]);
906 size_t src_len = strlen(tmp);
907 if (src_len > 1 && tmp[src_len - 1] == '/') {
908 tmp[src_len - 1] = '\0';
909 }
910 srcpath = get_absolute_path(tmp);
911 dstpath = strdup(argv[i + 1]);
912 size_t dst_len = strlen(dstpath);
913 if (dst_len > 1 && dstpath[dst_len - 1] == '/') {
914 dstpath[dst_len - 1] = '\0';
915 }
916 free(tmp);
917 } else {
918 printf("Error: Invalid number of arguments\n");
919 return;
920 }
921
922 // target is a directory, put file under this target
923 if (is_directory(dstpath)) {
924 const char *basen = path_get_basename(argv[0]);
925 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
926 size_t len = dst_is_root ? (strlen(basen) + 1) : (strlen(dstpath) + 1 + strlen(basen) + 1);
927 char *newdst = (char *) malloc(len);
928 if (dst_is_root) {
929 snprintf(newdst, len, "/%s", basen);
930 } else {
931 snprintf(newdst, len, "%s/%s", dstpath, basen);
932 }
933 get_file(afc, srcpath, newdst, force_overwrite, recursive_get);
934 free(srcpath);
935 free(newdst);
936 free(dstpath);
937 } else {
938 // target is not a dir or does not exist, just try to create or rewrite it
939 get_file(afc, srcpath, dstpath, force_overwrite, recursive_get);
940 free(srcpath);
941 free(dstpath);
942 }
943}
944
945static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite)
946{
947 char **info = NULL;
948 afc_error_t ret = afc_get_file_info(afc, dstpath, &info);
949 // file exists, only overwrite with '-f' option was set
950 if (ret == AFC_E_SUCCESS && info) {
951 afc_dictionary_free(info);
952 if (!force_overwrite) {
953 printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath);
954 return 0;
955 }
956 }
957 FILE *f = fopen(srcpath, "rb");
958 if (!f) {
959 printf("Error: Failed to open local file '%s': %s\n", srcpath, strerror(errno));
960 return 0;
961 }
962 struct timeval t1;
963 struct timeval t2;
964 struct timeval tdiff;
965 struct stat fst;
966 int progress = 0;
967 size_t bufsize = 0x100000;
968 char *buf = malloc(bufsize);
969
970 fstat(fileno(f), &fst);
971 if (fst.st_size >= 0x400000) {
972 progress = 1;
973 gettimeofday(&t1, NULL);
974 }
975 size_t total = 0;
976 int lastprog = 0;
977 uint64_t fh = 0;
978 afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh);
979 uint8_t succeed = 1;
980 while (err == AFC_E_SUCCESS) {
981 uint32_t bytes_read = fread(buf, 1, bufsize, f);
982 if (bytes_read == 0) {
983 if (!feof(f)) {
984 if (progress) {
985 printf("\n");
986 }
987 printf("Error: Failed to read from local file\n");
988 succeed = 0;
989 }
990 break;
991 }
992 uint32_t chunk = 0;
993 while (chunk < bytes_read) {
994 uint32_t bytes_written = 0;
995 err = afc_file_write(afc, fh, buf + chunk, bytes_read - chunk, &bytes_written);
996 if (err != AFC_E_SUCCESS) {
997 if (progress) {
998 printf("\n");
999 }
1000 printf("Error: Failed to write to device file\n");
1001 succeed = 0;
1002 break;
1003 }
1004 chunk += bytes_written;
1005 }
1006 total += chunk;
1007 if (progress) {
1008 int prog = (int) ((double) total / (double) fst.st_size * 100.0f);
1009 if (prog > lastprog) {
1010 gettimeofday(&t2, NULL);
1011 timeval_subtract(&tdiff, &t2, &t1);
1012 double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
1013 printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
1014 fflush(stdout);
1015 lastprog = prog;
1016 }
1017 }
1018 }
1019 free(buf);
1020 afc_file_close(afc, fh);
1021 fclose(f);
1022 return succeed;
1023}
1024
1025static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_put)
1026{
1027 if (is_directory(srcpath)) {
1028 if (!recursive_put) {
1029 printf("Error: Failed to put directory without '-r' option: %s\n", srcpath);
1030 return 0;
1031 }
1032 char **info = NULL;
1033 afc_error_t err = afc_get_file_info(afc, dstpath, &info);
1034 //create if target directory does not exist
1035 afc_dictionary_free(info);
1036 if (err == AFC_E_OBJECT_NOT_FOUND) {
1037 err = afc_make_directory(afc, dstpath);
1038 if (err != AFC_E_SUCCESS) {
1039 printf("Error: Failed to create directory '%s': %s (%d)\n", dstpath, afc_strerror(err), err);
1040 return 0;
1041 }
1042 } else if (!force_overwrite) {
1043 printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath);
1044 return 0;
1045 }
1046 afc_get_file_info(afc, dstpath, &info);
1047 uint8_t is_dir = 0;
1048 if (info) {
1049 char **p = info;
1050 while (p && *p) {
1051 if (!strcmp(*p, "st_ifmt")) {
1052 p++;
1053 is_dir = !strcmp(*p, "S_IFDIR");
1054 break;
1055 }
1056 p++;
1057 }
1058 afc_dictionary_free(info);
1059 }
1060 if (!is_dir) {
1061 printf("Error: Failed to create or access directory: '%s'\n", dstpath);
1062 return 0;
1063 }
1064
1065 // walk dir recursively to put files
1066 DIR *cur_dir = opendir(srcpath);
1067 if (cur_dir) {
1068 struct dirent *ep;
1069 while ((ep = readdir(cur_dir))) {
1070 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1071 continue;
1072 }
1073 char *fpath = string_build_path(srcpath, ep->d_name, NULL);
1074 if (fpath) {
1075 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
1076 size_t len = dst_is_root ? strlen(ep->d_name) + 1 : strlen(dstpath) + 1 + strlen(ep->d_name) + 1;
1077 char *newdst = (char *) malloc(len);
1078 if (dst_is_root) {
1079 snprintf(newdst, len, "/%s", ep->d_name);
1080 } else {
1081 snprintf(newdst, len, "%s/%s", dstpath, ep->d_name);
1082 }
1083 if (!put_file(afc, fpath, newdst, force_overwrite, recursive_put)) {
1084 free(newdst);
1085 free(fpath);
1086 return 0;
1087 }
1088 free(newdst);
1089 free(fpath);
1090 }
1091 }
1092 closedir(cur_dir);
1093 } else {
1094 printf("Error: Failed to visit directory: '%s': %s\n", srcpath, strerror(errno));
1095 return 0;
1096 }
1097 } else {
1098 return put_single_file(afc, srcpath, dstpath, force_overwrite);
1099 }
1100 return 1;
1101}
1102
1103static void handle_put(afc_client_t afc, int argc, char **argv)
1104{
1105 if (argc < 1) {
1106 printf("Error: Invalid number of arguments\n");
1107 return;
1108 }
1109 int i = 0;
1110 uint8_t force_overwrite = 0, recursive_put = 0;
1111 for ( ; i < argc; i++) {
1112 if (!strcmp(argv[i], "--")) {
1113 i++;
1114 break;
1115 } else if (!strcmp(argv[i], "-r")) {
1116 recursive_put = 1;
1117 } else if (!strcmp(argv[i], "-f")) {
1118 force_overwrite = 1;
1119 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
1120 recursive_put = 1;
1121 force_overwrite = 1;
1122 } else {
1123 break;
1124 }
1125 }
1126 if (i >= argc) {
1127 printf("Error: Invalid number of arguments\n");
1128 return;
1129 }
1130 char *srcpath = strdup(argv[i]);
1131 size_t src_len = strlen(srcpath);
1132 if (src_len > 1 && srcpath[src_len - 1] == '/') {
1133 srcpath[src_len - 1] = '\0';
1134 }
1135 char *dstpath = NULL;
1136 if (argc - i == 1) {
1137 dstpath = get_absolute_path(path_get_basename(srcpath));
1138 } else if (argc - i == 2) {
1139 char *tmp = strdup(argv[i + 1]);
1140 size_t dst_len = strlen(tmp);
1141 if (dst_len > 1 && tmp[dst_len - 1] == '/') {
1142 tmp[dst_len - 1] = '\0';
1143 }
1144 dstpath = get_absolute_path(tmp);
1145 free(tmp);
1146 } else {
1147 printf("Error: Invalid number of arguments\n");
1148 return;
1149 }
1150 char **info = NULL;
1151 afc_error_t err = afc_get_file_info(afc, dstpath, &info);
1152 // target does not exist, put directly
1153 if (err == AFC_E_OBJECT_NOT_FOUND) {
1154 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1155 free(srcpath);
1156 free(dstpath);
1157 } else {
1158 uint8_t is_dir = 0;
1159 if (info) {
1160 char **p = info;
1161 while (p && *p) {
1162 if (!strcmp(*p, "st_ifmt")) {
1163 p++;
1164 is_dir = !strcmp(*p, "S_IFDIR");
1165 break;
1166 }
1167 p++;
1168 }
1169 afc_dictionary_free(info);
1170 }
1171 // target is a directory, try to put under this directory
1172 if (is_dir) {
1173 const char *basen = path_get_basename(srcpath);
1174 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
1175 size_t len = dst_is_root ? strlen(basen) + 1 : strlen(dstpath) + 1 + strlen(basen) + 1;
1176 char *newdst = (char *) malloc(len);
1177 if (dst_is_root) {
1178 snprintf(newdst, len, "/%s", basen);
1179 } else {
1180 snprintf(newdst, len, "%s/%s", dstpath, basen);
1181 }
1182 free(dstpath);
1183 dstpath = get_absolute_path(newdst);
1184 free(newdst);
1185 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1186 } else {
1187 //target is common file, rewrite it
1188 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1189 }
1190 free(srcpath);
1191 free(dstpath);
1192 }
1193}
1194
1195static void handle_pwd(afc_client_t afc, int argc, char** argv)
1196{
1197 printf("%s\n", curdir);
1198}
1199
1200static void handle_cd(afc_client_t afc, int argc, char** argv)
1201{
1202 if (argc != 1) {
1203 printf("Error: Invalid number of arguments\n");
1204 return;
1205 }
1206
1207 if (!strcmp(argv[0], ".")) {
1208 return;
1209 }
1210
1211 if (!strcmp(argv[0], "..")) {
1212 if (!strcmp(curdir, "/")) {
1213 return;
1214 }
1215 char *p = strrchr(curdir, '/');
1216 if (!p) {
1217 strcpy(curdir, "/");
1218 return;
1219 }
1220 if (p == curdir) {
1221 *(p+1) = '\0';
1222 } else {
1223 *p = '\0';
1224 }
1225 return;
1226 }
1227
1228 char* path = get_realpath(argv[0]);
1229 int is_dir = 0;
1230 char **info = NULL;
1231 afc_error_t err = afc_get_file_info(afc, path, &info);
1232 if (err == AFC_E_SUCCESS && info) {
1233 int i;
1234 for (i = 0; info[i]; i += 2) {
1235 if (!strcmp(info[i], "st_ifmt")) {
1236 if (!strcmp(info[i+1], "S_IFDIR")) {
1237 is_dir = 1;
1238 }
1239 break;
1240 }
1241 }
1242 afc_dictionary_free(info);
1243 } else {
1244 printf("Error: Failed to get file info for %s: %s (%d)\n", path, afc_strerror(err), err);
1245 free(path);
1246 return;
1247 }
1248
1249 if (!is_dir) {
1250 printf("Error: '%s' is not a valid directory\n", path);
1251 free(path);
1252 return;
1253 }
1254
1255 free(curdir);
1256 curdir = path;
1257 curdir_len = strlen(curdir);
1258}
1259
1260static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline)
1261{
1262 char **argv = NULL;
1263 int argc = 0;
1264 size_t maxlen = strlen(cmdline);
1265 const char* pos = cmdline;
1266 const char* qpos = NULL;
1267 char *tmpbuf = NULL;
1268 int tmplen = 0;
1269 int is_error = 0;
1270
1271 /* skip initial whitespace */
1272 while (isspace(*pos)) pos++;
1273 maxlen -= (pos - cmdline);
1274
1275 tmpbuf = (char*)malloc(maxlen+1);
1276
1277 while (!is_error) {
1278 if (*pos == '\\') {
1279 pos++;
1280 switch (*pos) {
1281 case '"':
1282 case '\'':
1283 case '\\':
1284 case ' ':
1285 tmpbuf[tmplen++] = *pos;
1286 pos++;
1287 break;
1288 default:
1289 printf("Error: Invalid escape sequence\n");
1290 is_error++;
1291 break;
1292 }
1293 } else if (*pos == '"' || *pos == '\'') {
1294 if (!qpos) {
1295 qpos = pos;
1296 } else {
1297 qpos = NULL;
1298 }
1299 pos++;
1300 } else if (*pos == '\0' || (!qpos && isspace(*pos))) {
1301 tmpbuf[tmplen] = '\0';
1302 if (*pos == '\0' && qpos) {
1303 printf("Error: Unmatched `%c`\n", *qpos);
1304 is_error++;
1305 break;
1306 }
1307 char** new_argv = (char**)realloc(argv, (argc+1)*sizeof(char*));
1308 if (new_argv == NULL) {
1309 printf("Error: Out of memory?!\n");
1310 is_error++;
1311 break;
1312 }
1313 argv = new_argv;
1314 /* shrink buffer to actual argument size */
1315 argv[argc] = (char*)realloc(tmpbuf, tmplen+1);
1316 if (!argv[argc]) {
1317 printf("Error: Out of memory?!\n");
1318 is_error++;
1319 break;
1320 }
1321 argc++;
1322 tmpbuf = NULL;
1323 if (*pos == '\0') {
1324 break;
1325 }
1326 maxlen -= tmplen;
1327 tmpbuf = (char*)malloc(maxlen+1);
1328 tmplen = 0;
1329 while (isspace(*pos)) pos++;
1330 } else {
1331 tmpbuf[tmplen++] = *pos;
1332 pos++;
1333 }
1334 }
1335 if (tmpbuf) {
1336 free(tmpbuf);
1337 }
1338 if (is_error) {
1339 int i;
1340 for (i = 0; argv && i < argc; i++) free(argv[i]);
1341 free(argv);
1342 return;
1343 }
1344
1345 *p_argv = argv;
1346 *p_argc = argc;
1347}
1348
1349static int process_args(afc_client_t afc, int argc, char** argv)
1350{
1351 if (!strcmp(argv[0], "q") || !strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
1352 return -1;
1353 }
1354 else if (!strcmp(argv[0], "help")) {
1355 handle_help(afc, argc, argv);
1356 }
1357 else if (!strcmp(argv[0], "devinfo") || !strcmp(argv[0], "deviceinfo")) {
1358 handle_devinfo(afc, argc-1, argv+1);
1359 }
1360 else if (!strcmp(argv[0], "info")) {
1361 handle_file_info(afc, argc-1, argv+1);
1362 }
1363 else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) {
1364 handle_list(afc, argc-1, argv+1);
1365 }
1366 else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) {
1367 handle_rename(afc, argc-1, argv+1);
1368 }
1369 else if (!strcmp(argv[0], "mkdir")) {
1370 handle_mkdir(afc, argc-1, argv+1);
1371 }
1372 else if (!strcmp(argv[0], "ln")) {
1373 handle_link(afc, argc-1, argv+1);
1374 }
1375 else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) {
1376 handle_remove(afc, argc-1, argv+1);
1377 }
1378 else if (!strcmp(argv[0], "get")) {
1379 handle_get(afc, argc-1, argv+1);
1380 }
1381 else if (!strcmp(argv[0], "put")) {
1382 handle_put(afc, argc-1, argv+1);
1383 }
1384 else if (!strcmp(argv[0], "pwd")) {
1385 handle_pwd(afc, argc-1, argv+1);
1386 }
1387 else if (!strcmp(argv[0], "cd")) {
1388 handle_cd(afc, argc-1, argv+1);
1389 }
1390 else {
1391 printf("Unknown command '%s'. Type 'help' to get a list of available commands.\n", argv[0]);
1392 }
1393 return 0;
1394}
1395
1396static void start_cmdline(afc_client_t afc)
1397{
1398 while (!stop_requested) {
1399 int argc = 0;
1400 char **argv = NULL;
1401 char prompt[128];
1402 int plen = curdir_len;
1403 char *ppath = curdir;
1404 int plim = (int)(sizeof(prompt)/2)-8;
1405 if (plen > plim) {
1406 ppath = curdir + (plen - plim);
1407 plen = plim;
1408 }
1409 snprintf(prompt, 128, FG_BLACK BG_LIGHT_GRAY "afc:" COLOR_RESET FG_BRIGHT_YELLOW BG_BLUE "%.*s" COLOR_RESET " > ", plen, ppath);
1410#ifdef HAVE_READLINE
1411 char* cmd = readline(prompt);
1412 if (!cmd || !*cmd) {
1413 free(cmd);
1414 continue;
1415 }
1416 add_history(cmd);
1417 parse_cmdline(&argc, &argv, cmd);
1418#else
1419 char cmdbuf[4096];
1420 printf("%s", prompt);
1421 fflush(stdout);
1422 get_input(cmdbuf, sizeof(cmdbuf));
1423 parse_cmdline(&argc, &argv, cmdbuf);
1424#endif
1425#ifdef HAVE_READLINE
1426 free(cmd);
1427#endif
1428 /* process arguments */
1429 if (argv && argv[0]) {
1430 if (process_args(afc, argc, argv) < 0) {
1431 break;
1432 }
1433 }
1434 }
1435}
1436
1437static void device_event_cb(const idevice_event_t* event, void* userdata)
1438{
1439 if (use_network && event->conn_type != CONNECTION_NETWORK) {
1440 return;
1441 } else if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
1442 return;
1443 }
1444 if (event->event == IDEVICE_DEVICE_ADD) {
1445 if (!udid) {
1446 udid = strdup(event->udid);
1447 }
1448 if (strcmp(udid, event->udid) == 0) {
1449 connected = 1;
1450 }
1451 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
1452 if (strcmp(udid, event->udid) == 0) {
1453 connected = 0;
1454 printf("\n[disconnected]\n");
1455 handle_signal(SIGINT);
1456 }
1457 }
1458}
1459
1460int main(int argc, char** argv)
1461{
1462 const char* appid = NULL;
1463 int ret = 0;
1464 idevice_t device = NULL;
1465 lockdownd_client_t lockdown = NULL;
1466 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
1467 lockdownd_service_descriptor_t service = NULL;
1468 afc_client_t afc = NULL;
1469 house_arrest_client_t house_arrest = NULL;
1470 const char* service_name = AFC_SERVICE_NAME;
1471 int use_container = 0;
1472
1473 int c = 0;
1474 const struct option longopts[] = {
1475 { "udid", required_argument, NULL, 'u' },
1476 { "network", no_argument, NULL, 'n' },
1477 { "help", no_argument, NULL, 'h' },
1478 { "debug", no_argument, NULL, 'd' },
1479 { "version", no_argument, NULL, 'v' },
1480 { "documents", required_argument, NULL, OPT_DOCUMENTS },
1481 { "container", required_argument, NULL, OPT_CONTAINER },
1482 { NULL, 0, NULL, 0}
1483 };
1484
1485 signal(SIGTERM, handle_signal);
1486#ifndef WIN32
1487 signal(SIGQUIT, handle_signal);
1488 signal(SIGPIPE, SIG_IGN);
1489#endif
1490
1491 while ((c = getopt_long(argc, argv, "du:nhv", longopts, NULL)) != -1) {
1492 switch (c) {
1493 case 'd':
1494 idevice_set_debug_level(1);
1495 break;
1496 case 'u':
1497 if (!*optarg) {
1498 fprintf(stderr, "ERROR: UDID must not be empty!\n");
1499 print_usage(argc, argv, 1);
1500 return 2;
1501 }
1502 udid = strdup(optarg);
1503 break;
1504 case 'n':
1505 use_network = 1;
1506 break;
1507 case 'h':
1508 print_usage(argc, argv, 0);
1509 return 0;
1510 case 'v':
1511 printf("%s %s", TOOL_NAME, PACKAGE_VERSION);
1512#ifdef HAVE_READLINE
1513 printf(" (readline)");
1514#endif
1515 printf("\n");
1516 return 0;
1517 case OPT_DOCUMENTS:
1518 if (!*optarg) {
1519 fprintf(stderr, "ERROR: '--documents' requires a non-empty app ID!\n");
1520 print_usage(argc, argv, 1);
1521 return 2;
1522 }
1523 appid = optarg;
1524 use_container = 0;
1525 break;
1526 case OPT_CONTAINER:
1527 if (!*optarg) {
1528 fprintf(stderr, "ERROR: '--container' requires a not-empty app ID!\n");
1529 print_usage(argc, argv, 1);
1530 return 2;
1531 }
1532 appid = optarg;
1533 use_container = 1;
1534 break;
1535 default:
1536 print_usage(argc, argv, 1);
1537 return 2;
1538 }
1539 }
1540
1541 argc -= optind;
1542 argv += optind;
1543
1544 int num = 0;
1545 idevice_info_t *devices = NULL;
1546 idevice_get_device_list_extended(&devices, &num);
1547 int count = 0;
1548 for (int i = 0; i < num; i++) {
1549 if (devices[i]->conn_type == CONNECTION_NETWORK && use_network) {
1550 count++;
1551 } else if (devices[i]->conn_type == CONNECTION_USBMUXD) {
1552 count++;
1553 }
1554 }
1555 idevice_device_list_extended_free(devices);
1556 if (count == 0) {
1557 fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
1558 return 1;
1559 }
1560
1561 idevice_events_subscribe(&context, device_event_cb, NULL);
1562
1563 while (!connected && !stop_requested) {
1564#ifdef WIN32
1565 Sleep(100);
1566#else
1567 usleep(100000);
1568#endif
1569 }
1570 if (stop_requested) {
1571 return 0;
1572 }
1573
1574 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
1575 if (ret != IDEVICE_E_SUCCESS) {
1576 if (udid) {
1577 fprintf(stderr, "ERROR: Device %s not found!\n", udid);
1578 } else {
1579 fprintf(stderr, "ERROR: No device found!\n");
1580 }
1581 return 1;
1582 }
1583
1584 do {
1585 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
1586 fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
1587 ret = 1;
1588 break;
1589 }
1590
1591 if (appid) {
1592 service_name = HOUSE_ARREST_SERVICE_NAME;
1593 }
1594
1595 ldret = lockdownd_start_service(lockdown, service_name, &service);
1596 if (ldret != LOCKDOWN_E_SUCCESS) {
1597 fprintf(stderr, "ERROR: Failed to start service %s: %s (%d)\n", service_name, lockdownd_strerror(ldret), ldret);
1598 ret = 1;
1599 break;
1600 }
1601
1602 if (appid) {
1603 house_arrest_client_new(device, service, &house_arrest);
1604 if (!house_arrest) {
1605 fprintf(stderr, "Could not start document sharing service!\n");
1606 ret = 1;
1607 break;
1608 }
1609
1610 if (house_arrest_send_command(house_arrest, use_container ? "VendContainer": "VendDocuments", appid) != HOUSE_ARREST_E_SUCCESS) {
1611 fprintf(stderr, "Could not send house_arrest command!\n");
1612 ret = 1;
1613 break;
1614 }
1615
1616 plist_t dict = NULL;
1617 if (house_arrest_get_result(house_arrest, &dict) != HOUSE_ARREST_E_SUCCESS) {
1618 fprintf(stderr, "Could not get result from document sharing service!\n");
1619 break;
1620 }
1621 plist_t node = plist_dict_get_item(dict, "Error");
1622 if (node) {
1623 char *str = NULL;
1624 plist_get_string_val(node, &str);
1625 fprintf(stderr, "ERROR: %s\n", str);
1626 if (str && !strcmp(str, "InstallationLookupFailed")) {
1627 fprintf(stderr, "The App '%s' is either not present on the device, or the 'UIFileSharingEnabled' key is not set in its Info.plist. Starting with iOS 8.3 this key is mandatory to allow access to an app's Documents folder.\n", appid);
1628 }
1629 free(str);
1630 plist_free(dict);
1631 break;
1632 }
1633 plist_free(dict);
1634 afc_client_new_from_house_arrest_client(house_arrest, &afc);
1635 } else {
1636 afc_client_new(device, service, &afc);
1637 }
1638 lockdownd_service_descriptor_free(service);
1639 lockdownd_client_free(lockdown);
1640 lockdown = NULL;
1641
1642 curdir = strdup("/");
1643 curdir_len = 1;
1644
1645 if (argc > 0) {
1646 // command line mode
1647 process_args(afc, argc, argv);
1648 } else {
1649 // interactive mode
1650 start_cmdline(afc);
1651 }
1652
1653 } while (0);
1654
1655 if (afc) {
1656 afc_client_free(afc);
1657 }
1658 if (lockdown) {
1659 lockdownd_client_free(lockdown);
1660 }
1661 idevice_free(device);
1662
1663 return ret;
1664}
diff --git a/tools/idevice_id.c b/tools/idevice_id.c
index 1facb60..540a6f2 100644
--- a/tools/idevice_id.c
+++ b/tools/idevice_id.c
@@ -1,6 +1,34 @@
1/*
2 * idevice_id.c
3 * Prints device name or a list of attached devices
4 *
5 * Copyright (C) 2010-2018 Nikias Bassen <nikias@gmx.li>
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
26#define TOOL_NAME "idevice_id"
27
1#include <stdio.h> 28#include <stdio.h>
2#include <string.h> 29#include <string.h>
3#include <stdlib.h> 30#include <stdlib.h>
31#include <getopt.h>
4#include <libimobiledevice/libimobiledevice.h> 32#include <libimobiledevice/libimobiledevice.h>
5#include <libimobiledevice/lockdown.h> 33#include <libimobiledevice/lockdown.h>
6 34
@@ -8,100 +36,139 @@
8#define MODE_SHOW_ID 1 36#define MODE_SHOW_ID 1
9#define MODE_LIST_DEVICES 2 37#define MODE_LIST_DEVICES 2
10 38
11static void print_usage(int argc, char **argv) 39static void print_usage(int argc, char **argv, int is_error)
12{ 40{
13 char *name = NULL; 41 char *name = strrchr(argv[0], '/');
14 42 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [UDID]\n", (name ? name + 1: argv[0]));
15 name = strrchr(argv[0], '/'); 43 fprintf(is_error ? stderr : stdout,
16 printf("Usage: %s [OPTIONS] [UUID]\n", (name ? name + 1: argv[0])); 44 "\n"
17 printf("Prints device name or a list of attached iPhone/iPod Touch devices.\n\n"); 45 "List attached devices or print device name of given device.\n"
18 printf(" The UUID is a 40-digit hexadecimal number of the device\n"); 46 "\n" \
19 printf(" for which the name should be retrieved.\n\n"); 47 " If UDID is given, the name of the connected device with that UDID"
20 printf(" -l, --list\t\tlist UUID of all attached devices\n"); 48 " will be retrieved.\n"
21 printf(" -d, --debug\t\tenable communication debugging\n"); 49 "\n" \
22 printf(" -h, --help\t\tprints usage information\n"); 50 "OPTIONS:\n"
23 printf("\n"); 51 " -l, --list list UDIDs of all devices attached via USB\n"
52 " -n, --network list UDIDs of all devices available via network\n"
53 " -d, --debug enable communication debugging\n"
54 " -h, --help prints usage information\n"
55 " -v, --version prints version information\n"
56 "\n"
57 "Homepage: <" PACKAGE_URL ">\n"
58 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
59 );
24} 60}
25 61
26int main(int argc, char **argv) 62int main(int argc, char **argv)
27{ 63{
28 idevice_t phone = NULL; 64 idevice_t device = NULL;
29 lockdownd_client_t client = NULL; 65 lockdownd_client_t client = NULL;
30 char **dev_list = NULL; 66 idevice_info_t *dev_list = NULL;
31 char *devname = NULL; 67 char *device_name = NULL;
32 int ret = 0; 68 int ret = 0;
33 int i; 69 int i;
34 int mode = MODE_SHOW_ID; 70 int mode = MODE_LIST_DEVICES;
35 char uuid[41]; 71 int include_usb = 0;
36 uuid[0] = 0; 72 int include_network = 0;
73 const char* udid = NULL;
37 74
38 /* parse cmdline args */ 75 int c = 0;
39 for (i = 1; i < argc; i++) { 76 const struct option longopts[] = {
40 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 77 { "debug", no_argument, NULL, 'd' },
78 { "help", no_argument, NULL, 'h' },
79 { "list", no_argument, NULL, 'l' },
80 { "network", no_argument, NULL, 'n' },
81 { "version", no_argument, NULL, 'v' },
82 { NULL, 0, NULL, 0}
83 };
84
85 while ((c = getopt_long(argc, argv, "dhlnv", longopts, NULL)) != -1) {
86 switch (c) {
87 case 'd':
41 idevice_set_debug_level(1); 88 idevice_set_debug_level(1);
42 continue; 89 break;
43 } 90 case 'h':
44 else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--list")) { 91 print_usage(argc, argv, 0);
92 exit(EXIT_SUCCESS);
93 case 'l':
45 mode = MODE_LIST_DEVICES; 94 mode = MODE_LIST_DEVICES;
46 continue; 95 include_usb = 1;
47 } 96 break;
48 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 97 case 'n':
49 print_usage(argc, argv); 98 mode = MODE_LIST_DEVICES;
99 include_network = 1;
100 break;
101 case 'v':
102 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
50 return 0; 103 return 0;
104 default:
105 print_usage(argc, argv, 1);
106 exit(EXIT_FAILURE);
51 } 107 }
52 } 108 }
109 argc -= optind;
110 argv += optind;
53 111
54 /* check if uuid was passed */ 112 if (argc == 1) {
55 if (mode == MODE_SHOW_ID) { 113 mode = MODE_SHOW_ID;
56 i--; 114 } else if (argc == 0 && optind == 1) {
57 if (!argv[i] || (strlen(argv[i]) != 40)) { 115 include_usb = 1;
58 print_usage(argc, argv); 116 include_network = 1;
59 return 0;
60 }
61 strcpy(uuid, argv[i]);
62 } 117 }
118 udid = argv[0];
63 119
64 switch (mode) { 120 switch (mode) {
65 case MODE_SHOW_ID: 121 case MODE_SHOW_ID:
66 idevice_new(&phone, uuid); 122 idevice_new_with_options(&device, udid, IDEVICE_LOOKUP_USBMUX | IDEVICE_LOOKUP_NETWORK);
67 if (!phone) { 123 if (!device) {
68 fprintf(stderr, "ERROR: No device with UUID=%s attached.\n", uuid); 124 fprintf(stderr, "ERROR: No device with UDID %s attached.\n", udid);
69 return -2; 125 return -2;
70 } 126 }
71 127
72 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new(phone, &client, "idevice_id")) { 128 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new(device, &client, TOOL_NAME)) {
73 idevice_free(phone); 129 idevice_free(device);
74 fprintf(stderr, "ERROR: Connecting to device failed!\n"); 130 fprintf(stderr, "ERROR: Connecting to device failed!\n");
75 return -2; 131 return -2;
76 } 132 }
77 133
78 if ((LOCKDOWN_E_SUCCESS != lockdownd_get_device_name(client, &devname)) || !devname) { 134 if ((LOCKDOWN_E_SUCCESS != lockdownd_get_device_name(client, &device_name)) || !device_name) {
79 fprintf(stderr, "ERROR: Could not get device name!\n"); 135 fprintf(stderr, "ERROR: Could not get device name!\n");
80 ret = -2; 136 ret = -2;
81 } 137 }
82 138
83 lockdownd_client_free(client); 139 lockdownd_client_free(client);
84 idevice_free(phone); 140 idevice_free(device);
85 141
86 if (ret == 0) { 142 if (ret == 0) {
87 printf("%s\n", devname); 143 printf("%s\n", device_name);
88 } 144 }
89 145
90 if (devname) { 146 if (device_name) {
91 free(devname); 147 free(device_name);
92 } 148 }
149 break;
93 150
94 return ret;
95 case MODE_LIST_DEVICES: 151 case MODE_LIST_DEVICES:
96 default: 152 default:
97 if (idevice_get_device_list(&dev_list, &i) < 0) { 153 if (idevice_get_device_list_extended(&dev_list, &i) < 0) {
98 fprintf(stderr, "ERROR: Unable to retrieve device list!\n"); 154 fprintf(stderr, "ERROR: Unable to retrieve device list!\n");
99 return -1; 155 return -1;
100 } 156 }
101 for (i = 0; dev_list[i] != NULL; i++) { 157 for (i = 0; dev_list[i] != NULL; i++) {
102 printf("%s\n", dev_list[i]); 158 if (dev_list[i]->conn_type == CONNECTION_USBMUXD && !include_usb) continue;
159 if (dev_list[i]->conn_type == CONNECTION_NETWORK && !include_network) continue;
160 printf("%s", dev_list[i]->udid);
161 if (include_usb && include_network) {
162 if (dev_list[i]->conn_type == CONNECTION_NETWORK) {
163 printf(" (Network)");
164 } else {
165 printf(" (USB)");
166 }
167 }
168 printf("\n");
103 } 169 }
104 idevice_device_list_free(dev_list); 170 idevice_device_list_extended_free(dev_list);
105 return 0; 171 break;
106 } 172 }
173 return ret;
107} 174}
diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c
index 867eaad..c0537b8 100644
--- a/tools/idevicebackup.c
+++ b/tools/idevicebackup.c
@@ -9,31 +9,41 @@
9 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
12 * 12 *
13 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
17 * 17 *
18 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */ 21 */
22 22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "idevicebackup"
28
23#include <stdio.h> 29#include <stdio.h>
24#include <string.h> 30#include <string.h>
25#include <errno.h> 31#include <errno.h>
26#include <stdlib.h> 32#include <stdlib.h>
27#include <signal.h> 33#include <signal.h>
28#include <glib.h> 34#include <getopt.h>
29#include <gcrypt.h>
30#include <unistd.h> 35#include <unistd.h>
36#include <ctype.h>
37#include <time.h>
31 38
32#include <libimobiledevice/libimobiledevice.h> 39#include <libimobiledevice/libimobiledevice.h>
33#include <libimobiledevice/lockdown.h> 40#include <libimobiledevice/lockdown.h>
34#include <libimobiledevice/mobilebackup.h> 41#include <libimobiledevice/mobilebackup.h>
35#include <libimobiledevice/notification_proxy.h> 42#include <libimobiledevice/notification_proxy.h>
36#include <libimobiledevice/afc.h> 43#include <libimobiledevice/afc.h>
44#include <libimobiledevice-glue/sha.h>
45#include <libimobiledevice-glue/utils.h>
46#include <plist/plist.h>
37 47
38#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" 48#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup"
39#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy" 49#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
@@ -41,9 +51,14 @@
41#define LOCK_ATTEMPTS 50 51#define LOCK_ATTEMPTS 50
42#define LOCK_WAIT 200000 52#define LOCK_WAIT 200000
43 53
54#ifdef WIN32
55#include <windows.h>
56#define sleep(x) Sleep(x*1000)
57#endif
58
44static mobilebackup_client_t mobilebackup = NULL; 59static mobilebackup_client_t mobilebackup = NULL;
45static lockdownd_client_t client = NULL; 60static lockdownd_client_t client = NULL;
46static idevice_t phone = NULL; 61static idevice_t device = NULL;
47 62
48static int quit_flag = 0; 63static int quit_flag = 0;
49 64
@@ -53,22 +68,12 @@ enum cmd_mode {
53 CMD_LEAVE 68 CMD_LEAVE
54}; 69};
55 70
56enum plist_format_t {
57 PLIST_FORMAT_XML,
58 PLIST_FORMAT_BINARY
59};
60
61enum device_link_file_status_t { 71enum device_link_file_status_t {
62 DEVICE_LINK_FILE_STATUS_NONE = 0, 72 DEVICE_LINK_FILE_STATUS_NONE = 0,
63 DEVICE_LINK_FILE_STATUS_HUNK, 73 DEVICE_LINK_FILE_STATUS_HUNK,
64 DEVICE_LINK_FILE_STATUS_LAST_HUNK 74 DEVICE_LINK_FILE_STATUS_LAST_HUNK
65}; 75};
66 76
67static void sha1_of_data(const char *input, uint32_t size, unsigned char *hash_out)
68{
69 gcry_md_hash_buffer(GCRY_MD_SHA1, hash_out, input, size);
70}
71
72static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, int hash_len) 77static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, int hash_len)
73{ 78{
74 int i; 79 int i;
@@ -82,51 +87,47 @@ static int compare_hash(const unsigned char *hash1, const unsigned char *hash2,
82 87
83static void compute_datahash(const char *path, const char *destpath, uint8_t greylist, const char *domain, const char *appid, const char *version, unsigned char *hash_out) 88static void compute_datahash(const char *path, const char *destpath, uint8_t greylist, const char *domain, const char *appid, const char *version, unsigned char *hash_out)
84{ 89{
85 gcry_md_hd_t hd = NULL; 90 sha1_context sha1;
86 gcry_md_open(&hd, GCRY_MD_SHA1, 0); 91 sha1_init(&sha1);
87 if (!hd) {
88 printf("ERROR: Could not initialize libgcrypt/SHA1\n");
89 return;
90 }
91 gcry_md_reset(hd);
92
93 FILE *f = fopen(path, "rb"); 92 FILE *f = fopen(path, "rb");
94 if (f) { 93 if (f) {
95 unsigned char buf[16384]; 94 unsigned char buf[16384];
96 size_t len; 95 size_t len;
97 while ((len = fread(buf, 1, 16384, f)) > 0) { 96 while ((len = fread(buf, 1, 16384, f)) > 0) {
98 gcry_md_write(hd, buf, len); 97 sha1_update(&sha1, buf, len);
99 } 98 }
100 fclose(f); 99 fclose(f);
101 gcry_md_write(hd, destpath, strlen(destpath)); 100 sha1_update(&sha1, destpath, strlen(destpath));
102 gcry_md_write(hd, ";", 1); 101 sha1_update(&sha1, ";", 1);
102
103 if (greylist == 1) { 103 if (greylist == 1) {
104 gcry_md_write(hd, "true", 4); 104 sha1_update(&sha1, "true", 4);
105 } else { 105 } else {
106 gcry_md_write(hd, "false", 5); 106 sha1_update(&sha1, "false", 5);
107 } 107 }
108 gcry_md_write(hd, ";", 1); 108 sha1_update(&sha1, ";", 1);
109
109 if (domain) { 110 if (domain) {
110 gcry_md_write(hd, domain, strlen(domain)); 111 sha1_update(&sha1, domain, strlen(domain));
111 } else { 112 } else {
112 gcry_md_write(hd, "(null)", 6); 113 sha1_update(&sha1, "(null)", 6);
113 } 114 }
114 gcry_md_write(hd, ";", 1); 115 sha1_update(&sha1, ";", 1);
116
115 if (appid) { 117 if (appid) {
116 gcry_md_write(hd, appid, strlen(appid)); 118 sha1_update(&sha1, appid, strlen(appid));
117 } else { 119 } else {
118 gcry_md_write(hd, "(null)", 6); 120 sha1_update(&sha1, "(null)", 6);
119 } 121 }
120 gcry_md_write(hd, ";", 1); 122 sha1_update(&sha1, ";", 1);
123
121 if (version) { 124 if (version) {
122 gcry_md_write(hd, version, strlen(version)); 125 sha1_update(&sha1, version, strlen(version));
123 } else { 126 } else {
124 gcry_md_write(hd, "(null)", 6); 127 sha1_update(&sha1, "(null)", 6);
125 } 128 }
126 unsigned char *newhash = gcry_md_read(hd, GCRY_MD_SHA1); 129 sha1_final(&sha1, hash_out);
127 memcpy(hash_out, newhash, 20);
128 } 130 }
129 gcry_md_close(hd);
130} 131}
131 132
132static void print_hash(const unsigned char *hash, int len) 133static void print_hash(const unsigned char *hash, int len)
@@ -147,14 +148,12 @@ static void notify_cb(const char *notification, void *userdata)
147 } 148 }
148} 149}
149 150
150static plist_t mobilebackup_factory_info_plist_new() 151static plist_t mobilebackup_factory_info_plist_new(const char* udid)
151{ 152{
152 /* gather data from lockdown */ 153 /* gather data from lockdown */
153 GTimeVal tv = {0, 0};
154 plist_t value_node = NULL; 154 plist_t value_node = NULL;
155 plist_t root_node = NULL; 155 plist_t root_node = NULL;
156 char *uuid = NULL; 156 char *udid_uppercase = NULL;
157 char *uuid_uppercase = NULL;
158 157
159 plist_t ret = plist_new_dict(); 158 plist_t ret = plist_new_dict();
160 159
@@ -163,45 +162,42 @@ static plist_t mobilebackup_factory_info_plist_new()
163 162
164 /* set fields we understand */ 163 /* set fields we understand */
165 value_node = plist_dict_get_item(root_node, "BuildVersion"); 164 value_node = plist_dict_get_item(root_node, "BuildVersion");
166 plist_dict_insert_item(ret, "Build Version", plist_copy(value_node)); 165 plist_dict_set_item(ret, "Build Version", plist_copy(value_node));
167 166
168 value_node = plist_dict_get_item(root_node, "DeviceName"); 167 value_node = plist_dict_get_item(root_node, "DeviceName");
169 plist_dict_insert_item(ret, "Device Name", plist_copy(value_node)); 168 plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
170 plist_dict_insert_item(ret, "Display Name", plist_copy(value_node)); 169 plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
171 170
172 /* FIXME: How is the GUID generated? */ 171 /* FIXME: How is the GUID generated? */
173 plist_dict_insert_item(ret, "GUID", plist_new_string("---")); 172 plist_dict_set_item(ret, "GUID", plist_new_string("---"));
174 173
175 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity"); 174 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
176 if (value_node) 175 if (value_node)
177 plist_dict_insert_item(ret, "IMEI", plist_copy(value_node)); 176 plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
178 177
179 g_get_current_time(&tv); 178 plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
180 plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec));
181 179
182 value_node = plist_dict_get_item(root_node, "ProductType"); 180 value_node = plist_dict_get_item(root_node, "ProductType");
183 plist_dict_insert_item(ret, "Product Type", plist_copy(value_node)); 181 plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
184 182
185 value_node = plist_dict_get_item(root_node, "ProductVersion"); 183 value_node = plist_dict_get_item(root_node, "ProductVersion");
186 plist_dict_insert_item(ret, "Product Version", plist_copy(value_node)); 184 plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
187 185
188 value_node = plist_dict_get_item(root_node, "SerialNumber"); 186 value_node = plist_dict_get_item(root_node, "SerialNumber");
189 plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node)); 187 plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
190 188
191 value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); 189 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
192 idevice_get_uuid(phone, &uuid); 190 plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
193 plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid));
194 191
195 /* uppercase */ 192 /* uppercase */
196 uuid_uppercase = g_ascii_strup(uuid, -1); 193 udid_uppercase = string_toupper((char*)udid);
197 plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase)); 194 plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
198 free(uuid_uppercase); 195 free(udid_uppercase);
199 free(uuid);
200 196
201 /* FIXME: Embed files as <data> nodes */ 197 /* FIXME: Embed files as <data> nodes */
202 plist_t files = plist_new_dict(); 198 plist_t files = plist_new_dict();
203 plist_dict_insert_item(ret, "iTunes Files", files); 199 plist_dict_set_item(ret, "iTunes Files", files);
204 plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2")); 200 plist_dict_set_item(ret, "iTunes Version", plist_new_string("9.0.2"));
205 201
206 plist_free(root_node); 202 plist_free(root_node);
207 203
@@ -210,102 +206,17 @@ static plist_t mobilebackup_factory_info_plist_new()
210 206
211static void mobilebackup_info_update_last_backup_date(plist_t info_plist) 207static void mobilebackup_info_update_last_backup_date(plist_t info_plist)
212{ 208{
213 GTimeVal tv = {0, 0};
214 plist_t node = NULL; 209 plist_t node = NULL;
215 210
216 if (!info_plist) 211 if (!info_plist)
217 return; 212 return;
218 213
219 g_get_current_time(&tv);
220 node = plist_dict_get_item(info_plist, "Last Backup Date"); 214 node = plist_dict_get_item(info_plist, "Last Backup Date");
221 plist_set_date_val(node, tv.tv_sec, tv.tv_usec); 215 plist_set_date_val(node, time(NULL) - MAC_EPOCH, 0);
222 216
223 node = NULL; 217 node = NULL;
224} 218}
225 219
226static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
227{
228 FILE *f;
229 uint64_t size;
230
231 *length = 0;
232
233 f = fopen(filename, "rb");
234 if (!f) {
235 return;
236 }
237
238 fseek(f, 0, SEEK_END);
239 size = ftell(f);
240 rewind(f);
241
242 if (size == 0) {
243 return;
244 }
245
246 *buffer = (char*)malloc(sizeof(char)*size);
247 fread(*buffer, sizeof(char), size, f);
248 fclose(f);
249
250 *length = size;
251}
252
253static void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length)
254{
255 FILE *f;
256
257 f = fopen(filename, "ab");
258 fwrite(buffer, sizeof(char), length, f);
259 fclose(f);
260}
261
262static int plist_read_from_filename(plist_t *plist, const char *filename)
263{
264 char *buffer = NULL;
265 uint64_t length;
266
267 if (!filename)
268 return 0;
269
270 buffer_read_from_filename(filename, &buffer, &length);
271
272 if (!buffer) {
273 return 0;
274 }
275
276 if ((length > 8) && (memcmp(buffer, "bplist00", 8) == 0)) {
277 plist_from_bin(buffer, length, plist);
278 } else {
279 plist_from_xml(buffer, length, plist);
280 }
281
282 free(buffer);
283
284 return 1;
285}
286
287static int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format)
288{
289 char *buffer = NULL;
290 uint32_t length;
291
292 if (!plist || !filename)
293 return 0;
294
295 if (format == PLIST_FORMAT_XML)
296 plist_to_xml(plist, &buffer, &length);
297 else if (format == PLIST_FORMAT_BINARY)
298 plist_to_bin(plist, &buffer, &length);
299 else
300 return 0;
301
302 buffer_write_to_filename(filename, buffer, length);
303
304 free(buffer);
305
306 return 1;
307}
308
309static int plist_strcmp(plist_t node, const char *str) 220static int plist_strcmp(plist_t node, const char *str)
310{ 221{
311 char *buffer = NULL; 222 char *buffer = NULL;
@@ -321,11 +232,14 @@ static int plist_strcmp(plist_t node, const char *str)
321 return ret; 232 return ret;
322} 233}
323 234
324static gchar *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension) 235static char *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension)
325{ 236{
326 gchar *filename = g_strconcat(name, extension, NULL); 237 char* filename = (char*)malloc(strlen(name)+(extension == NULL ? 0: strlen(extension))+1);
327 gchar *path = g_build_path(G_DIR_SEPARATOR_S, backup_directory, filename, NULL); 238 strcpy(filename, name);
328 g_free(filename); 239 if (extension != NULL)
240 strcat(filename, extension);
241 char *path = string_build_path(backup_directory, filename, NULL);
242 free(filename);
329 return path; 243 return path;
330} 244}
331 245
@@ -333,28 +247,28 @@ static void mobilebackup_write_status(const char *path, int status)
333{ 247{
334 struct stat st; 248 struct stat st;
335 plist_t status_plist = plist_new_dict(); 249 plist_t status_plist = plist_new_dict();
336 plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status)); 250 plist_dict_set_item(status_plist, "Backup Success", plist_new_bool(status));
337 gchar *file_path = mobilebackup_build_path(path, "Status", ".plist"); 251 char *file_path = mobilebackup_build_path(path, "Status", ".plist");
338 252
339 if (stat(file_path, &st) == 0) 253 if (stat(file_path, &st) == 0)
340 remove(file_path); 254 remove(file_path);
341 255
342 plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML); 256 plist_write_to_file(status_plist, file_path, PLIST_FORMAT_XML, 0);
343 257
344 plist_free(status_plist); 258 plist_free(status_plist);
345 status_plist = NULL; 259 status_plist = NULL;
346 260
347 g_free(file_path); 261 free(file_path);
348} 262}
349 263
350static int mobilebackup_read_status(const char *path) 264static int mobilebackup_read_status(const char *path)
351{ 265{
352 int ret = -1; 266 int ret = -1;
353 plist_t status_plist = NULL; 267 plist_t status_plist = NULL;
354 gchar *file_path = mobilebackup_build_path(path, "Status", ".plist"); 268 char *file_path = mobilebackup_build_path(path, "Status", ".plist");
355 269
356 plist_read_from_filename(&status_plist, file_path); 270 plist_read_from_file(file_path, &status_plist, NULL);
357 g_free(file_path); 271 free(file_path);
358 if (!status_plist) { 272 if (!status_plist) {
359 printf("Could not read Status.plist!\n"); 273 printf("Could not read Status.plist!\n");
360 return ret; 274 return ret;
@@ -387,7 +301,7 @@ static int mobilebackup_info_is_current_device(plist_t info)
387 /* get basic device information in one go */ 301 /* get basic device information in one go */
388 lockdownd_get_value(client, NULL, NULL, &root_node); 302 lockdownd_get_value(client, NULL, NULL, &root_node);
389 303
390 /* verify UUID */ 304 /* verify UDID */
391 value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); 305 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
392 node = plist_dict_get_item(info, "Target Identifier"); 306 node = plist_dict_get_item(info, "Target Identifier");
393 307
@@ -435,14 +349,14 @@ static int mobilebackup_info_is_current_device(plist_t info)
435static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory, const char *hash) 349static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory, const char *hash)
436{ 350{
437 int ret = 0; 351 int ret = 0;
438 gchar *path = mobilebackup_build_path(backup_directory, hash, ".mddata"); 352 char *path = mobilebackup_build_path(backup_directory, hash, ".mddata");
439 printf("Removing \"%s\" ", path); 353 printf("Removing \"%s\" ", path);
440 if (!remove( path )) 354 if (!remove( path ))
441 ret = 1; 355 ret = 1;
442 else 356 else
443 ret = 0; 357 ret = 0;
444 358
445 g_free(path); 359 free(path);
446 360
447 if (!ret) 361 if (!ret)
448 return ret; 362 return ret;
@@ -454,7 +368,7 @@ static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory,
454 else 368 else
455 ret = 0; 369 ret = 0;
456 370
457 g_free(path); 371 free(path);
458 372
459 return ret; 373 return ret;
460} 374}
@@ -476,7 +390,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
476 } 390 }
477 391
478 infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo"); 392 infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
479 plist_read_from_filename(&mdinfo, infopath); 393 plist_read_from_file(infopath, &mdinfo, NULL);
480 free(infopath); 394 free(infopath);
481 if (!mdinfo) { 395 if (!mdinfo) {
482 printf("\r\n"); 396 printf("\r\n");
@@ -521,13 +435,13 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
521 435
522 char *version = NULL; 436 char *version = NULL;
523 node = plist_dict_get_item(metadata, "Version"); 437 node = plist_dict_get_item(metadata, "Version");
524 if (node && (plist_get_node_type(node) == PLIST_STRING)) { 438 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
525 plist_get_string_val(node, &version); 439 plist_get_string_val(node, &version);
526 } 440 }
527 441
528 char *destpath = NULL; 442 char *destpath = NULL;
529 node = plist_dict_get_item(metadata, "Path"); 443 node = plist_dict_get_item(metadata, "Path");
530 if (node && (plist_get_node_type(node) == PLIST_STRING)) { 444 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
531 plist_get_string_val(node, &destpath); 445 plist_get_string_val(node, &destpath);
532 } 446 }
533 447
@@ -539,7 +453,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
539 453
540 char *domain = NULL; 454 char *domain = NULL;
541 node = plist_dict_get_item(metadata, "Domain"); 455 node = plist_dict_get_item(metadata, "Domain");
542 if (node && (plist_get_node_type(node) == PLIST_STRING)) { 456 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
543 plist_get_string_val(node, &domain); 457 plist_get_string_val(node, &domain);
544 } 458 }
545 459
@@ -550,14 +464,14 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
550 unsigned char fnhash[20]; 464 unsigned char fnhash[20];
551 char fnamehash[41]; 465 char fnamehash[41];
552 char *p = fnamehash; 466 char *p = fnamehash;
553 sha1_of_data(fnstr, strlen(fnstr), fnhash); 467 sha1((const unsigned char*)fnstr, strlen(fnstr), fnhash);
554 free(fnstr); 468 free(fnstr);
555 int i; 469 int i;
556 for ( i = 0; i < 20; i++, p += 2 ) { 470 for ( i = 0; i < 20; i++, p += 2 ) {
557 snprintf (p, 3, "%02x", (unsigned char)fnhash[i] ); 471 snprintf (p, 3, "%02x", (unsigned char)fnhash[i] );
558 } 472 }
559 if (strcmp(fnamehash, hash)) { 473 if (strcmp(fnamehash, hash) != 0) {
560 printf("\r\n"); 474 printf("\r\n");
561 printf("WARNING: filename hash does not match for entry '%s'\n", hash); 475 printf("WARNING: filename hash does not match for entry '%s'\n", hash);
562 } 476 }
563 477
@@ -567,7 +481,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
567 plist_get_string_val(node, &auth_version); 481 plist_get_string_val(node, &auth_version);
568 } 482 }
569 483
570 if (strcmp(auth_version, "1.0")) { 484 if (strcmp(auth_version, "1.0") != 0) {
571 printf("\r\n"); 485 printf("\r\n");
572 printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version); 486 printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version);
573 } 487 }
@@ -591,9 +505,9 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
591 hash_ok = 1; 505 hash_ok = 1;
592 } 506 }
593 507
594 g_free(domain); 508 free(domain);
595 g_free(version); 509 free(version);
596 g_free(destpath); 510 free(destpath);
597 511
598 if (!hash_ok) { 512 if (!hash_ok) {
599 printf("\r\n"); 513 printf("\r\n");
@@ -605,31 +519,36 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
605 printf("\n"); 519 printf("\n");
606 res = 0; 520 res = 0;
607 } 521 }
608 g_free(data_hash); 522 free(data_hash);
609 plist_free(mdinfo); 523 plist_free(mdinfo);
610 return res; 524 return res;
611} 525}
612 526
613static void do_post_notification(const char *notification) 527static void do_post_notification(const char *notification)
614{ 528{
615 uint16_t nport = 0; 529 lockdownd_service_descriptor_t service = NULL;
616 np_client_t np; 530 np_client_t np;
617 531
618 if (!client) { 532 if (!client) {
619 if (lockdownd_client_new_with_handshake(phone, &client, "idevicebackup") != LOCKDOWN_E_SUCCESS) { 533 if (lockdownd_client_new_with_handshake(device, &client, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
620 return; 534 return;
621 } 535 }
622 } 536 }
623 537
624 lockdownd_start_service(client, NP_SERVICE_NAME, &nport); 538 lockdownd_error_t ldret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
625 if (nport) { 539 if (ldret == LOCKDOWN_E_SUCCESS) {
626 np_client_new(phone, nport, &np); 540 np_client_new(device, service, &np);
627 if (np) { 541 if (np) {
628 np_post_notification(np, notification); 542 np_post_notification(np, notification);
629 np_client_free(np); 543 np_client_free(np);
630 } 544 }
631 } else { 545 } else {
632 printf("Could not start %s\n", NP_SERVICE_NAME); 546 printf("Could not start %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
547 }
548
549 if (service) {
550 lockdownd_service_descriptor_free(service);
551 service = NULL;
633 } 552 }
634} 553}
635 554
@@ -665,29 +584,38 @@ static void clean_exit(int sig)
665 quit_flag++; 584 quit_flag++;
666} 585}
667 586
668static void print_usage(int argc, char **argv) 587static void print_usage(int argc, char **argv, int is_error)
669{ 588{
670 char *name = NULL; 589 char *name = strrchr(argv[0], '/');
671 name = strrchr(argv[0], '/'); 590 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD DIRECTORY\n", (name ? name + 1: argv[0]));
672 printf("Usage: %s [OPTIONS] CMD [DIRECTORY]\n", (name ? name + 1: argv[0])); 591 fprintf(is_error ? stderr : stdout,
673 printf("Create or restore backup from the current or specified directory.\n\n"); 592 "\n"
674 printf("commands:\n"); 593 "Create or restore backup in/from the specified directory.\n"
675 printf(" backup\tSaves a device backup into DIRECTORY\n"); 594 "\n"
676 printf(" restore\tRestores a device backup from DIRECTORY.\n\n"); 595 "CMD:\n"
677 printf("options:\n"); 596 " backup Saves a device backup into DIRECTORY\n"
678 printf(" -d, --debug\t\tenable communication debugging\n"); 597 " restore Restores a device backup from DIRECTORY.\n"
679 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); 598 "\n"
680 printf(" -h, --help\t\tprints usage information\n"); 599 "OPTIONS:\n"
681 printf("\n"); 600 " -u, --udid UDID target specific device by UDID\n"
601 " -n, --network connect to network device\n"
602 " -d, --debug enable communication debugging\n"
603 " -h, --help prints usage information\n"
604 " -v, --version prints version information\n"
605 "\n"
606 "Homepage: <" PACKAGE_URL ">\n"
607 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
608 );
682} 609}
683 610
684int main(int argc, char *argv[]) 611int main(int argc, char *argv[])
685{ 612{
686 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 613 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
614 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
687 int i; 615 int i;
688 char uuid[41]; 616 char* udid = NULL;
689 uint16_t port = 0; 617 int use_network = 0;
690 uuid[0] = 0; 618 lockdownd_service_descriptor_t service = NULL;
691 int cmd = -1; 619 int cmd = -1;
692 int is_full_backup = 0; 620 int is_full_backup = 0;
693 char *backup_directory = NULL; 621 char *backup_directory = NULL;
@@ -701,60 +629,77 @@ int main(int argc, char *argv[])
701 uint64_t length = 0; 629 uint64_t length = 0;
702 uint64_t backup_total_size = 0; 630 uint64_t backup_total_size = 0;
703 enum device_link_file_status_t file_status = DEVICE_LINK_FILE_STATUS_NONE; 631 enum device_link_file_status_t file_status = DEVICE_LINK_FILE_STATUS_NONE;
704 uint64_t c = 0; 632 int c = 0;
633 const struct option longopts[] = {
634 { "debug", no_argument, NULL, 'd' },
635 { "help", no_argument, NULL, 'h' },
636 { "udid", required_argument, NULL, 'u' },
637 { "network", no_argument, NULL, 'n' },
638 { "version", no_argument, NULL, 'v' },
639 { NULL, 0, NULL, 0}
640 };
705 641
706 /* we need to exit cleanly on running backups and restores or we cause havok */ 642 /* we need to exit cleanly on running backups and restores or we cause havok */
707 signal(SIGINT, clean_exit); 643 signal(SIGINT, clean_exit);
708 signal(SIGQUIT, clean_exit);
709 signal(SIGTERM, clean_exit); 644 signal(SIGTERM, clean_exit);
645#ifndef WIN32
646 signal(SIGQUIT, clean_exit);
710 signal(SIGPIPE, SIG_IGN); 647 signal(SIGPIPE, SIG_IGN);
648#endif
711 649
712 /* parse cmdline args */ 650 /* parse cmdline args */
713 for (i = 1; i < argc; i++) { 651 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
714 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 652 switch (c) {
653 case 'd':
715 idevice_set_debug_level(1); 654 idevice_set_debug_level(1);
716 continue; 655 break;
717 } 656 case 'u':
718 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 657 if (!*optarg) {
719 i++; 658 fprintf(stderr, "ERROR: UDID must not be empty!\n");
720 if (!argv[i] || (strlen(argv[i]) != 40)) { 659 print_usage(argc, argv, 1);
721 print_usage(argc, argv); 660 return 2;
722 return 0;
723 } 661 }
724 strcpy(uuid, argv[i]); 662 udid = strdup(optarg);
725 continue; 663 break;
726 } 664 case 'n':
727 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 665 use_network = 1;
728 print_usage(argc, argv); 666 break;
667 case 'h':
668 print_usage(argc, argv, 0);
729 return 0; 669 return 0;
730 } 670 case 'v':
731 else if (!strcmp(argv[i], "backup")) { 671 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
732 cmd = CMD_BACKUP;
733 }
734 else if (!strcmp(argv[i], "restore")) {
735 cmd = CMD_RESTORE;
736 }
737 else if (backup_directory == NULL) {
738 backup_directory = argv[i];
739 }
740 else {
741 print_usage(argc, argv);
742 return 0; 672 return 0;
673 default:
674 print_usage(argc, argv, 1);
675 return 2;
743 } 676 }
744 } 677 }
678 argc -= optind;
679 argv += optind;
745 680
746 /* verify options */ 681 if (argc < 1) {
747 if (cmd == -1) { 682 fprintf(stderr, "ERROR: Missing command.\n");
748 printf("No command specified.\n"); 683 print_usage(argc+optind, argv-optind, 1);
749 print_usage(argc, argv); 684 return 2;
750 return -1;
751 } 685 }
752 686
753 if (backup_directory == NULL) { 687 if (!strcmp(argv[0], "backup")) {
754 printf("No target backup directory specified.\n"); 688 cmd = CMD_BACKUP;
755 print_usage(argc, argv); 689 } else if (!strcmp(argv[0], "restore")) {
756 return -1; 690 cmd = CMD_RESTORE;
691 } else {
692 fprintf(stderr, "ERROR: Invalid command '%s'.\n", argv[0]);
693 print_usage(argc+optind, argv-optind, 1);
694 return 2;
695 }
696
697 if (argc < 2) {
698 fprintf(stderr, "No target backup directory specified.\n");
699 print_usage(argc+optind, argv-optind, 1);
700 return 2;
757 } 701 }
702 backup_directory = argv[1];
758 703
759 /* verify if passed backup directory exists */ 704 /* verify if passed backup directory exists */
760 if (stat(backup_directory, &st) != 0) { 705 if (stat(backup_directory, &st) != 0) {
@@ -766,7 +711,7 @@ int main(int argc, char *argv[])
766 char *info_path = mobilebackup_build_path(backup_directory, "Info", ".plist"); 711 char *info_path = mobilebackup_build_path(backup_directory, "Info", ".plist");
767 if (cmd == CMD_RESTORE) { 712 if (cmd == CMD_RESTORE) {
768 if (stat(info_path, &st) != 0) { 713 if (stat(info_path, &st) != 0) {
769 g_free(info_path); 714 free(info_path);
770 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory); 715 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory);
771 return -1; 716 return -1;
772 } 717 }
@@ -774,32 +719,54 @@ int main(int argc, char *argv[])
774 719
775 printf("Backup directory is \"%s\"\n", backup_directory); 720 printf("Backup directory is \"%s\"\n", backup_directory);
776 721
777 if (uuid[0] != 0) { 722 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
778 ret = idevice_new(&phone, uuid); 723 if (ret != IDEVICE_E_SUCCESS) {
779 if (ret != IDEVICE_E_SUCCESS) { 724 if (udid) {
780 printf("No device found with uuid %s, is it plugged in?\n", uuid); 725 printf("No device found with udid %s.\n", udid);
781 return -1; 726 } else {
727 printf("No device found.\n");
782 } 728 }
729 return -1;
783 } 730 }
784 else 731
785 { 732 if (!udid) {
786 ret = idevice_new(&phone, NULL); 733 idevice_get_udid(device, &udid);
787 if (ret != IDEVICE_E_SUCCESS) {
788 printf("No device found, is it plugged in?\n");
789 return -1;
790 }
791 } 734 }
792 735
793 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicebackup")) { 736 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
794 idevice_free(phone); 737 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
738 idevice_free(device);
739 free(udid);
795 return -1; 740 return -1;
796 } 741 }
797 742
743 node = NULL;
744 lockdownd_get_value(client, NULL, "ProductVersion", &node);
745 if (node) {
746 char* str = NULL;
747 if (plist_get_node_type(node) == PLIST_STRING) {
748 plist_get_string_val(node, &str);
749 }
750 plist_free(node);
751 node = NULL;
752 if (str) {
753 int maj = strtol(str, NULL, 10);
754 free(str);
755 if (maj > 3) {
756 printf("ERROR: This tool is only compatible with iOS 3 or below. For newer iOS versions please use the idevicebackup2 tool.\n");
757 lockdownd_client_free(client);
758 idevice_free(device);
759 free(udid);
760 return -1;
761 }
762 }
763 }
764
798 /* start notification_proxy */ 765 /* start notification_proxy */
799 np_client_t np = NULL; 766 np_client_t np = NULL;
800 ret = lockdownd_start_service(client, NP_SERVICE_NAME, &port); 767 ldret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
801 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 768 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
802 np_client_new(phone, port, &np); 769 np_client_new(device, service, &np);
803 np_set_notify_callback(np, notify_cb, NULL); 770 np_set_notify_callback(np, notify_cb, NULL);
804 const char *noties[5] = { 771 const char *noties[5] = {
805 NP_SYNC_CANCEL_REQUEST, 772 NP_SYNC_CANCEL_REQUEST,
@@ -810,25 +777,37 @@ int main(int argc, char *argv[])
810 }; 777 };
811 np_observe_notifications(np, noties); 778 np_observe_notifications(np, noties);
812 } else { 779 } else {
813 printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME); 780 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
814 } 781 }
815 782
816 afc_client_t afc = NULL; 783 afc_client_t afc = NULL;
817 if (cmd == CMD_BACKUP) { 784 if (cmd == CMD_BACKUP) {
818 /* start AFC, we need this for the lock file */ 785 /* start AFC, we need this for the lock file */
819 port = 0; 786 service->port = 0;
820 ret = lockdownd_start_service(client, "com.apple.afc", &port); 787 service->ssl_enabled = 0;
821 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 788 ldret = lockdownd_start_service(client, AFC_SERVICE_NAME, &service);
822 afc_client_new(phone, port, &afc); 789 if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
790 afc_client_new(device, service, &afc);
791 } else {
792 printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
823 } 793 }
824 } 794 }
825 795
796 if (service) {
797 lockdownd_service_descriptor_free(service);
798 service = NULL;
799 }
800
826 /* start mobilebackup service and retrieve port */ 801 /* start mobilebackup service and retrieve port */
827 port = 0; 802 ldret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &service);
828 ret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &port); 803 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
829 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 804 printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, service->port);
830 printf("Started \"%s\" service on port %d.\n", MOBILEBACKUP_SERVICE_NAME, port); 805 printf("%d\n", mobilebackup_client_new(device, service, &mobilebackup));
831 mobilebackup_client_new(phone, port, &mobilebackup); 806
807 if (service) {
808 lockdownd_service_descriptor_free(service);
809 service = NULL;
810 }
832 811
833 /* check abort conditions */ 812 /* check abort conditions */
834 if (quit_flag > 0) { 813 if (quit_flag > 0) {
@@ -839,7 +818,7 @@ int main(int argc, char *argv[])
839 /* verify existing Info.plist */ 818 /* verify existing Info.plist */
840 if (stat(info_path, &st) == 0) { 819 if (stat(info_path, &st) == 0) {
841 printf("Reading Info.plist from backup.\n"); 820 printf("Reading Info.plist from backup.\n");
842 plist_read_from_filename(&info_plist, info_path); 821 plist_read_from_file(info_path, &info_plist, NULL);
843 822
844 if (!info_plist) { 823 if (!info_plist) {
845 printf("Could not read Info.plist\n"); 824 printf("Could not read Info.plist\n");
@@ -850,7 +829,7 @@ int main(int argc, char *argv[])
850 /* update the last backup time within Info.plist */ 829 /* update the last backup time within Info.plist */
851 mobilebackup_info_update_last_backup_date(info_plist); 830 mobilebackup_info_update_last_backup_date(info_plist);
852 remove(info_path); 831 remove(info_path);
853 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); 832 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
854 } else { 833 } else {
855 printf("Aborting backup. Backup is not compatible with the current device.\n"); 834 printf("Aborting backup. Backup is not compatible with the current device.\n");
856 cmd = CMD_LEAVE; 835 cmd = CMD_LEAVE;
@@ -883,15 +862,16 @@ int main(int argc, char *argv[])
883 if (aerr == AFC_E_SUCCESS) { 862 if (aerr == AFC_E_SUCCESS) {
884 do_post_notification(NP_SYNC_DID_START); 863 do_post_notification(NP_SYNC_DID_START);
885 break; 864 break;
886 } else if (aerr == AFC_E_OP_WOULD_BLOCK) { 865 }
866 if (aerr == AFC_E_OP_WOULD_BLOCK) {
887 usleep(LOCK_WAIT); 867 usleep(LOCK_WAIT);
888 continue; 868 continue;
889 } else {
890 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
891 afc_file_close(afc, lockfile);
892 lockfile = 0;
893 cmd = CMD_LEAVE;
894 } 869 }
870
871 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
872 afc_file_close(afc, lockfile);
873 lockfile = 0;
874 cmd = CMD_LEAVE;
895 } 875 }
896 if (i == LOCK_ATTEMPTS) { 876 if (i == LOCK_ATTEMPTS) {
897 fprintf(stderr, "ERROR: timeout while locking for sync\n"); 877 fprintf(stderr, "ERROR: timeout while locking for sync\n");
@@ -910,12 +890,12 @@ int main(int argc, char *argv[])
910 case CMD_BACKUP: 890 case CMD_BACKUP:
911 printf("Starting backup...\n"); 891 printf("Starting backup...\n");
912 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */ 892 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
913 /* TODO: verify battery on AC enough battery remaining */ 893 /* TODO: verify battery on AC enough battery remaining */
914 894
915 /* read the last Manifest.plist */ 895 /* read the last Manifest.plist */
916 if (!is_full_backup) { 896 if (!is_full_backup) {
917 printf("Reading existing Manifest.\n"); 897 printf("Reading existing Manifest.\n");
918 plist_read_from_filename(&manifest_plist, manifest_path); 898 plist_read_from_file(manifest_path, &manifest_plist, NULL);
919 if (!manifest_plist) { 899 if (!manifest_plist) {
920 printf("Could not read Manifest.plist, switching to full backup mode.\n"); 900 printf("Could not read Manifest.plist, switching to full backup mode.\n");
921 is_full_backup = 1; 901 is_full_backup = 1;
@@ -932,10 +912,10 @@ int main(int argc, char *argv[])
932 } 912 }
933 remove(info_path); 913 remove(info_path);
934 printf("Creating Info.plist for new backup.\n"); 914 printf("Creating Info.plist for new backup.\n");
935 info_plist = mobilebackup_factory_info_plist_new(); 915 info_plist = mobilebackup_factory_info_plist_new(udid);
936 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); 916 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
937 } 917 }
938 g_free(info_path); 918 free(info_path);
939 919
940 plist_free(info_plist); 920 plist_free(info_plist);
941 info_plist = NULL; 921 info_plist = NULL;
@@ -965,7 +945,7 @@ int main(int argc, char *argv[])
965 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) { 945 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
966 printf("ERROR: Could not start backup process: device refused to start the backup process.\n"); 946 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
967 } else { 947 } else {
968 printf("ERROR: Could not start backup process: unspecified error occured\n"); 948 printf("ERROR: Could not start backup process: unspecified error occurred (%d)\n", err);
969 } 949 }
970 break; 950 break;
971 } 951 }
@@ -985,8 +965,9 @@ int main(int argc, char *argv[])
985 char *filename_mddata = NULL; 965 char *filename_mddata = NULL;
986 char *filename_source = NULL; 966 char *filename_source = NULL;
987 char *format_size = NULL; 967 char *format_size = NULL;
988 gboolean is_manifest = FALSE; 968 int is_manifest = 0;
989 uint8_t b = 0; 969 uint8_t b = 0;
970 uint64_t u64val = 0;
990 971
991 /* process series of DLSendFile messages */ 972 /* process series of DLSendFile messages */
992 do { 973 do {
@@ -996,7 +977,7 @@ int main(int argc, char *argv[])
996 sleep(2); 977 sleep(2);
997 goto files_out; 978 goto files_out;
998 } 979 }
999 980
1000 node = plist_array_get_item(message, 0); 981 node = plist_array_get_item(message, 0);
1001 982
1002 /* get out if we don't get a DLSendFile */ 983 /* get out if we don't get a DLSendFile */
@@ -1010,16 +991,16 @@ int main(int argc, char *argv[])
1010 node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey"); 991 node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey");
1011 if (node) { 992 if (node) {
1012 plist_get_uint_val(node, &backup_total_size); 993 plist_get_uint_val(node, &backup_total_size);
1013 format_size = g_format_size_for_display(backup_total_size); 994 format_size = string_format_size(backup_total_size);
1014 printf("Backup data requires %s on the disk.\n", format_size); 995 printf("Backup data requires %s on the disk.\n", format_size);
1015 g_free(format_size); 996 free(format_size);
1016 } 997 }
1017 } 998 }
1018 999
1019 /* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */ 1000 /* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */
1020 node = plist_dict_get_item(node_tmp, "DLFileStatusKey"); 1001 node = plist_dict_get_item(node_tmp, "DLFileStatusKey");
1021 plist_get_uint_val(node, &c); 1002 plist_get_uint_val(node, &u64val);
1022 file_status = c; 1003 file_status = u64val;
1023 1004
1024 /* get source filename */ 1005 /* get source filename */
1025 node = plist_dict_get_item(node_tmp, "BackupManifestKey"); 1006 node = plist_dict_get_item(node_tmp, "BackupManifestKey");
@@ -1027,7 +1008,7 @@ int main(int argc, char *argv[])
1027 if (node) { 1008 if (node) {
1028 plist_get_bool_val(node, &b); 1009 plist_get_bool_val(node, &b);
1029 } 1010 }
1030 is_manifest = (b == 1) ? TRUE: FALSE; 1011 is_manifest = (b == 1) ? 1 : 0;
1031 1012
1032 if ((hunk_index == 0) && (!is_manifest)) { 1013 if ((hunk_index == 0) && (!is_manifest)) {
1033 /* get source filename */ 1014 /* get source filename */
@@ -1040,17 +1021,17 @@ int main(int argc, char *argv[])
1040 plist_get_uint_val(node, &file_size); 1021 plist_get_uint_val(node, &file_size);
1041 backup_real_size += file_size; 1022 backup_real_size += file_size;
1042 1023
1043 format_size = g_format_size_for_display(backup_real_size); 1024 format_size = string_format_size(backup_real_size);
1044 printf("(%s", format_size); 1025 printf("(%s", format_size);
1045 g_free(format_size); 1026 free(format_size);
1046 1027
1047 format_size = g_format_size_for_display(backup_total_size); 1028 format_size = string_format_size(backup_total_size);
1048 printf("/%s): ", format_size); 1029 printf("/%s): ", format_size);
1049 g_free(format_size); 1030 free(format_size);
1050 1031
1051 format_size = g_format_size_for_display(file_size); 1032 format_size = string_format_size(file_size);
1052 printf("Receiving file %s (%s)... \n", filename_source, format_size); 1033 printf("Receiving file %s (%s)... \n", filename_source, format_size);
1053 g_free(format_size); 1034 free(format_size);
1054 1035
1055 if (filename_source) 1036 if (filename_source)
1056 free(filename_source); 1037 free(filename_source);
@@ -1071,9 +1052,9 @@ int main(int argc, char *argv[])
1071 remove(filename_mdinfo); 1052 remove(filename_mdinfo);
1072 1053
1073 node = plist_dict_get_item(node_tmp, "BackupFileInfo"); 1054 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1074 plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY); 1055 plist_write_to_file(node, filename_mdinfo, PLIST_FORMAT_BINARY, 0);
1075 1056
1076 g_free(filename_mdinfo); 1057 free(filename_mdinfo);
1077 } 1058 }
1078 1059
1079 file_index++; 1060 file_index++;
@@ -1107,15 +1088,14 @@ int main(int argc, char *argv[])
1107 free(buffer); 1088 free(buffer);
1108 buffer = NULL; 1089 buffer = NULL;
1109 1090
1110 g_free(filename_mddata); 1091 free(filename_mddata);
1111 } 1092 }
1112 1093
1113 if ((!is_manifest)) { 1094 if ((!is_manifest)) {
1114 if (hunk_index == 0 && file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) { 1095 if (hunk_index == 0 && file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1115 print_progress(100); 1096 print_progress(100);
1116 } else { 1097 } else if (file_size > 0) {
1117 if (file_size > 0) 1098 print_progress((double)(file_size_current*100)/file_size);
1118 print_progress((double)((file_size_current*100)/file_size));
1119 } 1099 }
1120 } 1100 }
1121 1101
@@ -1145,7 +1125,7 @@ files_out:
1145 1125
1146 /* remove any atomic Manifest.plist.tmp */ 1126 /* remove any atomic Manifest.plist.tmp */
1147 if (manifest_path) 1127 if (manifest_path)
1148 g_free(manifest_path); 1128 free(manifest_path);
1149 1129
1150 manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp"); 1130 manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
1151 if (stat(manifest_path, &st) == 0) 1131 if (stat(manifest_path, &st) == 0)
@@ -1184,7 +1164,7 @@ files_out:
1184 if (manifest_plist) { 1164 if (manifest_plist) {
1185 remove(manifest_path); 1165 remove(manifest_path);
1186 printf("Storing Manifest.plist...\n"); 1166 printf("Storing Manifest.plist...\n");
1187 plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); 1167 plist_write_to_file(manifest_plist, manifest_path, PLIST_FORMAT_XML, 0);
1188 } 1168 }
1189 1169
1190 backup_ok = 1; 1170 backup_ok = 1;
@@ -1215,21 +1195,21 @@ files_out:
1215 } 1195 }
1216 /* now make sure backup integrity is ok! verify all files */ 1196 /* now make sure backup integrity is ok! verify all files */
1217 printf("Reading existing Manifest.\n"); 1197 printf("Reading existing Manifest.\n");
1218 plist_read_from_filename(&manifest_plist, manifest_path); 1198 plist_read_from_file(manifest_path, &manifest_plist, NULL);
1219 if (!manifest_plist) { 1199 if (!manifest_plist) {
1220 printf("Could not read Manifest.plist. Aborting.\n"); 1200 printf("Could not read Manifest.plist. Aborting.\n");
1221 break; 1201 break;
1222 } 1202 }
1223 1203
1224 printf("Verifying backup integrity, please wait.\n"); 1204 printf("Verifying backup integrity, please wait.\n");
1225 char *bin = NULL; 1205 unsigned char *bin = NULL;
1226 uint64_t binsize = 0; 1206 uint64_t binsize = 0;
1227 node = plist_dict_get_item(manifest_plist, "Data"); 1207 node = plist_dict_get_item(manifest_plist, "Data");
1228 if (!node || (plist_get_node_type(node) != PLIST_DATA)) { 1208 if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
1229 printf("Could not read Data key from Manifest.plist!\n"); 1209 printf("Could not read Data key from Manifest.plist!\n");
1230 break; 1210 break;
1231 } 1211 }
1232 plist_get_data_val(node, &bin, &binsize); 1212 plist_get_data_val(node, (char**)&bin, &binsize);
1233 plist_t backup_data = NULL; 1213 plist_t backup_data = NULL;
1234 if (bin) { 1214 if (bin) {
1235 char *auth_ver = NULL; 1215 char *auth_ver = NULL;
@@ -1246,7 +1226,7 @@ files_out:
1246 if (auth_sig && (auth_sig_len == 20)) { 1226 if (auth_sig && (auth_sig_len == 20)) {
1247 /* calculate the sha1, then compare */ 1227 /* calculate the sha1, then compare */
1248 unsigned char data_sha1[20]; 1228 unsigned char data_sha1[20];
1249 sha1_of_data(bin, binsize, data_sha1); 1229 sha1(bin, binsize, data_sha1);
1250 if (compare_hash(auth_sig, data_sha1, 20)) { 1230 if (compare_hash(auth_sig, data_sha1, 20)) {
1251 printf("AuthSignature is valid\n"); 1231 printf("AuthSignature is valid\n");
1252 } else { 1232 } else {
@@ -1255,12 +1235,12 @@ files_out:
1255 } else { 1235 } else {
1256 printf("Could not get AuthSignature from manifest!\n"); 1236 printf("Could not get AuthSignature from manifest!\n");
1257 } 1237 }
1258 g_free(auth_sig); 1238 free(auth_sig);
1259 } else if (auth_ver) { 1239 } else if (auth_ver) {
1260 printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver); 1240 printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver);
1261 } 1241 }
1262 plist_from_bin(bin, (uint32_t)binsize, &backup_data); 1242 plist_from_bin((char*)bin, (uint32_t)binsize, &backup_data);
1263 g_free(bin); 1243 free(bin);
1264 } 1244 }
1265 if (!backup_data) { 1245 if (!backup_data) {
1266 printf("Could not read plist from Manifest.plist Data key!\n"); 1246 printf("Could not read plist from Manifest.plist Data key!\n");
@@ -1312,7 +1292,7 @@ files_out:
1312 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) { 1292 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
1313 printf("ERROR: Could not start restore process: device refused to start the restore process.\n"); 1293 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
1314 } else { 1294 } else {
1315 printf("ERROR: Could not start restore process: unspecified error occured (%d)\n", err); 1295 printf("ERROR: Could not start restore process: unspecified error occurred (%d)\n", err);
1316 } 1296 }
1317 plist_free(backup_data); 1297 plist_free(backup_data);
1318 break; 1298 break;
@@ -1342,7 +1322,7 @@ files_out:
1342 while (node) { 1322 while (node) {
1343 /* TODO: read mddata/mdinfo files and send to device using DLSendFile */ 1323 /* TODO: read mddata/mdinfo files and send to device using DLSendFile */
1344 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mdinfo"); 1324 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
1345 plist_read_from_filename(&file_info, file_info_path); 1325 plist_read_from_file(file_info_path, &file_info, NULL);
1346 1326
1347 /* get encryption state */ 1327 /* get encryption state */
1348 tmp_node = plist_dict_get_item(file_info, "IsEncrypted"); 1328 tmp_node = plist_dict_get_item(file_info, "IsEncrypted");
@@ -1360,42 +1340,62 @@ files_out:
1360 printf("Restoring file %s %d/%d (%d%%)... ", file_path, cur_file, total_files, (cur_file*100/total_files)); 1340 printf("Restoring file %s %d/%d (%d%%)... ", file_path, cur_file, total_files, (cur_file*100/total_files));
1361 1341
1362 /* add additional device link file information keys */ 1342 /* add additional device link file information keys */
1363 plist_dict_insert_item(file_info, "DLFileAttributesKey", plist_copy(node)); 1343 plist_dict_set_item(file_info, "DLFileAttributesKey", plist_copy(node));
1364 plist_dict_insert_item(file_info, "DLFileSource", plist_new_string(file_info_path)); 1344 plist_dict_set_item(file_info, "DLFileSource", plist_new_string(file_info_path));
1365 plist_dict_insert_item(file_info, "DLFileDest", plist_new_string("/tmp/RestoreFile.plist")); 1345 plist_dict_set_item(file_info, "DLFileDest", plist_new_string("/tmp/RestoreFile.plist"));
1366 plist_dict_insert_item(file_info, "DLFileIsEncrypted", plist_new_bool(is_encrypted)); 1346 plist_dict_set_item(file_info, "DLFileIsEncrypted", plist_new_bool(is_encrypted));
1367 plist_dict_insert_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset)); 1347 plist_dict_set_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset));
1368 plist_dict_insert_item(file_info, "DLFileStatusKey", plist_new_uint(file_status)); 1348 plist_dict_set_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1369 1349
1370 /* read data from file */ 1350 /* read data from file */
1371 free(file_info_path); 1351 free(file_info_path);
1372 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata"); 1352 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata");
1373 buffer_read_from_filename(file_info_path, &buffer, &length); 1353
1354 /* determine file size */
1355#ifdef WIN32
1356 struct _stati64 fst;
1357 if (_stati64(file_info_path, &fst) != 0)
1358#else
1359 struct stat fst;
1360 if (stat(file_info_path, &fst) != 0)
1361#endif
1362 {
1363 printf("ERROR: stat() failed for '%s': %s\n", file_info_path, strerror(errno));
1364 free(file_info_path);
1365 break;
1366 }
1367 length = fst.st_size;
1368
1369 FILE *f = fopen(file_info_path, "rb");
1370 if (!f) {
1371 printf("ERROR: could not open local file '%s': %s\n", file_info_path, strerror(errno));
1372 free(file_info_path);
1373 break;
1374 }
1374 free(file_info_path); 1375 free(file_info_path);
1375 1376
1376 /* send DLSendFile messages */ 1377 /* send DLSendFile messages */
1377 file_offset = 0; 1378 file_offset = 0;
1378 do { 1379 do {
1379 if ((length-file_offset) <= 8192) 1380 char buf[8192];
1381 size_t len = fread(buf, 1, sizeof(buf), f);
1382
1383 if ((length-file_offset) <= sizeof(buf))
1380 file_status = DEVICE_LINK_FILE_STATUS_LAST_HUNK; 1384 file_status = DEVICE_LINK_FILE_STATUS_LAST_HUNK;
1381 else 1385 else
1382 file_status = DEVICE_LINK_FILE_STATUS_HUNK; 1386 file_status = DEVICE_LINK_FILE_STATUS_HUNK;
1383 1387
1384 plist_dict_remove_item(file_info, "DLFileOffsetKey"); 1388 plist_dict_remove_item(file_info, "DLFileOffsetKey");
1385 plist_dict_insert_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset)); 1389 plist_dict_set_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset));
1386 1390
1387 plist_dict_remove_item(file_info, "DLFileStatusKey"); 1391 plist_dict_remove_item(file_info, "DLFileStatusKey");
1388 plist_dict_insert_item(file_info, "DLFileStatusKey", plist_new_uint(file_status)); 1392 plist_dict_set_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1389 1393
1390 send_file_node = plist_new_array(); 1394 send_file_node = plist_new_array();
1391 1395
1392 plist_array_append_item(send_file_node, plist_new_string("DLSendFile")); 1396 plist_array_append_item(send_file_node, plist_new_string("DLSendFile"));
1393 1397
1394 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) 1398 plist_array_append_item(send_file_node, plist_new_data(buf, len));
1395 plist_array_append_item(send_file_node, plist_new_data(buffer+file_offset, length-file_offset));
1396 else
1397 plist_array_append_item(send_file_node, plist_new_data(buffer+file_offset, 8192));
1398
1399 plist_array_append_item(send_file_node, plist_copy(file_info)); 1399 plist_array_append_item(send_file_node, plist_copy(file_info));
1400 1400
1401 err = mobilebackup_send(mobilebackup, send_file_node); 1401 err = mobilebackup_send(mobilebackup, send_file_node);
@@ -1413,13 +1413,13 @@ files_out:
1413 } 1413 }
1414 } 1414 }
1415 1415
1416 file_offset += 8192; 1416 file_offset += len;
1417 1417
1418 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) 1418 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK)
1419 printf("DONE\n"); 1419 printf("DONE\n");
1420 1420
1421 plist_free(send_file_node); 1421 plist_free(send_file_node);
1422 1422
1423 if (file_status == DEVICE_LINK_FILE_STATUS_NONE) 1423 if (file_status == DEVICE_LINK_FILE_STATUS_NONE)
1424 break; 1424 break;
1425 1425
@@ -1466,8 +1466,8 @@ files_out:
1466 tmp_node = plist_dict_get_item(node, "AppInfo"); 1466 tmp_node = plist_dict_get_item(node, "AppInfo");
1467 1467
1468 dict = plist_new_dict(); 1468 dict = plist_new_dict();
1469 plist_dict_insert_item(dict, "AppInfo", plist_copy(tmp_node)); 1469 plist_dict_set_item(dict, "AppInfo", plist_copy(tmp_node));
1470 plist_dict_insert_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageRestoreApplicationSent")); 1470 plist_dict_set_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageRestoreApplicationSent"));
1471 1471
1472 array = plist_new_array(); 1472 array = plist_new_array();
1473 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage")); 1473 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage"));
@@ -1540,9 +1540,9 @@ files_out:
1540 do_post_notification(NP_SYNC_DID_FINISH); 1540 do_post_notification(NP_SYNC_DID_FINISH);
1541 } 1541 }
1542 if (manifest_path) 1542 if (manifest_path)
1543 g_free(manifest_path); 1543 free(manifest_path);
1544 } else { 1544 } else {
1545 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); 1545 printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP_SERVICE_NAME, lockdownd_strerror(ldret));
1546 lockdownd_client_free(client); 1546 lockdownd_client_free(client);
1547 client = NULL; 1547 client = NULL;
1548 } 1548 }
@@ -1561,7 +1561,9 @@ files_out:
1561 if (mobilebackup) 1561 if (mobilebackup)
1562 mobilebackup_client_free(mobilebackup); 1562 mobilebackup_client_free(mobilebackup);
1563 1563
1564 idevice_free(phone); 1564 idevice_free(device);
1565
1566 free(udid);
1565 1567
1566 return 0; 1568 return 0;
1567} 1569}
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
new file mode 100644
index 0000000..c73b269
--- /dev/null
+++ b/tools/idevicebackup2.c
@@ -0,0 +1,2684 @@
1/*
2 * idevicebackup2.c
3 * Command line interface to use the device's backup and restore service
4 *
5 * Copyright (c) 2010-2022 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2009-2010 Martin Szulecki, All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "idevicebackup2"
28
29#include <stdio.h>
30#include <string.h>
31#include <errno.h>
32#include <stdlib.h>
33#include <signal.h>
34#include <unistd.h>
35#include <dirent.h>
36#include <libgen.h>
37#include <ctype.h>
38#include <time.h>
39#include <getopt.h>
40
41#include <libimobiledevice/libimobiledevice.h>
42#include <libimobiledevice/lockdown.h>
43#include <libimobiledevice/mobilebackup2.h>
44#include <libimobiledevice/notification_proxy.h>
45#include <libimobiledevice/afc.h>
46#include <libimobiledevice/installation_proxy.h>
47#include <libimobiledevice/sbservices.h>
48#include <libimobiledevice/diagnostics_relay.h>
49#include <libimobiledevice-glue/utils.h>
50#include <plist/plist.h>
51
52#include <endianness.h>
53
54#define LOCK_ATTEMPTS 50
55#define LOCK_WAIT 200000
56
57#ifdef WIN32
58#include <windows.h>
59#include <conio.h>
60#define sleep(x) Sleep(x*1000)
61#ifndef ELOOP
62#define ELOOP 114
63#endif
64#else
65#include <termios.h>
66#include <sys/statvfs.h>
67#endif
68#include <sys/stat.h>
69
70#define CODE_SUCCESS 0x00
71#define CODE_ERROR_LOCAL 0x06
72#define CODE_ERROR_REMOTE 0x0b
73#define CODE_FILE_DATA 0x0c
74
75static int verbose = 1;
76static int quit_flag = 0;
77
78#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
79
80enum cmd_mode {
81 CMD_BACKUP,
82 CMD_RESTORE,
83 CMD_INFO,
84 CMD_LIST,
85 CMD_UNBACK,
86 CMD_CHANGEPW,
87 CMD_LEAVE,
88 CMD_CLOUD
89};
90
91enum cmd_flags {
92 CMD_FLAG_RESTORE_SYSTEM_FILES = (1 << 1),
93 CMD_FLAG_RESTORE_NO_REBOOT = (1 << 2),
94 CMD_FLAG_RESTORE_COPY_BACKUP = (1 << 3),
95 CMD_FLAG_RESTORE_SETTINGS = (1 << 4),
96 CMD_FLAG_RESTORE_REMOVE_ITEMS = (1 << 5),
97 CMD_FLAG_ENCRYPTION_ENABLE = (1 << 6),
98 CMD_FLAG_ENCRYPTION_DISABLE = (1 << 7),
99 CMD_FLAG_ENCRYPTION_CHANGEPW = (1 << 8),
100 CMD_FLAG_FORCE_FULL_BACKUP = (1 << 9),
101 CMD_FLAG_CLOUD_ENABLE = (1 << 10),
102 CMD_FLAG_CLOUD_DISABLE = (1 << 11),
103 CMD_FLAG_RESTORE_SKIP_APPS = (1 << 12)
104};
105
106static int backup_domain_changed = 0;
107
108static void notify_cb(const char *notification, void *userdata)
109{
110 if (strlen(notification) == 0) {
111 return;
112 }
113 if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
114 PRINT_VERBOSE(1, "User has cancelled the backup process on the device.\n");
115 quit_flag++;
116 } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
117 backup_domain_changed = 1;
118 } else {
119 PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
120 }
121}
122
123static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *filename, char **data, uint64_t *size)
124{
125 if (!afc || !data || !size) {
126 return;
127 }
128
129 char **fileinfo = NULL;
130 uint32_t fsize = 0;
131
132 afc_get_file_info(afc, filename, &fileinfo);
133 if (!fileinfo) {
134 return;
135 }
136 int i;
137 for (i = 0; fileinfo[i]; i+=2) {
138 if (!strcmp(fileinfo[i], "st_size")) {
139 fsize = atol(fileinfo[i+1]);
140 break;
141 }
142 }
143 afc_dictionary_free(fileinfo);
144
145 if (fsize == 0) {
146 return;
147 }
148
149 uint64_t f = 0;
150 afc_file_open(afc, filename, AFC_FOPEN_RDONLY, &f);
151 if (!f) {
152 return;
153 }
154 char *buf = (char*)malloc((uint32_t)fsize);
155 uint32_t done = 0;
156 while (done < fsize) {
157 uint32_t bread = 0;
158 afc_file_read(afc, f, buf+done, 65536, &bread);
159 if (bread > 0) {
160 done += bread;
161 } else {
162 break;
163 }
164 }
165 if (done == fsize) {
166 *size = fsize;
167 *data = buf;
168 } else {
169 free(buf);
170 }
171 afc_file_close(afc, f);
172}
173
174static int __mkdir(const char* path, int mode)
175{
176#ifdef WIN32
177 return mkdir(path);
178#else
179 return mkdir(path, mode);
180#endif
181}
182
183static int mkdir_with_parents(const char *dir, int mode)
184{
185 if (!dir) return -1;
186 if (__mkdir(dir, mode) == 0) {
187 return 0;
188 }
189 if (errno == EEXIST) return 0;
190 int res;
191 char *parent = strdup(dir);
192 char *parentdir = dirname(parent);
193 if (parentdir) {
194 res = mkdir_with_parents(parentdir, mode);
195 } else {
196 res = -1;
197 }
198 free(parent);
199 if (res == 0) {
200 mkdir_with_parents(dir, mode);
201 }
202 return res;
203}
204
205#ifdef WIN32
206static int win32err_to_errno(int err_value)
207{
208 switch (err_value) {
209 case ERROR_FILE_NOT_FOUND:
210 return ENOENT;
211 case ERROR_ALREADY_EXISTS:
212 return EEXIST;
213 default:
214 return EFAULT;
215 }
216}
217#endif
218
219static int remove_file(const char* path)
220{
221 int e = 0;
222#ifdef WIN32
223 if (!DeleteFile(path)) {
224 e = win32err_to_errno(GetLastError());
225 }
226#else
227 if (remove(path) < 0) {
228 e = errno;
229 }
230#endif
231 return e;
232}
233
234static int remove_directory(const char* path)
235{
236 int e = 0;
237#ifdef WIN32
238 if (!RemoveDirectory(path)) {
239 e = win32err_to_errno(GetLastError());
240 }
241#else
242 if (remove(path) < 0) {
243 e = errno;
244 }
245#endif
246 return e;
247}
248
249struct entry {
250 char *name;
251 struct entry *next;
252};
253
254static void scan_directory(const char *path, struct entry **files, struct entry **directories)
255{
256 DIR* cur_dir = opendir(path);
257 if (cur_dir) {
258 struct dirent* ep;
259 while ((ep = readdir(cur_dir))) {
260 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
261 continue;
262 }
263 char *fpath = string_build_path(path, ep->d_name, NULL);
264 if (fpath) {
265#ifdef HAVE_DIRENT_D_TYPE
266 if (ep->d_type & DT_DIR) {
267#else
268 struct stat st;
269 if (stat(fpath, &st) != 0) return;
270 if (S_ISDIR(st.st_mode)) {
271#endif
272 struct entry *ent = malloc(sizeof(struct entry));
273 if (!ent) return;
274 ent->name = fpath;
275 ent->next = *directories;
276 *directories = ent;
277 scan_directory(fpath, files, directories);
278 fpath = NULL;
279 } else {
280 struct entry *ent = malloc(sizeof(struct entry));
281 if (!ent) return;
282 ent->name = fpath;
283 ent->next = *files;
284 *files = ent;
285 fpath = NULL;
286 }
287 }
288 }
289 closedir(cur_dir);
290 }
291}
292
293static int rmdir_recursive(const char* path)
294{
295 int res = 0;
296 struct entry *files = NULL;
297 struct entry *directories = NULL;
298 struct entry *ent;
299
300 ent = malloc(sizeof(struct entry));
301 if (!ent) return ENOMEM;
302 ent->name = strdup(path);
303 ent->next = NULL;
304 directories = ent;
305
306 scan_directory(path, &files, &directories);
307
308 ent = files;
309 while (ent) {
310 struct entry *del = ent;
311 res = remove_file(ent->name);
312 free(ent->name);
313 ent = ent->next;
314 free(del);
315 }
316 ent = directories;
317 while (ent) {
318 struct entry *del = ent;
319 res = remove_directory(ent->name);
320 free(ent->name);
321 ent = ent->next;
322 free(del);
323 }
324
325 return res;
326}
327
328static char* get_uuid()
329{
330 const char *chars = "ABCDEF0123456789";
331 int i = 0;
332 char *uuid = (char*)malloc(sizeof(char) * 33);
333
334 srand(time(NULL));
335
336 for (i = 0; i < 32; i++) {
337 uuid[i] = chars[rand() % 16];
338 }
339
340 uuid[32] = '\0';
341
342 return uuid;
343}
344
345static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t device, afc_client_t afc)
346{
347 /* gather data from lockdown */
348 plist_t value_node = NULL;
349 plist_t root_node = NULL;
350 plist_t itunes_settings = NULL;
351 plist_t min_itunes_version = NULL;
352 char *udid_uppercase = NULL;
353
354 lockdownd_client_t lockdown = NULL;
355 if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
356 return NULL;
357 }
358
359 plist_t ret = plist_new_dict();
360
361 /* get basic device information in one go */
362 lockdownd_get_value(lockdown, NULL, NULL, &root_node);
363
364 /* get iTunes settings */
365 lockdownd_get_value(lockdown, "com.apple.iTunes", NULL, &itunes_settings);
366
367 /* get minimum iTunes version */
368 lockdownd_get_value(lockdown, "com.apple.mobile.iTunes", "MinITunesVersion", &min_itunes_version);
369
370 lockdownd_client_free(lockdown);
371
372 /* get a list of installed user applications */
373 plist_t app_dict = plist_new_dict();
374 plist_t installed_apps = plist_new_array();
375 instproxy_client_t ip = NULL;
376 if (instproxy_client_start_service(device, &ip, TOOL_NAME) == INSTPROXY_E_SUCCESS) {
377 plist_t client_opts = instproxy_client_options_new();
378 instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
379 instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata", NULL);
380
381 plist_t apps = NULL;
382 instproxy_browse(ip, client_opts, &apps);
383
384 sbservices_client_t sbs = NULL;
385 if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {
386 printf("Couldn't establish sbservices connection. Continuing anyway.\n");
387 }
388
389 if (apps && (plist_get_node_type(apps) == PLIST_ARRAY)) {
390 uint32_t app_count = plist_array_get_size(apps);
391 uint32_t i;
392 for (i = 0; i < app_count; i++) {
393 plist_t app_entry = plist_array_get_item(apps, i);
394 plist_t bundle_id = plist_dict_get_item(app_entry, "CFBundleIdentifier");
395 if (bundle_id) {
396 char *bundle_id_str = NULL;
397 plist_array_append_item(installed_apps, plist_copy(bundle_id));
398
399 plist_get_string_val(bundle_id, &bundle_id_str);
400 plist_t sinf = plist_dict_get_item(app_entry, "ApplicationSINF");
401 plist_t meta = plist_dict_get_item(app_entry, "iTunesMetadata");
402 if (sinf && meta) {
403 plist_t adict = plist_new_dict();
404 plist_dict_set_item(adict, "ApplicationSINF", plist_copy(sinf));
405 if (sbs) {
406 char *pngdata = NULL;
407 uint64_t pngsize = 0;
408 sbservices_get_icon_pngdata(sbs, bundle_id_str, &pngdata, &pngsize);
409 if (pngdata) {
410 plist_dict_set_item(adict, "PlaceholderIcon", plist_new_data(pngdata, pngsize));
411 free(pngdata);
412 }
413 }
414 plist_dict_set_item(adict, "iTunesMetadata", plist_copy(meta));
415 plist_dict_set_item(app_dict, bundle_id_str, adict);
416 }
417 free(bundle_id_str);
418 }
419 }
420 }
421 plist_free(apps);
422
423 if (sbs) {
424 sbservices_client_free(sbs);
425 }
426
427 instproxy_client_options_free(client_opts);
428
429 instproxy_client_free(ip);
430 }
431
432 /* Applications */
433 plist_dict_set_item(ret, "Applications", app_dict);
434
435 /* set fields we understand */
436 value_node = plist_dict_get_item(root_node, "BuildVersion");
437 plist_dict_set_item(ret, "Build Version", plist_copy(value_node));
438
439 value_node = plist_dict_get_item(root_node, "DeviceName");
440 plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
441 plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
442
443 char *uuid = get_uuid();
444 plist_dict_set_item(ret, "GUID", plist_new_string(uuid));
445 free(uuid);
446
447 value_node = plist_dict_get_item(root_node, "IntegratedCircuitCardIdentity");
448 if (value_node)
449 plist_dict_set_item(ret, "ICCID", plist_copy(value_node));
450
451 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
452 if (value_node)
453 plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
454
455 /* Installed Applications */
456 plist_dict_set_item(ret, "Installed Applications", installed_apps);
457
458 plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
459
460 value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
461 if (value_node)
462 plist_dict_set_item(ret, "MEID", plist_copy(value_node));
463
464 value_node = plist_dict_get_item(root_node, "PhoneNumber");
465 if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
466 plist_dict_set_item(ret, "Phone Number", plist_copy(value_node));
467 }
468
469 /* FIXME Product Name */
470
471 value_node = plist_dict_get_item(root_node, "ProductType");
472 plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
473
474 value_node = plist_dict_get_item(root_node, "ProductVersion");
475 plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
476
477 value_node = plist_dict_get_item(root_node, "SerialNumber");
478 plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
479
480 /* FIXME Sync Settings? */
481
482 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
483 plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
484
485 plist_dict_set_item(ret, "Target Type", plist_new_string("Device"));
486
487 /* uppercase */
488 udid_uppercase = string_toupper((char*)udid);
489 plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
490 free(udid_uppercase);
491
492 char *data_buf = NULL;
493 uint64_t data_size = 0;
494 mobilebackup_afc_get_file_contents(afc, "/Books/iBooksData2.plist", &data_buf, &data_size);
495 if (data_buf) {
496 plist_dict_set_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
497 free(data_buf);
498 }
499
500 plist_t files = plist_new_dict();
501 const char *itunesfiles[] = {
502 "ApertureAlbumPrefs",
503 "IC-Info.sidb",
504 "IC-Info.sidv",
505 "PhotosFolderAlbums",
506 "PhotosFolderName",
507 "PhotosFolderPrefs",
508 "VoiceMemos.plist",
509 "iPhotoAlbumPrefs",
510 "iTunesApplicationIDs",
511 "iTunesPrefs",
512 "iTunesPrefs.plist",
513 NULL
514 };
515 int i = 0;
516 for (i = 0; itunesfiles[i]; i++) {
517 data_buf = NULL;
518 data_size = 0;
519 char *fname = (char*)malloc(strlen("/iTunes_Control/iTunes/") + strlen(itunesfiles[i]) + 1);
520 strcpy(fname, "/iTunes_Control/iTunes/");
521 strcat(fname, itunesfiles[i]);
522 mobilebackup_afc_get_file_contents(afc, fname, &data_buf, &data_size);
523 free(fname);
524 if (data_buf) {
525 plist_dict_set_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
526 free(data_buf);
527 }
528 }
529 plist_dict_set_item(ret, "iTunes Files", files);
530
531 plist_dict_set_item(ret, "iTunes Settings", itunes_settings ? plist_copy(itunes_settings) : plist_new_dict());
532
533 /* since we usually don't have iTunes, let's get the minimum required iTunes version from the device */
534 if (min_itunes_version) {
535 plist_dict_set_item(ret, "iTunes Version", plist_copy(min_itunes_version));
536 } else {
537 plist_dict_set_item(ret, "iTunes Version", plist_new_string("10.0.1"));
538 }
539
540 plist_free(itunes_settings);
541 plist_free(min_itunes_version);
542 plist_free(root_node);
543
544 return ret;
545}
546
547static int write_restore_applications(plist_t info_plist, afc_client_t afc)
548{
549 int res = -1;
550 uint64_t restore_applications_file = 0;
551 char * applications_plist_xml = NULL;
552 uint32_t applications_plist_xml_length = 0;
553
554 plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
555 if (!applications_plist) {
556 printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
557 return 0;
558 }
559 plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
560 if (!applications_plist_xml) {
561 printf("Error preparing RestoreApplications.plist\n");
562 goto leave;
563 }
564
565 afc_error_t afc_err = 0;
566 afc_err = afc_make_directory(afc, "/iTunesRestore");
567 if (afc_err != AFC_E_SUCCESS) {
568 printf("Error creating directory /iTunesRestore, error code %d\n", afc_err);
569 goto leave;
570 }
571
572 afc_err = afc_file_open(afc, "/iTunesRestore/RestoreApplications.plist", AFC_FOPEN_WR, &restore_applications_file);
573 if (afc_err != AFC_E_SUCCESS || !restore_applications_file) {
574 printf("Error creating /iTunesRestore/RestoreApplications.plist, error code %d\n", afc_err);
575 goto leave;
576 }
577
578 uint32_t bytes_written = 0;
579 afc_err = afc_file_write(afc, restore_applications_file, applications_plist_xml, applications_plist_xml_length, &bytes_written);
580 if (afc_err != AFC_E_SUCCESS || bytes_written != applications_plist_xml_length) {
581 printf("Error writing /iTunesRestore/RestoreApplications.plist, error code %d, wrote %u of %u bytes\n", afc_err, bytes_written, applications_plist_xml_length);
582 goto leave;
583 }
584
585 afc_err = afc_file_close(afc, restore_applications_file);
586 restore_applications_file = 0;
587 if (afc_err != AFC_E_SUCCESS) {
588 goto leave;
589 }
590 /* success */
591 res = 0;
592
593leave:
594 free(applications_plist_xml);
595
596 if (restore_applications_file) {
597 afc_file_close(afc, restore_applications_file);
598 restore_applications_file = 0;
599 }
600
601 return res;
602}
603
604static int mb2_status_check_snapshot_state(const char *path, const char *udid, const char *matches)
605{
606 int ret = 0;
607 plist_t status_plist = NULL;
608 char *file_path = string_build_path(path, udid, "Status.plist", NULL);
609
610 plist_read_from_file(file_path, &status_plist, NULL);
611 free(file_path);
612 if (!status_plist) {
613 printf("Could not read Status.plist!\n");
614 return ret;
615 }
616 plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
617 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
618 char* sval = NULL;
619 plist_get_string_val(node, &sval);
620 if (sval) {
621 ret = (strcmp(sval, matches) == 0) ? 1 : 0;
622 free(sval);
623 }
624 } else {
625 printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
626 }
627 plist_free(status_plist);
628 return ret;
629}
630
631static void do_post_notification(idevice_t device, const char *notification)
632{
633 lockdownd_service_descriptor_t service = NULL;
634 np_client_t np;
635
636 lockdownd_client_t lockdown = NULL;
637
638 if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
639 return;
640 }
641
642 lockdownd_error_t ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
643 if (ldret == LOCKDOWN_E_SUCCESS) {
644 np_client_new(device, service, &np);
645 if (np) {
646 np_post_notification(np, notification);
647 np_client_free(np);
648 }
649 } else {
650 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
651 }
652
653 if (service) {
654 lockdownd_service_descriptor_free(service);
655 service = NULL;
656 }
657 lockdownd_client_free(lockdown);
658}
659
660static void print_progress_real(double progress, int flush)
661{
662 int i = 0;
663 PRINT_VERBOSE(1, "\r[");
664 for(i = 0; i < 50; i++) {
665 if(i < progress / 2) {
666 PRINT_VERBOSE(1, "=");
667 } else {
668 PRINT_VERBOSE(1, " ");
669 }
670 }
671 PRINT_VERBOSE(1, "] %3.0f%%", progress);
672
673 if (flush > 0) {
674 fflush(stdout);
675 if (progress == 100)
676 PRINT_VERBOSE(1, "\n");
677 }
678}
679
680static void print_progress(uint64_t current, uint64_t total)
681{
682 char *format_size = NULL;
683 double progress = ((double)current/(double)total)*100;
684 if (progress < 0)
685 return;
686
687 if (progress > 100)
688 progress = 100;
689
690 print_progress_real((double)progress, 0);
691
692 format_size = string_format_size(current);
693 PRINT_VERBOSE(1, " (%s", format_size);
694 free(format_size);
695 format_size = string_format_size(total);
696 PRINT_VERBOSE(1, "/%s) ", format_size);
697 free(format_size);
698
699 fflush(stdout);
700 if (progress == 100)
701 PRINT_VERBOSE(1, "\n");
702}
703
704static double overall_progress = 0;
705
706static void mb2_set_overall_progress(double progress)
707{
708 if (progress > 0.0)
709 overall_progress = progress;
710}
711
712static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
713{
714 plist_t node = NULL;
715 double progress = 0.0;
716
717 if (!strcmp(identifier, "DLMessageDownloadFiles")) {
718 node = plist_array_get_item(message, 3);
719 } else if (!strcmp(identifier, "DLMessageUploadFiles")) {
720 node = plist_array_get_item(message, 2);
721 } else if (!strcmp(identifier, "DLMessageMoveFiles") || !strcmp(identifier, "DLMessageMoveItems")) {
722 node = plist_array_get_item(message, 3);
723 } else if (!strcmp(identifier, "DLMessageRemoveFiles") || !strcmp(identifier, "DLMessageRemoveItems")) {
724 node = plist_array_get_item(message, 3);
725 }
726
727 if (node != NULL) {
728 plist_get_real_val(node, &progress);
729 mb2_set_overall_progress(progress);
730 }
731}
732
733static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
734{
735 if (!status_dict) return;
736 plist_t filedict = plist_new_dict();
737 plist_dict_set_item(filedict, "DLFileErrorString", plist_new_string(error_message));
738 plist_dict_set_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
739 plist_dict_set_item(status_dict, path, filedict);
740}
741
742static int errno_to_device_error(int errno_value)
743{
744 switch (errno_value) {
745 case ENOENT:
746 return -6;
747 case EEXIST:
748 return -7;
749 case ENOTDIR:
750 return -8;
751 case EISDIR:
752 return -9;
753 case ELOOP:
754 return -10;
755 case EIO:
756 return -11;
757 case ENOSPC:
758 return -15;
759 default:
760 return -1;
761 }
762}
763
764static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char *backup_dir, const char *path, plist_t *errplist)
765{
766 uint32_t nlen = 0;
767 uint32_t pathlen = strlen(path);
768 uint32_t bytes = 0;
769 char *localfile = string_build_path(backup_dir, path, NULL);
770 char buf[32768];
771#ifdef WIN32
772 struct _stati64 fst;
773#else
774 struct stat fst;
775#endif
776
777 FILE *f = NULL;
778 uint32_t slen = 0;
779 int errcode = -1;
780 int result = -1;
781 uint32_t length;
782#ifdef WIN32
783 uint64_t total;
784 uint64_t sent;
785#else
786 off_t total;
787 off_t sent;
788#endif
789
790 mobilebackup2_error_t err;
791
792 /* send path length */
793 nlen = htobe32(pathlen);
794 err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), &bytes);
795 if (err != MOBILEBACKUP2_E_SUCCESS) {
796 goto leave_proto_err;
797 }
798 if (bytes != (uint32_t)sizeof(nlen)) {
799 err = MOBILEBACKUP2_E_MUX_ERROR;
800 goto leave_proto_err;
801 }
802
803 /* send path */
804 err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, &bytes);
805 if (err != MOBILEBACKUP2_E_SUCCESS) {
806 goto leave_proto_err;
807 }
808 if (bytes != pathlen) {
809 err = MOBILEBACKUP2_E_MUX_ERROR;
810 goto leave_proto_err;
811 }
812
813#ifdef WIN32
814 if (_stati64(localfile, &fst) < 0)
815#else
816 if (stat(localfile, &fst) < 0)
817#endif
818 {
819 if (errno != ENOENT)
820 printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
821 errcode = errno;
822 goto leave;
823 }
824
825 total = fst.st_size;
826
827 char *format_size = string_format_size(total);
828 PRINT_VERBOSE(1, "Sending '%s' (%s)\n", path, format_size);
829 free(format_size);
830
831 if (total == 0) {
832 errcode = 0;
833 goto leave;
834 }
835
836 f = fopen(localfile, "rb");
837 if (!f) {
838 printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
839 errcode = errno;
840 goto leave;
841 }
842
843 sent = 0;
844 do {
845 length = ((total-sent) < (long long)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
846 /* send data size (file size + 1) */
847 nlen = htobe32(length+1);
848 memcpy(buf, &nlen, sizeof(nlen));
849 buf[4] = CODE_FILE_DATA;
850 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, &bytes);
851 if (err != MOBILEBACKUP2_E_SUCCESS) {
852 goto leave_proto_err;
853 }
854 if (bytes != 5) {
855 goto leave_proto_err;
856 }
857
858 /* send file contents */
859 size_t r = fread(buf, 1, sizeof(buf), f);
860 if (r <= 0) {
861 printf("%s: read error\n", __func__);
862 errcode = errno;
863 goto leave;
864 }
865 err = mobilebackup2_send_raw(mobilebackup2, buf, r, &bytes);
866 if (err != MOBILEBACKUP2_E_SUCCESS) {
867 goto leave_proto_err;
868 }
869 if (bytes != (uint32_t)r) {
870 printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
871 goto leave_proto_err;
872 }
873 sent += r;
874 } while (sent < total);
875 fclose(f);
876 f = NULL;
877 errcode = 0;
878
879leave:
880 if (errcode == 0) {
881 result = 0;
882 nlen = 1;
883 nlen = htobe32(nlen);
884 memcpy(buf, &nlen, 4);
885 buf[4] = CODE_SUCCESS;
886 mobilebackup2_send_raw(mobilebackup2, buf, 5, &bytes);
887 } else {
888 if (!*errplist) {
889 *errplist = plist_new_dict();
890 }
891 char *errdesc = strerror(errcode);
892 mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
893
894 length = strlen(errdesc);
895 nlen = htobe32(length+1);
896 memcpy(buf, &nlen, 4);
897 buf[4] = CODE_ERROR_LOCAL;
898 slen = 5;
899 memcpy(buf+slen, errdesc, length);
900 slen += length;
901 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
902 if (err != MOBILEBACKUP2_E_SUCCESS) {
903 printf("could not send message\n");
904 }
905 if (bytes != slen) {
906 printf("could only send %d from %d\n", bytes, slen);
907 }
908 }
909
910leave_proto_err:
911 if (f)
912 fclose(f);
913 free(localfile);
914 return result;
915}
916
917static void mb2_handle_send_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
918{
919 uint32_t cnt;
920 uint32_t i = 0;
921 uint32_t sent;
922 plist_t errplist = NULL;
923
924 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
925
926 plist_t files = plist_array_get_item(message, 1);
927 cnt = plist_array_get_size(files);
928
929 for (i = 0; i < cnt; i++) {
930 plist_t val = plist_array_get_item(files, i);
931 if (plist_get_node_type(val) != PLIST_STRING) {
932 continue;
933 }
934 char *str = NULL;
935 plist_get_string_val(val, &str);
936 if (!str)
937 continue;
938
939 if (mb2_handle_send_file(mobilebackup2, backup_dir, str, &errplist) < 0) {
940 free(str);
941 //printf("Error when sending file '%s' to device\n", str);
942 // TODO: perhaps we can continue, we've got a multi status response?!
943 break;
944 }
945 free(str);
946 }
947
948 /* send terminating 0 dword */
949 uint32_t zero = 0;
950 mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
951
952 if (!errplist) {
953 plist_t emptydict = plist_new_dict();
954 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, emptydict);
955 plist_free(emptydict);
956 } else {
957 mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
958 plist_free(errplist);
959 }
960}
961
962static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** filename)
963{
964 uint32_t nlen = 0;
965 uint32_t rlen = 0;
966
967 do {
968 nlen = 0;
969 rlen = 0;
970 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &rlen);
971 nlen = be32toh(nlen);
972
973 if ((nlen == 0) && (rlen == 4)) {
974 // a zero length means no more files to receive
975 return 0;
976 }
977 if (rlen == 0) {
978 // device needs more time, waiting...
979 continue;
980 }
981 if (nlen > 4096) {
982 // filename length is too large
983 printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
984 return 0;
985 }
986
987 if (*filename != NULL) {
988 free(*filename);
989 *filename = NULL;
990 }
991
992 *filename = (char*)malloc(nlen+1);
993
994 rlen = 0;
995 mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
996 if (rlen != nlen) {
997 printf("ERROR: %s: could not read filename\n", __func__);
998 return 0;
999 }
1000
1001 char* p = *filename;
1002 p[rlen] = 0;
1003
1004 break;
1005 } while(1 && !quit_flag);
1006
1007 return nlen;
1008}
1009
1010static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1011{
1012 uint64_t backup_real_size = 0;
1013 uint64_t backup_total_size = 0;
1014 uint32_t blocksize;
1015 uint32_t bdone;
1016 uint32_t rlen;
1017 uint32_t nlen = 0;
1018 uint32_t r;
1019 char buf[32768];
1020 char *fname = NULL;
1021 char *dname = NULL;
1022 char *bname = NULL;
1023 char code = 0;
1024 char last_code = 0;
1025 plist_t node = NULL;
1026 FILE *f = NULL;
1027 unsigned int file_count = 0;
1028 int errcode = 0;
1029 char *errdesc = NULL;
1030
1031 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
1032
1033 node = plist_array_get_item(message, 3);
1034 if (plist_get_node_type(node) == PLIST_UINT) {
1035 plist_get_uint_val(node, &backup_total_size);
1036 }
1037 if (backup_total_size > 0) {
1038 PRINT_VERBOSE(1, "Receiving files\n");
1039 }
1040
1041 do {
1042 if (quit_flag)
1043 break;
1044
1045 nlen = mb2_receive_filename(mobilebackup2, &dname);
1046 if (nlen == 0) {
1047 break;
1048 }
1049
1050 nlen = mb2_receive_filename(mobilebackup2, &fname);
1051 if (!nlen) {
1052 break;
1053 }
1054
1055 if (bname != NULL) {
1056 free(bname);
1057 bname = NULL;
1058 }
1059
1060 bname = string_build_path(backup_dir, fname, NULL);
1061
1062 if (fname != NULL) {
1063 free(fname);
1064 fname = NULL;
1065 }
1066
1067 r = 0;
1068 nlen = 0;
1069 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1070 if (r != 4) {
1071 printf("ERROR: %s: could not receive code length!\n", __func__);
1072 break;
1073 }
1074 nlen = be32toh(nlen);
1075
1076 last_code = code;
1077 code = 0;
1078
1079 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1080 if (r != 1) {
1081 printf("ERROR: %s: could not receive code!\n", __func__);
1082 break;
1083 }
1084
1085 /* TODO remove this */
1086 if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
1087 PRINT_VERBOSE(1, "Found new flag %02x\n", code);
1088 }
1089
1090 remove_file(bname);
1091 f = fopen(bname, "wb");
1092 while (f && (code == CODE_FILE_DATA)) {
1093 blocksize = nlen-1;
1094 bdone = 0;
1095 rlen = 0;
1096 while (bdone < blocksize) {
1097 if ((blocksize - bdone) < sizeof(buf)) {
1098 rlen = blocksize - bdone;
1099 } else {
1100 rlen = sizeof(buf);
1101 }
1102 mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
1103 if ((int)r <= 0) {
1104 break;
1105 }
1106 fwrite(buf, 1, r, f);
1107 bdone += r;
1108 }
1109 if (bdone == blocksize) {
1110 backup_real_size += blocksize;
1111 }
1112 if (backup_total_size > 0) {
1113 print_progress(backup_real_size, backup_total_size);
1114 }
1115 if (quit_flag)
1116 break;
1117 nlen = 0;
1118 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1119 nlen = be32toh(nlen);
1120 if (nlen > 0) {
1121 last_code = code;
1122 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1123 } else {
1124 break;
1125 }
1126 }
1127 if (f) {
1128 fclose(f);
1129 file_count++;
1130 } else {
1131 errcode = errno_to_device_error(errno);
1132 errdesc = strerror(errno);
1133 printf("Error opening '%s' for writing: %s\n", bname, errdesc);
1134 break;
1135 }
1136 if (nlen == 0) {
1137 break;
1138 }
1139
1140 /* check if an error message was received */
1141 if (code == CODE_ERROR_REMOTE) {
1142 /* error message */
1143 char *msg = (char*)malloc(nlen);
1144 mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
1145 msg[r] = 0;
1146 /* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
1147 if (last_code != CODE_FILE_DATA) {
1148 fprintf(stdout, "\nReceived an error message from device: %s\n", msg);
1149 }
1150 free(msg);
1151 }
1152 } while (1);
1153
1154 if (fname != NULL)
1155 free(fname);
1156
1157 /* if there are leftovers to read, finish up cleanly */
1158 if ((int)nlen-1 > 0) {
1159 PRINT_VERBOSE(1, "\nDiscarding current data hunk.\n");
1160 fname = (char*)malloc(nlen-1);
1161 mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
1162 free(fname);
1163 remove_file(bname);
1164 }
1165
1166 /* clean up */
1167 if (bname != NULL)
1168 free(bname);
1169
1170 if (dname != NULL)
1171 free(dname);
1172
1173 plist_t empty_plist = plist_new_dict();
1174 mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_plist);
1175 plist_free(empty_plist);
1176
1177 return file_count;
1178}
1179
1180static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1181{
1182 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1183
1184 plist_t node = plist_array_get_item(message, 1);
1185 char *str = NULL;
1186 if (plist_get_node_type(node) == PLIST_STRING) {
1187 plist_get_string_val(node, &str);
1188 }
1189 if (!str) {
1190 printf("ERROR: Malformed DLContentsOfDirectory message\n");
1191 // TODO error handling
1192 return;
1193 }
1194
1195 char *path = string_build_path(backup_dir, str, NULL);
1196 free(str);
1197
1198 plist_t dirlist = plist_new_dict();
1199
1200 DIR* cur_dir = opendir(path);
1201 if (cur_dir) {
1202 struct dirent* ep;
1203 while ((ep = readdir(cur_dir))) {
1204 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1205 continue;
1206 }
1207 char *fpath = string_build_path(path, ep->d_name, NULL);
1208 if (fpath) {
1209 plist_t fdict = plist_new_dict();
1210 struct stat st;
1211 stat(fpath, &st);
1212 const char *ftype = "DLFileTypeUnknown";
1213 if (S_ISDIR(st.st_mode)) {
1214 ftype = "DLFileTypeDirectory";
1215 } else if (S_ISREG(st.st_mode)) {
1216 ftype = "DLFileTypeRegular";
1217 }
1218 plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
1219 plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
1220 plist_dict_set_item(fdict, "DLFileModificationDate",
1221 plist_new_date(st.st_mtime - MAC_EPOCH, 0));
1222
1223 plist_dict_set_item(dirlist, ep->d_name, fdict);
1224 free(fpath);
1225 }
1226 }
1227 closedir(cur_dir);
1228 }
1229 free(path);
1230
1231 /* TODO error handling */
1232 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, dirlist);
1233 plist_free(dirlist);
1234 if (err != MOBILEBACKUP2_E_SUCCESS) {
1235 printf("Could not send status response, error %d\n", err);
1236 }
1237}
1238
1239static void mb2_handle_make_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1240{
1241 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1242
1243 plist_t dir = plist_array_get_item(message, 1);
1244 char *str = NULL;
1245 int errcode = 0;
1246 char *errdesc = NULL;
1247 plist_get_string_val(dir, &str);
1248
1249 char *newpath = string_build_path(backup_dir, str, NULL);
1250 free(str);
1251
1252 if (mkdir_with_parents(newpath, 0755) < 0) {
1253 errdesc = strerror(errno);
1254 if (errno != EEXIST) {
1255 printf("mkdir: %s (%d)\n", errdesc, errno);
1256 }
1257 errcode = errno_to_device_error(errno);
1258 }
1259 free(newpath);
1260 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
1261 if (err != MOBILEBACKUP2_E_SUCCESS) {
1262 printf("Could not send status response, error %d\n", err);
1263 }
1264}
1265
1266static void mb2_copy_file_by_path(const char *src, const char *dst)
1267{
1268 FILE *from, *to;
1269 char buf[BUFSIZ];
1270 size_t length;
1271
1272 /* open source file */
1273 if ((from = fopen(src, "rb")) == NULL) {
1274 printf("Cannot open source path '%s'.\n", src);
1275 return;
1276 }
1277
1278 /* open destination file */
1279 if ((to = fopen(dst, "wb")) == NULL) {
1280 printf("Cannot open destination file '%s'.\n", dst);
1281 fclose(from);
1282 return;
1283 }
1284
1285 /* copy the file */
1286 while ((length = fread(buf, 1, BUFSIZ, from)) != 0) {
1287 fwrite(buf, 1, length, to);
1288 }
1289
1290 if(fclose(from) == EOF) {
1291 printf("Error closing source file.\n");
1292 }
1293
1294 if(fclose(to) == EOF) {
1295 printf("Error closing destination file.\n");
1296 }
1297}
1298
1299static void mb2_copy_directory_by_path(const char *src, const char *dst)
1300{
1301 if (!src || !dst) {
1302 return;
1303 }
1304
1305 struct stat st;
1306
1307 /* if src does not exist */
1308 if ((stat(src, &st) < 0) || !S_ISDIR(st.st_mode)) {
1309 printf("ERROR: Source directory does not exist '%s': %s (%d)\n", src, strerror(errno), errno);
1310 return;
1311 }
1312
1313 /* if dst directory does not exist */
1314 if ((stat(dst, &st) < 0) || !S_ISDIR(st.st_mode)) {
1315 /* create it */
1316 if (mkdir_with_parents(dst, 0755) < 0) {
1317 printf("ERROR: Unable to create destination directory '%s': %s (%d)\n", dst, strerror(errno), errno);
1318 return;
1319 }
1320 }
1321
1322 /* loop over src directory contents */
1323 DIR *cur_dir = opendir(src);
1324 if (cur_dir) {
1325 struct dirent* ep;
1326 while ((ep = readdir(cur_dir))) {
1327 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1328 continue;
1329 }
1330 char *srcpath = string_build_path(src, ep->d_name, NULL);
1331 char *dstpath = string_build_path(dst, ep->d_name, NULL);
1332 if (srcpath && dstpath) {
1333 /* copy file */
1334 mb2_copy_file_by_path(srcpath, dstpath);
1335 }
1336
1337 if (srcpath)
1338 free(srcpath);
1339 if (dstpath)
1340 free(dstpath);
1341 }
1342 closedir(cur_dir);
1343 }
1344}
1345
1346#ifdef WIN32
1347#define BS_CC '\b'
1348#define my_getch getch
1349#else
1350#define BS_CC 0x7f
1351static int my_getch(void)
1352{
1353 struct termios oldt, newt;
1354 int ch;
1355 tcgetattr(STDIN_FILENO, &oldt);
1356 newt = oldt;
1357 newt.c_lflag &= ~(ICANON | ECHO);
1358 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1359 ch = getchar();
1360 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
1361 return ch;
1362}
1363#endif
1364
1365static void get_hidden_input(char *buf, int maxlen)
1366{
1367 int pwlen = 0;
1368 int c;
1369
1370 while ((c = my_getch())) {
1371 if ((c == '\r') || (c == '\n')) {
1372 break;
1373 }
1374 if (isprint(c)) {
1375 if (pwlen < maxlen-1)
1376 buf[pwlen++] = c;
1377 fputc('*', stderr);
1378 } else if (c == BS_CC) {
1379 if (pwlen > 0) {
1380 fputs("\b \b", stderr);
1381 pwlen--;
1382 }
1383 }
1384 }
1385 buf[pwlen] = 0;
1386}
1387
1388static char* ask_for_password(const char* msg, int type_again)
1389{
1390 char pwbuf[256];
1391
1392 fprintf(stderr, "%s: ", msg);
1393 fflush(stderr);
1394 get_hidden_input(pwbuf, 256);
1395 fputc('\n', stderr);
1396
1397 if (type_again) {
1398 char pwrep[256];
1399
1400 fprintf(stderr, "%s (repeat): ", msg);
1401 fflush(stderr);
1402 get_hidden_input(pwrep, 256);
1403 fputc('\n', stderr);
1404
1405 if (strcmp(pwbuf, pwrep) != 0) {
1406 printf("ERROR: passwords don't match\n");
1407 return NULL;
1408 }
1409 }
1410 return strdup(pwbuf);
1411}
1412
1413/**
1414 * signal handler function for cleaning up properly
1415 */
1416static void clean_exit(int sig)
1417{
1418 fprintf(stderr, "Exiting...\n");
1419 quit_flag++;
1420}
1421
1422static void print_usage(int argc, char **argv, int is_error)
1423{
1424 char *name = strrchr(argv[0], '/');
1425 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
1426 fprintf(is_error ? stderr : stdout,
1427 "\n"
1428 "Create or restore backup in/from the specified directory.\n"
1429 "\n"
1430 "CMD:\n"
1431 " backup create backup for the device\n"
1432 " --full force full backup from device.\n"
1433 " restore restore last backup to the device\n"
1434 " --system restore system files, too.\n"
1435 " --no-reboot do NOT reboot the device when done (default: yes).\n"
1436 " --copy create a copy of backup folder before restoring.\n"
1437 " --settings restore device settings from the backup.\n"
1438 " --remove remove items which are not being restored\n"
1439 " --skip-apps do not trigger re-installation of apps after restore\n"
1440 " --password PWD supply the password for the encrypted source backup\n"
1441 " info show details about last completed backup of device\n"
1442 " list list files of last completed backup in CSV format\n"
1443 " unback unpack a completed backup in DIRECTORY/_unback_/\n"
1444 " encryption on|off [PWD] enable or disable backup encryption\n"
1445 " changepw [OLD NEW] change backup password on target device\n"
1446 " cloud on|off enable or disable cloud use (requires iCloud account)\n"
1447 "\n"
1448 "NOTE: Passwords will be requested in interactive mode (-i) if omitted, or can\n"
1449 "be passed via environment variable BACKUP_PASSWORD/BACKUP_PASSWORD_NEW.\n"
1450 "See man page for further details.\n"
1451 "\n"
1452 "OPTIONS:\n"
1453 " -u, --udid UDID target specific device by UDID\n"
1454 " -s, --source UDID use backup data from device specified by UDID\n"
1455 " -n, --network connect to network device\n"
1456 " -i, --interactive request passwords interactively\n"
1457 " -d, --debug enable communication debugging\n"
1458 " -h, --help prints usage information\n"
1459 " -v, --version prints version information\n"
1460 "\n"
1461 "Homepage: <" PACKAGE_URL ">\n"
1462 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
1463 );
1464}
1465
1466#define DEVICE_VERSION(maj, min, patch) ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF))
1467
1468int main(int argc, char *argv[])
1469{
1470 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
1471 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
1472 int i = 0;
1473 char* udid = NULL;
1474 char* source_udid = NULL;
1475 int use_network = 0;
1476 lockdownd_service_descriptor_t service = NULL;
1477 int cmd = -1;
1478 int cmd_flags = 0;
1479 int is_full_backup = 0;
1480 int result_code = -1;
1481 char* backup_directory = NULL;
1482 int interactive_mode = 0;
1483 char* backup_password = NULL;
1484 char* newpw = NULL;
1485 struct stat st;
1486 plist_t node_tmp = NULL;
1487 plist_t info_plist = NULL;
1488 plist_t opts = NULL;
1489
1490 idevice_t device = NULL;
1491 afc_client_t afc = NULL;
1492 np_client_t np = NULL;
1493 lockdownd_client_t lockdown = NULL;
1494 mobilebackup2_client_t mobilebackup2 = NULL;
1495 mobilebackup2_error_t err;
1496 uint64_t lockfile = 0;
1497
1498#define OPT_SYSTEM 1
1499#define OPT_REBOOT 2
1500#define OPT_NO_REBOOT 3
1501#define OPT_COPY 4
1502#define OPT_SETTINGS 5
1503#define OPT_REMOVE 6
1504#define OPT_SKIP_APPS 7
1505#define OPT_PASSWORD 8
1506#define OPT_FULL 9
1507
1508 int c = 0;
1509 const struct option longopts[] = {
1510 { "debug", no_argument, NULL, 'd' },
1511 { "help", no_argument, NULL, 'h' },
1512 { "udid", required_argument, NULL, 'u' },
1513 { "source", required_argument, NULL, 's' },
1514 { "interactive", no_argument, NULL, 'i' },
1515 { "network", no_argument, NULL, 'n' },
1516 { "version", no_argument, NULL, 'v' },
1517 // command options:
1518 { "system", no_argument, NULL, OPT_SYSTEM },
1519 { "reboot", no_argument, NULL, OPT_REBOOT },
1520 { "no-reboot", no_argument, NULL, OPT_NO_REBOOT },
1521 { "copy", no_argument, NULL, OPT_COPY },
1522 { "settings", no_argument, NULL, OPT_SETTINGS },
1523 { "remove", no_argument, NULL, OPT_REMOVE },
1524 { "skip-apps", no_argument, NULL, OPT_SKIP_APPS },
1525 { "password", required_argument, NULL, OPT_PASSWORD },
1526 { "full", no_argument, NULL, OPT_FULL },
1527 { NULL, 0, NULL, 0}
1528 };
1529
1530 /* we need to exit cleanly on running backups and restores or we cause havok */
1531 signal(SIGINT, clean_exit);
1532 signal(SIGTERM, clean_exit);
1533#ifndef WIN32
1534 signal(SIGQUIT, clean_exit);
1535 signal(SIGPIPE, SIG_IGN);
1536#endif
1537
1538 /* parse cmdline args */
1539 while ((c = getopt_long(argc, argv, "dhu:s:inv", longopts, NULL)) != -1) {
1540 switch (c) {
1541 case 'd':
1542 idevice_set_debug_level(1);
1543 break;
1544 case 'u':
1545 if (!*optarg) {
1546 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
1547 print_usage(argc, argv, 1);
1548 return 2;
1549 }
1550 udid = strdup(optarg);
1551 break;
1552 case 's':
1553 if (!*optarg) {
1554 fprintf(stderr, "ERROR: SOURCE argument must not be empty!\n");
1555 print_usage(argc, argv, 1);
1556 return 2;
1557 }
1558 source_udid = strdup(optarg);
1559 break;
1560 case 'i':
1561 interactive_mode = 1;
1562 break;
1563 case 'n':
1564 use_network = 1;
1565 break;
1566 case 'h':
1567 print_usage(argc, argv, 0);
1568 return 0;
1569 case 'v':
1570 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
1571 return 0;
1572 case OPT_SYSTEM:
1573 cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
1574 break;
1575 case OPT_REBOOT:
1576 cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT;
1577 break;
1578 case OPT_NO_REBOOT:
1579 cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT;
1580 break;
1581 case OPT_COPY:
1582 cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
1583 break;
1584 case OPT_SETTINGS:
1585 cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
1586 break;
1587 case OPT_REMOVE:
1588 cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
1589 break;
1590 case OPT_SKIP_APPS:
1591 cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
1592 break;
1593 case OPT_PASSWORD:
1594 free(backup_password);
1595 backup_password = strdup(optarg);
1596 break;
1597 case OPT_FULL:
1598 cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
1599 break;
1600 default:
1601 print_usage(argc, argv, 1);
1602 return 2;
1603 }
1604 }
1605 argc -= optind;
1606 argv += optind;
1607
1608 if (!argv[0]) {
1609 fprintf(stderr, "ERROR: No command specified.\n");
1610 print_usage(argc+optind, argv-optind, 1);
1611 return 2;
1612 }
1613
1614 if (!strcmp(argv[0], "backup")) {
1615 cmd = CMD_BACKUP;
1616 }
1617 else if (!strcmp(argv[0], "restore")) {
1618 cmd = CMD_RESTORE;
1619 }
1620 else if (!strcmp(argv[0], "cloud")) {
1621 cmd = CMD_CLOUD;
1622 i = 1;
1623 if (!argv[i]) {
1624 fprintf(stderr, "ERROR: No argument given for cloud command; requires either 'on' or 'off'.\n");
1625 print_usage(argc+optind, argv-optind, 1);
1626 return 2;
1627 }
1628 if (!strcmp(argv[i], "on")) {
1629 cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
1630 } else if (!strcmp(argv[i], "off")) {
1631 cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
1632 } else {
1633 fprintf(stderr, "ERROR: Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
1634 print_usage(argc+optind, argv-optind, 1);
1635 return 2;
1636 }
1637 }
1638 else if (!strcmp(argv[0], "info")) {
1639 cmd = CMD_INFO;
1640 verbose = 0;
1641 }
1642 else if (!strcmp(argv[0], "list")) {
1643 cmd = CMD_LIST;
1644 verbose = 0;
1645 }
1646 else if (!strcmp(argv[0], "unback")) {
1647 cmd = CMD_UNBACK;
1648 }
1649 else if (!strcmp(argv[0], "encryption")) {
1650 cmd = CMD_CHANGEPW;
1651 i = 1;
1652 if (!argv[i]) {
1653 fprintf(stderr, "ERROR: No argument given for encryption command; requires either 'on' or 'off'.\n");
1654 print_usage(argc+optind, argv-optind, 1);
1655 return 2;
1656 }
1657 if (!strcmp(argv[i], "on")) {
1658 cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
1659 } else if (!strcmp(argv[i], "off")) {
1660 cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
1661 } else {
1662 fprintf(stderr, "ERROR: Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
1663 print_usage(argc+optind, argv-optind, 1);
1664 return 2;
1665 }
1666 // check if a password was given on the command line
1667 free(newpw);
1668 newpw = NULL;
1669 free(backup_password);
1670 backup_password = NULL;
1671 i++;
1672 if (argv[i]) {
1673 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
1674 newpw = strdup(argv[i]);
1675 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
1676 backup_password = strdup(argv[i]);
1677 }
1678 }
1679 }
1680 else if (!strcmp(argv[0], "changepw")) {
1681 cmd = CMD_CHANGEPW;
1682 cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
1683 // check if passwords were given on command line
1684 free(newpw);
1685 newpw = NULL;
1686 free(backup_password);
1687 backup_password = NULL;
1688 i = 1;
1689 if (argv[i]) {
1690 backup_password = strdup(argv[i]);
1691 i++;
1692 if (!argv[i]) {
1693 fprintf(stderr, "ERROR: Old and new passwords have to be passed as arguments for the changepw command\n");
1694 print_usage(argc+optind, argv-optind, 1);
1695 return 2;
1696 }
1697 newpw = strdup(argv[i]);
1698 }
1699 }
1700
1701 i++;
1702 if (argv[i]) {
1703 backup_directory = argv[i];
1704 }
1705
1706 /* verify options */
1707 if (cmd == -1) {
1708 fprintf(stderr, "ERROR: Unsupported command '%s'.\n", argv[0]);
1709 print_usage(argc+optind, argv-optind, 1);
1710 return 2;
1711 }
1712
1713 if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
1714 backup_directory = (char*)".this_folder_is_not_present_on_purpose";
1715 } else {
1716 if (backup_directory == NULL) {
1717 fprintf(stderr, "ERROR: No target backup directory specified.\n");
1718 print_usage(argc+optind, argv-optind, 1);
1719 return 2;
1720 }
1721
1722 /* verify if passed backup directory exists */
1723 if (stat(backup_directory, &st) != 0) {
1724 fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
1725 return -1;
1726 }
1727 }
1728
1729 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
1730 if (ret != IDEVICE_E_SUCCESS) {
1731 if (udid) {
1732 printf("No device found with udid %s.\n", udid);
1733 } else {
1734 printf("No device found.\n");
1735 }
1736 return -1;
1737 }
1738
1739 if (!udid) {
1740 idevice_get_udid(device, &udid);
1741 }
1742
1743 if (!source_udid) {
1744 source_udid = strdup(udid);
1745 }
1746
1747 uint8_t is_encrypted = 0;
1748 char *info_path = NULL;
1749 if (cmd == CMD_CHANGEPW) {
1750 if (!interactive_mode) {
1751 if (!newpw) {
1752 newpw = getenv("BACKUP_PASSWORD_NEW");
1753 if (newpw) {
1754 newpw = strdup(newpw);
1755 }
1756 }
1757 if (!backup_password) {
1758 backup_password = getenv("BACKUP_PASSWORD");
1759 if (backup_password) {
1760 backup_password = strdup(backup_password);
1761 }
1762 }
1763 }
1764 if (!interactive_mode && !backup_password && !newpw) {
1765 idevice_free(device);
1766 printf("ERROR: Can't get password input in non-interactive mode. Either pass password(s) on the command line, or enable interactive mode with -i or --interactive.\n");
1767 return -1;
1768 }
1769 } else if (cmd != CMD_CLOUD) {
1770 /* backup directory must contain an Info.plist */
1771 info_path = string_build_path(backup_directory, source_udid, "Info.plist", NULL);
1772 if (cmd == CMD_RESTORE || cmd == CMD_UNBACK) {
1773 if (stat(info_path, &st) != 0) {
1774 idevice_free(device);
1775 free(info_path);
1776 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, source_udid);
1777 return -1;
1778 }
1779 char* manifest_path = string_build_path(backup_directory, source_udid, "Manifest.plist", NULL);
1780 if (stat(manifest_path, &st) != 0) {
1781 free(info_path);
1782 }
1783 plist_t manifest_plist = NULL;
1784 plist_read_from_file(manifest_path, &manifest_plist, NULL);
1785 if (!manifest_plist) {
1786 idevice_free(device);
1787 free(info_path);
1788 free(manifest_path);
1789 printf("ERROR: Backup directory \"%s\" is invalid. No Manifest.plist found for UDID %s.\n", backup_directory, source_udid);
1790 return -1;
1791 }
1792 node_tmp = plist_dict_get_item(manifest_plist, "IsEncrypted");
1793 if (node_tmp && (plist_get_node_type(node_tmp) == PLIST_BOOLEAN)) {
1794 plist_get_bool_val(node_tmp, &is_encrypted);
1795 }
1796 plist_free(manifest_plist);
1797 free(manifest_path);
1798 }
1799 PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
1800 }
1801
1802 if (cmd != CMD_CLOUD && is_encrypted) {
1803 PRINT_VERBOSE(1, "This is an encrypted backup.\n");
1804 if (backup_password == NULL) {
1805 backup_password = getenv("BACKUP_PASSWORD");
1806 if (backup_password) {
1807 backup_password = strdup(backup_password);
1808 }
1809 }
1810 if (backup_password == NULL) {
1811 if (interactive_mode) {
1812 backup_password = ask_for_password("Enter backup password", 0);
1813 }
1814 if (!backup_password || (strlen(backup_password) == 0)) {
1815 if (backup_password) {
1816 free(backup_password);
1817 }
1818 idevice_free(device);
1819 if (cmd == CMD_RESTORE) {
1820 printf("ERROR: a backup password is required to restore an encrypted backup. Cannot continue.\n");
1821 } else if (cmd == CMD_UNBACK) {
1822 printf("ERROR: a backup password is required to unback an encrypted backup. Cannot continue.\n");
1823 }
1824 return -1;
1825 }
1826 }
1827 }
1828
1829 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
1830 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
1831 idevice_free(device);
1832 return -1;
1833 }
1834
1835 uint8_t willEncrypt = 0;
1836 node_tmp = NULL;
1837 lockdownd_get_value(lockdown, "com.apple.mobile.backup", "WillEncrypt", &node_tmp);
1838 if (node_tmp) {
1839 if (plist_get_node_type(node_tmp) == PLIST_BOOLEAN) {
1840 plist_get_bool_val(node_tmp, &willEncrypt);
1841 }
1842 plist_free(node_tmp);
1843 node_tmp = NULL;
1844 }
1845
1846 /* get ProductVersion */
1847 char *product_version = NULL;
1848 int device_version = 0;
1849 node_tmp = NULL;
1850 lockdownd_get_value(lockdown, NULL, "ProductVersion", &node_tmp);
1851 if (node_tmp) {
1852 if (plist_get_node_type(node_tmp) == PLIST_STRING) {
1853 plist_get_string_val(node_tmp, &product_version);
1854 }
1855 plist_free(node_tmp);
1856 node_tmp = NULL;
1857 }
1858 if (product_version) {
1859 int vers[3] = { 0, 0, 0 };
1860 if (sscanf(product_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
1861 device_version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
1862 }
1863 }
1864
1865 /* start notification_proxy */
1866 ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
1867 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
1868 np_client_new(device, service, &np);
1869 np_set_notify_callback(np, notify_cb, NULL);
1870 const char *noties[5] = {
1871 NP_SYNC_CANCEL_REQUEST,
1872 NP_SYNC_SUSPEND_REQUEST,
1873 NP_SYNC_RESUME_REQUEST,
1874 NP_BACKUP_DOMAIN_CHANGED,
1875 NULL
1876 };
1877 np_observe_notifications(np, noties);
1878 } else {
1879 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
1880 cmd = CMD_LEAVE;
1881 goto checkpoint;
1882 }
1883 if (service) {
1884 lockdownd_service_descriptor_free(service);
1885 service = NULL;
1886 }
1887
1888 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
1889 /* start AFC, we need this for the lock file */
1890 ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);
1891 if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
1892 afc_client_new(device, service, &afc);
1893 } else {
1894 printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
1895 cmd = CMD_LEAVE;
1896 goto checkpoint;
1897 }
1898 }
1899
1900 if (service) {
1901 lockdownd_service_descriptor_free(service);
1902 service = NULL;
1903 }
1904
1905 /* start mobilebackup service and retrieve port */
1906 ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
1907 lockdownd_client_free(lockdown);
1908 lockdown = NULL;
1909 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
1910 PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
1911 mobilebackup2_client_new(device, service, &mobilebackup2);
1912
1913 if (service) {
1914 lockdownd_service_descriptor_free(service);
1915 service = NULL;
1916 }
1917
1918 /* send Hello message */
1919 double local_versions[2] = {2.0, 2.1};
1920 double remote_version = 0.0;
1921 err = mobilebackup2_version_exchange(mobilebackup2, local_versions, 2, &remote_version);
1922 if (err != MOBILEBACKUP2_E_SUCCESS) {
1923 printf("Could not perform backup protocol version exchange, error code %d\n", err);
1924 cmd = CMD_LEAVE;
1925 goto checkpoint;
1926 }
1927
1928 PRINT_VERBOSE(1, "Negotiated Protocol Version %.1f\n", remote_version);
1929
1930 /* check abort conditions */
1931 if (quit_flag > 0) {
1932 PRINT_VERBOSE(1, "Aborting as requested by user...\n");
1933 cmd = CMD_LEAVE;
1934 goto checkpoint;
1935 }
1936
1937 /* verify existing Info.plist */
1938 if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {
1939 PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
1940 plist_read_from_file(info_path, &info_plist, NULL);
1941
1942 if (!info_plist) {
1943 printf("Could not read Info.plist\n");
1944 is_full_backup = 1;
1945 }
1946 } else {
1947 if (cmd == CMD_RESTORE) {
1948 printf("Aborting restore. Info.plist is missing.\n");
1949 cmd = CMD_LEAVE;
1950 } else {
1951 is_full_backup = 1;
1952 }
1953 }
1954
1955 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
1956 do_post_notification(device, NP_SYNC_WILL_START);
1957 afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
1958 }
1959 if (lockfile) {
1960 afc_error_t aerr;
1961 do_post_notification(device, NP_SYNC_LOCK_REQUEST);
1962 for (i = 0; i < LOCK_ATTEMPTS; i++) {
1963 aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
1964 if (aerr == AFC_E_SUCCESS) {
1965 do_post_notification(device, NP_SYNC_DID_START);
1966 break;
1967 }
1968 if (aerr == AFC_E_OP_WOULD_BLOCK) {
1969 usleep(LOCK_WAIT);
1970 continue;
1971 }
1972
1973 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
1974 afc_file_close(afc, lockfile);
1975 lockfile = 0;
1976 cmd = CMD_LEAVE;
1977 }
1978 if (i == LOCK_ATTEMPTS) {
1979 fprintf(stderr, "ERROR: timeout while locking for sync\n");
1980 afc_file_close(afc, lockfile);
1981 lockfile = 0;
1982 cmd = CMD_LEAVE;
1983 }
1984 }
1985
1986checkpoint:
1987
1988 switch(cmd) {
1989 case CMD_CLOUD:
1990 opts = plist_new_dict();
1991 plist_dict_set_item(opts, "CloudBackupState", plist_new_bool(cmd_flags & CMD_FLAG_CLOUD_ENABLE ? 1: 0));
1992 err = mobilebackup2_send_request(mobilebackup2, "EnableCloudBackup", udid, source_udid, opts);
1993 plist_free(opts);
1994 opts = NULL;
1995 if (err != MOBILEBACKUP2_E_SUCCESS) {
1996 printf("Error setting cloud backup state on device, error code %d\n", err);
1997 cmd = CMD_LEAVE;
1998 }
1999 break;
2000 case CMD_BACKUP:
2001 PRINT_VERBOSE(1, "Starting backup...\n");
2002
2003 /* make sure backup device sub-directory exists */
2004 char* devbackupdir = string_build_path(backup_directory, source_udid, NULL);
2005 __mkdir(devbackupdir, 0755);
2006 free(devbackupdir);
2007
2008 if (strcmp(source_udid, udid) != 0) {
2009 /* handle different source backup directory */
2010 // make sure target backup device sub-directory exists
2011 devbackupdir = string_build_path(backup_directory, udid, NULL);
2012 __mkdir(devbackupdir, 0755);
2013 free(devbackupdir);
2014
2015 // use Info.plist path in target backup folder */
2016 free(info_path);
2017 info_path = string_build_path(backup_directory, udid, "Info.plist", NULL);
2018 }
2019
2020 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
2021 /* TODO: verify battery on AC enough battery remaining */
2022
2023 /* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
2024 if (info_plist) {
2025 plist_free(info_plist);
2026 info_plist = NULL;
2027 }
2028 info_plist = mobilebackup_factory_info_plist_new(udid, device, afc);
2029 if (!info_plist) {
2030 fprintf(stderr, "Failed to generate Info.plist - aborting\n");
2031 cmd = CMD_LEAVE;
2032 }
2033 remove_file(info_path);
2034 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
2035 free(info_path);
2036
2037 plist_free(info_plist);
2038 info_plist = NULL;
2039
2040 if (cmd_flags & CMD_FLAG_FORCE_FULL_BACKUP) {
2041 PRINT_VERBOSE(1, "Enforcing full backup from device.\n");
2042 opts = plist_new_dict();
2043 plist_dict_set_item(opts, "ForceFullBackup", plist_new_bool(1));
2044 }
2045 /* request backup from device with manifest from last backup */
2046 if (willEncrypt) {
2047 PRINT_VERBOSE(1, "Backup will be encrypted.\n");
2048 } else {
2049 PRINT_VERBOSE(1, "Backup will be unencrypted.\n");
2050 }
2051 PRINT_VERBOSE(1, "Requesting backup from device...\n");
2052 err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, source_udid, opts);
2053 if (opts)
2054 plist_free(opts);
2055 if (err == MOBILEBACKUP2_E_SUCCESS) {
2056 if (is_full_backup) {
2057 PRINT_VERBOSE(1, "Full backup mode.\n");
2058 } else {
2059 PRINT_VERBOSE(1, "Incremental backup mode.\n");
2060 }
2061 } else {
2062 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2063 printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
2064 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2065 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
2066 } else {
2067 printf("ERROR: Could not start backup process: unspecified error occurred\n");
2068 }
2069 cmd = CMD_LEAVE;
2070 }
2071 break;
2072 case CMD_RESTORE:
2073 /* TODO: verify battery on AC enough battery remaining */
2074
2075 /* verify if Status.plist says we read from an successful backup */
2076 if (!mb2_status_check_snapshot_state(backup_directory, source_udid, "finished")) {
2077 printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
2078 cmd = CMD_LEAVE;
2079 break;
2080 }
2081
2082 PRINT_VERBOSE(1, "Starting Restore...\n");
2083
2084 opts = plist_new_dict();
2085 plist_dict_set_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
2086 PRINT_VERBOSE(1, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "Yes":"No"));
2087 if (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT)
2088 plist_dict_set_item(opts, "RestoreShouldReboot", plist_new_bool(0));
2089 PRINT_VERBOSE(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT ? "No":"Yes"));
2090 if ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0)
2091 plist_dict_set_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
2092 PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
2093 plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
2094 PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
2095 plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS));
2096 PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
2097 if (backup_password != NULL) {
2098 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2099 }
2100 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2101
2102 if (cmd_flags & CMD_FLAG_RESTORE_SKIP_APPS) {
2103 PRINT_VERBOSE(1, "Not writing RestoreApplications.plist - apps will not be re-installed after restore\n");
2104 } else {
2105 /* Write /iTunesRestore/RestoreApplications.plist so that the device will start
2106 * restoring applications once the rest of the restore process is finished */
2107 if (write_restore_applications(info_plist, afc) < 0) {
2108 cmd = CMD_LEAVE;
2109 break;
2110 }
2111 PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
2112 }
2113
2114 /* Start restore */
2115 err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, source_udid, opts);
2116 plist_free(opts);
2117 if (err != MOBILEBACKUP2_E_SUCCESS) {
2118 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2119 printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
2120 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2121 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
2122 } else {
2123 printf("ERROR: Could not start restore process: unspecified error occurred\n");
2124 }
2125 cmd = CMD_LEAVE;
2126 }
2127 break;
2128 case CMD_INFO:
2129 PRINT_VERBOSE(1, "Requesting backup info from device...\n");
2130 err = mobilebackup2_send_request(mobilebackup2, "Info", udid, source_udid, NULL);
2131 if (err != MOBILEBACKUP2_E_SUCCESS) {
2132 printf("Error requesting backup info from device, error code %d\n", err);
2133 cmd = CMD_LEAVE;
2134 }
2135 break;
2136 case CMD_LIST:
2137 PRINT_VERBOSE(1, "Requesting backup list from device...\n");
2138 err = mobilebackup2_send_request(mobilebackup2, "List", udid, source_udid, NULL);
2139 if (err != MOBILEBACKUP2_E_SUCCESS) {
2140 printf("Error requesting backup list from device, error code %d\n", err);
2141 cmd = CMD_LEAVE;
2142 }
2143 break;
2144 case CMD_UNBACK:
2145 PRINT_VERBOSE(1, "Starting to unpack backup...\n");
2146 if (backup_password != NULL) {
2147 opts = plist_new_dict();
2148 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2149 }
2150 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2151 err = mobilebackup2_send_request(mobilebackup2, "Unback", udid, source_udid, opts);
2152 if (backup_password !=NULL) {
2153 plist_free(opts);
2154 }
2155 if (err != MOBILEBACKUP2_E_SUCCESS) {
2156 printf("Error requesting unback operation from device, error code %d\n", err);
2157 cmd = CMD_LEAVE;
2158 }
2159 break;
2160 case CMD_CHANGEPW:
2161 opts = plist_new_dict();
2162 plist_dict_set_item(opts, "TargetIdentifier", plist_new_string(udid));
2163 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2164 if (!willEncrypt) {
2165 if (!newpw) {
2166 newpw = getenv("BACKUP_PASSWORD");
2167 if (newpw) {
2168 newpw = strdup(newpw);
2169 }
2170 }
2171 if (!newpw) {
2172 newpw = ask_for_password("Enter new backup password", 1);
2173 }
2174 if (!newpw) {
2175 printf("No backup password given. Aborting.\n");
2176 }
2177 } else {
2178 printf("ERROR: Backup encryption is already enabled. Aborting.\n");
2179 cmd = CMD_LEAVE;
2180 if (newpw) {
2181 free(newpw);
2182 newpw = NULL;
2183 }
2184 }
2185 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2186 if (willEncrypt) {
2187 if (!backup_password) {
2188 backup_password = getenv("BACKUP_PASSWORD");
2189 if (backup_password) {
2190 backup_password = strdup(backup_password);
2191 }
2192 }
2193 if (!backup_password) {
2194 backup_password = ask_for_password("Enter current backup password", 0);
2195 }
2196 } else {
2197 printf("ERROR: Backup encryption is not enabled. Aborting.\n");
2198 cmd = CMD_LEAVE;
2199 if (backup_password) {
2200 free(backup_password);
2201 backup_password = NULL;
2202 }
2203 }
2204 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2205 if (willEncrypt) {
2206 if (!backup_password) {
2207 backup_password = ask_for_password("Enter old backup password", 0);
2208 newpw = ask_for_password("Enter new backup password", 1);
2209 }
2210 } else {
2211 printf("ERROR: Backup encryption is not enabled so can't change password. Aborting.\n");
2212 cmd = CMD_LEAVE;
2213 if (newpw) {
2214 free(newpw);
2215 newpw = NULL;
2216 }
2217 if (backup_password) {
2218 free(backup_password);
2219 backup_password = NULL;
2220 }
2221 }
2222 }
2223 if (newpw) {
2224 plist_dict_set_item(opts, "NewPassword", plist_new_string(newpw));
2225 }
2226 if (backup_password) {
2227 plist_dict_set_item(opts, "OldPassword", plist_new_string(backup_password));
2228 }
2229 if (newpw || backup_password) {
2230 mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
2231 uint8_t passcode_hint = 0;
2232 if (device_version >= DEVICE_VERSION(13,0,0)) {
2233 diagnostics_relay_client_t diag = NULL;
2234 if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2235 plist_t dict = NULL;
2236 plist_t keys = plist_new_array();
2237 plist_array_append_item(keys, plist_new_string("PasswordConfigured"));
2238 if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2239 plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured");
2240 plist_get_bool_val(node, &passcode_hint);
2241 }
2242 plist_free(keys);
2243 plist_free(dict);
2244 diagnostics_relay_goodbye(diag);
2245 diagnostics_relay_client_free(diag);
2246 }
2247 }
2248 if (passcode_hint) {
2249 if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2250 PRINT_VERBOSE(1, "Please confirm changing the backup password by entering the passcode on the device.\n");
2251 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2252 PRINT_VERBOSE(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n");
2253 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2254 PRINT_VERBOSE(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n");
2255 }
2256 }
2257 /*if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2258 int retr = 10;
2259 while ((retr-- >= 0) && !backup_domain_changed) {
2260 sleep(1);
2261 }
2262 }*/
2263 } else {
2264 cmd = CMD_LEAVE;
2265 }
2266 plist_free(opts);
2267 break;
2268 default:
2269 break;
2270 }
2271
2272 if (cmd != CMD_LEAVE) {
2273 /* reset operation success status */
2274 int operation_ok = 0;
2275 plist_t message = NULL;
2276
2277 mobilebackup2_error_t mberr;
2278 char *dlmsg = NULL;
2279 int file_count = 0;
2280 int errcode = 0;
2281 const char *errdesc = NULL;
2282 int progress_finished = 0;
2283
2284 /* process series of DLMessage* operations */
2285 do {
2286 free(dlmsg);
2287 dlmsg = NULL;
2288 mberr = mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
2289 if (mberr == MOBILEBACKUP2_E_RECEIVE_TIMEOUT) {
2290 PRINT_VERBOSE(2, "Device is not ready yet, retrying...\n");
2291 goto files_out;
2292 } else if (mberr != MOBILEBACKUP2_E_SUCCESS) {
2293 PRINT_VERBOSE(0, "ERROR: Could not receive from mobilebackup2 (%d)\n", mberr);
2294 quit_flag++;
2295 goto files_out;
2296 }
2297
2298 if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
2299 /* device wants to download files from the computer */
2300 mb2_set_overall_progress_from_message(message, dlmsg);
2301 mb2_handle_send_files(mobilebackup2, message, backup_directory);
2302 } else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
2303 /* device wants to send files to the computer */
2304 mb2_set_overall_progress_from_message(message, dlmsg);
2305 file_count += mb2_handle_receive_files(mobilebackup2, message, backup_directory);
2306 } else if (!strcmp(dlmsg, "DLMessageGetFreeDiskSpace")) {
2307 /* device wants to know how much disk space is available on the computer */
2308 uint64_t freespace = 0;
2309 int res = -1;
2310#ifdef WIN32
2311 if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
2312 res = 0;
2313 }
2314#else
2315 struct statvfs fs;
2316 memset(&fs, '\0', sizeof(fs));
2317 res = statvfs(backup_directory, &fs);
2318 if (res == 0) {
2319 freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_bsize;
2320 }
2321#endif
2322 plist_t freespace_item = plist_new_uint(freespace);
2323 mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);
2324 plist_free(freespace_item);
2325 } else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) {
2326 /* device wants to purge disk space on the host - not supported */
2327 plist_t empty_dict = plist_new_dict();
2328 err = mobilebackup2_send_status_response(mobilebackup2, -1, "Operation not supported", empty_dict);
2329 plist_free(empty_dict);
2330 } else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
2331 /* list directory contents */
2332 mb2_handle_list_directory(mobilebackup2, message, backup_directory);
2333 } else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
2334 /* make a directory */
2335 mb2_handle_make_directory(mobilebackup2, message, backup_directory);
2336 } else if (!strcmp(dlmsg, "DLMessageMoveFiles") || !strcmp(dlmsg, "DLMessageMoveItems")) {
2337 /* perform a series of rename operations */
2338 mb2_set_overall_progress_from_message(message, dlmsg);
2339 plist_t moves = plist_array_get_item(message, 1);
2340 uint32_t cnt = plist_dict_get_size(moves);
2341 PRINT_VERBOSE(1, "Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
2342 plist_dict_iter iter = NULL;
2343 plist_dict_new_iter(moves, &iter);
2344 errcode = 0;
2345 errdesc = NULL;
2346 if (iter) {
2347 char *key = NULL;
2348 plist_t val = NULL;
2349 do {
2350 plist_dict_next_item(moves, iter, &key, &val);
2351 if (key && (plist_get_node_type(val) == PLIST_STRING)) {
2352 char *str = NULL;
2353 plist_get_string_val(val, &str);
2354 if (str) {
2355 char *newpath = string_build_path(backup_directory, str, NULL);
2356 free(str);
2357 char *oldpath = string_build_path(backup_directory, key, NULL);
2358
2359 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode))
2360 rmdir_recursive(newpath);
2361 else
2362 remove_file(newpath);
2363 if (rename(oldpath, newpath) < 0) {
2364 printf("Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
2365 errcode = errno_to_device_error(errno);
2366 errdesc = strerror(errno);
2367 break;
2368 }
2369 free(oldpath);
2370 free(newpath);
2371 }
2372 free(key);
2373 key = NULL;
2374 }
2375 } while (val);
2376 free(iter);
2377 } else {
2378 errcode = -1;
2379 errdesc = "Could not create dict iterator";
2380 printf("Could not create dict iterator\n");
2381 }
2382 plist_t empty_dict = plist_new_dict();
2383 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2384 plist_free(empty_dict);
2385 if (err != MOBILEBACKUP2_E_SUCCESS) {
2386 printf("Could not send status response, error %d\n", err);
2387 }
2388 } else if (!strcmp(dlmsg, "DLMessageRemoveFiles") || !strcmp(dlmsg, "DLMessageRemoveItems")) {
2389 mb2_set_overall_progress_from_message(message, dlmsg);
2390 plist_t removes = plist_array_get_item(message, 1);
2391 uint32_t cnt = plist_array_get_size(removes);
2392 PRINT_VERBOSE(1, "Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
2393 uint32_t ii = 0;
2394 errcode = 0;
2395 errdesc = NULL;
2396 for (ii = 0; ii < cnt; ii++) {
2397 plist_t val = plist_array_get_item(removes, ii);
2398 if (plist_get_node_type(val) == PLIST_STRING) {
2399 char *str = NULL;
2400 plist_get_string_val(val, &str);
2401 if (str) {
2402 const char *checkfile = strchr(str, '/');
2403 int suppress_warning = 0;
2404 if (checkfile) {
2405 if (strcmp(checkfile+1, "Manifest.mbdx") == 0) {
2406 suppress_warning = 1;
2407 }
2408 }
2409 char *newpath = string_build_path(backup_directory, str, NULL);
2410 free(str);
2411 int res = 0;
2412 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2413 res = rmdir_recursive(newpath);
2414 } else {
2415 res = remove_file(newpath);
2416 }
2417 if (res != 0 && res != ENOENT) {
2418 if (!suppress_warning)
2419 printf("Could not remove '%s': %s (%d)\n", newpath, strerror(res), res);
2420 errcode = errno_to_device_error(res);
2421 errdesc = strerror(res);
2422 }
2423 free(newpath);
2424 }
2425 }
2426 }
2427 plist_t empty_dict = plist_new_dict();
2428 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2429 plist_free(empty_dict);
2430 if (err != MOBILEBACKUP2_E_SUCCESS) {
2431 printf("Could not send status response, error %d\n", err);
2432 }
2433 } else if (!strcmp(dlmsg, "DLMessageCopyItem")) {
2434 plist_t srcpath = plist_array_get_item(message, 1);
2435 plist_t dstpath = plist_array_get_item(message, 2);
2436 errcode = 0;
2437 errdesc = NULL;
2438 if ((plist_get_node_type(srcpath) == PLIST_STRING) && (plist_get_node_type(dstpath) == PLIST_STRING)) {
2439 char *src = NULL;
2440 char *dst = NULL;
2441 plist_get_string_val(srcpath, &src);
2442 plist_get_string_val(dstpath, &dst);
2443 if (src && dst) {
2444 char *oldpath = string_build_path(backup_directory, src, NULL);
2445 char *newpath = string_build_path(backup_directory, dst, NULL);
2446
2447 PRINT_VERBOSE(1, "Copying '%s' to '%s'\n", src, dst);
2448
2449 /* check that src exists */
2450 if ((stat(oldpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2451 mb2_copy_directory_by_path(oldpath, newpath);
2452 } else if ((stat(oldpath, &st) == 0) && S_ISREG(st.st_mode)) {
2453 mb2_copy_file_by_path(oldpath, newpath);
2454 }
2455
2456 free(newpath);
2457 free(oldpath);
2458 }
2459 free(src);
2460 free(dst);
2461 }
2462 plist_t empty_dict = plist_new_dict();
2463 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2464 plist_free(empty_dict);
2465 if (err != MOBILEBACKUP2_E_SUCCESS) {
2466 printf("Could not send status response, error %d\n", err);
2467 }
2468 } else if (!strcmp(dlmsg, "DLMessageDisconnect")) {
2469 break;
2470 } else if (!strcmp(dlmsg, "DLMessageProcessMessage")) {
2471 node_tmp = plist_array_get_item(message, 1);
2472 if (plist_get_node_type(node_tmp) != PLIST_DICT) {
2473 printf("Unknown message received!\n");
2474 }
2475 plist_t nn;
2476 int error_code = -1;
2477 nn = plist_dict_get_item(node_tmp, "ErrorCode");
2478 if (nn && (plist_get_node_type(nn) == PLIST_UINT)) {
2479 uint64_t ec = 0;
2480 plist_get_uint_val(nn, &ec);
2481 error_code = (uint32_t)ec;
2482 if (error_code == 0) {
2483 operation_ok = 1;
2484 result_code = 0;
2485 } else {
2486 result_code = -error_code;
2487 }
2488 }
2489 nn = plist_dict_get_item(node_tmp, "ErrorDescription");
2490 char *str = NULL;
2491 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2492 plist_get_string_val(nn, &str);
2493 }
2494 if (error_code != 0) {
2495 if (str) {
2496 printf("ErrorCode %d: %s\n", error_code, str);
2497 } else {
2498 printf("ErrorCode %d: (Unknown)\n", error_code);
2499 }
2500 }
2501 if (str) {
2502 free(str);
2503 }
2504 nn = plist_dict_get_item(node_tmp, "Content");
2505 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2506 str = NULL;
2507 plist_get_string_val(nn, &str);
2508 PRINT_VERBOSE(1, "Content:\n");
2509 printf("%s", str);
2510 free(str);
2511 }
2512 break;
2513 }
2514
2515 /* print status */
2516 if ((overall_progress > 0) && !progress_finished) {
2517 if (overall_progress >= 100.0F) {
2518 progress_finished = 1;
2519 }
2520 print_progress_real(overall_progress, 0);
2521 PRINT_VERBOSE(1, " Finished\n");
2522 }
2523
2524files_out:
2525 plist_free(message);
2526 message = NULL;
2527 free(dlmsg);
2528 dlmsg = NULL;
2529
2530 if (quit_flag > 0) {
2531 /* need to cancel the backup here */
2532 //mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
2533
2534 /* remove any atomic Manifest.plist.tmp */
2535
2536 /*manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
2537 if (stat(manifest_path, &st) == 0)
2538 remove(manifest_path);*/
2539 break;
2540 }
2541 } while (1);
2542
2543 plist_free(message);
2544 free(dlmsg);
2545
2546 /* report operation status to user */
2547 switch (cmd) {
2548 case CMD_CLOUD:
2549 if (cmd_flags & CMD_FLAG_CLOUD_ENABLE) {
2550 if (operation_ok) {
2551 PRINT_VERBOSE(1, "Cloud backup has been enabled successfully.\n");
2552 } else {
2553 PRINT_VERBOSE(1, "Could not enable cloud backup.\n");
2554 }
2555 } else if (cmd_flags & CMD_FLAG_CLOUD_DISABLE) {
2556 if (operation_ok) {
2557 PRINT_VERBOSE(1, "Cloud backup has been disabled successfully.\n");
2558 } else {
2559 PRINT_VERBOSE(1, "Could not disable cloud backup.\n");
2560 }
2561 }
2562 break;
2563 case CMD_BACKUP:
2564 PRINT_VERBOSE(1, "Received %d files from device.\n", file_count);
2565 if (operation_ok && mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
2566 PRINT_VERBOSE(1, "Backup Successful.\n");
2567 } else {
2568 if (quit_flag) {
2569 PRINT_VERBOSE(1, "Backup Aborted.\n");
2570 } else {
2571 PRINT_VERBOSE(1, "Backup Failed (Error Code %d).\n", -result_code);
2572 }
2573 }
2574 break;
2575 case CMD_UNBACK:
2576 if (quit_flag) {
2577 PRINT_VERBOSE(1, "Unback Aborted.\n");
2578 } else {
2579 PRINT_VERBOSE(1, "The files can now be found in the \"_unback_\" directory.\n");
2580 PRINT_VERBOSE(1, "Unback Successful.\n");
2581 }
2582 break;
2583 case CMD_CHANGEPW:
2584 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2585 if (operation_ok) {
2586 PRINT_VERBOSE(1, "Backup encryption has been enabled successfully.\n");
2587 } else {
2588 PRINT_VERBOSE(1, "Could not enable backup encryption.\n");
2589 }
2590 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2591 if (operation_ok) {
2592 PRINT_VERBOSE(1, "Backup encryption has been disabled successfully.\n");
2593 } else {
2594 PRINT_VERBOSE(1, "Could not disable backup encryption.\n");
2595 }
2596 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2597 if (operation_ok) {
2598 PRINT_VERBOSE(1, "Backup encryption password has been changed successfully.\n");
2599 } else {
2600 PRINT_VERBOSE(1, "Could not change backup encryption password.\n");
2601 }
2602 }
2603 break;
2604 case CMD_RESTORE:
2605 if (operation_ok) {
2606 if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
2607 PRINT_VERBOSE(1, "The device should reboot now.\n");
2608 PRINT_VERBOSE(1, "Restore Successful.\n");
2609 } else {
2610 afc_remove_path(afc, "/iTunesRestore/RestoreApplications.plist");
2611 afc_remove_path(afc, "/iTunesRestore");
2612 if (quit_flag) {
2613 PRINT_VERBOSE(1, "Restore Aborted.\n");
2614 } else {
2615 PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code);
2616 }
2617 }
2618 break;
2619 case CMD_INFO:
2620 case CMD_LIST:
2621 case CMD_LEAVE:
2622 default:
2623 if (quit_flag) {
2624 PRINT_VERBOSE(1, "Operation Aborted.\n");
2625 } else if (cmd == CMD_LEAVE) {
2626 PRINT_VERBOSE(1, "Operation Failed.\n");
2627 } else {
2628 PRINT_VERBOSE(1, "Operation Successful.\n");
2629 }
2630 break;
2631 }
2632 }
2633 if (lockfile) {
2634 afc_file_lock(afc, lockfile, AFC_LOCK_UN);
2635 afc_file_close(afc, lockfile);
2636 lockfile = 0;
2637 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE)
2638 do_post_notification(device, NP_SYNC_DID_FINISH);
2639 }
2640 } else {
2641 printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));
2642 lockdownd_client_free(lockdown);
2643 lockdown = NULL;
2644 }
2645
2646 if (lockdown) {
2647 lockdownd_client_free(lockdown);
2648 lockdown = NULL;
2649 }
2650
2651 if (mobilebackup2) {
2652 mobilebackup2_client_free(mobilebackup2);
2653 mobilebackup2 = NULL;
2654 }
2655
2656 if (afc) {
2657 afc_client_free(afc);
2658 afc = NULL;
2659 }
2660
2661 if (np) {
2662 np_client_free(np);
2663 np = NULL;
2664 }
2665
2666 idevice_free(device);
2667 device = NULL;
2668
2669 if (backup_password) {
2670 free(backup_password);
2671 }
2672
2673 if (udid) {
2674 free(udid);
2675 udid = NULL;
2676 }
2677 if (source_udid) {
2678 free(source_udid);
2679 source_udid = NULL;
2680 }
2681
2682 return result_code;
2683}
2684
diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c
new file mode 100644
index 0000000..8de6b22
--- /dev/null
+++ b/tools/idevicebtlogger.c
@@ -0,0 +1,458 @@
1/*
2 * idevicebt_packet_logger.c
3 * Capture Bluetooth HCI traffic to native PKLG or PCAP
4 *
5 * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved.
6 * Copyright (c) 2022 Matthias Ringwald, All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#define TOOL_NAME "idevicebtlogger"
28
29#include <stdio.h>
30#include <string.h>
31#include <errno.h>
32#include <signal.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <getopt.h>
36#include <assert.h>
37#include <fcntl.h>
38
39#ifdef WIN32
40#include <windows.h>
41#define sleep(x) Sleep(x*1000)
42#else
43#include <arpa/inet.h>
44#endif
45
46
47#include <libimobiledevice/libimobiledevice.h>
48#include <libimobiledevice/bt_packet_logger.h>
49
50typedef enum {
51 HCI_COMMAND = 0x00,
52 HCI_EVENT = 0x01,
53 SENT_ACL_DATA = 0x02,
54 RECV_ACL_DATA = 0x03,
55 SENT_SCO_DATA = 0x08,
56 RECV_SCO_DATA = 0x09,
57} PacketLoggerPacketType;
58
59static int quit_flag = 0;
60static int exit_on_disconnect = 0;
61
62static char* udid = NULL;
63static idevice_t device = NULL;
64static bt_packet_logger_client_t bt_packet_logger = NULL;
65static int use_network = 0;
66static char* out_filename = NULL;
67static char* log_format_string = NULL;
68static FILE * packetlogger_file = NULL;
69
70static enum {
71 LOG_FORMAT_PACKETLOGGER,
72 LOG_FORMAT_PCAP
73} log_format = LOG_FORMAT_PACKETLOGGER;
74
75const uint8_t pcap_file_header[] = {
76 // Magic Number
77 0xA1, 0xB2, 0xC3, 0xD4,
78 // Major / Minor Version
79 0x00, 0x02, 0x00, 0x04,
80 // Reserved1
81 0x00, 0x00, 0x00, 0x00,
82 // Reserved2
83 0x00, 0x00, 0x00, 0x00,
84 // Snaplen == max packet size - use 2kB (larger than any ACL)
85 0x00, 0x00, 0x08, 0x00,
86 // LinkType: DLT_BLUETOOTH_HCI_H4_WITH_PHDR
87 0x00, 0x00, 0x00, 201,
88};
89
90static uint32_t big_endian_read_32(const uint8_t * buffer, int position)
91{
92 return ((uint32_t) buffer[position+3]) | (((uint32_t)buffer[position+2]) << 8) | (((uint32_t)buffer[position+1]) << 16) | (((uint32_t) buffer[position]) << 24);
93}
94
95static void big_endian_store_32(uint8_t * buffer, uint16_t position, uint32_t value)
96{
97 uint16_t pos = position;
98 buffer[pos++] = (uint8_t)(value >> 24);
99 buffer[pos++] = (uint8_t)(value >> 16);
100 buffer[pos++] = (uint8_t)(value >> 8);
101 buffer[pos++] = (uint8_t)(value);
102}
103
104/**
105 * Callback from the packet logger service to handle packets and log to PacketLogger format
106 */
107static void bt_packet_logger_callback_packetlogger(uint8_t * data, uint16_t len, void *user_data)
108{
109 (void) fwrite(data, 1, len, packetlogger_file);
110}
111
112/**
113 * Callback from the packet logger service to handle packets and log to pcap
114 */
115static void bt_packet_logger_callback_pcap(uint8_t * data, uint16_t len, void *user_data)
116{
117 // check len
118 if (len < 13) {
119 return;
120 }
121
122 // parse packet header (ignore len field)
123 uint32_t ts_secs = big_endian_read_32(data, 4);
124 uint32_t ts_us = big_endian_read_32(data, 8);
125 uint8_t packet_type = data[12];
126 data += 13;
127 len -= 13;
128
129 // map PacketLogger packet type onto PCAP direction flag and hci_h4_type
130 uint8_t direction_in = 0;
131 uint8_t hci_h4_type = 0xff;
132 switch(packet_type) {
133 case HCI_COMMAND:
134 hci_h4_type = 0x01;
135 direction_in = 0;
136 break;
137 case SENT_ACL_DATA:
138 hci_h4_type = 0x02;
139 direction_in = 0;
140 break;
141 case RECV_ACL_DATA:
142 hci_h4_type = 0x02;
143 direction_in = 1;
144 break;
145 case SENT_SCO_DATA:
146 hci_h4_type = 0x03;
147 direction_in = 0;
148 break;
149 case RECV_SCO_DATA:
150 hci_h4_type = 0x03;
151 direction_in = 1;
152 break;
153 case HCI_EVENT:
154 hci_h4_type = 0x04;
155 direction_in = 1;
156 break;
157 default:
158 // unknown packet logger type, drop packet
159 return;
160 }
161
162 // setup pcap record header, 4 byte direction flag, 1 byte HCI H4 packet type, data
163 uint8_t pcap_record_header[21];
164 big_endian_store_32(pcap_record_header, 0, ts_secs); // Timestamp seconds
165 big_endian_store_32(pcap_record_header, 4, ts_us); // Timestamp microseconds
166 big_endian_store_32(pcap_record_header, 8, 4 + 1 + len); // Captured Packet Length
167 big_endian_store_32(pcap_record_header, 12, 4 + 1 + len); // Original Packet Length
168 big_endian_store_32(pcap_record_header, 16, direction_in); // Direction: Incoming = 1
169 pcap_record_header[20] = hci_h4_type;
170
171 // write header
172 (void) fwrite(pcap_record_header, 1, sizeof(pcap_record_header), packetlogger_file);
173
174 // write packet
175 (void) fwrite(data, 1, len, packetlogger_file);
176
177 // flush
178 (void) fflush(packetlogger_file);
179}
180
181/**
182 * Disable HCI log capture
183 */
184static void stop_logging(void)
185{
186 fflush(NULL);
187
188 if (bt_packet_logger) {
189 bt_packet_logger_client_free(bt_packet_logger);
190 bt_packet_logger = NULL;
191 }
192
193 if (device) {
194 idevice_free(device);
195 device = NULL;
196 }
197}
198
199/**
200 * Enable HCI log capture
201 */
202static int start_logging(void)
203{
204 idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
205 if (ret != IDEVICE_E_SUCCESS) {
206 fprintf(stderr, "Device with udid %s not found!?\n", udid);
207 return -1;
208 }
209
210 lockdownd_client_t lockdown = NULL;
211 lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
212 if (lerr != LOCKDOWN_E_SUCCESS) {
213 fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
214 idevice_free(device);
215 device = NULL;
216 return -1;
217 }
218
219 /* start bt_packet_logger service */
220 bt_packet_logger_client_start_service(device, &bt_packet_logger, TOOL_NAME);
221
222 /* start capturing bt_packet_logger */
223 void (*callback)(uint8_t * data, uint16_t len, void *user_data);
224 switch (log_format){
225 case LOG_FORMAT_PCAP:
226 callback = bt_packet_logger_callback_pcap;
227 break;
228 case LOG_FORMAT_PACKETLOGGER:
229 callback = bt_packet_logger_callback_packetlogger;
230 break;
231 default:
232 assert(0);
233 return 0;
234 }
235 bt_packet_logger_error_t serr = bt_packet_logger_start_capture(bt_packet_logger, callback, NULL);
236 if (serr != BT_PACKET_LOGGER_E_SUCCESS) {
237 fprintf(stderr, "ERROR: Unable to start capturing bt_packet_logger.\n");
238 bt_packet_logger_client_free(bt_packet_logger);
239 bt_packet_logger = NULL;
240 idevice_free(device);
241 device = NULL;
242 return -1;
243 }
244
245 fprintf(stderr, "[connected:%s]\n", udid);
246 fflush(stderr);
247
248 return 0;
249}
250
251/**
252 * Callback for device events
253 */
254static void device_event_cb(const idevice_event_t* event, void* userdata)
255{
256 if (use_network && event->conn_type != CONNECTION_NETWORK) {
257 return;
258 } else if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
259 return;
260 }
261 if (event->event == IDEVICE_DEVICE_ADD) {
262 if (!bt_packet_logger) {
263 if (!udid) {
264 udid = strdup(event->udid);
265 }
266 if (strcmp(udid, event->udid) == 0) {
267 if (start_logging() != 0) {
268 fprintf(stderr, "Could not start logger for udid %s\n", udid);
269 }
270 }
271 }
272 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
273 if (bt_packet_logger && (strcmp(udid, event->udid) == 0)) {
274 stop_logging();
275 fprintf(stderr, "[disconnected:%s]\n", udid);
276 if (exit_on_disconnect) {
277 quit_flag++;
278 }
279 }
280 }
281}
282
283/**
284 * signal handler function for cleaning up properly
285 */
286static void clean_exit(int sig)
287{
288 fprintf(stderr, "\nExiting...\n");
289 quit_flag++;
290}
291
292/**
293 * print usage information
294 */
295static void print_usage(int argc, char **argv, int is_error)
296{
297 char *name = NULL;
298 name = strrchr(argv[0], '/');
299 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] <FILE>\n", (name ? name + 1: argv[0]));
300 fprintf(is_error ? stderr : stdout,
301 "\n" \
302 "Capture HCI packets from a connected device.\n" \
303 "\n" \
304 "OPTIONS:\n" \
305 " -u, --udid UDID target specific device by UDID\n" \
306 " -n, --network connect to network device\n" \
307 " -f, --format FORMAT logging format: packetlogger (default) or pcap\n" \
308 " -x, --exit exit when device disconnects\n" \
309 " -h, --help prints usage information\n" \
310 " -d, --debug enable communication debugging\n" \
311 " -v, --version prints version information\n" \
312 "\n" \
313 "Homepage: <" PACKAGE_URL ">\n"
314 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
315 );
316}
317
318/**
319 * Program entry
320 */
321int main(int argc, char *argv[])
322{
323 int c = 0;
324 const struct option longopts[] = {
325 { "debug", no_argument, NULL, 'd' },
326 { "help", no_argument, NULL, 'h' },
327 { "udid", required_argument, NULL, 'u' },
328 { "format", required_argument, NULL, 'f' },
329 { "network", no_argument, NULL, 'n' },
330 { "exit", no_argument, NULL, 'x' },
331 { "version", no_argument, NULL, 'v' },
332 { NULL, 0, NULL, 0}
333 };
334
335 signal(SIGINT, clean_exit);
336 signal(SIGTERM, clean_exit);
337#ifndef WIN32
338 signal(SIGQUIT, clean_exit);
339 signal(SIGPIPE, SIG_IGN);
340#endif
341
342 while ((c = getopt_long(argc, argv, "dhu:f:nxv", longopts, NULL)) != -1) {
343 switch (c) {
344 case 'd':
345 idevice_set_debug_level(1);
346 break;
347 case 'u':
348 if (!*optarg) {
349 fprintf(stderr, "ERROR: UDID must not be empty!\n");
350 print_usage(argc, argv, 1);
351 return 2;
352 }
353 free(udid);
354 udid = strdup(optarg);
355 break;
356 case 'f':
357 if (!*optarg) {
358 fprintf(stderr, "ERROR: FORMAT must not be empty!\n");
359 print_usage(argc, argv, 1);
360 return 2;
361 }
362 free(log_format_string);
363 log_format_string = strdup(optarg);
364 break;
365 case 'n':
366 use_network = 1;
367 break;
368 case 'x':
369 exit_on_disconnect = 1;
370 break;
371 case 'h':
372 print_usage(argc, argv, 0);
373 return 0;
374 case 'v':
375 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
376 return 0;
377 default:
378 print_usage(argc, argv, 1);
379 return 2;
380 }
381 }
382
383 if (optind < argc) {
384 out_filename = argv[optind];
385 // printf("Output File: %s\n", out_filename);
386 }
387 else {
388 print_usage(argc, argv, 1);
389 return 2;
390 }
391
392 if (log_format_string != NULL){
393 if (strcmp("packetlogger", log_format_string) == 0){
394 log_format = LOG_FORMAT_PACKETLOGGER;
395 } else if (strcmp("pcap", log_format_string) == 0){
396 log_format = LOG_FORMAT_PCAP;
397 } else {
398 printf("Unknown logging format: '%s'\n", log_format_string);
399 print_usage(argc, argv, 1);
400 return 2;
401 }
402 }
403
404 int num = 0;
405 idevice_info_t *devices = NULL;
406 idevice_get_device_list_extended(&devices, &num);
407 idevice_device_list_extended_free(devices);
408 if (num == 0) {
409 if (!udid) {
410 fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
411 return -1;
412 } else {
413 fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
414 }
415 }
416
417 // support streaming to stdout
418 if (strcmp(out_filename, "-") == 0){
419 packetlogger_file = stdout;
420 } else {
421 packetlogger_file = fopen(out_filename, "wb");
422 }
423
424
425 if (packetlogger_file == NULL){
426 fprintf(stderr, "Failed to open file %s, errno = %d\n", out_filename, errno);
427 return -2;
428 }
429
430 switch (log_format){
431 case LOG_FORMAT_PCAP:
432 // printf("Output Format: PCAP\n");
433 // write PCAP file header
434 (void) fwrite(&pcap_file_header, 1, sizeof(pcap_file_header), packetlogger_file);
435 break;
436 case LOG_FORMAT_PACKETLOGGER:
437 printf("Output Format: PacketLogger\n");
438 break;
439 default:
440 assert(0);
441 return -2;
442 }
443 idevice_subscription_context_t context = NULL;
444 idevice_events_subscribe(&context, device_event_cb, NULL);
445
446 while (!quit_flag) {
447 sleep(1);
448 }
449
450 idevice_events_unsubscribe(context);
451 stop_logging();
452
453 fclose(packetlogger_file);
454
455 free(udid);
456
457 return 0;
458}
diff --git a/tools/idevicecrashreport.c b/tools/idevicecrashreport.c
new file mode 100644
index 0000000..09bd537
--- /dev/null
+++ b/tools/idevicecrashreport.c
@@ -0,0 +1,529 @@
1/*
2 * idevicecrashreport.c
3 * Simple utility to move crash reports from a device to a local directory.
4 *
5 * Copyright (c) 2014 Martin Szulecki. All Rights Reserved.
6 * Copyright (c) 2014 Nikias Bassen. All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "idevicecrashreport"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <getopt.h>
34#ifndef WIN32
35#include <signal.h>
36#endif
37#include <libimobiledevice-glue/utils.h>
38
39#include <libimobiledevice/libimobiledevice.h>
40#include <libimobiledevice/lockdown.h>
41#include <libimobiledevice/service.h>
42#include <libimobiledevice/afc.h>
43#include <plist/plist.h>
44
45#ifdef WIN32
46#include <windows.h>
47#define S_IFLNK S_IFREG
48#define S_IFSOCK S_IFREG
49#endif
50
51#define CRASH_REPORT_MOVER_SERVICE "com.apple.crashreportmover"
52#define CRASH_REPORT_COPY_MOBILE_SERVICE "com.apple.crashreportcopymobile"
53
54const char* target_directory = NULL;
55static int extract_raw_crash_reports = 0;
56static int keep_crash_reports = 0;
57
58static int file_exists(const char* path)
59{
60 struct stat tst;
61#ifdef WIN32
62 return (stat(path, &tst) == 0);
63#else
64 return (lstat(path, &tst) == 0);
65#endif
66}
67
68static int extract_raw_crash_report(const char* filename)
69{
70 int res = 0;
71 plist_t report = NULL;
72 char* raw = NULL;
73 char* raw_filename = strdup(filename);
74
75 /* create filename with '.crash' extension */
76 char* p = strrchr(raw_filename, '.');
77 if ((p == NULL) || (strcmp(p, ".plist") != 0)) {
78 free(raw_filename);
79 return res;
80 }
81 strcpy(p, ".crash");
82
83 /* read plist crash report */
84 if (plist_read_from_file(filename, &report, NULL)) {
85 plist_t description_node = plist_dict_get_item(report, "description");
86 if (description_node && plist_get_node_type(description_node) == PLIST_STRING) {
87 plist_get_string_val(description_node, &raw);
88
89 if (raw != NULL) {
90 /* write file */
91 buffer_write_to_filename(raw_filename, raw, strlen(raw));
92 free(raw);
93 res = 1;
94 }
95 }
96 }
97
98 if (report)
99 plist_free(report);
100
101 if (raw_filename)
102 free(raw_filename);
103
104 return res;
105}
106
107static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char* device_directory, const char* host_directory, const char* filename_filter)
108{
109 afc_error_t afc_error;
110 int k;
111 int res = -1;
112 int crash_report_count = 0;
113 uint64_t handle;
114 char source_filename[512];
115 char target_filename[512];
116
117 if (!afc)
118 return res;
119
120 char** list = NULL;
121 afc_error = afc_read_directory(afc, device_directory, &list);
122 if (afc_error != AFC_E_SUCCESS) {
123 fprintf(stderr, "ERROR: Could not read device directory '%s'\n", device_directory);
124 return res;
125 }
126
127 /* ensure we have a trailing slash */
128 strcpy(source_filename, device_directory);
129 if (source_filename[strlen(source_filename)-1] != '/') {
130 strcat(source_filename, "/");
131 }
132 int device_directory_length = strlen(source_filename);
133
134 /* ensure we have a trailing slash */
135 strcpy(target_filename, host_directory);
136 if (target_filename[strlen(target_filename)-1] != '/') {
137 strcat(target_filename, "/");
138 }
139 int host_directory_length = strlen(target_filename);
140
141 /* loop over file entries */
142 for (k = 0; list[k]; k++) {
143 if (!strcmp(list[k], ".") || !strcmp(list[k], "..")) {
144 continue;
145 }
146
147 char **fileinfo = NULL;
148 struct stat stbuf;
149 memset(&stbuf, '\0', sizeof(struct stat));
150
151 /* assemble absolute source filename */
152 strcpy(((char*)source_filename) + device_directory_length, list[k]);
153
154 /* assemble absolute target filename */
155#ifdef WIN32
156 /* replace every ':' with '-' since ':' is an illegal character for file names in windows */
157 char* current_pos = strchr(list[k], ':');
158 while (current_pos) {
159 *current_pos = '-';
160 current_pos = strchr(current_pos, ':');
161 }
162#endif
163 char* p = strrchr(list[k], '.');
164 if (p != NULL && !strncmp(p, ".synced", 7)) {
165 /* make sure to strip ".synced" extension as seen on iOS 5 */
166 size_t newlen = p - list[k];
167 strncpy(((char*)target_filename) + host_directory_length, list[k], newlen);
168 target_filename[host_directory_length + newlen] = '\0';
169 } else {
170 strcpy(((char*)target_filename) + host_directory_length, list[k]);
171 }
172
173 /* get file information */
174 afc_get_file_info(afc, source_filename, &fileinfo);
175 if (!fileinfo) {
176 printf("Failed to read information for '%s'. Skipping...\n", source_filename);
177 continue;
178 }
179
180 /* parse file information */
181 int i;
182 for (i = 0; fileinfo[i]; i+=2) {
183 if (!strcmp(fileinfo[i], "st_size")) {
184 stbuf.st_size = atoll(fileinfo[i+1]);
185 } else if (!strcmp(fileinfo[i], "st_ifmt")) {
186 if (!strcmp(fileinfo[i+1], "S_IFREG")) {
187 stbuf.st_mode = S_IFREG;
188 } else if (!strcmp(fileinfo[i+1], "S_IFDIR")) {
189 stbuf.st_mode = S_IFDIR;
190 } else if (!strcmp(fileinfo[i+1], "S_IFLNK")) {
191 stbuf.st_mode = S_IFLNK;
192 } else if (!strcmp(fileinfo[i+1], "S_IFBLK")) {
193 stbuf.st_mode = S_IFBLK;
194 } else if (!strcmp(fileinfo[i+1], "S_IFCHR")) {
195 stbuf.st_mode = S_IFCHR;
196 } else if (!strcmp(fileinfo[i+1], "S_IFIFO")) {
197 stbuf.st_mode = S_IFIFO;
198 } else if (!strcmp(fileinfo[i+1], "S_IFSOCK")) {
199 stbuf.st_mode = S_IFSOCK;
200 }
201 } else if (!strcmp(fileinfo[i], "st_nlink")) {
202 stbuf.st_nlink = atoi(fileinfo[i+1]);
203 } else if (!strcmp(fileinfo[i], "st_mtime")) {
204 stbuf.st_mtime = (time_t)(atoll(fileinfo[i+1]) / 1000000000);
205 } else if (!strcmp(fileinfo[i], "LinkTarget")) {
206 /* report latest crash report filename */
207 printf("Link: %s\n", (char*)target_filename + strlen(target_directory));
208
209 /* remove any previous symlink */
210 if (file_exists(target_filename)) {
211 remove(target_filename);
212 }
213
214#ifndef WIN32
215 /* use relative filename */
216 char* b = strrchr(fileinfo[i+1], '/');
217 if (b == NULL) {
218 b = fileinfo[i+1];
219 } else {
220 b++;
221 }
222
223 /* create a symlink pointing to latest log */
224 if (symlink(b, target_filename) < 0) {
225 fprintf(stderr, "Can't create symlink to %s\n", b);
226 }
227#endif
228
229 if (!keep_crash_reports)
230 afc_remove_path(afc, source_filename);
231
232 res = 0;
233 }
234 }
235
236 /* free file information */
237 afc_dictionary_free(fileinfo);
238
239 /* recurse into child directories */
240 if (S_ISDIR(stbuf.st_mode)) {
241#ifdef WIN32
242 mkdir(target_filename);
243#else
244 mkdir(target_filename, 0755);
245#endif
246 res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename, filename_filter);
247
248 /* remove directory from device */
249 if (!keep_crash_reports)
250 afc_remove_path(afc, source_filename);
251 } else if (S_ISREG(stbuf.st_mode)) {
252 if (filename_filter != NULL && strstr(source_filename, filename_filter) == NULL) {
253 continue;
254 }
255
256 /* copy file to host */
257 afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle);
258 if(afc_error != AFC_E_SUCCESS) {
259 if (afc_error == AFC_E_OBJECT_NOT_FOUND) {
260 continue;
261 }
262 fprintf(stderr, "Unable to open device file '%s' (%d). Skipping...\n", source_filename, afc_error);
263 continue;
264 }
265
266 FILE* output = fopen(target_filename, "wb");
267 if(output == NULL) {
268 fprintf(stderr, "Unable to open local file '%s'. Skipping...\n", target_filename);
269 afc_file_close(afc, handle);
270 continue;
271 }
272
273 printf("%s: %s\n", (keep_crash_reports ? "Copy": "Move") , (char*)target_filename + strlen(target_directory));
274
275 uint32_t bytes_read = 0;
276 uint32_t bytes_total = 0;
277 unsigned char data[0x1000];
278
279 afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
280 while(afc_error == AFC_E_SUCCESS && bytes_read > 0) {
281 fwrite(data, 1, bytes_read, output);
282 bytes_total += bytes_read;
283 afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
284 }
285 afc_file_close(afc, handle);
286 fclose(output);
287
288 if ((uint32_t)stbuf.st_size != bytes_total) {
289 fprintf(stderr, "File size mismatch. Skipping...\n");
290 continue;
291 }
292
293 /* remove file from device */
294 if (!keep_crash_reports) {
295 afc_remove_path(afc, source_filename);
296 }
297
298 /* extract raw crash information into separate '.crash' file */
299 if (extract_raw_crash_reports) {
300 extract_raw_crash_report(target_filename);
301 }
302
303 crash_report_count++;
304
305 res = 0;
306 }
307 }
308 afc_dictionary_free(list);
309
310 /* no reports, no error */
311 if (crash_report_count == 0)
312 res = 0;
313
314 return res;
315}
316
317static void print_usage(int argc, char **argv, int is_error)
318{
319 char *name = strrchr(argv[0], '/');
320 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
321 fprintf(is_error ? stderr : stdout,
322 "\n"
323 "Move crash reports from device to a local DIRECTORY.\n"
324 "\n"
325 "OPTIONS:\n"
326 " -u, --udid UDID target specific device by UDID\n"
327 " -n, --network connect to network device\n"
328 " -e, --extract extract raw crash report into separate '.crash' file\n"
329 " -k, --keep copy but do not remove crash reports from device\n"
330 " -d, --debug enable communication debugging\n"
331 " -f, --filter NAME filter crash reports by NAME (case sensitive)\n"
332 " -h, --help prints usage information\n"
333 " -v, --version prints version information\n"
334 "\n"
335 "Homepage: <" PACKAGE_URL ">\n"
336 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
337 );
338}
339
340int main(int argc, char* argv[])
341{
342 idevice_t device = NULL;
343 lockdownd_client_t lockdownd = NULL;
344 afc_client_t afc = NULL;
345
346 idevice_error_t device_error = IDEVICE_E_SUCCESS;
347 lockdownd_error_t lockdownd_error = LOCKDOWN_E_SUCCESS;
348 afc_error_t afc_error = AFC_E_SUCCESS;
349
350 const char* udid = NULL;
351 int use_network = 0;
352 const char* filename_filter = NULL;
353
354 int c = 0;
355 const struct option longopts[] = {
356 { "debug", no_argument, NULL, 'd' },
357 { "help", no_argument, NULL, 'h' },
358 { "udid", required_argument, NULL, 'u' },
359 { "network", no_argument, NULL, 'n' },
360 { "version", no_argument, NULL, 'v' },
361 { "filter", required_argument, NULL, 'f' },
362 { "extract", no_argument, NULL, 'e' },
363 { "keep", no_argument, NULL, 'k' },
364 { NULL, 0, NULL, 0}
365 };
366
367#ifndef WIN32
368 signal(SIGPIPE, SIG_IGN);
369#endif
370
371 /* parse cmdline args */
372 while ((c = getopt_long(argc, argv, "dhu:nvf:ek", longopts, NULL)) != -1) {
373 switch (c) {
374 case 'd':
375 idevice_set_debug_level(1);
376 break;
377 case 'u':
378 if (!*optarg) {
379 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
380 print_usage(argc, argv, 1);
381 return 2;
382 }
383 udid = optarg;
384 break;
385 case 'n':
386 use_network = 1;
387 break;
388 case 'h':
389 print_usage(argc, argv, 0);
390 return 0;
391 case 'v':
392 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
393 return 0;
394 case 'f':
395 if (!*optarg) {
396 fprintf(stderr, "ERROR: filter argument must not be empty!\n");
397 print_usage(argc, argv, 1);
398 return 2;
399 }
400 filename_filter = optarg;
401 break;
402 case 'e':
403 extract_raw_crash_reports = 1;
404 break;
405 case 'k':
406 keep_crash_reports = 1;
407 break;
408 default:
409 print_usage(argc, argv, 1);
410 return 2;
411 }
412 }
413 argc -= optind;
414 argv += optind;
415
416 /* ensure a target directory was supplied */
417 if (!argv[0]) {
418 fprintf(stderr, "ERROR: missing target directory.\n");
419 print_usage(argc+optind, argv-optind, 1);
420 return 2;
421 }
422 target_directory = argv[0];
423
424 /* check if target directory exists */
425 if (!file_exists(target_directory)) {
426 fprintf(stderr, "ERROR: Directory '%s' does not exist.\n", target_directory);
427 return 1;
428 }
429
430 device_error = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
431 if (device_error != IDEVICE_E_SUCCESS) {
432 if (udid) {
433 printf("No device found with udid %s.\n", udid);
434 } else {
435 printf("No device found.\n");
436 }
437 return -1;
438 }
439
440 lockdownd_error = lockdownd_client_new_with_handshake(device, &lockdownd, TOOL_NAME);
441 if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
442 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lockdownd_error);
443 idevice_free(device);
444 return -1;
445 }
446
447 /* start crash log mover service */
448 lockdownd_service_descriptor_t service = NULL;
449 lockdownd_error = lockdownd_start_service(lockdownd, CRASH_REPORT_MOVER_SERVICE, &service);
450 if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
451 fprintf(stderr, "ERROR: Could not start service %s: %s\n", CRASH_REPORT_MOVER_SERVICE, lockdownd_strerror(lockdownd_error));
452 lockdownd_client_free(lockdownd);
453 idevice_free(device);
454 return -1;
455 }
456
457 /* trigger move operation on device */
458 service_client_t svcmove = NULL;
459 service_error_t service_error = service_client_new(device, service, &svcmove);
460 lockdownd_service_descriptor_free(service);
461 service = NULL;
462 if (service_error != SERVICE_E_SUCCESS) {
463 lockdownd_client_free(lockdownd);
464 idevice_free(device);
465 return -1;
466 }
467
468 /* read "ping" message which indicates the crash logs have been moved to a safe harbor */
469 char *ping = malloc(4);
470 memset(ping, '\0', 4);
471 int attempts = 0;
472 while ((strncmp(ping, "ping", 4) != 0) && (attempts < 10)) {
473 uint32_t bytes = 0;
474 service_error = service_receive_with_timeout(svcmove, ping, 4, &bytes, 2000);
475 if (service_error == SERVICE_E_SUCCESS || service_error == SERVICE_E_TIMEOUT) {
476 attempts++;
477 continue;
478 }
479
480 fprintf(stderr, "ERROR: Crash logs could not be moved. Connection interrupted (%d).\n", service_error);
481 break;
482 }
483 service_client_free(svcmove);
484 free(ping);
485
486 if (device_error != IDEVICE_E_SUCCESS || attempts > 10) {
487 fprintf(stderr, "ERROR: Failed to receive ping message from crash report mover.\n");
488 lockdownd_client_free(lockdownd);
489 idevice_free(device);
490 return -1;
491 }
492
493 lockdownd_error = lockdownd_start_service(lockdownd, CRASH_REPORT_COPY_MOBILE_SERVICE, &service);
494 if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
495 fprintf(stderr, "ERROR: Could not start service %s: %s\n", CRASH_REPORT_COPY_MOBILE_SERVICE, lockdownd_strerror(lockdownd_error));
496 lockdownd_client_free(lockdownd);
497 idevice_free(device);
498 return -1;
499 }
500 lockdownd_client_free(lockdownd);
501
502 afc = NULL;
503 afc_error = afc_client_new(device, service, &afc);
504 if(afc_error != AFC_E_SUCCESS) {
505 lockdownd_client_free(lockdownd);
506 idevice_free(device);
507 return -1;
508 }
509
510 if (service) {
511 lockdownd_service_descriptor_free(service);
512 service = NULL;
513 }
514
515 /* recursively copy crash reports from the device to a local directory */
516 if (afc_client_copy_and_remove_crash_reports(afc, ".", target_directory, filename_filter) < 0) {
517 fprintf(stderr, "ERROR: Failed to get crash reports from device.\n");
518 afc_client_free(afc);
519 idevice_free(device);
520 return -1;
521 }
522
523 printf("Done.\n");
524
525 afc_client_free(afc);
526 idevice_free(device);
527
528 return 0;
529}
diff --git a/tools/idevicedate.c b/tools/idevicedate.c
index e4e0a33..d05f63e 100644
--- a/tools/idevicedate.c
+++ b/tools/idevicedate.c
@@ -1,6 +1,6 @@
1/* 1/*
2 * idevicedate.c 2 * idevicedate.c
3 * Simple utility to get and set the clock on an iDevice 3 * Simple utility to get and set the clock on a device
4 * 4 *
5 * Copyright (c) 2011 Martin Szulecki All Rights Reserved. 5 * Copyright (c) 2011 Martin Szulecki All Rights Reserved.
6 * 6 *
@@ -8,176 +8,249 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 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 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 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define TOOL_NAME "idevicedate"
27
22#include <stdio.h> 28#include <stdio.h>
23#include <stdlib.h> 29#include <stdlib.h>
24#include <string.h> 30#include <string.h>
31#include <getopt.h>
25#include <time.h> 32#include <time.h>
26#if HAVE_LANGINFO_CODESET 33#if HAVE_LANGINFO_CODESET
27# include <langinfo.h> 34#include <langinfo.h>
35#endif
36#ifndef WIN32
37#include <signal.h>
28#endif 38#endif
29 39
30#include <libimobiledevice/libimobiledevice.h> 40#include <libimobiledevice/libimobiledevice.h>
31#include <libimobiledevice/lockdown.h> 41#include <libimobiledevice/lockdown.h>
32 42
33#ifdef _DATE_FMT 43#ifdef _DATE_FMT
34# define DATE_FMT_LANGINFO() nl_langinfo (_DATE_FMT) 44#define DATE_FMT_LANGINFO nl_langinfo (_DATE_FMT)
45#else
46#ifdef WIN32
47#define DATE_FMT_LANGINFO "%a %b %#d %H:%M:%S %Z %Y"
35#else 48#else
36# define DATE_FMT_LANGINFO() "" 49#define DATE_FMT_LANGINFO "%a %b %e %H:%M:%S %Z %Y"
50#endif
37#endif 51#endif
38 52
39static void print_usage(int argc, char **argv) 53static void print_usage(int argc, char **argv, int is_error)
40{ 54{
41 char *name = NULL; 55 char *name = strrchr(argv[0], '/');
42 56 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
43 name = strrchr(argv[0], '/'); 57 fprintf(is_error ? stderr : stdout,
44 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0])); 58 "\n"
45 printf("Display the current date or set it on an iDevice.\n\n"); 59 "Display the current date or set it on a device.\n"
46 printf(" -d, --debug\t\tenable communication debugging\n"); 60 "\n"
47 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); 61 "NOTE: Setting the time on iOS 6 and later is only supported\n"
48 printf(" -s, --set TIMESTAMP\tset UTC time described by TIMESTAMP\n"); 62 " in the setup wizard screens before device activation.\n"
49 printf(" -c, --sync\t\tset time of device to current system time\n"); 63 "\n"
50 printf(" -h, --help\t\tprints usage information\n"); 64 "OPTIONS:\n"
51 printf("\n"); 65 " -u, --udid UDID target specific device by UDID\n"
66 " -n, --network connect to network device\n"
67 " -s, --set TIMESTAMP set UTC time described by TIMESTAMP\n"
68 " -c, --sync set time of device to current system time\n"
69 " -d, --debug enable communication debugging\n"
70 " -h, --help prints usage information\n"
71 " -v, --version prints version information\n"
72 "\n"
73 "Homepage: <" PACKAGE_URL ">\n"
74 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
75 );
52} 76}
53 77
54int main(int argc, char *argv[]) 78int main(int argc, char *argv[])
55{ 79{
56 lockdownd_client_t client = NULL; 80 lockdownd_client_t client = NULL;
57 idevice_t phone = NULL; 81 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
82 idevice_t device = NULL;
58 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 83 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
59 int i; 84 const char* udid = NULL;
60 char uuid[41]; 85 int use_network = 0;
61 time_t setdate = 0; 86 time_t setdate = 0;
62 plist_t node = NULL; 87 plist_t node = NULL;
63 uuid[0] = 0; 88 int node_type = -1;
64 uint64_t datetime = 0; 89 uint64_t datetime = 0;
65 time_t rawtime; 90 time_t rawtime;
66 struct tm * tmp; 91 struct tm * tmp;
67 char const *format = NULL;
68 char buffer[80]; 92 char buffer[80];
93 int result = 0;
94
95 int c = 0;
96 const struct option longopts[] = {
97 { "debug", no_argument, NULL, 'd' },
98 { "help", no_argument, NULL, 'h' },
99 { "udid", required_argument, NULL, 'u' },
100 { "network", no_argument, NULL, 'n' },
101 { "version", no_argument, NULL, 'v' },
102 { "set", required_argument, NULL, 's' },
103 { "sync", no_argument, NULL, 'c' },
104 { NULL, 0, NULL, 0}
105 };
69 106
107#ifndef WIN32
108 signal(SIGPIPE, SIG_IGN);
109#endif
70 /* parse cmdline args */ 110 /* parse cmdline args */
71 for (i = 1; i < argc; i++) { 111 while ((c = getopt_long(argc, argv, "dhu:nvs:c", longopts, NULL)) != -1) {
72 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 112 switch (c) {
113 case 'd':
73 idevice_set_debug_level(1); 114 idevice_set_debug_level(1);
74 continue; 115 break;
75 } 116 case 'u':
76 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 117 if (!*optarg) {
77 i++; 118 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
78 if (!argv[i] || (strlen(argv[i]) != 40)) { 119 print_usage(argc, argv, 1);
79 print_usage(argc, argv); 120 return 2;
80 return 0;
81 } 121 }
82 strcpy(uuid, argv[i]); 122 udid = optarg;
83 continue; 123 break;
84 } 124 case 'n':
85 else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--set")) { 125 use_network = 1;
86 i++; 126 break;
87 if (!argv[i] || (strlen(argv[i]) <= 1)) { 127 case 'h':
88 print_usage(argc, argv); 128 print_usage(argc, argv, 0);
89 return 0; 129 return 0;
130 case 'v':
131 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
132 return 0;
133 case 's':
134 if (!*optarg) {
135 fprintf(stderr, "ERROR: set argument must not be empty!\n");
136 print_usage(argc, argv, 1);
137 return 2;
90 } 138 }
91 setdate = atoi(argv[i]); 139 setdate = atoi(optarg);
92 if (setdate == 0) { 140 if (setdate == 0) {
93 printf("ERROR: Invalid timestamp value.\n"); 141 fprintf(stderr, "ERROR: Invalid timestamp value.\n");
94 print_usage(argc, argv); 142 print_usage(argc, argv, 1);
95 return 0; 143 return 0;
96 } 144 }
97 continue; 145 break;
98 } 146 case 'c':
99 else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--sync")) {
100 i++;
101 /* get current time */ 147 /* get current time */
102 setdate = time(NULL); 148 setdate = time(NULL);
103 /* convert it to local time which sets timezone/daylight variables */ 149 /* convert it to local time which sets timezone/daylight variables */
104 tmp = localtime(&setdate); 150 tmp = localtime(&setdate);
105 /* recalculate to make it UTC */ 151 /* recalculate to make it UTC */
106 setdate = mktime(tmp); 152 setdate = mktime(tmp);
107 continue; 153 break;
108 } 154 default:
109 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 155 print_usage(argc, argv, 1);
110 print_usage(argc, argv); 156 return 2;
111 return 0;
112 }
113 else {
114 print_usage(argc, argv);
115 return 0;
116 } 157 }
117 } 158 }
159 argc -= optind;
160 argv += optind;
118 161
119 /* determine a date format */ 162 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
120 if (!format) { 163 if (ret != IDEVICE_E_SUCCESS) {
121 format = DATE_FMT_LANGINFO (); 164 if (udid) {
122 if (!*format) { 165 printf("No device found with udid %s.\n", udid);
123 format = "%a %b %e %H:%M:%S %Z %Y"; 166 } else {
167 printf("No device found.\n");
124 } 168 }
169 return -1;
125 } 170 }
126 171
127 if (uuid[0] != 0) { 172 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
128 ret = idevice_new(&phone, uuid); 173 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
129 if (ret != IDEVICE_E_SUCCESS) { 174 result = -1;
130 printf("No device found with uuid %s, is it plugged in?\n", uuid); 175 goto cleanup;
131 return -1;
132 }
133 } 176 }
134 else 177
135 { 178 if(lockdownd_get_value(client, NULL, "TimeIntervalSince1970", &node) != LOCKDOWN_E_SUCCESS) {
136 ret = idevice_new(&phone, NULL); 179 fprintf(stderr, "ERROR: Unable to retrieve 'TimeIntervalSince1970' node from device.\n");
137 if (ret != IDEVICE_E_SUCCESS) { 180 result = -1;
138 printf("No device found, is it plugged in?\n"); 181 goto cleanup;
139 return -1;
140 }
141 } 182 }
142 183
143 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicedate")) { 184 if (node == NULL) {
144 idevice_free(phone); 185 fprintf(stderr, "ERROR: Empty node for 'TimeIntervalSince1970' received.\n");
145 return -1; 186 result = -1;
187 goto cleanup;
146 } 188 }
147 189
190 node_type = plist_get_node_type(node);
191
148 /* get or set? */ 192 /* get or set? */
149 if (setdate == 0) { 193 if (setdate == 0) {
150 /* get time value from device */ 194 /* get time value from device */
151 if(lockdownd_get_value(client, NULL, "TimeIntervalSince1970", &node) == LOCKDOWN_E_SUCCESS) { 195 switch (node_type) {
152 if (node) { 196 case PLIST_UINT:
153 plist_get_uint_val(node, &datetime); 197 plist_get_uint_val(node, &datetime);
154 plist_free(node); 198 break;
155 node = NULL; 199 case PLIST_REAL:
200 {
201 double rv = 0;
202 plist_get_real_val(node, &rv);
203 datetime = rv;
204 }
205 break;
206 default:
207 fprintf(stderr, "ERROR: Unexpected node type for 'TimeIntervalSince1970'\n");
208 break;
209 }
210 plist_free(node);
211 node = NULL;
156 212
157 /* date/time calculations */ 213 /* date/time calculations */
158 rawtime = (time_t)datetime; 214 rawtime = (time_t)datetime;
159 tmp = localtime(&rawtime); 215 tmp = localtime(&rawtime);
160 216
161 /* finally we format and print the current date */ 217 /* finally we format and print the current date */
162 strftime(buffer, 80, format, tmp); 218 strftime(buffer, 80, DATE_FMT_LANGINFO, tmp);
163 puts(buffer); 219 puts(buffer);
164 }
165 }
166 } else { 220 } else {
167 datetime = setdate; 221 datetime = setdate;
168 222
169 if(lockdownd_set_value(client, NULL, "TimeIntervalSince1970", plist_new_uint(datetime)) == LOCKDOWN_E_SUCCESS) { 223 plist_free(node);
224 node = NULL;
225
226 switch (node_type) {
227 case PLIST_UINT:
228 node = plist_new_uint(datetime);
229 break;
230 case PLIST_REAL:
231 node = plist_new_real((double)datetime);
232 break;
233 default:
234 fprintf(stderr, "ERROR: Unexpected node type for 'TimeIntervalSince1970'\n");
235 break;
236 }
237
238 if(lockdownd_set_value(client, NULL, "TimeIntervalSince1970", node) == LOCKDOWN_E_SUCCESS) {
170 tmp = localtime(&setdate); 239 tmp = localtime(&setdate);
171 strftime(buffer, 80, format, tmp); 240 strftime(buffer, 80, DATE_FMT_LANGINFO, tmp);
172 puts(buffer); 241 puts(buffer);
173 } else { 242 } else {
174 printf("ERROR: Failed to set date on device.\n"); 243 printf("ERROR: Failed to set date on device.\n");
175 } 244 }
245 node = NULL;
176 } 246 }
177 247
178 lockdownd_client_free(client); 248cleanup:
179 idevice_free(phone); 249 if (client)
250 lockdownd_client_free(client);
180 251
181 return 0; 252 if (device)
182} 253 idevice_free(device);
183 254
255 return result;
256}
diff --git a/tools/idevicedebug.c b/tools/idevicedebug.c
new file mode 100644
index 0000000..36c594e
--- /dev/null
+++ b/tools/idevicedebug.c
@@ -0,0 +1,625 @@
1/*
2 * idevicedebug.c
3 * Interact with the debugserver service of a device.
4 *
5 * Copyright (c) 2014-2015 Martin Szulecki 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
26#define TOOL_NAME "idevicedebug"
27
28#include <signal.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33#include <unistd.h>
34#include <libgen.h>
35#include <getopt.h>
36
37#ifdef WIN32
38#include <windows.h>
39#define sleep(x) Sleep(x*1000)
40#endif
41
42#include <libimobiledevice/installation_proxy.h>
43#include <libimobiledevice/libimobiledevice.h>
44#include <libimobiledevice/debugserver.h>
45#include <plist/plist.h>
46#include "common/debug.h"
47
48static int debug_level = 0;
49
50#define log_debug(...) if (debug_level > 0) { printf(__VA_ARGS__); fputc('\n', stdout); }
51
52enum cmd_mode {
53 CMD_NONE = 0,
54 CMD_RUN,
55 CMD_KILL
56};
57
58static int quit_flag = 0;
59
60static void on_signal(int sig)
61{
62 fprintf(stderr, "Exiting...\n");
63 quit_flag++;
64}
65
66static int cancel_receive()
67{
68 return quit_flag;
69}
70
71static instproxy_error_t instproxy_client_get_object_by_key_from_info_dictionary_for_bundle_identifier(instproxy_client_t client, const char* appid, const char* key, plist_t* node)
72{
73 if (!client || !appid || !key)
74 return INSTPROXY_E_INVALID_ARG;
75
76 plist_t apps = NULL;
77
78 // create client options for any application types
79 plist_t client_opts = instproxy_client_options_new();
80 instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL);
81
82 // only return attributes we need
83 instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "CFBundleExecutable", key, NULL);
84
85 // only query for specific appid
86 const char* appids[] = {appid, NULL};
87
88 // query device for list of apps
89 instproxy_error_t ierr = instproxy_lookup(client, appids, client_opts, &apps);
90
91 instproxy_client_options_free(client_opts);
92
93 if (ierr != INSTPROXY_E_SUCCESS) {
94 return ierr;
95 }
96
97 plist_t app_found = plist_access_path(apps, 1, appid);
98 if (!app_found) {
99 if (apps)
100 plist_free(apps);
101 *node = NULL;
102 return INSTPROXY_E_OP_FAILED;
103 }
104
105 plist_t object = plist_dict_get_item(app_found, key);
106 if (object) {
107 *node = plist_copy(object);
108 } else {
109 log_debug("key %s not found", key);
110 return INSTPROXY_E_OP_FAILED;
111 }
112
113 plist_free(apps);
114
115 return INSTPROXY_E_SUCCESS;
116}
117
118static debugserver_error_t debugserver_client_handle_response(debugserver_client_t client, char** response, int* exit_status)
119{
120 debugserver_error_t dres = DEBUGSERVER_E_SUCCESS;
121 char* o = NULL;
122 char* r = *response;
123
124 /* Documentation of response codes can be found here:
125 https://github.com/llvm/llvm-project/blob/4fe839ef3a51e0ea2e72ea2f8e209790489407a2/lldb/docs/lldb-gdb-remote.txt#L1269
126 */
127
128 if (r[0] == 'O') {
129 /* stdout/stderr */
130 debugserver_decode_string(r + 1, strlen(r) - 1, &o);
131 printf("%s", o);
132 fflush(stdout);
133 } else if (r[0] == 'T') {
134 /* thread stopped information */
135 log_debug("Thread stopped. Details:\n%s", r + 1);
136 if (exit_status != NULL) {
137 /* "Thread stopped" seems to happen when assert() fails.
138 Use bash convention where signals cause an exit
139 status of 128 + signal
140 */
141 *exit_status = 128 + SIGABRT;
142 }
143 /* Break out of the loop. */
144 dres = DEBUGSERVER_E_UNKNOWN_ERROR;
145 } else if (r[0] == 'E') {
146 printf("ERROR: %s\n", r + 1);
147 } else if (r[0] == 'W' || r[0] == 'X') {
148 /* process exited */
149 debugserver_decode_string(r + 1, strlen(r) - 1, &o);
150 if (o != NULL) {
151 printf("Exit %s: %u\n", (r[0] == 'W' ? "status" : "due to signal"), o[0]);
152 if (exit_status != NULL) {
153 /* Use bash convention where signals cause an
154 exit status of 128 + signal
155 */
156 *exit_status = o[0] + (r[0] == 'W' ? 0 : 128);
157 }
158 } else {
159 log_debug("Unable to decode exit status from %s", r);
160 dres = DEBUGSERVER_E_UNKNOWN_ERROR;
161 }
162 } else if (r && strlen(r) == 0) {
163 log_debug("empty response");
164 } else {
165 log_debug("ERROR: unhandled response '%s'", r);
166 }
167
168 if (o != NULL) {
169 free(o);
170 o = NULL;
171 }
172
173 free(*response);
174 *response = NULL;
175 return dres;
176}
177
178static void print_usage(int argc, char **argv, int is_error)
179{
180 char *name = strrchr(argv[0], '/');
181 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
182 fprintf(is_error ? stderr : stdout,
183 "\n"
184 "Interact with the debugserver service of a device.\n"
185 "\n"
186 "Where COMMAND is one of:\n"
187 " run BUNDLEID [ARGS...] run app with BUNDLEID and optional ARGS on device.\n"
188 " kill BUNDLEID kill app with BUNDLEID\n"
189 "\n"
190 "The following OPTIONS are accepted:\n"
191 " -u, --udid UDID target specific device by UDID\n"
192 " -n, --network connect to network device\n"
193 " --detach detach from app after launch, keeping it running\n"
194 " -e, --env NAME=VALUE set environment variable NAME to VALUE\n"
195 " -d, --debug enable communication debugging\n"
196 " -h, --help prints usage information\n"
197 " -v, --version prints version information\n"
198 "\n"
199 "Homepage: <" PACKAGE_URL ">\n"
200 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
201 );
202}
203
204int main(int argc, char *argv[])
205{
206 int res = -1;
207 idevice_t device = NULL;
208 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
209 instproxy_client_t instproxy_client = NULL;
210 debugserver_client_t debugserver_client = NULL;
211 int i;
212 int cmd = CMD_NONE;
213 const char* udid = NULL;
214 int use_network = 0;
215 int detach_after_start = 0;
216 const char* bundle_identifier = NULL;
217 char* path = NULL;
218 char* working_directory = NULL;
219 char **newlist = NULL;
220 char** environment = NULL;
221 int environment_index = 0;
222 int environment_count = 0;
223 char* response = NULL;
224 debugserver_command_t command = NULL;
225 debugserver_error_t dres = DEBUGSERVER_E_UNKNOWN_ERROR;
226
227 int c = 0;
228 const struct option longopts[] = {
229 { "debug", no_argument, NULL, 'd' },
230 { "help", no_argument, NULL, 'h' },
231 { "udid", required_argument, NULL, 'u' },
232 { "network", no_argument, NULL, 'n' },
233 { "detach", no_argument, NULL, 1 },
234 { "env", required_argument, NULL, 'e' },
235 { "version", no_argument, NULL, 'v' },
236 { NULL, 0, NULL, 0 }
237 };
238
239 /* map signals */
240 signal(SIGINT, on_signal);
241 signal(SIGTERM, on_signal);
242#ifndef WIN32
243 signal(SIGQUIT, on_signal);
244 signal(SIGPIPE, SIG_IGN);
245#endif
246
247 while ((c = getopt_long(argc, argv, "dhu:ne:v", longopts, NULL)) != -1) {
248 switch (c) {
249 case 'd':
250 debug_level++;
251 if (debug_level > 1) {
252 idevice_set_debug_level(debug_level-1);
253 }
254 break;
255 case 'u':
256 if (!*optarg) {
257 fprintf(stderr, "ERROR: UDID must not be empty!\n");
258 print_usage(argc, argv, 1);
259 return 2;
260 }
261 udid = optarg;
262 break;
263 case 'n':
264 use_network = 1;
265 break;
266 case 1:
267 detach_after_start = 1;
268 break;
269 case 'e':
270 if (!*optarg || strchr(optarg, '=') == NULL) {
271 fprintf(stderr, "ERROR: environment variables need to be specified as -e KEY=VALUE\n");
272 print_usage(argc, argv, 1);
273 res = 2;
274 goto cleanup;
275 }
276 /* add environment variable */
277 if (!newlist)
278 newlist = malloc((environment_count + 1) * sizeof(char*));
279 else
280 newlist = realloc(environment, (environment_count + 1) * sizeof(char*));
281 newlist[environment_count++] = strdup(optarg);
282 environment = newlist;
283 break;
284 case 'h':
285 print_usage(argc, argv, 0);
286 res = 0;
287 goto cleanup;
288 break;
289 case 'v':
290 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
291 res = 0;
292 goto cleanup;
293 break;
294 default:
295 print_usage(argc, argv, 1);
296 res = 2;
297 goto cleanup;
298 break;
299 }
300 }
301 argc -= optind;
302 argv += optind;
303
304 if (argc < 1) {
305 fprintf(stderr, "ERROR: Missing command.\n");
306 print_usage(argc+optind, argv-optind, 1);
307 return 2;
308 }
309
310 if (!strcmp(argv[0], "run")) {
311 cmd = CMD_RUN;
312 if (argc < 2) {
313 /* make sure at least the bundle identifier was provided */
314 fprintf(stderr, "ERROR: Please supply the bundle identifier of the app to run.\n");
315 print_usage(argc+optind, argv-optind, 1);
316 res = 2;
317 goto cleanup;
318 }
319 /* read bundle identifier */
320 bundle_identifier = argv[1];
321 i = 1;
322 } else if (!strcmp(argv[0], "kill")) {
323 cmd = CMD_KILL;
324 if (argc < 2) {
325 /* make sure at least the bundle identifier was provided */
326 fprintf(stderr, "ERROR: Please supply the bundle identifier of the app to run.\n");
327 print_usage(argc+optind, argv-optind, 1);
328 res = 2;
329 goto cleanup;
330 }
331 /* read bundle identifier */
332 bundle_identifier = argv[1];
333 i = 1;
334 }
335
336 /* verify options */
337 if (cmd == CMD_NONE) {
338 fprintf(stderr, "ERROR: Unsupported command specified.\n");
339 print_usage(argc+optind, argv-optind, 1);
340 res = 2;
341 goto cleanup;
342 }
343
344 if (environment) {
345 newlist = realloc(environment, (environment_count + 1) * sizeof(char*));
346 newlist[environment_count] = NULL;
347 environment = newlist;
348 }
349
350 /* connect to the device */
351 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
352 if (ret != IDEVICE_E_SUCCESS) {
353 if (udid) {
354 printf("No device found with udid %s.\n", udid);
355 } else {
356 printf("No device found.\n");
357 }
358 goto cleanup;
359 }
360
361 /* get the path to the app and it's working directory */
362 if (instproxy_client_start_service(device, &instproxy_client, TOOL_NAME) != INSTPROXY_E_SUCCESS) {
363 fprintf(stderr, "Could not start installation proxy service.\n");
364 goto cleanup;
365 }
366
367 instproxy_client_get_path_for_bundle_identifier(instproxy_client, bundle_identifier, &path);
368 if (!path) {
369 fprintf(stderr, "Invalid bundle identifier: %s\n", bundle_identifier);
370 goto cleanup;
371 }
372
373 plist_t container = NULL;
374 instproxy_client_get_object_by_key_from_info_dictionary_for_bundle_identifier(instproxy_client, bundle_identifier, "Container", &container);
375 instproxy_client_free(instproxy_client);
376 instproxy_client = NULL;
377
378 if (container && (plist_get_node_type(container) == PLIST_STRING)) {
379 plist_get_string_val(container, &working_directory);
380 log_debug("working_directory: %s\n", working_directory);
381 plist_free(container);
382 } else {
383 plist_free(container);
384 fprintf(stderr, "Could not determine container path for bundle identifier %s.\n", bundle_identifier);
385 goto cleanup;
386 }
387
388 /* start and connect to debugserver */
389 if (debugserver_client_start_service(device, &debugserver_client, TOOL_NAME) != DEBUGSERVER_E_SUCCESS) {
390 fprintf(stderr,
391 "Could not start com.apple.debugserver!\n"
392 "Please make sure to mount the developer disk image first:\n"
393 " 1) Get the iOS version from `ideviceinfo -k ProductVersion`.\n"
394 " 2) Find the matching iPhoneOS DeveloperDiskImage.dmg files.\n"
395 " 3) Run `ideviceimagemounter` with the above path.\n");
396 goto cleanup;
397 }
398
399 /* set receive params */
400 if (debugserver_client_set_receive_params(debugserver_client, cancel_receive, 250) != DEBUGSERVER_E_SUCCESS) {
401 fprintf(stderr, "Error in debugserver_client_set_receive_params\n");
402 goto cleanup;
403 }
404
405 /* enable logging for the session in debug mode */
406 if (debug_level) {
407 log_debug("Setting logging bitmask...");
408 debugserver_command_new("QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE|LOG_RNB_PACKETS;", 0, NULL, &command);
409 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
410 debugserver_command_free(command);
411 command = NULL;
412 if (response) {
413 if (strncmp(response, "OK", 2) != 0) {
414 debugserver_client_handle_response(debugserver_client, &response, NULL);
415 goto cleanup;
416 }
417 free(response);
418 response = NULL;
419 }
420 }
421
422 /* set maximum packet size */
423 log_debug("Setting maximum packet size...");
424 char* packet_size[2] = { (char*)"1024", NULL};
425 debugserver_command_new("QSetMaxPacketSize:", 1, packet_size, &command);
426 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
427 debugserver_command_free(command);
428 command = NULL;
429 if (response) {
430 if (strncmp(response, "OK", 2) != 0) {
431 debugserver_client_handle_response(debugserver_client, &response, NULL);
432 goto cleanup;
433 }
434 free(response);
435 response = NULL;
436 }
437
438 /* set working directory */
439 log_debug("Setting working directory...");
440 char* working_dir[2] = {working_directory, NULL};
441 debugserver_command_new("QSetWorkingDir:", 1, working_dir, &command);
442 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
443 debugserver_command_free(command);
444 command = NULL;
445 if (response) {
446 if (strncmp(response, "OK", 2) != 0) {
447 debugserver_client_handle_response(debugserver_client, &response, NULL);
448 goto cleanup;
449 }
450 free(response);
451 response = NULL;
452 }
453
454 /* set environment */
455 if (environment) {
456 log_debug("Setting environment...");
457 for (environment_index = 0; environment_index < environment_count; environment_index++) {
458 log_debug("setting environment variable: %s", environment[environment_index]);
459 debugserver_client_set_environment_hex_encoded(debugserver_client, environment[environment_index], NULL);
460 }
461 }
462
463 /* set arguments and run app */
464 log_debug("Setting argv...");
465 i++; /* i is the offset of the bundle identifier, thus skip it */
466 int app_argc = (argc - i + 2);
467 char **app_argv = (char**)malloc(sizeof(char*) * app_argc);
468 app_argv[0] = path;
469 log_debug("app_argv[%d] = %s", 0, app_argv[0]);
470 app_argc = 1;
471 while (i < argc && argv && argv[i]) {
472 log_debug("app_argv[%d] = %s", app_argc, argv[i]);
473 app_argv[app_argc++] = argv[i];
474 i++;
475 }
476 app_argv[app_argc] = NULL;
477 debugserver_client_set_argv(debugserver_client, app_argc, app_argv, NULL);
478 free(app_argv);
479
480 /* check if launch succeeded */
481 log_debug("Checking if launch succeeded...");
482 debugserver_command_new("qLaunchSuccess", 0, NULL, &command);
483 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
484 debugserver_command_free(command);
485 command = NULL;
486 if (response) {
487 if (strncmp(response, "OK", 2) != 0) {
488 debugserver_client_handle_response(debugserver_client, &response, NULL);
489 goto cleanup;
490 }
491 free(response);
492 response = NULL;
493 }
494
495 if (cmd == CMD_KILL) {
496 debugserver_command_new("k", 0, NULL, &command);
497 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
498 debugserver_command_free(command);
499 command = NULL;
500 goto cleanup;
501 } else
502 if (cmd == CMD_RUN) {
503 if (detach_after_start) {
504 log_debug("Detaching from app");
505 debugserver_command_new("D", 0, NULL, &command);
506 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
507 debugserver_command_free(command);
508 command = NULL;
509
510 res = (dres == DEBUGSERVER_E_SUCCESS) ? 0: -1;
511 goto cleanup;
512 }
513
514 /* set thread */
515 log_debug("Setting thread...");
516 debugserver_command_new("Hc0", 0, NULL, &command);
517 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
518 debugserver_command_free(command);
519 command = NULL;
520 if (response) {
521 if (strncmp(response, "OK", 2) != 0) {
522 debugserver_client_handle_response(debugserver_client, &response, NULL);
523 goto cleanup;
524 }
525 free(response);
526 response = NULL;
527 }
528
529 /* continue running process */
530 log_debug("Continue running process...");
531 debugserver_command_new("c", 0, NULL, &command);
532 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
533 debugserver_command_free(command);
534 command = NULL;
535 log_debug("Continue response: %s", response);
536
537 /* main loop which is parsing/handling packets during the run */
538 log_debug("Entering run loop...");
539 while (!quit_flag) {
540 if (dres != DEBUGSERVER_E_SUCCESS) {
541 log_debug("failed to receive response; error %d", dres);
542 break;
543 }
544
545 if (response) {
546 log_debug("response: %s", response);
547 if (strncmp(response, "OK", 2) != 0) {
548 dres = debugserver_client_handle_response(debugserver_client, &response, &res);
549 if (dres != DEBUGSERVER_E_SUCCESS) {
550 log_debug("failed to process response; error %d; %s", dres, response);
551 break;
552 }
553 }
554 }
555 if (res >= 0) {
556 goto cleanup;
557 }
558
559 dres = debugserver_client_receive_response(debugserver_client, &response, NULL);
560 }
561
562 /* ignore quit_flag after this point */
563 if (debugserver_client_set_receive_params(debugserver_client, NULL, 5000) != DEBUGSERVER_E_SUCCESS) {
564 fprintf(stderr, "Error in debugserver_client_set_receive_params\n");
565 goto cleanup;
566 }
567
568 /* interrupt execution */
569 debugserver_command_new("\x03", 0, NULL, &command);
570 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
571 debugserver_command_free(command);
572 command = NULL;
573 if (response) {
574 if (strncmp(response, "OK", 2) != 0) {
575 debugserver_client_handle_response(debugserver_client, &response, NULL);
576 }
577 free(response);
578 response = NULL;
579 }
580
581 /* kill process after we finished */
582 log_debug("Killing process...");
583 debugserver_command_new("k", 0, NULL, &command);
584 dres = debugserver_client_send_command(debugserver_client, command, &response, NULL);
585 debugserver_command_free(command);
586 command = NULL;
587 if (response) {
588 if (strncmp(response, "OK", 2) != 0) {
589 debugserver_client_handle_response(debugserver_client, &response, NULL);
590 }
591 free(response);
592 response = NULL;
593 }
594
595 if (res < 0) {
596 res = (dres == DEBUGSERVER_E_SUCCESS) ? 0: -1;
597 }
598 }
599
600cleanup:
601 /* cleanup the house */
602 if (environment) {
603 for (environment_index = 0; environment_index < environment_count; environment_index++) {
604 free(environment[environment_index]);
605 }
606 free(environment);
607 }
608
609 if (working_directory)
610 free(working_directory);
611
612 if (path)
613 free(path);
614
615 if (response)
616 free(response);
617
618 if (debugserver_client)
619 debugserver_client_free(debugserver_client);
620
621 if (device)
622 idevice_free(device);
623
624 return res;
625}
diff --git a/tools/idevicedebugserverproxy.c b/tools/idevicedebugserverproxy.c
new file mode 100644
index 0000000..9fe7051
--- /dev/null
+++ b/tools/idevicedebugserverproxy.c
@@ -0,0 +1,375 @@
1/*
2 * idevicedebugserverproxy.c
3 * Proxy a debugserver connection from device for remote debugging
4 *
5 * Copyright (c) 2021 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2012 Martin Szulecki All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "idevicedebugserverproxy"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <getopt.h>
33#include <errno.h>
34#include <signal.h>
35#ifdef WIN32
36#include <winsock2.h>
37#include <windows.h>
38#else
39#include <sys/select.h>
40#endif
41
42#include <libimobiledevice/libimobiledevice.h>
43#include <libimobiledevice/debugserver.h>
44
45#include <libimobiledevice-glue/socket.h>
46#include <libimobiledevice-glue/thread.h>
47
48#ifndef ETIMEDOUT
49#define ETIMEDOUT 138
50#endif
51
52#define info(...) fprintf(stdout, __VA_ARGS__); fflush(stdout)
53#define debug(...) if(debug_mode) fprintf(stdout, __VA_ARGS__)
54
55static int support_lldb = 0;
56static int debug_mode = 0;
57static int quit_flag = 0;
58static uint16_t local_port = 0;
59
60typedef struct {
61 int client_fd;
62 idevice_t device;
63 debugserver_client_t debugserver_client;
64} socket_info_t;
65
66struct thread_info {
67 THREAD_T th;
68 int client_fd;
69 struct thread_info *next;
70};
71
72typedef struct thread_info thread_info_t;
73
74
75static void clean_exit(int sig)
76{
77 fprintf(stderr, "Exiting...\n");
78 quit_flag++;
79}
80
81static void print_usage(int argc, char **argv, int is_error)
82{
83 char *name = strrchr(argv[0], '/');
84 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [PORT]\n", (name ? name + 1: argv[0]));
85 fprintf(is_error ? stderr : stdout,
86 "\n"
87 "Proxy debugserver connection from device to a local socket at PORT.\n"
88 "If PORT is omitted, the next available port will be used and printed\n"
89 "to stdout.\n"
90 "\n"
91 "OPTIONS:\n"
92 " -u, --udid UDID target specific device by UDID\n"
93 " -n, --network connect to network device\n"
94 " -d, --debug enable communication debugging\n"
95 " -l, --lldb enable lldb support\n"
96 " -h, --help prints usage information\n"
97 " -v, --version prints version information\n"
98 "\n"
99 "Homepage: <" PACKAGE_URL ">\n"
100 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
101 );
102}
103
104static int intercept_packet(char *packet, ssize_t *packet_len) {
105 static const char kReqLaunchServer[] = "$qLaunchGDBServer;#4b";
106
107 char buffer[64] = {0};
108 if (*packet_len == (ssize_t)(sizeof(kReqLaunchServer) - 1)
109 && memcmp(packet, kReqLaunchServer, sizeof(kReqLaunchServer) - 1) == 0) {
110 sprintf(buffer, "port:%d;", local_port);
111 } else {
112 return 0;
113 }
114 int sum = 0;
115 for (size_t i = 0; i < strlen(buffer); i++) {
116 sum += buffer[i];
117 }
118 sum = sum & 255;
119 sprintf(packet, "$%s#%02x", buffer, sum);
120 *packet_len = strlen(packet);
121 return 1;
122}
123
124static void* connection_handler(void* data)
125{
126 debugserver_error_t derr = DEBUGSERVER_E_SUCCESS;
127 socket_info_t* socket_info = (socket_info_t*)data;
128 const int bufsize = 65536;
129 char* buf;
130
131 int client_fd = socket_info->client_fd;
132
133 debug("%s: client_fd = %d\n", __func__, client_fd);
134
135 derr = debugserver_client_start_service(socket_info->device, &socket_info->debugserver_client, TOOL_NAME);
136 if (derr != DEBUGSERVER_E_SUCCESS) {
137 fprintf(stderr, "Could not start debugserver on device!\nPlease make sure to mount a developer disk image first.\n");
138 return NULL;
139 }
140
141 buf = malloc(bufsize);
142 if (!buf) {
143 fprintf(stderr, "Failed to allocate buffer\n");
144 return NULL;
145 }
146
147 fd_set fds;
148 FD_ZERO(&fds);
149 FD_SET(client_fd, &fds);
150
151 int dtimeout = 1;
152
153 while (!quit_flag) {
154 ssize_t n = socket_receive_timeout(client_fd, buf, bufsize, 0, 1);
155 if (n != -ETIMEDOUT) {
156 if (n < 0) {
157 fprintf(stderr, "Failed to read from client fd: %s\n", strerror(-n));
158 break;
159 } else if (n == 0) {
160 fprintf(stderr, "connection closed\n");
161 break;
162 }
163 if (support_lldb && intercept_packet(buf, &n)) {
164 socket_send(client_fd, buf, n);
165 continue;
166 }
167 uint32_t sent = 0;
168 debugserver_client_send(socket_info->debugserver_client, buf, n, &sent);
169 }
170 do {
171 uint32_t r = 0;
172 derr = debugserver_client_receive_with_timeout(socket_info->debugserver_client, buf, bufsize, &r, dtimeout);
173 if (r > 0) {
174 socket_send(client_fd, buf, r);
175 dtimeout = 1;
176 } else if (derr == DEBUGSERVER_E_TIMEOUT) {
177 dtimeout = 5;
178 break;
179 } else {
180 fprintf(stderr, "debugserver connection closed\n");
181 break;
182 }
183 } while (derr == DEBUGSERVER_E_SUCCESS);
184 if (derr != DEBUGSERVER_E_TIMEOUT && derr != DEBUGSERVER_E_SUCCESS) {
185 break;
186 }
187 }
188 free(buf);
189
190 debug("%s: shutting down...\n", __func__);
191
192 debugserver_client_free(socket_info->debugserver_client);
193 socket_info->debugserver_client = NULL;
194
195 /* shutdown client socket */
196 socket_shutdown(socket_info->client_fd, SHUT_RDWR);
197 socket_close(socket_info->client_fd);
198
199 return NULL;
200}
201
202int main(int argc, char *argv[])
203{
204 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
205 idevice_t device = NULL;
206 thread_info_t *thread_list = NULL;
207 const char* udid = NULL;
208 int use_network = 0;
209 int server_fd;
210 int result = EXIT_SUCCESS;
211 int c = 0;
212 const struct option longopts[] = {
213 { "debug", no_argument, NULL, 'd' },
214 { "help", no_argument, NULL, 'h' },
215 { "udid", required_argument, NULL, 'u' },
216 { "network", no_argument, NULL, 'n' },
217 { "lldb", no_argument, NULL, 'l' },
218 { "version", no_argument, NULL, 'v' },
219 { NULL, 0, NULL, 0}
220 };
221
222#ifndef WIN32
223 struct sigaction sa;
224 struct sigaction si;
225 memset(&sa, '\0', sizeof(struct sigaction));
226 memset(&si, '\0', sizeof(struct sigaction));
227
228 sa.sa_handler = clean_exit;
229 sigemptyset(&sa.sa_mask);
230
231 si.sa_handler = SIG_IGN;
232 sigemptyset(&si.sa_mask);
233
234 sigaction(SIGINT, &sa, NULL);
235 sigaction(SIGTERM, &sa, NULL);
236 sigaction(SIGQUIT, &sa, NULL);
237 sigaction(SIGPIPE, &si, NULL);
238#else
239 /* bind signals */
240 signal(SIGINT, clean_exit);
241 signal(SIGTERM, clean_exit);
242#endif
243
244 /* parse cmdline arguments */
245 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
246 switch (c) {
247 case 'd':
248 debug_mode = 1;
249 idevice_set_debug_level(1);
250 socket_set_verbose(3);
251 break;
252 case 'u':
253 if (!*optarg) {
254 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
255 print_usage(argc, argv, 1);
256 return 2;
257 }
258 udid = optarg;
259 break;
260 case 'n':
261 use_network = 1;
262 break;
263 case 'l':
264 support_lldb = 1;
265 break;
266 case 'h':
267 print_usage(argc, argv, 0);
268 return 0;
269 case 'v':
270 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
271 return 0;
272 default:
273 print_usage(argc, argv, 1);
274 return 2;
275 }
276 }
277 argc -= optind;
278 argv += optind;
279
280 if (argv[0] && (atoi(argv[0]) > 0)) {
281 local_port = atoi(argv[0]);
282 }
283
284 /* start services and connect to device */
285 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
286 if (ret != IDEVICE_E_SUCCESS) {
287 if (udid) {
288 fprintf(stderr, "No device found with udid %s.\n", udid);
289 } else {
290 fprintf(stderr, "No device found.\n");
291 }
292 result = EXIT_FAILURE;
293 goto leave_cleanup;
294 }
295
296 /* create local socket */
297 server_fd = socket_create("127.0.0.1", local_port);
298 if (server_fd < 0) {
299 fprintf(stderr, "Could not create socket\n");
300 result = EXIT_FAILURE;
301 goto leave_cleanup;
302 }
303
304 if (local_port == 0) {
305 /* The user asked for any available port. Report the actual port. */
306 uint16_t port;
307 if (0 > socket_get_socket_port(server_fd, &port)) {
308 fprintf(stderr, "Could not determine socket port\n");
309 result = EXIT_FAILURE;
310 goto leave_cleanup;
311 }
312 printf("Listening on port %d\n", port);
313 }
314
315 while (!quit_flag) {
316 debug("%s: Waiting for connection on local port %d\n", __func__, local_port);
317
318 /* wait for client */
319 int client_fd = socket_accept(server_fd, local_port);
320 if (client_fd < 0) {
321 continue;
322 }
323
324 debug("%s: Handling new client connection...\n", __func__);
325
326 thread_info_t *el = (thread_info_t*)malloc(sizeof(thread_info_t));
327 if (!el) {
328 fprintf(stderr, "Out of memory\n");
329 exit(EXIT_FAILURE);
330 }
331 el->client_fd = client_fd;
332 el->next = NULL;
333
334 if (thread_list) {
335 thread_list->next = el;
336 } else {
337 thread_list = el;
338 }
339
340 socket_info_t *sinfo = (socket_info_t*)malloc(sizeof(socket_info_t));
341 if (!sinfo) {
342 fprintf(stderr, "Out of memory\n");
343 exit(EXIT_FAILURE);
344 }
345 sinfo->client_fd = client_fd;
346 sinfo->device = device;
347
348 if (thread_new(&(el->th), connection_handler, (void*)sinfo) != 0) {
349 fprintf(stderr, "Could not start connection handler.\n");
350 socket_shutdown(server_fd, SHUT_RDWR);
351 socket_close(server_fd);
352 break;
353 }
354 }
355
356 debug("%s: Shutting down debugserver proxy...\n", __func__);
357
358 /* join and clean up threads */
359 while (thread_list) {
360 thread_info_t *el = thread_list;
361 socket_shutdown(el->client_fd, SHUT_RDWR);
362 socket_close(el->client_fd);
363 thread_join(el->th);
364 thread_free(el->th);
365 thread_list = el->next;
366 free(el);
367 }
368
369leave_cleanup:
370 if (device) {
371 idevice_free(device);
372 }
373
374 return result;
375}
diff --git a/tools/idevicedevmodectl.c b/tools/idevicedevmodectl.c
new file mode 100644
index 0000000..bd1de6a
--- /dev/null
+++ b/tools/idevicedevmodectl.c
@@ -0,0 +1,462 @@
1/*
2 * idevicedevmodectl.c
3 * List or enable Developer Mode on iOS 16+ devices
4 *
5 * Copyright (c) 2022 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
26#define TOOL_NAME "idevicedevmodectl"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <getopt.h>
32#include <sys/stat.h>
33#include <unistd.h>
34#include <errno.h>
35#ifndef WIN32
36#include <signal.h>
37#endif
38
39#ifdef WIN32
40#include <windows.h>
41#define __usleep(x) Sleep(x/1000)
42#else
43#include <arpa/inet.h>
44#include <unistd.h>
45#define __usleep(x) usleep(x)
46#endif
47
48#include <libimobiledevice/libimobiledevice.h>
49#include <libimobiledevice/lockdown.h>
50#include <libimobiledevice/property_list_service.h>
51#include <libimobiledevice-glue/utils.h>
52
53#define AMFI_LOCKDOWN_SERVICE_NAME "com.apple.amfi.lockdown"
54
55static char* udid = NULL;
56static int use_network = 0;
57
58static void print_usage(int argc, char **argv, int is_error)
59{
60 char *name = strrchr(argv[0], '/');
61 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
62 fprintf(is_error ? stderr : stdout,
63 "\n"
64 "Enable Developer Mode on iOS 16+ devices or print the current status.\n"
65 "\n"
66 "Where COMMAND is one of:\n"
67 " list Print the Developer Mode status of all connected devices\n"
68 " or for a specific one if --udid is given.\n"
69 " enable Enable Developer Mode (device will reboot),\n"
70 " and confirm it after device booted up again.\n"
71 "\n"
72 " arm Arm the Developer Mode (device will reboot)\n"
73 " confirm Confirm enabling of Developer Mode\n"
74 " reveal Reveal the Developer Mode menu on the device\n"
75 "\n"
76 "The following OPTIONS are accepted:\n"
77 " -u, --udid UDID target specific device by UDID\n"
78 " -n, --network connect to network device\n"
79 " -d, --debug enable communication debugging\n"
80 " -h, --help print usage information\n"
81 " -v, --version print version information\n"
82 "\n"
83 "Homepage: <" PACKAGE_URL ">\n"
84 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
85 );
86}
87
88enum {
89 OP_LIST,
90 OP_ENABLE,
91 OP_ARM,
92 OP_CONFIRM,
93 OP_REVEAL,
94 NUM_OPS
95};
96#define DEV_MODE_REVEAL 0
97#define DEV_MODE_ARM 1
98#define DEV_MODE_ENABLE 2
99
100static int get_developer_mode_status(const char* device_udid, int _use_network)
101{
102 idevice_error_t ret;
103 idevice_t device = NULL;
104 lockdownd_client_t lockdown = NULL;
105 lockdownd_error_t lerr = LOCKDOWN_E_UNKNOWN_ERROR;
106 plist_t val = NULL;
107
108 ret = idevice_new_with_options(&device, device_udid, (_use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
109 if (ret != IDEVICE_E_SUCCESS) {
110 return -1;
111 }
112
113 if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
114 idevice_free(device);
115 return -1;
116 }
117
118 lerr = lockdownd_get_value(lockdown, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
119 if (lerr != LOCKDOWN_E_SUCCESS) {
120 fprintf(stderr, "ERROR: Could not get DeveloperModeStatus: %s\nPlease note that this feature is only available on iOS 16+.\n", lockdownd_strerror(lerr));
121 lockdownd_client_free(lockdown);
122 idevice_free(device);
123 return -2;
124 }
125
126 uint8_t dev_mode_status = 0;
127 plist_get_bool_val(val, &dev_mode_status);
128 plist_free(val);
129
130 lockdownd_client_free(lockdown);
131 idevice_free(device);
132
133 return dev_mode_status;
134}
135
136static int amfi_service_send_msg(property_list_service_client_t amfi, plist_t msg)
137{
138 int res;
139 property_list_service_error_t perr;
140
141 perr = property_list_service_send_xml_plist(amfi, plist_copy(msg));
142 if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
143 fprintf(stderr, "Could not send request to device: %d\n", perr);
144 res = 2;
145 } else {
146 plist_t reply = NULL;
147 perr = property_list_service_receive_plist(amfi, &reply);
148 if (perr == PROPERTY_LIST_SERVICE_E_SUCCESS) {
149 plist_t val = plist_dict_get_item(reply, "Error");
150 if (val) {
151 const char* err = plist_get_string_ptr(val, NULL);
152 fprintf(stderr, "Request failed: %s\n", err);
153 if (strstr(err, "passcode")) {
154 res = 2;
155 } else {
156 res = 1;
157 }
158 } else {
159 res = plist_dict_get_item(reply, "success") ? 0 : 1;
160 }
161 } else {
162 fprintf(stderr, "Could not receive reply from device: %d\n", perr);
163 res = 2;
164 }
165 plist_free(reply);
166 }
167 return res;
168}
169
170static int amfi_send_action(idevice_t device, unsigned int action)
171{
172 lockdownd_client_t lockdown = NULL;
173 lockdownd_service_descriptor_t service = NULL;
174 lockdownd_error_t lerr;
175
176 if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
177 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
178 return 1;
179 }
180
181 lerr = lockdownd_start_service(lockdown, AMFI_LOCKDOWN_SERVICE_NAME, &service);
182 if (lerr != LOCKDOWN_E_SUCCESS) {
183 fprintf(stderr, "Could not start service %s: %s\nPlease note that this feature is only available on iOS 16+.\n", AMFI_LOCKDOWN_SERVICE_NAME, lockdownd_strerror(lerr));
184 lockdownd_client_free(lockdown);
185 return 1;
186 }
187 lockdownd_client_free(lockdown);
188 lockdown = NULL;
189
190 property_list_service_client_t amfi = NULL;
191 if (property_list_service_client_new(device, service, &amfi) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
192 fprintf(stderr, "Could not connect to %s on device\n", AMFI_LOCKDOWN_SERVICE_NAME);
193 if (service)
194 lockdownd_service_descriptor_free(service);
195 idevice_free(device);
196 return 1;
197 }
198 lockdownd_service_descriptor_free(service);
199
200 plist_t dict = plist_new_dict();
201 plist_dict_set_item(dict, "action", plist_new_uint(action));
202
203 int result = amfi_service_send_msg(amfi, dict);
204 plist_free(dict);
205
206 property_list_service_client_free(amfi);
207 amfi = NULL;
208
209 return result;
210}
211
212static int device_connected = 0;
213
214static void device_event_cb(const idevice_event_t* event, void* userdata)
215{
216 if (use_network && event->conn_type != CONNECTION_NETWORK) {
217 return;
218 }
219 if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
220 return;
221 }
222 if (event->event == IDEVICE_DEVICE_ADD) {
223 if (!udid) {
224 udid = strdup(event->udid);
225 }
226 if (strcmp(udid, event->udid) == 0) {
227 device_connected = 1;
228 }
229 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
230 if (strcmp(udid, event->udid) == 0) {
231 device_connected = 0;
232 }
233 }
234}
235
236
237#define WAIT_INTERVAL 200000
238#define WAIT_MAX(x) (x * (1000000 / WAIT_INTERVAL))
239#define WAIT_FOR(cond, timeout) { int __repeat = WAIT_MAX(timeout); while (!(cond) && __repeat-- > 0) { __usleep(WAIT_INTERVAL); } }
240
241int main(int argc, char *argv[])
242{
243 idevice_t device = NULL;
244 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
245 lockdownd_client_t lockdown = NULL;
246 lockdownd_error_t lerr = LOCKDOWN_E_UNKNOWN_ERROR;
247 int res = 0;
248 int i;
249 int op = -1;
250 plist_t val = NULL;
251
252 int c = 0;
253 const struct option longopts[] = {
254 { "debug", no_argument, NULL, 'd' },
255 { "help", no_argument, NULL, 'h' },
256 { "udid", required_argument, NULL, 'u' },
257 { "network", no_argument, NULL, 'n' },
258 { "version", no_argument, NULL, 'v' },
259 { NULL, 0, NULL, 0}
260 };
261
262#ifndef WIN32
263 signal(SIGPIPE, SIG_IGN);
264#endif
265 /* parse cmdline args */
266 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
267 switch (c) {
268 case 'd':
269 idevice_set_debug_level(1);
270 break;
271 case 'u':
272 if (!*optarg) {
273 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
274 print_usage(argc, argv, 1);
275 return 2;
276 }
277 udid = optarg;
278 break;
279 case 'n':
280 use_network = 1;
281 break;
282 case 'h':
283 print_usage(argc, argv, 0);
284 return 0;
285 case 'v':
286 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
287 return 0;
288 default:
289 print_usage(argc, argv, 1);
290 return 2;
291 }
292 }
293 argc -= optind;
294 argv += optind;
295
296 if (!argv[0]) {
297 fprintf(stderr, "ERROR: Missing command.\n");
298 print_usage(argc+optind, argv-optind, 1);
299 return 2;
300 }
301
302 i = 0;
303 if (!strcmp(argv[i], "list")) {
304 op = OP_LIST;
305 }
306 else if (!strcmp(argv[i], "enable")) {
307 op = OP_ENABLE;
308 }
309 else if (!strcmp(argv[i], "arm")) {
310 op = OP_ARM;
311 }
312 else if (!strcmp(argv[i], "confirm")) {
313 op = OP_CONFIRM;
314 }
315 else if (!strcmp(argv[i], "reveal")) {
316 op = OP_REVEAL;
317 }
318
319 if ((op == -1) || (op >= NUM_OPS)) {
320 fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[i]);
321 print_usage(argc+optind, argv-optind, 1);
322 return 2;
323 }
324
325 if (op == OP_LIST) {
326 idevice_info_t *dev_list = NULL;
327
328 if (idevice_get_device_list_extended(&dev_list, &i) < 0) {
329 fprintf(stderr, "ERROR: Unable to retrieve device list!\n");
330 return -1;
331 }
332 if (i > 0) {
333 printf("%-40s %s\n", "Device", "DeveloperMode");
334 }
335 for (i = 0; dev_list[i] != NULL; i++) {
336 if (dev_list[i]->conn_type == CONNECTION_USBMUXD && use_network) continue;
337 if (dev_list[i]->conn_type == CONNECTION_NETWORK && !use_network) continue;
338 if (udid && (strcmp(dev_list[i]->udid, udid) != 0)) continue;
339 int mode = get_developer_mode_status(dev_list[i]->udid, use_network);
340 const char *mode_str = "N/A";
341 if (mode == 1) {
342 mode_str = "enabled";
343 } else if (mode == 0) {
344 mode_str = "disabled";
345 }
346 printf("%-40s %s\n", dev_list[i]->udid, mode_str);
347 }
348 idevice_device_list_extended_free(dev_list);
349
350 return 0;
351 }
352
353 idevice_subscription_context_t context = NULL;
354 idevice_events_subscribe(&context, device_event_cb, NULL);
355
356 WAIT_FOR(device_connected, 10);
357
358 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
359 if (ret != IDEVICE_E_SUCCESS) {
360 if (udid) {
361 printf("No device found with udid %s.\n", udid);
362 } else {
363 printf("No device found.\n");
364 }
365 return 1;
366 }
367
368 if (!udid) {
369 idevice_get_udid(device, &udid);
370 }
371
372 if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
373 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
374 idevice_free(device);
375 return 1;
376 }
377
378 lerr = lockdownd_get_value(lockdown, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
379 lockdownd_client_free(lockdown);
380 lockdown = NULL;
381 if (lerr != LOCKDOWN_E_SUCCESS) {
382 fprintf(stderr, "ERROR: Could not get DeveloperModeStatus: %s\nPlease note that this feature is only available on iOS 16+.\n", lockdownd_strerror(lerr));
383 idevice_free(device);
384 return 1;
385 }
386
387 uint8_t dev_mode_status = 0;
388 plist_get_bool_val(val, &dev_mode_status);
389
390 if ((op == OP_ENABLE || op == OP_ARM) && dev_mode_status) {
391 if (dev_mode_status) {
392 idevice_free(device);
393 printf("DeveloperMode is already enabled.\n");
394 return 0;
395 }
396 res = 0;
397 } else {
398 if (op == OP_ENABLE || op == OP_ARM) {
399 res = amfi_send_action(device, DEV_MODE_ARM);
400 if (res == 0) {
401 if (op == OP_ARM) {
402 printf("%s: Developer Mode armed, device will reboot now.\n", udid);
403 } else {
404 printf("%s: Developer Mode armed, waiting for reboot...\n", udid);
405
406 do {
407 // waiting for device to disconnect...
408 idevice_free(device);
409 device = NULL;
410 WAIT_FOR(!device_connected, 40);
411 if (device_connected) {
412 printf("%s: ERROR: Device didn't reboot?!\n", udid);
413 res = 2;
414 break;
415 }
416 printf("disconnected\n");
417
418 // waiting for device to reconnect...
419 WAIT_FOR(device_connected, 60);
420 if (!device_connected) {
421 printf("%s: ERROR: Device didn't re-connect?!\n", udid);
422 res = 2;
423 break;
424 }
425 printf("connected\n");
426
427 idevice_new(&device, udid);
428 res = amfi_send_action(device, DEV_MODE_ENABLE);
429 } while (0);
430 if (res == 0) {
431 printf("%s: Developer Mode successfully enabled.\n", udid);
432 } else {
433 printf("%s: Failed to enable developer mode (%d)\n", udid, res);
434 }
435 }
436 } else if (res == 2) {
437 amfi_send_action(device, DEV_MODE_REVEAL);
438 printf("%s: Developer Mode could not be enabled because the device has a passcode set. You have to enable it on the device itself under Settings -> Privacy & Security -> Developer Mode.\n", udid);
439 } else {
440 printf("%s: Failed to arm Developer Mode (%d)\n", udid, res);
441 }
442 } else if (op == OP_CONFIRM) {
443 res = amfi_send_action(device, DEV_MODE_ENABLE);
444 if (res == 0) {
445 printf("%s: Developer Mode successfully enabled.\n", udid);
446 } else {
447 printf("%s: Failed to enable Developer Mode (%d)\n", udid, res);
448 }
449 } else if (op == OP_REVEAL) {
450 res = amfi_send_action(device, DEV_MODE_REVEAL);
451 if (res == 0) {
452 printf("%s: Developer Mode menu revealed successfully.\n", udid);
453 } else {
454 printf("%s: Failed to reveal Developer Mode menu (%d)\n", udid, res);
455 }
456 }
457 }
458
459 idevice_free(device);
460
461 return res;
462}
diff --git a/tools/idevicediagnostics.c b/tools/idevicediagnostics.c
new file mode 100644
index 0000000..e699bc4
--- /dev/null
+++ b/tools/idevicediagnostics.c
@@ -0,0 +1,344 @@
1/*
2 * idevicediagnostics.c
3 * Retrieves diagnostics information from device
4 *
5 * Copyright (c) 2012 Martin Szulecki 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
26#define TOOL_NAME "idevicediagnostics"
27
28#include <stdio.h>
29#include <string.h>
30#include <stdlib.h>
31#include <getopt.h>
32#include <errno.h>
33#include <time.h>
34#ifndef WIN32
35#include <signal.h>
36#endif
37
38#include <libimobiledevice/libimobiledevice.h>
39#include <libimobiledevice/lockdown.h>
40#include <libimobiledevice/diagnostics_relay.h>
41
42enum cmd_mode {
43 CMD_NONE = 0,
44 CMD_SLEEP,
45 CMD_RESTART,
46 CMD_SHUTDOWN,
47 CMD_DIAGNOSTICS,
48 CMD_MOBILEGESTALT,
49 CMD_IOREGISTRY,
50 CMD_IOREGISTRY_ENTRY
51};
52
53static void print_xml(plist_t node)
54{
55 char *xml = NULL;
56 uint32_t len = 0;
57 plist_to_xml(node, &xml, &len);
58 if (xml) {
59 puts(xml);
60 }
61}
62
63static void print_usage(int argc, char **argv, int is_error)
64{
65 char *name = strrchr(argv[0], '/');
66 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
67 fprintf(is_error ? stderr : stdout,
68 "\n"
69 "Use diagnostics interface of a device running iOS 4 or later.\n"
70 "\n"
71 "Where COMMAND is one of:\n"
72 " diagnostics [TYPE] print diagnostics information from device by TYPE (All, WiFi, GasGauge, NAND)\n"
73 " mobilegestalt KEY [...] print mobilegestalt keys passed as arguments separated by a space.\n"
74 " ioreg [PLANE] print IORegistry of device, optionally by PLANE (IODeviceTree, IOPower, IOService) (iOS 5+ only)\n"
75 " ioregentry [KEY] print IORegistry entry of device (AppleARMPMUCharger, ASPStorage, ...) (iOS 5+ only)\n"
76 " shutdown shutdown device\n"
77 " restart restart device\n"
78 " sleep put device into sleep mode (disconnects from host)\n"
79 "\n"
80 "The following OPTIONS are accepted:\n"
81 " -u, --udid UDID target specific device by UDID\n"
82 " -n, --network connect to network device\n"
83 " -d, --debug enable communication debugging\n"
84 " -h, --help prints usage information\n"
85 " -v, --version prints version information\n"
86 "\n"
87 "Homepage: <" PACKAGE_URL ">\n"
88 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
89 );
90}
91
92int main(int argc, char **argv)
93{
94 idevice_t device = NULL;
95 lockdownd_client_t lockdown_client = NULL;
96 diagnostics_relay_client_t diagnostics_client = NULL;
97 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
98 lockdownd_service_descriptor_t service = NULL;
99 int result = EXIT_FAILURE;
100 const char *udid = NULL;
101 int use_network = 0;
102 int cmd = CMD_NONE;
103 char* cmd_arg = NULL;
104 plist_t node = NULL;
105 plist_t keys = NULL;
106 int c = 0;
107 const struct option longopts[] = {
108 { "debug", no_argument, NULL, 'd' },
109 { "help", no_argument, NULL, 'h' },
110 { "udid", required_argument, NULL, 'u' },
111 { "network", no_argument, NULL, 'n' },
112 { "version", no_argument, NULL, 'v' },
113 { NULL, 0, NULL, 0}
114 };
115
116#ifndef WIN32
117 signal(SIGPIPE, SIG_IGN);
118#endif
119 /* parse cmdline args */
120 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
121 switch (c) {
122 case 'd':
123 idevice_set_debug_level(1);
124 break;
125 case 'u':
126 if (!*optarg) {
127 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
128 print_usage(argc, argv, 1);
129 return 2;
130 }
131 udid = optarg;
132 break;
133 case 'n':
134 use_network = 1;
135 break;
136 case 'h':
137 print_usage(argc, argv, 0);
138 return 0;
139 case 'v':
140 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
141 return 0;
142 default:
143 print_usage(argc, argv, 1);
144 return 2;
145 }
146 }
147 argc -= optind;
148 argv += optind;
149
150 if (!argv[0]) {
151 fprintf(stderr, "ERROR: No command specified\n");
152 print_usage(argc+optind, argv-optind, 1);
153 return 2;
154 }
155
156 if (!strcmp(argv[0], "sleep")) {
157 cmd = CMD_SLEEP;
158 }
159 else if (!strcmp(argv[0], "restart")) {
160 cmd = CMD_RESTART;
161 }
162 else if (!strcmp(argv[0], "shutdown")) {
163 cmd = CMD_SHUTDOWN;
164 }
165 else if (!strcmp(argv[0], "diagnostics")) {
166 cmd = CMD_DIAGNOSTICS;
167 /* read type */
168 if (!argv[1] || ((strcmp(argv[1], "All") != 0) && (strcmp(argv[1], "WiFi") != 0) && (strcmp(argv[1], "GasGauge") != 0) && (strcmp(argv[1], "NAND") != 0) && (strcmp(argv[1], "HDMI") != 0))) {
169 if (argv[1] == NULL) {
170 cmd_arg = strdup("All");
171 } else {
172 fprintf(stderr, "ERROR: Unknown TYPE %s\n", argv[1]);
173 print_usage(argc+optind, argv-optind, 1);
174 goto cleanup;
175 }
176 }
177 cmd_arg = strdup(argv[1]);
178 }
179 else if (!strcmp(argv[0], "mobilegestalt")) {
180 cmd = CMD_MOBILEGESTALT;
181 /* read keys */
182 if (!argv[1] || !*argv[1]) {
183 fprintf(stderr, "ERROR: Please supply the key to query.\n");
184 print_usage(argc, argv, 1);
185 goto cleanup;
186 }
187 int i = 1;
188 keys = plist_new_array();
189 while (argv[i] && *argv[i]) {
190 plist_array_append_item(keys, plist_new_string(argv[i]));
191 i++;
192 }
193 }
194 else if (!strcmp(argv[0], "ioreg")) {
195 cmd = CMD_IOREGISTRY;
196 /* read plane */
197 if (argv[1]) {
198 cmd_arg = strdup(argv[1]);
199 }
200 }
201 else if (!strcmp(argv[0], "ioregentry")) {
202 cmd = CMD_IOREGISTRY_ENTRY;
203 /* read key */
204 if (argv[1]) {
205 cmd_arg = strdup(argv[1]);
206 }
207 }
208
209 /* verify options */
210 if (cmd == CMD_NONE) {
211 fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[0]);
212 print_usage(argc+optind, argv-optind, 1);
213 goto cleanup;
214 }
215
216 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
217 if (udid) {
218 printf("No device found with udid %s.\n", udid);
219 } else {
220 printf("No device found.\n");
221 }
222 goto cleanup;
223 }
224
225 if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &lockdown_client, TOOL_NAME))) {
226 idevice_free(device);
227 printf("ERROR: Could not connect to lockdownd, error code %d\n", ret);
228 goto cleanup;
229 }
230
231 /* attempt to use newer diagnostics service available on iOS 5 and later */
232 ret = lockdownd_start_service(lockdown_client, "com.apple.mobile.diagnostics_relay", &service);
233 if (ret == LOCKDOWN_E_INVALID_SERVICE) {
234 /* attempt to use older diagnostics service */
235 ret = lockdownd_start_service(lockdown_client, "com.apple.iosdiagnostics.relay", &service);
236 }
237 lockdownd_client_free(lockdown_client);
238
239 if (ret != LOCKDOWN_E_SUCCESS) {
240 idevice_free(device);
241 printf("ERROR: Could not start diagnostics relay service: %s\n", lockdownd_strerror(ret));
242 goto cleanup;
243 }
244
245 result = EXIT_FAILURE;
246
247 if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) {
248 if (diagnostics_relay_client_new(device, service, &diagnostics_client) != DIAGNOSTICS_RELAY_E_SUCCESS) {
249 printf("ERROR: Could not connect to diagnostics_relay!\n");
250 } else {
251 switch (cmd) {
252 case CMD_SLEEP:
253 if (diagnostics_relay_sleep(diagnostics_client) == DIAGNOSTICS_RELAY_E_SUCCESS) {
254 printf("Putting device into deep sleep mode.\n");
255 result = EXIT_SUCCESS;
256 } else {
257 printf("ERROR: Failed to put device into deep sleep mode.\n");
258 }
259 break;
260 case CMD_RESTART:
261 if (diagnostics_relay_restart(diagnostics_client, DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) == DIAGNOSTICS_RELAY_E_SUCCESS) {
262 printf("Restarting device.\n");
263 result = EXIT_SUCCESS;
264 } else {
265 printf("ERROR: Failed to restart device.\n");
266 }
267 break;
268 case CMD_SHUTDOWN:
269 if (diagnostics_relay_shutdown(diagnostics_client, DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) == DIAGNOSTICS_RELAY_E_SUCCESS) {
270 printf("Shutting down device.\n");
271 result = EXIT_SUCCESS;
272 } else {
273 printf("ERROR: Failed to shutdown device.\n");
274 }
275 break;
276 case CMD_MOBILEGESTALT:
277 if (diagnostics_relay_query_mobilegestalt(diagnostics_client, keys, &node) == DIAGNOSTICS_RELAY_E_SUCCESS) {
278 if (node) {
279 print_xml(node);
280 result = EXIT_SUCCESS;
281 }
282 } else {
283 printf("ERROR: Unable to query mobilegestalt keys.\n");
284 }
285 break;
286 case CMD_IOREGISTRY_ENTRY:
287 if (diagnostics_relay_query_ioregistry_entry(diagnostics_client, cmd_arg == NULL ? "": cmd_arg, "", &node) == DIAGNOSTICS_RELAY_E_SUCCESS) {
288 if (node) {
289 print_xml(node);
290 result = EXIT_SUCCESS;
291 }
292 } else {
293 printf("ERROR: Unable to retrieve IORegistry from device.\n");
294 }
295 break;
296 case CMD_IOREGISTRY:
297 if (diagnostics_relay_query_ioregistry_plane(diagnostics_client, cmd_arg == NULL ? "": cmd_arg, &node) == DIAGNOSTICS_RELAY_E_SUCCESS) {
298 if (node) {
299 print_xml(node);
300 result = EXIT_SUCCESS;
301 }
302 } else {
303 printf("ERROR: Unable to retrieve IORegistry from device.\n");
304 }
305 break;
306 case CMD_DIAGNOSTICS:
307 default:
308 if (diagnostics_relay_request_diagnostics(diagnostics_client, cmd_arg, &node) == DIAGNOSTICS_RELAY_E_SUCCESS) {
309 if (node) {
310 print_xml(node);
311 result = EXIT_SUCCESS;
312 }
313 } else {
314 printf("ERROR: Unable to retrieve diagnostics from device.\n");
315 }
316 break;
317 }
318
319 diagnostics_relay_goodbye(diagnostics_client);
320 diagnostics_relay_client_free(diagnostics_client);
321 }
322 } else {
323 printf("ERROR: Could not start diagnostics service!\n");
324 }
325
326 if (service) {
327 lockdownd_service_descriptor_free(service);
328 service = NULL;
329 }
330
331 idevice_free(device);
332
333cleanup:
334 if (node) {
335 plist_free(node);
336 }
337 if (keys) {
338 plist_free(keys);
339 }
340 if (cmd_arg) {
341 free(cmd_arg);
342 }
343 return result;
344}
diff --git a/tools/ideviceenterrecovery.c b/tools/ideviceenterrecovery.c
index 827946b..29cc5c9 100644
--- a/tools/ideviceenterrecovery.c
+++ b/tools/ideviceenterrecovery.c
@@ -8,86 +8,132 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 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 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 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define TOOL_NAME "ideviceenterrecovery"
27
22#include <stdio.h> 28#include <stdio.h>
23#include <string.h> 29#include <string.h>
24#include <errno.h>
25#include <stdlib.h> 30#include <stdlib.h>
31#include <getopt.h>
32#include <errno.h>
33#ifndef WIN32
34#include <signal.h>
35#endif
26 36
27#include <libimobiledevice/libimobiledevice.h> 37#include <libimobiledevice/libimobiledevice.h>
28#include <libimobiledevice/lockdown.h> 38#include <libimobiledevice/lockdown.h>
29 39
30static void print_usage(int argc, char **argv) 40static void print_usage(int argc, char **argv, int is_error)
31{ 41{
32 char *name = NULL; 42 char *name = strrchr(argv[0], '/');
33 43 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] UDID\n", (name ? name + 1: argv[0]));
34 name = strrchr(argv[0], '/'); 44 fprintf(is_error ? stderr : stdout,
35 printf("Usage: %s [OPTIONS] UUID\n", (name ? name + 1: argv[0])); 45 "\n"
36 printf("Makes a device with the supplied 40-digit UUID enter recovery mode immediately.\n\n"); 46 "Makes a device with the supplied UDID enter recovery mode immediately.\n"
37 printf(" -d, --debug\t\tenable communication debugging\n"); 47 "\n"
38 printf(" -h, --help\t\tprints usage information\n"); 48 "OPTIONS:\n"
39 printf("\n"); 49 " -d, --debug enable communication debugging\n"
50 " -h, --help prints usage information\n"
51 " -v, --version prints version information\n"
52 "\n"
53 "Homepage: <" PACKAGE_URL ">\n"
54 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
55 );
40} 56}
41 57
42int main(int argc, char *argv[]) 58int main(int argc, char *argv[])
43{ 59{
44 lockdownd_client_t client = NULL; 60 lockdownd_client_t client = NULL;
45 idevice_t phone = NULL; 61 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
62 idevice_t device = NULL;
46 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 63 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
47 int i; 64 const char* udid = NULL;
48 char uuid[41]; 65 int c = 0;
49 uuid[0] = 0; 66 const struct option longopts[] = {
67 { "debug", no_argument, NULL, 'd' },
68 { "help", no_argument, NULL, 'h' },
69 { "version", no_argument, NULL, 'v' },
70 { NULL, 0, NULL, 0}
71 };
50 72
73#ifndef WIN32
74 signal(SIGPIPE, SIG_IGN);
75#endif
51 /* parse cmdline args */ 76 /* parse cmdline args */
52 for (i = 1; i < argc; i++) { 77 while ((c = getopt_long(argc, argv, "dhv", longopts, NULL)) != -1) {
53 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 78 switch (c) {
79 case 'd':
54 idevice_set_debug_level(1); 80 idevice_set_debug_level(1);
55 continue; 81 break;
56 } 82 case 'h':
57 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 83 print_usage(argc, argv, 0);
58 print_usage(argc, argv); 84 return 0;
85 case 'v':
86 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
59 return 0; 87 return 0;
88 default:
89 print_usage(argc, argv, 1);
90 return 2;
60 } 91 }
61 } 92 }
93 argc -= optind;
94 argv += optind;
62 95
63 i--; 96 if (!argv[0]) {
64 if (!argv[i] || (strlen(argv[i]) != 40)) { 97 fprintf(stderr, "ERROR: No UDID specified\n");
65 print_usage(argc, argv); 98 print_usage(argc+optind, argv-optind, 1);
66 return 0; 99 return 2;
67 } 100 }
68 strcpy(uuid, argv[i]); 101 udid = argv[0];
69 102
70 ret = idevice_new(&phone, uuid); 103 ret = idevice_new(&device, udid);
71 if (ret != IDEVICE_E_SUCCESS) { 104 if (ret != IDEVICE_E_SUCCESS) {
72 printf("No device found with uuid %s, is it plugged in?\n", uuid); 105 printf("No device found with udid %s.\n", udid);
73 return -1; 106 return 1;
74 } 107 }
75 108
76 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new(phone, &client, "ideviceenterrecovery")) { 109 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new(device, &client, TOOL_NAME))) {
77 idevice_free(phone); 110 printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
78 return -1; 111 idevice_free(device);
112 return 1;
79 } 113 }
80 114
81 /* run query and output information */ 115 int res = 0;
82 printf("Telling device with uuid %s to enter recovery mode.\n", uuid); 116 printf("Telling device with udid %s to enter recovery mode.\n", udid);
83 if(lockdownd_enter_recovery(client) != LOCKDOWN_E_SUCCESS) 117 ldret = lockdownd_enter_recovery(client);
84 { 118 if (ldret == LOCKDOWN_E_SESSION_INACTIVE) {
119 lockdownd_client_free(client);
120 client = NULL;
121 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
122 printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
123 idevice_free(device);
124 return 1;
125 }
126 ldret = lockdownd_enter_recovery(client);
127 }
128 if (ldret != LOCKDOWN_E_SUCCESS) {
85 printf("Failed to enter recovery mode.\n"); 129 printf("Failed to enter recovery mode.\n");
130 res = 1;
131 } else {
132 printf("Device is successfully switching to recovery mode.\n");
86 } 133 }
87 printf("Device is successfully switching to recovery mode.\n");
88 134
89 lockdownd_client_free(client); 135 lockdownd_client_free(client);
90 idevice_free(phone); 136 idevice_free(device);
91 137
92 return 0; 138 return res;
93} 139}
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
index 3d299f8..511583e 100644
--- a/tools/ideviceimagemounter.c
+++ b/tools/ideviceimagemounter.c
@@ -1,26 +1,30 @@
1/** 1/*
2 * ideviceimagemounter -- Mount developer/debug disk images on the iPhone/iPod 2 * ideviceimagemounter.c
3 * Mount developer/debug disk images on the device
3 * 4 *
4 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li> 5 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
5 * 6 *
6 * Licensed under the GNU General Public License Version 2 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.
7 * 11 *
8 * This program is free software; you can redistribute it and/or modify 12 * This library is distributed in the hope that it will be useful,
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * GNU General Public License for more profile. 15 * Lesser General Public License for more details.
17 * 16 *
18 * You should have received a copy of the GNU General Public License 17 * You should have received a copy of the GNU Lesser General Public
19 * along with this program; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * USA
22 */ 20 */
23 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define TOOL_NAME "ideviceimagemounter"
27
24#include <stdlib.h> 28#include <stdlib.h>
25#define _GNU_SOURCE 1 29#define _GNU_SOURCE 1
26#define __USE_GNU 1 30#define __USE_GNU 1
@@ -28,297 +32,311 @@
28#include <string.h> 32#include <string.h>
29#include <getopt.h> 33#include <getopt.h>
30#include <errno.h> 34#include <errno.h>
31#include <glib.h>
32#include <libgen.h> 35#include <libgen.h>
36#include <time.h>
37#include <sys/time.h>
38#include <inttypes.h>
39#ifndef WIN32
40#include <signal.h>
41#endif
33 42
34#include <libimobiledevice/libimobiledevice.h> 43#include <libimobiledevice/libimobiledevice.h>
35#include <libimobiledevice/lockdown.h> 44#include <libimobiledevice/lockdown.h>
36#include <libimobiledevice/afc.h> 45#include <libimobiledevice/afc.h>
37#include <libimobiledevice/notification_proxy.h> 46#include <libimobiledevice/notification_proxy.h>
38#include <libimobiledevice/mobile_image_mounter.h> 47#include <libimobiledevice/mobile_image_mounter.h>
39 48#include <libimobiledevice-glue/sha.h>
40static int indent_level = 0; 49#include <libimobiledevice-glue/utils.h>
50#include <asprintf.h>
51#include <plist/plist.h>
52#include <libtatsu/tss.h>
41 53
42static int list_mode = 0; 54static int list_mode = 0;
55static int use_network = 0;
43static int xml_mode = 0; 56static int xml_mode = 0;
44static char *uuid = NULL; 57static const char *udid = NULL;
45static char *imagetype = NULL; 58static const char *imagetype = NULL;
46 59
47static const char PKG_PATH[] = "PublicStaging"; 60static const char PKG_PATH[] = "PublicStaging";
48static const char PATH_PREFIX[] = "/private/var/mobile/Media"; 61static const char PATH_PREFIX[] = "/private/var/mobile/Media";
49 62
50static void print_usage(int argc, char **argv) 63typedef enum {
64 DISK_IMAGE_UPLOAD_TYPE_AFC,
65 DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
66} disk_image_upload_type_t;
67
68enum cmd_mode {
69 CMD_NONE = 0,
70 CMD_MOUNT,
71 CMD_UNMOUNT,
72 CMD_LIST,
73 CMD_DEVMODESTATUS
74};
75
76int cmd = CMD_NONE;
77
78static void print_usage(int argc, char **argv, int is_error)
51{ 79{
52 char *name = NULL; 80 char *name = strrchr(argv[0], '/');
53 81 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0]));
54 name = strrchr(argv[0], '/'); 82 fprintf(is_error ? stderr : stdout,
55 printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0])); 83 "\n"
56 printf("Mounts the specified disk image on the device.\n\n"); 84 "Mount, list, or unmount a disk image on the device.\n"
57 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); 85 "\n"
58 printf(" -l, --list\t\tList mount information\n"); 86 "COMMANDS:\n"
59 printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n"); 87 " mount PATH Mount the developer disk image at PATH.\n"
60 printf(" -x, --xml\t\tUse XML output\n"); 88 " For iOS 17+, PATH is a directory containing a .dmg image,\n"
61 printf(" -d, --debug\t\tenable communication debugging\n"); 89 " a BuildManifest.plist, and a Firmware sub-directory;\n"
62 printf(" -h, --help\t\tprints usage information\n"); 90 " for older versions PATH is a .dmg filename with a"
63 printf("\n"); 91 " .dmg.signature in the same directory, or with another\n"
92 " parameter pointing to a file elsewhere.\n"
93 " list List mounted disk images.\n"
94 " unmount PATH Unmount the image mounted at PATH.\n"
95 " devmodestatus Query the developer mode status (iOS 16+)\n"
96 "\n"
97 "OPTIONS:\n"
98 " -u, --udid UDID target specific device by UDID\n"
99 " -n, --network connect to network device\n"
100 " -t, --imagetype TYPE Image type to use, default is 'Developer'\n"
101 " -x, --xml Use XML output\n"
102 " -d, --debug enable communication debugging\n"
103 " -h, --help prints usage information\n"
104 " -v, --version prints version information\n"
105 "\n"
106 "Homepage: <" PACKAGE_URL ">\n"
107 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
108 );
64} 109}
65 110
66static void parse_opts(int argc, char **argv) 111static void parse_opts(int argc, char **argv)
67{ 112{
113 int debug_level = 0;
68 static struct option longopts[] = { 114 static struct option longopts[] = {
69 {"help", 0, NULL, 'h'}, 115 { "help", no_argument, NULL, 'h' },
70 {"uuid", 0, NULL, 'u'}, 116 { "udid", required_argument, NULL, 'u' },
71 {"list", 0, NULL, 'l'}, 117 { "network", no_argument, NULL, 'n' },
72 {"imagetype", 0, NULL, 't'}, 118 { "imagetype", required_argument, NULL, 't' },
73 {"xml", 0, NULL, 'x'}, 119 { "xml", no_argument, NULL, 'x' },
74 {"debug", 0, NULL, 'd'}, 120 { "debug", no_argument, NULL, 'd' },
75 {NULL, 0, NULL, 0} 121 { "version", no_argument, NULL, 'v' },
122 { NULL, 0, NULL, 0 }
76 }; 123 };
77 int c; 124 int c;
78 125
79 while (1) { 126 while (1) {
80 c = getopt_long(argc, argv, "hu:lt:xd", longopts, 127 c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL);
81 (int *) 0);
82 if (c == -1) { 128 if (c == -1) {
83 break; 129 break;
84 } 130 }
85 131
86 switch (c) { 132 switch (c) {
87 case 'h': 133 case 'h':
88 print_usage(argc, argv); 134 print_usage(argc, argv, 0);
89 exit(0); 135 exit(0);
90 case 'u': 136 case 'u':
91 if (strlen(optarg) != 40) { 137 if (!*optarg) {
92 printf("%s: invalid UUID specified (length != 40)\n", 138 fprintf(stderr, "ERROR: UDID must not be empty!\n");
93 argv[0]); 139 print_usage(argc, argv, 1);
94 print_usage(argc, argv);
95 exit(2); 140 exit(2);
96 } 141 }
97 uuid = strdup(optarg); 142 udid = optarg;
98 break; 143 break;
99 case 'l': 144 case 'n':
100 list_mode = 1; 145 use_network = 1;
101 break; 146 break;
102 case 't': 147 case 't':
103 imagetype = strdup(optarg); 148 imagetype = optarg;
104 break; 149 break;
105 case 'x': 150 case 'x':
106 xml_mode = 1; 151 xml_mode = 1;
107 break; 152 break;
108 case 'd': 153 case 'd':
109 idevice_set_debug_level(1); 154 debug_level++;
110 break; 155 break;
156 case 'v':
157 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
158 exit(0);
111 default: 159 default:
112 print_usage(argc, argv); 160 print_usage(argc, argv, 1);
113 exit(2); 161 exit(2);
114 } 162 }
115 } 163 }
164 idevice_set_debug_level(debug_level);
165 tss_set_debug_level(debug_level);
116} 166}
117 167
118static void plist_node_to_string(plist_t node); 168static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
119
120static void plist_array_to_string(plist_t node)
121{ 169{
122 /* iterate over items */ 170 return fread(buf, 1, size, (FILE*)userdata);
123 int i, count;
124 plist_t subnode = NULL;
125
126 count = plist_array_get_size(node);
127
128 for (i = 0; i < count; i++) {
129 subnode = plist_array_get_item(node, i);
130 printf("%*s", indent_level, "");
131 printf("%d: ", i);
132 plist_node_to_string(subnode);
133 }
134}
135
136static void plist_dict_to_string(plist_t node)
137{
138 /* iterate over key/value pairs */
139 plist_dict_iter it = NULL;
140
141 char* key = NULL;
142 plist_t subnode = NULL;
143 plist_dict_new_iter(node, &it);
144 plist_dict_next_item(node, it, &key, &subnode);
145 while (subnode)
146 {
147 printf("%*s", indent_level, "");
148 printf("%s", key);
149 if (plist_get_node_type(subnode) == PLIST_ARRAY)
150 printf("[%d]: ", plist_array_get_size(subnode));
151 else
152 printf(": ");
153 free(key);
154 key = NULL;
155 plist_node_to_string(subnode);
156 plist_dict_next_item(node, it, &key, &subnode);
157 }
158 free(it);
159}
160
161static void plist_node_to_string(plist_t node)
162{
163 char *s = NULL;
164 char *data = NULL;
165 double d;
166 uint8_t b;
167 uint64_t u = 0;
168 GTimeVal tv = { 0, 0 };
169
170 plist_type t;
171
172 if (!node)
173 return;
174
175 t = plist_get_node_type(node);
176
177 switch (t) {
178 case PLIST_BOOLEAN:
179 plist_get_bool_val(node, &b);
180 printf("%s\n", (b ? "true" : "false"));
181 break;
182
183 case PLIST_UINT:
184 plist_get_uint_val(node, &u);
185 printf("%llu\n", (long long)u);
186 break;
187
188 case PLIST_REAL:
189 plist_get_real_val(node, &d);
190 printf("%f\n", d);
191 break;
192
193 case PLIST_STRING:
194 plist_get_string_val(node, &s);
195 printf("%s\n", s);
196 free(s);
197 break;
198
199 case PLIST_KEY:
200 plist_get_key_val(node, &s);
201 printf("%s: ", s);
202 free(s);
203 break;
204
205 case PLIST_DATA:
206 plist_get_data_val(node, &data, &u);
207 uint64_t i;
208 for (i = 0; i < u; i++) {
209 printf("%02x", (unsigned char)data[i]);
210 }
211 free(data);
212 printf("\n");
213 g_free(s);
214 break;
215
216 case PLIST_DATE:
217 plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec);
218 s = g_time_val_to_iso8601(&tv);
219 printf("%s\n", s);
220 free(s);
221 break;
222
223 case PLIST_ARRAY:
224 printf("\n");
225 indent_level++;
226 plist_array_to_string(node);
227 indent_level--;
228 break;
229
230 case PLIST_DICT:
231 printf("\n");
232 indent_level++;
233 plist_dict_to_string(node);
234 indent_level--;
235 break;
236
237 default:
238 break;
239 }
240}
241
242static void print_xml(plist_t node)
243{
244 char *xml = NULL;
245 uint32_t len = 0;
246 plist_to_xml(node, &xml, &len);
247 if (xml)
248 puts(xml);
249} 171}
250 172
251int main(int argc, char **argv) 173int main(int argc, char **argv)
252{ 174{
253 idevice_t device = NULL; 175 idevice_t device = NULL;
254 lockdownd_client_t lckd = NULL; 176 lockdownd_client_t lckd = NULL;
177 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
255 mobile_image_mounter_client_t mim = NULL; 178 mobile_image_mounter_client_t mim = NULL;
256 afc_client_t afc = NULL; 179 afc_client_t afc = NULL;
257 uint16_t port = 0; 180 lockdownd_service_descriptor_t service = NULL;
258 int res = -1; 181 int res = -1;
259 char *image_path = NULL; 182 char *image_path = NULL;
183 size_t image_size = 0;
260 char *image_sig_path = NULL; 184 char *image_sig_path = NULL;
261 185
186#ifndef WIN32
187 signal(SIGPIPE, SIG_IGN);
188#endif
262 parse_opts(argc, argv); 189 parse_opts(argc, argv);
263 190
264 argc -= optind; 191 argc -= optind;
265 argv += optind; 192 argv += optind;
266 193
267 if (!list_mode) { 194 if (argc == 0) {
268 if (argc < 1) { 195 fprintf(stderr, "ERROR: Missing command.\n\n");
269 printf("ERROR: No IMAGE_FILE has been given!\n"); 196 print_usage(argc+optind, argv-optind, 1);
270 return -1; 197 return 2;
271 } 198 }
272 image_path = strdup(argv[0]); 199
273 if (argc >= 2) { 200 char* cmdstr = argv[0];
274 image_sig_path = strdup(argv[1]); 201
202 int optind2 = 0;
203 if (!strcmp(cmdstr, "mount")) {
204 cmd = CMD_MOUNT;
205 optind2++;
206 } else if (!strcmp(cmdstr, "list")) {
207 cmd = CMD_LIST;
208 optind2++;
209 } else if (!strcmp(cmdstr, "umount") || !strcmp(cmdstr, "unmount")) {
210 cmd = CMD_UNMOUNT;
211 optind2++;
212 } else if (!strcmp(cmdstr, "devmodestatus")) {
213 cmd = CMD_DEVMODESTATUS;
214 optind2++;
215 } else {
216 // assume mount command, unless -l / --list was specified
217 if (list_mode) {
218 cmd = CMD_LIST;
275 } else { 219 } else {
276 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { 220 cmd = CMD_MOUNT;
277 printf("Out of memory?!\n");
278 return -1;
279 }
280 } 221 }
281 } 222 }
282 223
283 if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) { 224 argc -= optind2;
284 printf("No device found, is it plugged in?\n"); 225 argv += optind2;
285 return -1; 226 optind += optind2;
227
228 switch (cmd) {
229 case CMD_MOUNT:
230 if (argc < 1) {
231 fprintf(stderr, "ERROR: Missing IMAGE_FILE for mount command\n");
232 print_usage(argc+optind, argv-optind, 1);
233 return 2;
234 }
235 image_path = strdup(argv[0]);
236 if (argc >= 2) {
237 image_sig_path = strdup(argv[1]);
238 } else {
239 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
240 printf("Out of memory?!\n");
241 return 1;
242 }
243 }
244 break;
245 case CMD_UNMOUNT:
246 if (argc != 1) {
247 fprintf(stderr, "ERROR: Missing mount path (argc = %d)\n", argc);
248 print_usage(argc+optind, argv-optind, 1);
249 return 2;
250 }
251 break;
252 default:
253 break;
254 }
255
256 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
257 if (udid) {
258 printf("No device found with udid %s.\n", udid);
259 } else {
260 printf("No device found.\n");
261 }
262 return 1;
286 } 263 }
287 264
288 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) { 265 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
289 printf("ERROR: could not connect to lockdown. Exiting.\n"); 266 printf("ERROR: Could not connect to lockdown, error code %d.\n", ldret);
290 goto leave; 267 goto leave;
291 } 268 }
292 269
293 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &port); 270 plist_t pver = NULL;
271 char *product_version = NULL;
272 lockdownd_get_value(lckd, NULL, "ProductVersion", &pver);
273 if (pver && plist_get_node_type(pver) == PLIST_STRING) {
274 plist_get_string_val(pver, &product_version);
275 }
276 disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC;
277 int product_version_major = 0;
278 int product_version_minor = 0;
279 if (product_version) {
280 if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) {
281 if (product_version_major >= 7)
282 disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
283 }
284 }
294 285
295 if (port == 0) { 286 if (product_version_major >= 16) {
287 uint8_t dev_mode_status = 0;
288 plist_t val = NULL;
289 ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
290 if (ldret == LOCKDOWN_E_SUCCESS) {
291 plist_get_bool_val(val, &dev_mode_status);
292 plist_free(val);
293 }
294 if (!dev_mode_status) {
295 printf("ERROR: You have to enable Developer Mode on the given device in order to allowing mounting a developer disk image.\n");
296 goto leave;
297 }
298 }
299
300 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service);
301
302 if (!service || service->port == 0) {
296 printf("ERROR: Could not start mobile_image_mounter service!\n"); 303 printf("ERROR: Could not start mobile_image_mounter service!\n");
297 goto leave; 304 goto leave;
298 } 305 }
299 306
300 if (mobile_image_mounter_new(device, port, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 307 if (mobile_image_mounter_new(device, service, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
301 printf("ERROR: Could not connect to mobile_image_mounter!\n"); 308 printf("ERROR: Could not connect to mobile_image_mounter!\n");
302 goto leave; 309 goto leave;
303 } 310 }
311
312 if (service) {
313 lockdownd_service_descriptor_free(service);
314 service = NULL;
315 }
304 316
305 if (!list_mode) { 317 if (cmd == CMD_MOUNT) {
306 struct stat fst; 318 struct stat fst;
307 port = 0; 319 if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
308 if ((lockdownd_start_service(lckd, "com.apple.afc", &port) != 320 if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
309 LOCKDOWN_E_SUCCESS) || !port) { 321 LOCKDOWN_E_SUCCESS) || !service || !service->port) {
310 fprintf(stderr, "Could not start com.apple.afc!\n"); 322 fprintf(stderr, "Could not start com.apple.afc!\n");
311 goto leave; 323 goto leave;
312 } 324 }
313 if (afc_client_new(device, port, &afc) != AFC_E_SUCCESS) { 325 if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) {
314 fprintf(stderr, "Could not connect to AFC!\n"); 326 fprintf(stderr, "Could not connect to AFC!\n");
315 goto leave; 327 goto leave;
328 }
329 if (service) {
330 lockdownd_service_descriptor_free(service);
331 service = NULL;
332 }
316 } 333 }
317 if (stat(image_path, &fst) != 0) { 334 if (stat(image_path, &fst) != 0) {
318 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno)); 335 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
319 goto leave; 336 goto leave;
320 } 337 }
321 if (stat(image_sig_path, &fst) != 0) { 338 image_size = fst.st_size;
339 if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) {
322 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno)); 340 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
323 goto leave; 341 goto leave;
324 } 342 }
@@ -327,49 +345,240 @@ int main(int argc, char **argv)
327 lockdownd_client_free(lckd); 345 lockdownd_client_free(lckd);
328 lckd = NULL; 346 lckd = NULL;
329 347
330 mobile_image_mounter_error_t err; 348 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
331 plist_t result = NULL; 349 plist_t result = NULL;
332 350
333 if (list_mode) { 351 if (cmd == CMD_LIST) {
334 /* list mounts mode */ 352 /* list mounts mode */
335 if (!imagetype) { 353 if (!imagetype) {
336 imagetype = strdup("Developer"); 354 if (product_version_major < 17) {
355 imagetype = "Developer";
356 } else {
357 imagetype = "Personalized";
358 }
337 } 359 }
338 err = mobile_image_mounter_lookup_image(mim, imagetype, &result); 360 err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
339 free(imagetype);
340 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 361 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
341 res = 0; 362 res = 0;
342 if (xml_mode) { 363 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
343 print_xml(result);
344 } else {
345 plist_dict_to_string(result);
346 }
347 } else { 364 } else {
348 printf("Error: lookup_image returned %d\n", err); 365 printf("Error: lookup_image returned %d\n", err);
349 } 366 }
350 } else { 367 } else if (cmd == CMD_MOUNT) {
351 char sig[8192]; 368 unsigned char *sig = NULL;
352 size_t sig_length = 0; 369 size_t sig_length = 0;
353 FILE *f = fopen(image_sig_path, "r"); 370 FILE *f;
354 if (!f) { 371 struct stat fst;
355 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); 372 plist_t mount_options = NULL;
356 goto leave;
357 }
358 sig_length = fread(sig, 1, sizeof(sig), f);
359 fclose(f);
360 if (sig_length == 0) {
361 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
362 goto leave;
363 }
364 373
365 f = fopen(image_path, "r"); 374 if (product_version_major < 17) {
366 if (!f) { 375 f = fopen(image_sig_path, "rb");
367 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); 376 if (!f) {
368 goto leave; 377 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
378 goto leave;
379 }
380 if (fstat(fileno(f), &fst) != 0) {
381 fprintf(stderr, "Error: fstat: %s\n", strerror(errno));
382 goto leave;
383 }
384 sig = malloc(fst.st_size);
385 sig_length = fread(sig, 1, fst.st_size, f);
386 fclose(f);
387 if (sig_length == 0) {
388 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
389 goto leave;
390 }
391
392 f = fopen(image_path, "rb");
393 if (!f) {
394 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
395 goto leave;
396 }
397 } else {
398 if (stat(image_path, &fst) != 0) {
399 fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno));
400 goto leave;
401 }
402 if (!S_ISDIR(fst.st_mode)) {
403 fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n");
404 goto leave;
405 }
406 char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL);
407 plist_t build_manifest = NULL;
408 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) {
409 free(build_manifest_path);
410 build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL);
411 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) {
412 char* image_path_new = string_build_path(image_path, "Restore", NULL);
413 free(image_path);
414 image_path = image_path_new;
415 }
416 }
417 if (!build_manifest) {
418 fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n");
419 goto leave;
420 }
421
422 plist_t identifiers = NULL;
423 mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers);
424 if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
425 fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr);
426 goto error_out;
427 }
428
429 unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId");
430 unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID");
431
432 plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities");
433 plist_array_iter iter;
434 plist_array_new_iter(build_identities, &iter);
435 plist_t item = NULL;
436 plist_t build_identity = NULL;
437 do {
438 plist_array_next_item(build_identities, iter, &item);
439 if (!item) {
440 break;
441 }
442 unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID");
443 unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID");
444 if (bi_chip_id == chip_id && bi_board_id == board_id) {
445 build_identity = item;
446 break;
447 }
448 } while (item);
449 plist_mem_free(iter);
450 if (!build_identity) {
451 fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n");
452 goto leave;
453 }
454 plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path");
455 if (!p_tc_path) {
456 fprintf(stderr, "Error: Could not determine path for trust cache!\n");
457 goto leave;
458 }
459 plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path");
460 if (!p_dmg_path) {
461 fprintf(stderr, "Error: Could not determine path for disk image!\n");
462 goto leave;
463 }
464 char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL);
465 unsigned char* trust_cache = NULL;
466 uint64_t trust_cache_size = 0;
467 if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) {
468 fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path);
469 goto leave;
470 }
471 mount_options = plist_new_dict();
472 plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size));
473 free(trust_cache);
474 char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL);
475 free(image_path);
476 image_path = dmg_path;
477 f = fopen(image_path, "rb");
478 if (!f) {
479 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
480 goto leave;
481 }
482
483 unsigned char buf[8192];
484 unsigned char sha384_digest[48];
485 sha384_context ctx;
486 sha384_init(&ctx);
487 fstat(fileno(f), &fst);
488 image_size = fst.st_size;
489 while (!feof(f)) {
490 ssize_t fr = fread(buf, 1, sizeof(buf), f);
491 if (fr <= 0) {
492 break;
493 }
494 sha384_update(&ctx, buf, fr);
495 }
496 rewind(f);
497 sha384_final(&ctx, sha384_digest);
498 unsigned char* manifest = NULL;
499 unsigned int manifest_size = 0;
500 /* check if the device already has a personalization manifest for this image */
501 if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
502 printf("Using existing personalization manifest from device.\n");
503 } else {
504 /* we need to re-connect in this case */
505 mobile_image_mounter_free(mim);
506 mim = NULL;
507 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
508 goto error_out;
509 }
510 printf("No personalization manifest, requesting from TSS...\n");
511 unsigned char* nonce = NULL;
512 unsigned int nonce_size = 0;
513
514 /* create new TSS request and fill parameters */
515 plist_t request = tss_request_new(NULL);
516 plist_t params = plist_new_dict();
517 tss_parameters_add_from_manifest(params, build_identity, 1);
518
519 /* copy all `Ap,*` items from identifiers */
520 plist_dict_iter di = NULL;
521 plist_dict_new_iter(identifiers, &di);
522 plist_t node = NULL;
523 do {
524 char* key = NULL;
525 plist_dict_next_item(identifiers, di, &key, &node);
526 if (node) {
527 if (!strncmp(key, "Ap,", 3)) {
528 plist_dict_set_item(request, key, plist_copy(node));
529 }
530 }
531 free(key);
532 } while (node);
533 plist_mem_free(di);
534
535 plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID");
536 plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1));
537 plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1));
538 plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1));
539
540 /* query nonce from image mounter service */
541 merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size);
542 if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
543 plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size));
544 } else {
545 fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr);
546 goto error_out;
547 }
548 mobile_image_mounter_free(mim);
549 mim = NULL;
550
551 plist_dict_set_item(params, "ApSepNonce", plist_new_data("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20));
552 plist_dict_set_item(params, "UID_MODE", plist_new_bool(0));
553 tss_request_add_ap_tags(request, params, NULL);
554 tss_request_add_common_tags(request, params, NULL);
555 tss_request_add_ap_img4_tags(request, params);
556 plist_free(params);
557
558 /* request IM4M from TSS */
559 plist_t response = tss_request_send(request, NULL);
560 plist_free(request);
561
562 plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket");
563 if (!PLIST_IS_DATA(p_manifest)) {
564 fprintf(stderr, "Failed to get Img4Ticket\n");
565 goto error_out;
566 }
567
568 uint64_t m4m_len = 0;
569 plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len);
570 manifest_size = m4m_len;
571 plist_free(response);
572 printf("Done.\n");
573 }
574 sig = manifest;
575 sig_length = manifest_size;
576
577 imagetype = "Personalized";
369 } 578 }
370 579
371 char *targetname = NULL; 580 char *targetname = NULL;
372 if (asprintf(&targetname, "%s/%s", PKG_PATH, basename(image_path)) < 0) { 581 if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) {
373 fprintf(stderr, "Out of memory!?\n"); 582 fprintf(stderr, "Out of memory!?\n");
374 goto leave; 583 goto leave;
375 } 584 }
@@ -379,68 +588,91 @@ int main(int argc, char **argv)
379 goto leave; 588 goto leave;
380 } 589 }
381 590
382 printf("Copying '%s' --> '%s'\n", image_path, targetname); 591 if (!imagetype) {
383 592 imagetype = "Developer";
384 char **strs = NULL;
385 if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
386 if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
387 fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
388 }
389 }
390 if (strs) {
391 int i = 0;
392 while (strs[i]) {
393 free(strs[i]);
394 i++;
395 }
396 free(strs);
397 } 593 }
398 594
399 uint64_t af = 0; 595 if (!mim) {
400 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) != 596 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
401 AFC_E_SUCCESS) || !af) { 597 goto error_out;
402 fclose(f); 598 }
403 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
404 goto leave;
405 } 599 }
406 600
407 char buf[8192]; 601 switch(disk_image_upload_type) {
408 size_t amount = 0; 602 case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
409 do { 603 printf("Uploading %s\n", image_path);
410 amount = fread(buf, 1, sizeof(buf), f); 604 err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig, sig_length, mim_upload_cb, f);
411 if (amount > 0) { 605 break;
412 uint32_t written, total = 0; 606 case DISK_IMAGE_UPLOAD_TYPE_AFC:
413 while (total < amount) { 607 default:
414 written = 0; 608 printf("Uploading %s --> afc:///%s\n", image_path, targetname);
415 if (afc_file_write(afc, af, buf, amount, &written) != 609 char **strs = NULL;
416 AFC_E_SUCCESS) { 610 if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
417 fprintf(stderr, "AFC Write error!\n"); 611 if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
418 break; 612 fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
613 }
614 }
615 if (strs) {
616 int i = 0;
617 while (strs[i]) {
618 free(strs[i]);
619 i++;
419 } 620 }
420 total += written; 621 free(strs);
421 } 622 }
422 if (total != amount) { 623
423 fprintf(stderr, "Error: wrote only %d of %d\n", total, 624 uint64_t af = 0;
424 (unsigned int)amount); 625 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
425 afc_file_close(afc, af); 626 AFC_E_SUCCESS) || !af) {
426 fclose(f); 627 fclose(f);
628 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
427 goto leave; 629 goto leave;
428 } 630 }
429 } 631
632 char buf[8192];
633 size_t amount = 0;
634 do {
635 amount = fread(buf, 1, sizeof(buf), f);
636 if (amount > 0) {
637 uint32_t written, total = 0;
638 while (total < amount) {
639 written = 0;
640 if (afc_file_write(afc, af, buf + total, amount - total, &written) !=
641 AFC_E_SUCCESS) {
642 fprintf(stderr, "AFC Write error!\n");
643 break;
644 }
645 total += written;
646 }
647 if (total != amount) {
648 fprintf(stderr, "Error: wrote only %d of %d\n", total,
649 (unsigned int)amount);
650 afc_file_close(afc, af);
651 fclose(f);
652 goto leave;
653 }
654 }
655 }
656 while (amount > 0);
657
658 afc_file_close(afc, af);
659 break;
430 } 660 }
431 while (amount > 0);
432 661
433 afc_file_close(afc, af);
434 fclose(f); 662 fclose(f);
435 663
664 if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
665 if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) {
666 printf("ERROR: Device is locked, can't mount. Unlock device and try again.\n");
667 } else {
668 printf("ERROR: Unknown error occurred, can't mount.\n");
669 }
670 goto error_out;
671 }
436 printf("done.\n"); 672 printf("done.\n");
437 673
438 printf("Mounting...\n"); 674 printf("Mounting...\n");
439 if (!imagetype) { 675 err = mobile_image_mounter_mount_image_with_options(mim, mountname, sig, sig_length, imagetype, mount_options, &result);
440 imagetype = strdup("Developer");
441 }
442 err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
443 free(imagetype);
444 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 676 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
445 if (result) { 677 if (result) {
446 plist_t node = plist_dict_get_item(result, "Status"); 678 plist_t node = plist_dict_get_item(result, "Status");
@@ -453,20 +685,12 @@ int main(int argc, char **argv)
453 res = 0; 685 res = 0;
454 } else { 686 } else {
455 printf("unexpected status value:\n"); 687 printf("unexpected status value:\n");
456 if (xml_mode) { 688 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
457 print_xml(result);
458 } else {
459 plist_dict_to_string(result);
460 }
461 } 689 }
462 free(status); 690 free(status);
463 } else { 691 } else {
464 printf("unexpected result:\n"); 692 printf("unexpected result:\n");
465 if (xml_mode) { 693 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
466 print_xml(result);
467 } else {
468 plist_dict_to_string(result);
469 }
470 } 694 }
471 } 695 }
472 node = plist_dict_get_item(result, "Error"); 696 node = plist_dict_get_item(result, "Error");
@@ -478,31 +702,58 @@ int main(int argc, char **argv)
478 free(error); 702 free(error);
479 } else { 703 } else {
480 printf("unexpected result:\n"); 704 printf("unexpected result:\n");
481 if (xml_mode) { 705 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
482 print_xml(result);
483 } else {
484 plist_dict_to_string(result);
485 }
486 } 706 }
487 707 node = plist_dict_get_item(result, "DetailedError");
488 } else { 708 if (node) {
489 if (xml_mode) { 709 printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL));
490 print_xml(result);
491 } else {
492 plist_dict_to_string(result);
493 } 710 }
711 } else {
712 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
494 } 713 }
495 } 714 }
496 } else { 715 } else {
497 printf("Error: mount_image returned %d\n", err); 716 printf("Error: mount_image returned %d\n", err);
498 717
499 } 718 }
719 } else if (cmd == CMD_UNMOUNT) {
720 err = mobile_image_mounter_unmount_image(mim, argv[0]);
721 switch (err) {
722 case MOBILE_IMAGE_MOUNTER_E_SUCCESS:
723 printf("Success\n");
724 res = 0;
725 break;
726 case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED:
727 printf("Error: '%s' is not mounted\n", argv[0]);
728 res = 1;
729 break;
730 case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED:
731 printf("Error: 'unmount' is not supported on this device\n");
732 res = 1;
733 break;
734 case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED:
735 printf("Error: device is locked\n");
736 res = 1;
737 break;
738 default:
739 printf("Error: unmount returned %d\n", err);
740 break;
741 }
742 } else if (cmd == CMD_DEVMODESTATUS) {
743 err = mobile_image_mounter_query_developer_mode_status(mim, &result);
744 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
745 res = 0;
746 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
747 } else {
748 printf("Error: query_developer_mode_status returned %d\n", err);
749 }
500 } 750 }
501 751
502 if (result) { 752 if (result) {
503 plist_free(result); 753 plist_free(result);
504 } 754 }
505 755
756error_out:
506 /* perform hangup command */ 757 /* perform hangup command */
507 mobile_image_mounter_hangup(mim); 758 mobile_image_mounter_hangup(mim);
508 /* free client */ 759 /* free client */
@@ -518,7 +769,7 @@ leave:
518 idevice_free(device); 769 idevice_free(device);
519 770
520 if (image_path) 771 if (image_path)
521 free(image_path); 772 free(image_path);
522 if (image_sig_path) 773 if (image_sig_path)
523 free(image_sig_path); 774 free(image_sig_path);
524 775
diff --git a/tools/ideviceinfo.c b/tools/ideviceinfo.c
index e05165b..fd45763 100644
--- a/tools/ideviceinfo.c
+++ b/tools/ideviceinfo.c
@@ -2,40 +2,59 @@
2 * ideviceinfo.c 2 * ideviceinfo.c
3 * Simple utility to show information about an attached device 3 * Simple utility to show information about an attached device
4 * 4 *
5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
5 * Copyright (c) 2009 Martin Szulecki All Rights Reserved. 6 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * 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 * 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 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21 22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "ideviceinfo"
28
22#include <stdio.h> 29#include <stdio.h>
23#include <string.h> 30#include <string.h>
24#include <errno.h> 31#include <errno.h>
25#include <stdlib.h> 32#include <stdlib.h>
26#include <glib.h> 33#include <getopt.h>
34#ifndef WIN32
35#include <signal.h>
36#endif
27 37
28#include <libimobiledevice/libimobiledevice.h> 38#include <libimobiledevice/libimobiledevice.h>
29#include <libimobiledevice/lockdown.h> 39#include <libimobiledevice/lockdown.h>
40#include <plist/plist.h>
30 41
31#define FORMAT_KEY_VALUE 1 42#define FORMAT_KEY_VALUE 1
32#define FORMAT_XML 2 43#define FORMAT_XML 2
33 44
34static const char *domains[] = { 45static const char *domains[] = {
35 "com.apple.disk_usage", 46 "com.apple.disk_usage",
47 "com.apple.disk_usage.factory",
36 "com.apple.mobile.battery", 48 "com.apple.mobile.battery",
37/* FIXME: For some reason lockdownd segfaults on this, works sometimes though 49/* FIXME: For some reason lockdownd segfaults on this, works sometimes though
38 "com.apple.mobile.debug",. */ 50 "com.apple.mobile.debug",. */
51 "com.apple.iqagent",
52 "com.apple.purplebuddy",
53 "com.apple.PurpleBuddy",
54 "com.apple.mobile.chaperone",
55 "com.apple.mobile.third_party_termination",
56 "com.apple.mobile.lockdownd",
57 "com.apple.mobile.lockdown_cache",
39 "com.apple.xcode.developerdomain", 58 "com.apple.xcode.developerdomain",
40 "com.apple.international", 59 "com.apple.international",
41 "com.apple.mobile.data_sync", 60 "com.apple.mobile.data_sync",
@@ -55,12 +74,12 @@ static const char *domains[] = {
55 "com.apple.iTunes", 74 "com.apple.iTunes",
56 "com.apple.mobile.iTunes.store", 75 "com.apple.mobile.iTunes.store",
57 "com.apple.mobile.iTunes", 76 "com.apple.mobile.iTunes",
77 "com.apple.fmip",
78 "com.apple.Accessibility",
58 NULL 79 NULL
59}; 80};
60 81
61static int indent_level = 0; 82static int is_domain_known(const char *domain)
62
63static int is_domain_known(char *domain)
64{ 83{
65 int i = 0; 84 int i = 0;
66 while (domains[i] != NULL) { 85 while (domains[i] != NULL) {
@@ -71,244 +90,147 @@ static int is_domain_known(char *domain)
71 return 0; 90 return 0;
72} 91}
73 92
74static void plist_node_to_string(plist_t node); 93static void print_usage(int argc, char **argv, int is_error)
75
76static void plist_array_to_string(plist_t node)
77{
78 /* iterate over items */
79 int i, count;
80 plist_t subnode = NULL;
81
82 count = plist_array_get_size(node);
83
84 for (i = 0; i < count; i++) {
85 subnode = plist_array_get_item(node, i);
86 printf("%*s", indent_level, "");
87 printf("%d: ", i);
88 plist_node_to_string(subnode);
89 }
90}
91
92static void plist_dict_to_string(plist_t node)
93{
94 /* iterate over key/value pairs */
95 plist_dict_iter it = NULL;
96
97 char* key = NULL;
98 plist_t subnode = NULL;
99 plist_dict_new_iter(node, &it);
100 plist_dict_next_item(node, it, &key, &subnode);
101 while (subnode)
102 {
103 printf("%*s", indent_level, "");
104 printf("%s", key);
105 if (plist_get_node_type(subnode) == PLIST_ARRAY)
106 printf("[%d]: ", plist_array_get_size(subnode));
107 else
108 printf(": ");
109 free(key);
110 key = NULL;
111 plist_node_to_string(subnode);
112 plist_dict_next_item(node, it, &key, &subnode);
113 }
114 free(it);
115}
116
117static void plist_node_to_string(plist_t node)
118{
119 char *s = NULL;
120 char *data = NULL;
121 double d;
122 uint8_t b;
123 uint64_t u = 0;
124 GTimeVal tv = { 0, 0 };
125
126 plist_type t;
127
128 if (!node)
129 return;
130
131 t = plist_get_node_type(node);
132
133 switch (t) {
134 case PLIST_BOOLEAN:
135 plist_get_bool_val(node, &b);
136 printf("%s\n", (b ? "true" : "false"));
137 break;
138
139 case PLIST_UINT:
140 plist_get_uint_val(node, &u);
141 printf("%llu\n", (long long)u);
142 break;
143
144 case PLIST_REAL:
145 plist_get_real_val(node, &d);
146 printf("%f\n", d);
147 break;
148
149 case PLIST_STRING:
150 plist_get_string_val(node, &s);
151 printf("%s\n", s);
152 free(s);
153 break;
154
155 case PLIST_KEY:
156 plist_get_key_val(node, &s);
157 printf("%s: ", s);
158 free(s);
159 break;
160
161 case PLIST_DATA:
162 plist_get_data_val(node, &data, &u);
163 s = g_base64_encode((guchar *)data, u);
164 free(data);
165 printf("%s\n", s);
166 g_free(s);
167 break;
168
169 case PLIST_DATE:
170 plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec);
171 s = g_time_val_to_iso8601(&tv);
172 printf("%s\n", s);
173 free(s);
174 break;
175
176 case PLIST_ARRAY:
177 printf("\n");
178 indent_level++;
179 plist_array_to_string(node);
180 indent_level--;
181 break;
182
183 case PLIST_DICT:
184 printf("\n");
185 indent_level++;
186 plist_dict_to_string(node);
187 indent_level--;
188 break;
189
190 default:
191 break;
192 }
193}
194
195static void print_usage(int argc, char **argv)
196{ 94{
197 int i = 0; 95 int i = 0;
198 char *name = NULL; 96 char *name = strrchr(argv[0], '/');
199 97 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
200 name = strrchr(argv[0], '/'); 98 fprintf(is_error ? stderr : stdout,
201 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0])); 99 "\n"
202 printf("Show information about a connected iPhone/iPod Touch.\n\n"); 100 "Show information about a connected device.\n"
203 printf(" -d, --debug\t\tenable communication debugging\n"); 101 "\n"
204 printf(" -s, --simple\t\tuse a simple connection to avoid auto-pairing with the device\n"); 102 "OPTIONS:\n"
205 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); 103 " -u, --udid UDID target specific device by UDID\n"
206 printf(" -q, --domain NAME\tset domain of query to NAME. Default: None\n"); 104 " -n, --network connect to network device\n"
207 printf(" -k, --key NAME\tonly query key specified by NAME. Default: All keys.\n"); 105 " -s, --simple use simple connection to avoid auto-pairing with device\n"
208 printf(" -x, --xml\t\toutput information as xml plist instead of key/value pairs\n"); 106 " -q, --domain NAME set domain of query to NAME. Default: None\n" \
209 printf(" -h, --help\t\tprints usage information\n"); 107 " -k, --key NAME only query key specified by NAME. Default: All keys.\n" \
210 printf("\n"); 108 " -x, --xml output information in XML property list format\n" \
211 printf(" Known domains are:\n\n"); 109 " -h, --help prints usage information\n" \
110 " -d, --debug enable communication debugging\n" \
111 " -v, --version prints version information\n" \
112 "\n"
113 );
114 fprintf(is_error ? stderr : stdout, "Known domains are:\n\n");
212 while (domains[i] != NULL) { 115 while (domains[i] != NULL) {
213 printf(" %s\n", domains[i++]); 116 fprintf(is_error ? stderr : stdout, " %s\n", domains[i++]);
214 } 117 }
215 printf("\n"); 118 fprintf(is_error ? stderr : stdout,
119 "\n" \
120 "Homepage: <" PACKAGE_URL ">\n"
121 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
122 );
216} 123}
217 124
218int main(int argc, char *argv[]) 125int main(int argc, char *argv[])
219{ 126{
220 lockdownd_client_t client = NULL; 127 lockdownd_client_t client = NULL;
221 idevice_t phone = NULL; 128 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
129 idevice_t device = NULL;
222 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 130 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
223 int i;
224 int simple = 0; 131 int simple = 0;
225 int format = FORMAT_KEY_VALUE; 132 int format = FORMAT_KEY_VALUE;
226 char uuid[41]; 133 const char* udid = NULL;
227 char *domain = NULL; 134 int use_network = 0;
228 char *key = NULL; 135 const char *domain = NULL;
136 const char *key = NULL;
229 char *xml_doc = NULL; 137 char *xml_doc = NULL;
230 uint32_t xml_length; 138 uint32_t xml_length;
231 plist_t node = NULL; 139 plist_t node = NULL;
232 plist_type node_type;
233 uuid[0] = 0;
234 140
235 /* parse cmdline args */ 141 int c = 0;
236 for (i = 1; i < argc; i++) { 142 const struct option longopts[] = {
237 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 143 { "debug", no_argument, NULL, 'd' },
144 { "help", no_argument, NULL, 'h' },
145 { "udid", required_argument, NULL, 'u' },
146 { "network", no_argument, NULL, 'n' },
147 { "domain", required_argument, NULL, 'q' },
148 { "key", required_argument, NULL, 'k' },
149 { "simple", no_argument, NULL, 's' },
150 { "xml", no_argument, NULL, 'x' },
151 { "version", no_argument, NULL, 'v' },
152 { NULL, 0, NULL, 0}
153 };
154
155#ifndef WIN32
156 signal(SIGPIPE, SIG_IGN);
157#endif
158
159 while ((c = getopt_long(argc, argv, "dhu:nq:k:sxv", longopts, NULL)) != -1) {
160 switch (c) {
161 case 'd':
238 idevice_set_debug_level(1); 162 idevice_set_debug_level(1);
239 continue; 163 break;
240 } 164 case 'u':
241 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 165 if (!*optarg) {
242 i++; 166 fprintf(stderr, "ERROR: UDID must not be empty!\n");
243 if (!argv[i] || (strlen(argv[i]) != 40)) { 167 print_usage(argc, argv, 1);
244 print_usage(argc, argv); 168 return 2;
245 return 0;
246 }
247 strcpy(uuid, argv[i]);
248 continue;
249 }
250 else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--domain")) {
251 i++;
252 if (!argv[i] || (strlen(argv[i]) < 4)) {
253 print_usage(argc, argv);
254 return 0;
255 } 169 }
256 if (!is_domain_known(argv[i])) { 170 udid = optarg;
257 fprintf(stderr, "WARNING: Sending query with unknown domain \"%s\".\n", argv[i]); 171 break;
172 case 'n':
173 use_network = 1;
174 break;
175 case 'q':
176 if (!*optarg) {
177 fprintf(stderr, "ERROR: 'domain' must not be empty!\n");
178 print_usage(argc, argv, 1);
179 return 2;
258 } 180 }
259 domain = strdup(argv[i]); 181 domain = optarg;
260 continue; 182 break;
261 } 183 case 'k':
262 else if (!strcmp(argv[i], "-k") || !strcmp(argv[i], "--key")) { 184 if (!*optarg) {
263 i++; 185 fprintf(stderr, "ERROR: 'key' must not be empty!\n");
264 if (!argv[i] || (strlen(argv[i]) <= 1)) { 186 print_usage(argc, argv, 1);
265 print_usage(argc, argv); 187 return 2;
266 return 0;
267 } 188 }
268 key = strdup(argv[i]); 189 key = optarg;
269 continue; 190 break;
270 } 191 case 'x':
271 else if (!strcmp(argv[i], "-x") || !strcmp(argv[i], "--xml")) {
272 format = FORMAT_XML; 192 format = FORMAT_XML;
273 continue; 193 break;
274 } 194 case 's':
275 else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--simple")) {
276 simple = 1; 195 simple = 1;
277 continue; 196 break;
278 } 197 case 'h':
279 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 198 print_usage(argc, argv, 0);
280 print_usage(argc, argv);
281 return 0; 199 return 0;
282 } 200 case 'v':
283 else { 201 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
284 print_usage(argc, argv);
285 return 0; 202 return 0;
203 default:
204 print_usage(argc, argv, 1);
205 return 2;
286 } 206 }
287 } 207 }
288 208
289 if (uuid[0] != 0) { 209 argc -= optind;
290 ret = idevice_new(&phone, uuid); 210 argv += optind;
291 if (ret != IDEVICE_E_SUCCESS) { 211
292 printf("No device found with uuid %s, is it plugged in?\n", uuid); 212 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
293 return -1; 213 if (ret != IDEVICE_E_SUCCESS) {
294 } 214 if (udid) {
295 } 215 fprintf(stderr, "ERROR: Device %s not found!\n", udid);
296 else 216 } else {
297 { 217 fprintf(stderr, "ERROR: No device found!\n");
298 ret = idevice_new(&phone, NULL);
299 if (ret != IDEVICE_E_SUCCESS) {
300 printf("No device found, is it plugged in?\n");
301 return -1;
302 } 218 }
219 return -1;
303 } 220 }
304 221
305 if (LOCKDOWN_E_SUCCESS != (simple ? 222 if (LOCKDOWN_E_SUCCESS != (ldret = simple ?
306 lockdownd_client_new(phone, &client, "ideviceinfo"): 223 lockdownd_client_new(device, &client, TOOL_NAME):
307 lockdownd_client_new_with_handshake(phone, &client, "ideviceinfo"))) { 224 lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
308 idevice_free(phone); 225 fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
226 idevice_free(device);
309 return -1; 227 return -1;
310 } 228 }
311 229
230 if (domain && !is_domain_known(domain)) {
231 fprintf(stderr, "WARNING: Sending query with unknown domain \"%s\".\n", domain);
232 }
233
312 /* run query and output information */ 234 /* run query and output information */
313 if(lockdownd_get_value(client, domain, key, &node) == LOCKDOWN_E_SUCCESS) { 235 if(lockdownd_get_value(client, domain, key, &node) == LOCKDOWN_E_SUCCESS) {
314 if (node) { 236 if (node) {
@@ -319,16 +241,11 @@ int main(int argc, char *argv[])
319 free(xml_doc); 241 free(xml_doc);
320 break; 242 break;
321 case FORMAT_KEY_VALUE: 243 case FORMAT_KEY_VALUE:
322 node_type = plist_get_node_type(node); 244 plist_write_to_stream(node, stdout, PLIST_FORMAT_LIMD, 0);
323 if (node_type == PLIST_DICT) { 245 break;
324 plist_dict_to_string(node);
325 } else if (node_type == PLIST_ARRAY) {
326 plist_array_to_string(node);
327 break;
328 }
329 default: 246 default:
330 if (key != NULL) 247 if (key != NULL)
331 plist_node_to_string(node); 248 plist_write_to_stream(node, stdout, PLIST_FORMAT_LIMD, 0);
332 break; 249 break;
333 } 250 }
334 plist_free(node); 251 plist_free(node);
@@ -336,10 +253,8 @@ int main(int argc, char *argv[])
336 } 253 }
337 } 254 }
338 255
339 if (domain != NULL)
340 free(domain);
341 lockdownd_client_free(client); 256 lockdownd_client_free(client);
342 idevice_free(phone); 257 idevice_free(device);
343 258
344 return 0; 259 return 0;
345} 260}
diff --git a/tools/idevicename.c b/tools/idevicename.c
new file mode 100644
index 0000000..69b76f6
--- /dev/null
+++ b/tools/idevicename.c
@@ -0,0 +1,159 @@
1/*
2 * idevicename.c
3 * Simple utility to get or set the device name
4 *
5 * Copyright (c) 2014 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
26#define TOOL_NAME "idevicename"
27
28#include <stdio.h>
29#include <string.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <getopt.h>
33#ifndef WIN32
34#include <signal.h>
35#endif
36
37#include <libimobiledevice/libimobiledevice.h>
38#include <libimobiledevice/lockdown.h>
39
40static void print_usage(int argc, char** argv, int is_error)
41{
42 char *name = strrchr(argv[0], '/');
43 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [NAME]\n", (name ? name + 1: argv[0]));
44 fprintf(is_error ? stderr : stdout,
45 "\n"
46 "Display the device name or set it to NAME if specified.\n"
47 "\n"
48 "OPTIONS:\n"
49 " -u, --udid UDID target specific device by UDID\n"
50 " -n, --network connect to network device\n"
51 " -d, --debug enable communication debugging\n"
52 " -h, --help print usage information\n"
53 " -v, --version print version information\n"
54 "\n"
55 "Homepage: <" PACKAGE_URL ">\n"
56 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
57 );
58}
59
60int main(int argc, char** argv)
61{
62 int c = 0;
63 const struct option longopts[] = {
64 { "udid", required_argument, NULL, 'u' },
65 { "network", no_argument, NULL, 'n' },
66 { "debug", no_argument, NULL, 'd' },
67 { "help", no_argument, NULL, 'h' },
68 { "version", no_argument, NULL, 'v' },
69 { NULL, 0, NULL, 0}
70 };
71 int res = -1;
72 const char* udid = NULL;
73 int use_network = 0;
74
75#ifndef WIN32
76 signal(SIGPIPE, SIG_IGN);
77#endif
78
79 while ((c = getopt_long(argc, argv, "du:hnv", longopts, NULL)) != -1) {
80 switch (c) {
81 case 'u':
82 if (!*optarg) {
83 fprintf(stderr, "ERROR: UDID must not be empty!\n");
84 print_usage(argc, argv, 1);
85 exit(2);
86 }
87 udid = optarg;
88 break;
89 case 'n':
90 use_network = 1;
91 break;
92 case 'h':
93 print_usage(argc, argv, 0);
94 return 0;
95 case 'd':
96 idevice_set_debug_level(1);
97 break;
98 case 'v':
99 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
100 return 0;
101 default:
102 print_usage(argc, argv, 1);
103 return 2;
104 }
105 }
106
107 argc -= optind;
108 argv += optind;
109
110 if (argc > 1) {
111 print_usage(argc, argv, 1);
112 return 2;
113 }
114
115 idevice_t device = NULL;
116 if (idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS) {
117 if (udid) {
118 fprintf(stderr, "ERROR: No device found with udid %s.\n", udid);
119 } else {
120 fprintf(stderr, "ERROR: No device found.\n");
121 }
122 return -1;
123 }
124
125 lockdownd_client_t lockdown = NULL;
126 lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
127 if (lerr != LOCKDOWN_E_SUCCESS) {
128 idevice_free(device);
129 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
130 return -1;
131 }
132
133 if (argc == 0) {
134 // getting device name
135 char* name = NULL;
136 lerr = lockdownd_get_device_name(lockdown, &name);
137 if (name) {
138 printf("%s\n", name);
139 free(name);
140 res = 0;
141 } else {
142 fprintf(stderr, "ERROR: Could not get device name, lockdown error %d\n", lerr);
143 }
144 } else {
145 // setting device name
146 lerr = lockdownd_set_value(lockdown, NULL, "DeviceName", plist_new_string(argv[0]));
147 if (lerr == LOCKDOWN_E_SUCCESS) {
148 printf("device name set to '%s'\n", argv[0]);
149 res = 0;
150 } else {
151 fprintf(stderr, "ERROR: Could not set device name, lockdown error %d\n", lerr);
152 }
153 }
154
155 lockdownd_client_free(lockdown);
156 idevice_free(device);
157
158 return res;
159}
diff --git a/tools/idevicenotificationproxy.c b/tools/idevicenotificationproxy.c
new file mode 100644
index 0000000..d1e25c1
--- /dev/null
+++ b/tools/idevicenotificationproxy.c
@@ -0,0 +1,293 @@
1/*
2 * idevicenotificationproxy.c
3 * Simple client for the notification_proxy service
4 *
5 * Copyright (c) 2009-2015 Martin Szulecki 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
26#define TOOL_NAME "idevicenotificationproxy"
27
28#include <stdio.h>
29#include <string.h>
30#include <stdlib.h>
31#include <getopt.h>
32#include <errno.h>
33#include <signal.h>
34
35#ifdef WIN32
36#include <windows.h>
37#define sleep(x) Sleep(x*1000)
38#else
39#include <unistd.h>
40#endif
41
42#include <libimobiledevice/libimobiledevice.h>
43#include <libimobiledevice/lockdown.h>
44#include <libimobiledevice/notification_proxy.h>
45
46enum cmd_mode {
47 CMD_NONE = 0,
48 CMD_OBSERVE,
49 CMD_POST
50};
51
52static int quit_flag = 0;
53
54/**
55 * signal handler function for cleaning up properly
56 */
57static void clean_exit(int sig)
58{
59 fprintf(stderr, "Exiting...\n");
60 quit_flag++;
61}
62
63static void print_usage(int argc, char **argv, int is_error)
64{
65 char *name = strrchr(argv[0], '/');
66 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
67 fprintf(is_error ? stderr : stdout,
68 "\n"
69 "Post or observe notifications on a device.\n"
70 "\n"
71 "Where COMMAND is one of:\n"
72 " post ID [...] post notification IDs to device and exit\n"
73 " observe ID [...] observe notification IDs in foreground until CTRL+C\n"
74 " or signal is received\n"
75 "\n"
76 "The following OPTIONS are accepted:\n"
77 " -u, --udid UDID target specific device by UDID\n"
78 " -n, --network connect to network device\n"
79 " -d, --debug enable communication debugging\n"
80 " -h, --help prints usage information\n"
81 " -v, --version prints version information\n"
82 "\n"
83 "Homepage: <" PACKAGE_URL ">\n"
84 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
85 );
86}
87
88static void notify_cb(const char *notification, void *user_data)
89{
90 printf("> %s\n", notification);
91}
92
93int main(int argc, char *argv[])
94{
95 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
96 lockdownd_service_descriptor_t service = NULL;
97 lockdownd_client_t client = NULL;
98 idevice_t device = NULL;
99 np_client_t gnp = NULL;
100
101 int result = -1;
102 int i = 0;
103 const char* udid = NULL;
104 int use_network = 0;
105 int cmd = CMD_NONE;
106 char* cmd_arg = NULL;
107
108 int count = 0;
109 char **nspec = NULL;
110 char **nspectmp = NULL;
111
112 int c = 0;
113 const struct option longopts[] = {
114 { "debug", no_argument, NULL, 'd' },
115 { "help", no_argument, NULL, 'h' },
116 { "udid", required_argument, NULL, 'u' },
117 { "network", no_argument, NULL, 'n' },
118 { "version", no_argument, NULL, 'v' },
119 { NULL, 0, NULL, 0}
120 };
121
122 signal(SIGINT, clean_exit);
123 signal(SIGTERM, clean_exit);
124#ifndef WIN32
125 signal(SIGQUIT, clean_exit);
126 signal(SIGPIPE, SIG_IGN);
127#endif
128
129 /* parse cmdline args */
130 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
131 switch (c) {
132 case 'd':
133 idevice_set_debug_level(1);
134 break;
135 case 'u':
136 if (!*optarg) {
137 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
138 print_usage(argc, argv, 1);
139 return 2;
140 }
141 udid = optarg;
142 break;
143 case 'n':
144 use_network = 1;
145 break;
146 case 'h':
147 print_usage(argc, argv, 0);
148 return 0;
149 case 'v':
150 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
151 return 0;
152 default:
153 print_usage(argc, argv, 1);
154 return 2;
155 }
156 }
157 argc -= optind;
158 argv += optind;
159
160 if (!argv[i]) {
161 fprintf(stderr, "ERROR: Missing command\n");
162 print_usage(argc+optind, argv-optind, 1);
163 return 2;
164 }
165
166 if (!strcmp(argv[i], "post")) {
167 cmd = CMD_POST;
168 } else if (!strcmp(argv[i], "observe")) {
169 cmd = CMD_OBSERVE;
170 }
171
172 if (cmd == CMD_POST || cmd == CMD_OBSERVE) {
173 i++;
174 if (!argv[i]) {
175 fprintf(stderr, "ERROR: Please supply a valid notification identifier.\n");
176 print_usage(argc+optind, argv-optind, 1);
177 return 2;
178 }
179
180 count = 0;
181 nspec = malloc(sizeof(char*) * (count+1));
182
183 while(1) {
184 if (argv[i] && (strlen(argv[i]) >= 2) && (strncmp(argv[i], "-", 1) != 0)) {
185 nspectmp = realloc(nspec, sizeof(char*) * (count+1));
186 nspectmp[count] = strdup(argv[i]);
187 nspec = nspectmp;
188 count = count+1;
189 i++;
190 } else {
191 i--;
192 break;
193 }
194 }
195
196 nspectmp = realloc(nspec, sizeof(char*) * (count+1));
197 nspectmp[count] = NULL;
198 nspec = nspectmp;
199 }
200
201 /* verify options */
202 if (cmd == CMD_NONE) {
203 fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[0]);
204 print_usage(argc+optind, argv-optind, 1);
205 return 2;
206 }
207
208 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
209 if (udid) {
210 printf("No device found with udid %s.\n", udid);
211 } else {
212 printf("No device found.\n");
213 }
214 goto cleanup;
215 }
216
217 if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
218 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ret);
219 goto cleanup;
220 }
221
222 ret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
223
224 lockdownd_client_free(client);
225
226 if ((ret == LOCKDOWN_E_SUCCESS) && (service->port > 0)) {
227 if (np_client_new(device, service, &gnp) != NP_E_SUCCESS) {
228 printf("Could not connect to notification_proxy!\n");
229 result = -1;
230 } else {
231 np_set_notify_callback(gnp, notify_cb, NULL);
232
233 switch (cmd) {
234 case CMD_POST:
235 i = 0;
236 while(nspec[i] != NULL && i < (count+1)) {
237 printf("< posting \"%s\"\n", nspec[i]);
238 np_post_notification(gnp, nspec[i]);
239 i++;
240 }
241 break;
242 case CMD_OBSERVE:
243 default:
244 i = 0;
245 while(nspec[i] != NULL && i < (count+1)) {
246 printf("! observing \"%s\"\n", nspec[i]);
247 np_observe_notification(gnp, nspec[i]);
248 i++;
249 }
250
251 /* just sleep and wait for notifications */
252 while (!quit_flag) {
253 sleep(1);
254 }
255
256 break;
257 }
258
259 result = EXIT_SUCCESS;
260
261 if (gnp) {
262 np_client_free(gnp);
263 gnp = NULL;
264 }
265 }
266 } else {
267 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ret));
268 }
269
270 if (service) {
271 lockdownd_service_descriptor_free(service);
272 service = NULL;
273 }
274
275cleanup:
276 if (nspec) {
277 i = 0;
278 while(nspec[i] != NULL && i < (count+1)) {
279 free(nspec[i]);
280 i++;
281 }
282 free(nspec);
283 }
284
285 if (cmd_arg) {
286 free(cmd_arg);
287 }
288
289 if (device)
290 idevice_free(device);
291
292 return result;
293}
diff --git a/tools/idevicepair.c b/tools/idevicepair.c
index b9676b9..94d3f04 100644
--- a/tools/idevicepair.c
+++ b/tools/idevicepair.c
@@ -1,114 +1,314 @@
1/* 1/*
2 * idevicepair.c 2 * idevicepair.c
3 * Simple utility to pair/unpair an iDevice 3 * Manage pairings with devices and this host
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen All Rights Reserved. 5 * Copyright (c) 2010-2021 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2014 Martin Szulecki, All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * 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 * 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 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21 22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "idevicepair"
28
22#include <stdio.h> 29#include <stdio.h>
23#include <string.h> 30#include <string.h>
24#include <stdlib.h> 31#include <stdlib.h>
25#include <getopt.h> 32#include <getopt.h>
26#include "userpref.h" 33#include <ctype.h>
34#include <unistd.h>
35#ifdef WIN32
36#include <windows.h>
37#include <conio.h>
38#else
39#include <termios.h>
40#include <signal.h>
41#endif
42
43#include "common/userpref.h"
27 44
28#include <libimobiledevice/libimobiledevice.h> 45#include <libimobiledevice/libimobiledevice.h>
29#include <libimobiledevice/lockdown.h> 46#include <libimobiledevice/lockdown.h>
47#include <plist/plist.h>
48
49static char *udid = NULL;
30 50
31static char *uuid = NULL; 51#ifdef HAVE_WIRELESS_PAIRING
32 52
33static void print_usage(int argc, char **argv) 53#ifdef WIN32
54#define BS_CC '\b'
55#define my_getch getch
56#else
57#define BS_CC 0x7f
58static int my_getch(void)
34{ 59{
35 char *name = NULL; 60 struct termios oldt, newt;
36 61 int ch;
37 name = strrchr(argv[0], '/'); 62 tcgetattr(STDIN_FILENO, &oldt);
38 printf("\n%s - Manage pairings with iPhone/iPod Touch/iPad devices and this host.\n\n", (name ? name + 1: argv[0])); 63 newt = oldt;
39 printf("Usage: %s [OPTIONS] COMMAND\n\n", (name ? name + 1: argv[0])); 64 newt.c_lflag &= ~(ICANON | ECHO);
40 printf(" Where COMMAND is one of:\n"); 65 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
41 printf(" hostid print the host id of this computer\n"); 66 ch = getchar();
42 printf(" pair pair device with this computer\n"); 67 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
43 printf(" validate validate if device is paired with this computer\n"); 68 return ch;
44 printf(" unpair unpair device with this computer\n");
45 printf(" list list devices paired with this computer\n\n");
46 printf(" The following OPTIONS are accepted:\n");
47 printf(" -d, --debug enable communication debugging\n");
48 printf(" -u, --uuid UUID target specific device by its 40-digit device UUID\n");
49 printf(" -h, --help prints usage information\n");
50 printf("\n");
51} 69}
70#endif
52 71
53static void parse_opts(int argc, char **argv) 72static int get_hidden_input(char *buf, int maxlen)
54{ 73{
55 static struct option longopts[] = { 74 int pwlen = 0;
56 {"help", 0, NULL, 'h'},
57 {"uuid", 1, NULL, 'u'},
58 {"debug", 0, NULL, 'd'},
59 {NULL, 0, NULL, 0}
60 };
61 int c; 75 int c;
62 76
63 while (1) { 77 while ((c = my_getch())) {
64 c = getopt_long(argc, argv, "hu:d", longopts, (int*)0); 78 if ((c == '\r') || (c == '\n')) {
65 if (c == -1) {
66 break; 79 break;
80 } else if (isprint(c)) {
81 if (pwlen < maxlen-1)
82 buf[pwlen++] = c;
83 fputc('*', stderr);
84 } else if (c == BS_CC) {
85 if (pwlen > 0) {
86 fputs("\b \b", stderr);
87 pwlen--;
88 }
67 } 89 }
90 }
91 buf[pwlen] = 0;
92 return pwlen;
93}
68 94
69 switch (c) { 95static void pairing_cb(lockdownd_cu_pairing_cb_type_t cb_type, void *user_data, void* data_ptr, unsigned int* data_size)
70 case 'h': 96{
71 print_usage(argc, argv); 97 if (cb_type == LOCKDOWN_CU_PAIRING_PIN_REQUESTED) {
72 exit(EXIT_SUCCESS); 98 printf("Enter PIN: ");
73 case 'u': 99 fflush(stdout);
74 if (strlen(optarg) != 40) { 100
75 printf("%s: invalid UUID specified (length != 40)\n", argv[0]); 101 *data_size = get_hidden_input((char*)data_ptr, *data_size);
76 print_usage(argc, argv); 102
77 exit(2); 103 printf("\n");
78 } 104 } else if (cb_type == LOCKDOWN_CU_PAIRING_DEVICE_INFO) {
79 uuid = strdup(optarg); 105 printf("Device info:\n");
106 plist_write_to_stream((plist_t)data_ptr, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_INDENT | PLIST_OPT_INDENT_BY(2));
107 } else if (cb_type == LOCKDOWN_CU_PAIRING_ERROR) {
108 printf("ERROR: %s\n", (data_ptr) ? (char*)data_ptr : "(unknown)");
109 }
110}
111
112#endif /* HAVE_WIRELESS_PAIRING */
113
114static void print_error_message(lockdownd_error_t err)
115{
116 switch (err) {
117 case LOCKDOWN_E_PASSWORD_PROTECTED:
118 printf("ERROR: Could not validate with device %s because a passcode is set. Please enter the passcode on the device and retry.\n", udid);
80 break; 119 break;
81 case 'd': 120 case LOCKDOWN_E_INVALID_CONF:
82 idevice_set_debug_level(1); 121 case LOCKDOWN_E_INVALID_HOST_ID:
122 printf("ERROR: Device %s is not paired with this host\n", udid);
123 break;
124 case LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING:
125 printf("ERROR: Please accept the trust dialog on the screen of device %s, then attempt to pair again.\n", udid);
126 break;
127 case LOCKDOWN_E_USER_DENIED_PAIRING:
128 printf("ERROR: Device %s said that the user denied the trust dialog.\n", udid);
129 break;
130 case LOCKDOWN_E_PAIRING_FAILED:
131 printf("ERROR: Pairing with device %s failed.\n", udid);
132 break;
133 case LOCKDOWN_E_GET_PROHIBITED:
134 case LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION:
135 printf("ERROR: Pairing is not possible over this connection.\n");
136#ifdef HAVE_WIRELESS_PAIRING
137 printf("To perform a wireless pairing use the -w command line switch. See usage or man page for details.\n");
138#endif
83 break; 139 break;
84 default: 140 default:
85 print_usage(argc, argv); 141 printf("ERROR: Device %s returned unhandled error code %d\n", udid, err);
86 exit(EXIT_SUCCESS); 142 break;
87 }
88 } 143 }
89} 144}
90 145
146static void print_usage(int argc, char **argv, int is_error)
147{
148 char *name = strrchr(argv[0], '/');
149 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
150 fprintf(is_error ? stderr : stdout,
151 "\n"
152 "Manage host pairings with devices and usbmuxd.\n"
153 "\n"
154 "Where COMMAND is one of:\n"
155 " systembuid print the system buid of the usbmuxd host\n"
156 " hostid print the host id for target device\n"
157 " pair pair device with this host\n"
158 " validate validate if device is paired with this host\n"
159 " unpair unpair device with this host\n"
160 " list list devices paired with this host\n"
161 "\n"
162 "The following OPTIONS are accepted:\n"
163 " -u, --udid UDID target specific device by UDID\n"
164 );
165#ifdef HAVE_WIRELESS_PAIRING
166 fprintf(is_error ? stderr : stdout,
167 " -w, --wireless perform wireless pairing (see NOTE)\n"
168 " -n, --network connect to network device (see NOTE)\n"
169 );
170#endif
171 fprintf(is_error ? stderr : stdout,
172 " -d, --debug enable communication debugging\n"
173 " -h, --help prints usage information\n"
174 " -v, --version prints version information\n"
175 );
176#ifdef HAVE_WIRELESS_PAIRING
177 fprintf(is_error ? stderr : stdout,
178 "\n"
179 "NOTE: Pairing over network (wireless pairing) is only supported by Apple TV\n"
180 "devices. To perform a wireless pairing, you need to use the -w command line\n"
181 "switch. Make sure to put the device into pairing mode first by opening\n"
182 "Settings > Remotes and Devices > Remote App and Devices.\n"
183 );
184#endif
185 fprintf(is_error ? stderr : stdout,
186 "\n"
187 "Homepage: <" PACKAGE_URL ">\n"
188 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
189 );
190}
191
91int main(int argc, char **argv) 192int main(int argc, char **argv)
92{ 193{
194 int c = 0;
195 static struct option longopts[] = {
196 { "help", no_argument, NULL, 'h' },
197 { "udid", required_argument, NULL, 'u' },
198#ifdef HAVE_WIRELESS_PAIRING
199 { "wireless", no_argument, NULL, 'w' },
200 { "network", no_argument, NULL, 'n' },
201 { "hostinfo", required_argument, NULL, 1 },
202#endif
203 { "debug", no_argument, NULL, 'd' },
204 { "version", no_argument, NULL, 'v' },
205 { NULL, 0, NULL, 0}
206 };
207#ifdef HAVE_WIRELESS_PAIRING
208#define SHORT_OPTIONS "hu:wndv"
209#else
210#define SHORT_OPTIONS "hu:dv"
211#endif
93 lockdownd_client_t client = NULL; 212 lockdownd_client_t client = NULL;
94 idevice_t phone = NULL; 213 idevice_t device = NULL;
95 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 214 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
96 lockdownd_error_t lerr; 215 lockdownd_error_t lerr;
97 int result; 216 int result;
98 217
99 char *type = NULL; 218 char *type = NULL;
219 int use_network = 0;
220 int wireless_pairing = 0;
221#ifdef HAVE_WIRELESS_PAIRING
222 plist_t host_info_plist = NULL;
223#endif
100 char *cmd; 224 char *cmd;
101 typedef enum { 225 typedef enum {
102 OP_NONE = 0, OP_PAIR, OP_VALIDATE, OP_UNPAIR, OP_LIST, OP_HOSTID 226 OP_NONE = 0, OP_PAIR, OP_VALIDATE, OP_UNPAIR, OP_LIST, OP_HOSTID, OP_SYSTEMBUID
103 } op_t; 227 } op_t;
104 op_t op = OP_NONE; 228 op_t op = OP_NONE;
105 229
106 parse_opts(argc, argv); 230 while ((c = getopt_long(argc, argv, SHORT_OPTIONS, longopts, NULL)) != -1) {
231 switch (c) {
232 case 'h':
233 print_usage(argc, argv, 0);
234 exit(EXIT_SUCCESS);
235 case 'u':
236 if (!*optarg) {
237 fprintf(stderr, "ERROR: UDID must not be empty!\n");
238 print_usage(argc, argv, 1);
239 result = EXIT_FAILURE;
240 goto leave;
241 }
242 free(udid);
243 udid = strdup(optarg);
244 break;
245#ifdef HAVE_WIRELESS_PAIRING
246 case 'w':
247 wireless_pairing = 1;
248 break;
249 case 'n':
250 use_network = 1;
251 break;
252 case 1:
253 if (!*optarg) {
254 fprintf(stderr, "ERROR: --hostinfo argument must not be empty!\n");
255 result = EXIT_FAILURE;
256 goto leave;
257 }
258 if (*optarg == '@') {
259 plist_read_from_file(optarg+1, &host_info_plist, NULL);
260 if (!host_info_plist) {
261 fprintf(stderr, "ERROR: Could not read from file '%s'\n", optarg+1);
262 result = EXIT_FAILURE;
263 goto leave;
264 }
265 }
266#ifdef HAVE_PLIST_JSON
267 else if (*optarg == '{') {
268 if (plist_from_json(optarg, strlen(optarg), &host_info_plist) != PLIST_ERR_SUCCESS) {
269 fprintf(stderr, "ERROR: --hostinfo argument not valid. Make sure it is a JSON dictionary.\n");
270 result = EXIT_FAILURE;
271 goto leave;
272 }
273 }
274#endif
275 else {
276 fprintf(stderr, "ERROR: --hostinfo argument not valid. To specify a path prefix with '@'\n");
277 result = EXIT_FAILURE;
278 goto leave;
279 }
280 break;
281#endif
282 case 'd':
283 idevice_set_debug_level(1);
284 break;
285 case 'v':
286 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
287 result = EXIT_SUCCESS;
288 goto leave;
289 default:
290 print_usage(argc, argv, 1);
291 result = EXIT_FAILURE;
292 goto leave;
293 }
294 }
295
296#ifndef WIN32
297 signal(SIGPIPE, SIG_IGN);
298#endif
107 299
108 if ((argc - optind) < 1) { 300 if ((argc - optind) < 1) {
109 printf("ERROR: You need to specify a COMMAND!\n"); 301 fprintf(stderr, "ERROR: You need to specify a COMMAND!\n");
110 print_usage(argc, argv); 302 print_usage(argc, argv, 1);
111 exit(EXIT_FAILURE); 303 result = EXIT_FAILURE;
304 goto leave;
305 }
306
307 if (wireless_pairing && use_network) {
308 fprintf(stderr, "ERROR: You cannot use -w and -n together.\n");
309 print_usage(argc, argv, 1);
310 result = EXIT_FAILURE;
311 goto leave;
112 } 312 }
113 313
114 cmd = (argv+optind)[0]; 314 cmd = (argv+optind)[0];
@@ -123,60 +323,91 @@ int main(int argc, char **argv)
123 op = OP_LIST; 323 op = OP_LIST;
124 } else if (!strcmp(cmd, "hostid")) { 324 } else if (!strcmp(cmd, "hostid")) {
125 op = OP_HOSTID; 325 op = OP_HOSTID;
326 } else if (!strcmp(cmd, "systembuid")) {
327 op = OP_SYSTEMBUID;
126 } else { 328 } else {
127 printf("ERROR: Invalid command '%s' specified\n", cmd); 329 fprintf(stderr, "ERROR: Invalid command '%s' specified\n", cmd);
128 print_usage(argc, argv); 330 print_usage(argc, argv, 1);
129 exit(EXIT_FAILURE); 331 result = EXIT_FAILURE;
332 goto leave;
130 } 333 }
131 334
132 if (op == OP_HOSTID) { 335 if (wireless_pairing) {
133 char *hostid = NULL; 336 if (op == OP_VALIDATE || op == OP_UNPAIR) {
134 userpref_get_host_id(&hostid); 337 fprintf(stderr, "ERROR: Command '%s' is not supported with -w\n", cmd);
338 print_usage(argc, argv, 1);
339 result = EXIT_FAILURE;
340 goto leave;
341 }
342 use_network = 1;
343 }
135 344
136 printf("%s\n", hostid); 345 if (op == OP_SYSTEMBUID) {
346 char *systembuid = NULL;
347 userpref_read_system_buid(&systembuid);
137 348
138 if (hostid) 349 printf("%s\n", systembuid);
139 free(hostid);
140 350
141 return EXIT_SUCCESS; 351 free(systembuid);
352
353 result = EXIT_SUCCESS;
354 goto leave;
142 } 355 }
143 356
144 if (op == OP_LIST) { 357 if (op == OP_LIST) {
145 unsigned int i; 358 unsigned int i;
146 char **uuids = NULL; 359 char **udids = NULL;
147 unsigned int count = 0; 360 unsigned int count = 0;
148 userpref_get_paired_uuids(&uuids, &count); 361 userpref_get_paired_udids(&udids, &count);
149 for (i = 0; i < count; i++) { 362 for (i = 0; i < count; i++) {
150 printf("%s\n", uuids[i]); 363 printf("%s\n", udids[i]);
364 free(udids[i]);
151 } 365 }
152 if (uuids) 366 free(udids);
153 g_strfreev(uuids); 367 result = EXIT_SUCCESS;
154 if (uuid) 368 goto leave;
155 free(uuid);
156 return EXIT_SUCCESS;
157 } 369 }
158 370
159 if (uuid) { 371 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
160 ret = idevice_new(&phone, uuid); 372 if (ret != IDEVICE_E_SUCCESS) {
161 free(uuid); 373 if (udid) {
162 uuid = NULL; 374 printf("No device found with udid %s.\n", udid);
163 if (ret != IDEVICE_E_SUCCESS) { 375 } else {
164 printf("No device found with uuid %s, is it plugged in?\n", uuid); 376 printf("No device found.\n");
165 return EXIT_FAILURE;
166 } 377 }
167 } else { 378 result = EXIT_FAILURE;
168 ret = idevice_new(&phone, NULL); 379 goto leave;
380 }
381 if (!udid) {
382 ret = idevice_get_udid(device, &udid);
169 if (ret != IDEVICE_E_SUCCESS) { 383 if (ret != IDEVICE_E_SUCCESS) {
170 printf("No device found, is it plugged in?\n"); 384 printf("ERROR: Could not get device udid, error code %d\n", ret);
171 return EXIT_FAILURE; 385 result = EXIT_FAILURE;
386 goto leave;
172 } 387 }
173 } 388 }
174 389
175 lerr = lockdownd_client_new(phone, &client, "idevicepair"); 390 if (op == OP_HOSTID) {
391 plist_t pair_record = NULL;
392 char *hostid = NULL;
393
394 userpref_read_pair_record(udid, &pair_record);
395 pair_record_get_host_id(pair_record, &hostid);
396
397 printf("%s\n", hostid);
398
399 free(hostid);
400 plist_free(pair_record);
401
402 result = EXIT_SUCCESS;
403 goto leave;
404 }
405
406 lerr = lockdownd_client_new(device, &client, TOOL_NAME);
176 if (lerr != LOCKDOWN_E_SUCCESS) { 407 if (lerr != LOCKDOWN_E_SUCCESS) {
177 idevice_free(phone); 408 printf("ERROR: Could not connect to lockdownd, error code %d\n", lerr);
178 printf("ERROR: lockdownd_client_new failed with error code %d\n", lerr); 409 result = EXIT_FAILURE;
179 return EXIT_FAILURE; 410 goto leave;
180 } 411 }
181 412
182 result = EXIT_SUCCESS; 413 result = EXIT_SUCCESS;
@@ -187,76 +418,62 @@ int main(int argc, char **argv)
187 result = EXIT_FAILURE; 418 result = EXIT_FAILURE;
188 goto leave; 419 goto leave;
189 } else { 420 } else {
190 if (strcmp("com.apple.mobile.lockdown", type)) { 421 if (strcmp("com.apple.mobile.lockdown", type) != 0) {
191 printf("WARNING: QueryType request returned '%s'\n", type); 422 printf("WARNING: QueryType request returned '%s'\n", type);
192 } 423 }
193 if (type) { 424 free(type);
194 free(type);
195 }
196 }
197
198 ret = idevice_get_uuid(phone, &uuid);
199 if (ret != IDEVICE_E_SUCCESS) {
200 printf("ERROR: Could not get device uuid, error code %d\n", ret);
201 result = EXIT_FAILURE;
202 goto leave;
203 } 425 }
204 426
205 switch(op) { 427 switch(op) {
206 default: 428 default:
207 case OP_PAIR: 429 case OP_PAIR:
208 lerr = lockdownd_pair(client, NULL); 430#ifdef HAVE_WIRELESS_PAIRING
431 if (wireless_pairing) {
432 lerr = lockdownd_cu_pairing_create(client, pairing_cb, NULL, host_info_plist, NULL);
433 if (lerr == LOCKDOWN_E_SUCCESS) {
434 lerr = lockdownd_pair_cu(client);
435 }
436 } else
437#endif
438 {
439 lerr = lockdownd_pair(client, NULL);
440 }
209 if (lerr == LOCKDOWN_E_SUCCESS) { 441 if (lerr == LOCKDOWN_E_SUCCESS) {
210 printf("SUCCESS: Paired with device %s\n", uuid); 442 printf("SUCCESS: Paired with device %s\n", udid);
211 } else { 443 } else {
212 result = EXIT_FAILURE; 444 result = EXIT_FAILURE;
213 if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { 445 print_error_message(lerr);
214 printf("ERROR: Could not pair with the device because a passcode is set. Please enter the passcode on the device and retry.\n");
215 } else {
216 printf("ERROR: Pairing with device %s failed with unhandled error code %d\n", uuid, lerr);
217 }
218 } 446 }
219 break; 447 break;
220 448
221 case OP_VALIDATE: 449 case OP_VALIDATE:
222 lerr = lockdownd_validate_pair(client, NULL); 450 lockdownd_client_free(client);
451 client = NULL;
452 lerr = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME);
223 if (lerr == LOCKDOWN_E_SUCCESS) { 453 if (lerr == LOCKDOWN_E_SUCCESS) {
224 printf("SUCCESS: Validated pairing with device %s\n", uuid); 454 printf("SUCCESS: Validated pairing with device %s\n", udid);
225 } else { 455 } else {
226 result = EXIT_FAILURE; 456 result = EXIT_FAILURE;
227 if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { 457 print_error_message(lerr);
228 printf("ERROR: Could not validate with the device because a passcode is set. Please enter the passcode on the device and retry.\n");
229 } else if (lerr == LOCKDOWN_E_INVALID_HOST_ID) {
230 printf("ERROR: Device %s is not paired with this host\n", uuid);
231 } else {
232 printf("ERROR: Pairing failed with unhandled error code %d\n", lerr);
233 }
234 } 458 }
235 break; 459 break;
236 460
237 case OP_UNPAIR: 461 case OP_UNPAIR:
238 lerr = lockdownd_unpair(client, NULL); 462 lerr = lockdownd_unpair(client, NULL);
239 if (lerr == LOCKDOWN_E_SUCCESS) { 463 if (lerr == LOCKDOWN_E_SUCCESS) {
240 /* also remove local device public key */ 464 printf("SUCCESS: Unpaired with device %s\n", udid);
241 userpref_remove_device_public_key(uuid);
242 printf("SUCCESS: Unpaired with device %s\n", uuid);
243 } else { 465 } else {
244 result = EXIT_FAILURE; 466 result = EXIT_FAILURE;
245 if (lerr == LOCKDOWN_E_INVALID_HOST_ID) { 467 print_error_message(lerr);
246 printf("ERROR: Device %s is not paired with this host\n", uuid);
247 } else {
248 printf("ERROR: Unpairing with device %s failed with unhandled error code %d\n", uuid, lerr);
249 }
250 } 468 }
251 break; 469 break;
252 } 470 }
253 471
254leave: 472leave:
255 lockdownd_client_free(client); 473 lockdownd_client_free(client);
256 idevice_free(phone); 474 idevice_free(device);
257 if (uuid) { 475 free(udid);
258 free(uuid); 476
259 }
260 return result; 477 return result;
261} 478}
262 479
diff --git a/tools/ideviceprovision.c b/tools/ideviceprovision.c
new file mode 100644
index 0000000..4080a28
--- /dev/null
+++ b/tools/ideviceprovision.c
@@ -0,0 +1,689 @@
1/*
2 * ideviceprovision.c
3 * Simple utility to install, get, or remove provisioning profiles
4 * to/from idevices
5 *
6 * Copyright (c) 2012-2016 Nikias Bassen, All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "ideviceprovision"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <getopt.h>
33#include <sys/stat.h>
34#include <errno.h>
35#ifndef WIN32
36#include <signal.h>
37#endif
38
39#ifdef WIN32
40#include <windows.h>
41#else
42#include <arpa/inet.h>
43#endif
44
45#include <libimobiledevice/libimobiledevice.h>
46#include <libimobiledevice/lockdown.h>
47#include <libimobiledevice/misagent.h>
48#include <plist/plist.h>
49
50static void print_usage(int argc, char **argv, int is_error)
51{
52 char *name = strrchr(argv[0], '/');
53 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
54 fprintf(is_error ? stderr : stdout,
55 "\n"
56 "Manage provisioning profiles on a device.\n"
57 "\n"
58 "Where COMMAND is one of:\n"
59 " install FILE Installs the provisioning profile specified by FILE.\n"
60 " A valid .mobileprovision file is expected.\n"
61 " list Get a list of all provisioning profiles on the device.\n"
62 " copy PATH Retrieves all provisioning profiles from the device and\n"
63 " stores them into the existing directory specified by PATH.\n"
64 " The files will be stored as UUID.mobileprovision\n"
65 " copy UUID PATH Retrieves the provisioning profile identified by UUID\n"
66 " from the device and stores it into the existing directory\n"
67 " specified by PATH. The file will be stored as UUID.mobileprovision.\n"
68 " remove UUID Removes the provisioning profile identified by UUID.\n"
69 " remove-all Removes all installed provisioning profiles.\n"
70 " dump FILE Prints detailed information about the provisioning profile\n"
71 " specified by FILE.\n"
72 "\n"
73 "The following OPTIONS are accepted:\n"
74 " -u, --udid UDID target specific device by UDID\n"
75 " -n, --network connect to network device\n"
76 " -x, --xml print XML output when using the 'dump' command\n"
77 " -d, --debug enable communication debugging\n"
78 " -h, --help prints usage information\n"
79 " -v, --version prints version information\n"
80 "\n"
81 "Homepage: <" PACKAGE_URL ">\n"
82 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
83 );
84}
85
86enum {
87 OP_INSTALL,
88 OP_LIST,
89 OP_COPY,
90 OP_REMOVE,
91 OP_DUMP,
92 NUM_OPS
93};
94
95#define ASN1_SEQUENCE 0x30
96#define ASN1_CONTAINER 0xA0
97#define ASN1_OBJECT_IDENTIFIER 0x06
98#define ASN1_OCTET_STRING 0x04
99
100static void asn1_next_item(unsigned char** p)
101{
102 char bsize = *(*p+1);
103 if (bsize & 0x80) {
104 *p += 2 + (bsize & 0xF);
105 } else {
106 *p += 3;
107 }
108}
109
110static size_t asn1_item_get_size(const unsigned char* p)
111{
112 size_t res = 0;
113 char bsize = *(p+1);
114 if (bsize & 0x80) {
115 uint16_t ws = 0;
116 uint32_t ds = 0;
117 switch (bsize & 0xF) {
118 case 2:
119 ws = *(uint16_t*)(p+2);
120 res = ntohs(ws);
121 break;
122 case 3:
123 ds = *(uint32_t*)(p+2);
124 res = ntohl(ds) >> 8;
125 break;
126 case 4:
127 ds = *(uint32_t*)(p+2);
128 res = ntohl(ds);
129 break;
130 default:
131 fprintf(stderr, "ERROR: Invalid or unimplemented byte size %d\n", bsize & 0xF);
132 break;
133 }
134 } else {
135 res = (int)bsize;
136 }
137 return res;
138}
139
140static void asn1_skip_item(unsigned char** p)
141{
142 size_t sz = asn1_item_get_size(*p);
143 *p += 2;
144 *p += sz;
145}
146
147static plist_t profile_get_embedded_plist(plist_t profile)
148{
149 if (plist_get_node_type(profile) != PLIST_DATA) {
150 fprintf(stderr, "%s: unexpected plist node type for profile (PLIST_DATA expected)\n", __func__);
151 return NULL;
152 }
153 char* bbuf = NULL;
154 uint64_t blen = 0;
155 plist_get_data_val(profile, &bbuf, &blen);
156 if (!bbuf) {
157 fprintf(stderr, "%s: could not get data value from plist node\n", __func__);
158 return NULL;
159 }
160
161 unsigned char* pp = (unsigned char*)bbuf;
162
163 if (*pp != ASN1_SEQUENCE) {
164 free(bbuf);
165 fprintf(stderr, "%s: unexpected profile data (0)\n", __func__);
166 return NULL;
167 }
168 size_t slen = asn1_item_get_size(pp);
169 char bsize = *(pp+1);
170 if (bsize & 0x80) {
171 slen += 2 + (bsize & 0xF);
172 } else {
173 slen += 3;
174 }
175 if (slen != blen) {
176 free(bbuf);
177 fprintf(stderr, "%s: unexpected profile data (1)\n", __func__);
178 return NULL;
179 }
180 asn1_next_item(&pp);
181
182 if (*pp != ASN1_OBJECT_IDENTIFIER) {
183 free(bbuf);
184 fprintf(stderr, "%s: unexpected profile data (2)\n", __func__);
185 return NULL;
186 }
187 asn1_skip_item(&pp);
188
189 if (*pp != ASN1_CONTAINER) {
190 free(bbuf);
191 fprintf(stderr, "%s: unexpected profile data (3)\n", __func__);
192 return NULL;
193 }
194 asn1_next_item(&pp);
195
196 if (*pp != ASN1_SEQUENCE) {
197 free(bbuf);
198 fprintf(stderr, "%s: unexpected profile data (4)\n", __func__);
199 return NULL;
200 }
201 asn1_next_item(&pp);
202
203 int k = 0;
204 // go to the 3rd element (skip 2)
205 while (k < 2) {
206 asn1_skip_item(&pp);
207 k++;
208 }
209 if (*pp != ASN1_SEQUENCE) {
210 free(bbuf);
211 fprintf(stderr, "%s: unexpected profile data (5)\n", __func__);
212 return NULL;
213 }
214 asn1_next_item(&pp);
215
216 if (*pp != ASN1_OBJECT_IDENTIFIER) {
217 free(bbuf);
218 fprintf(stderr, "%s: unexpected profile data (6)\n", __func__);
219 return NULL;
220 }
221 asn1_skip_item(&pp);
222
223 if (*pp != ASN1_CONTAINER) {
224 free(bbuf);
225 fprintf(stderr, "%s: unexpected profile data (7)\n", __func__);
226 return NULL;
227 }
228 asn1_next_item(&pp);
229
230 if (*pp != ASN1_OCTET_STRING) {
231 free(bbuf);
232 fprintf(stderr, "%s: unexpected profile data (8)\n", __func__);
233 return NULL;
234 }
235 slen = asn1_item_get_size(pp);
236 asn1_next_item(&pp);
237
238 plist_t pl = NULL;
239 plist_from_xml((char*)pp, slen, &pl);
240 free(bbuf);
241
242 return pl;
243}
244
245static int profile_read_from_file(const char* path, unsigned char **profile_data, unsigned int *profile_size)
246{
247 FILE* f = fopen(path, "rb");
248 if (!f) {
249 fprintf(stderr, "Could not open file '%s'\n", path);
250 return -1;
251 }
252 fseek(f, 0, SEEK_END);
253 long int size = ftell(f);
254 fseek(f, 0, SEEK_SET);
255
256 if (size >= 0x1000000) {
257 fprintf(stderr, "The file '%s' is too large for processing.\n", path);
258 fclose(f);
259 return -1;
260 }
261
262 unsigned char* buf = malloc(size);
263 if (!buf) {
264 fprintf(stderr, "Could not allocate memory...\n");
265 fclose(f);
266 return -1;
267 }
268
269 long int cur = 0;
270 while (cur < size) {
271 ssize_t r = fread(buf+cur, 1, 512, f);
272 if (r <= 0) {
273 break;
274 }
275 cur += r;
276 }
277 fclose(f);
278
279 if (cur != size) {
280 free(buf);
281 fprintf(stderr, "Could not read in file '%s' (size %ld read %ld)\n", path, size, cur);
282 return -1;
283 }
284
285 *profile_data = buf;
286 *profile_size = (unsigned int)size;
287
288 return 0;
289}
290
291int main(int argc, char *argv[])
292{
293 lockdownd_client_t client = NULL;
294 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
295 lockdownd_service_descriptor_t service = NULL;
296 idevice_t device = NULL;
297 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
298 int res = 0;
299 int i;
300 int op = -1;
301 int output_xml = 0;
302 const char* udid = NULL;
303 const char* param = NULL;
304 const char* param2 = NULL;
305 int use_network = 0;
306 int c = 0;
307 const struct option longopts[] = {
308 { "debug", no_argument, NULL, 'd' },
309 { "help", no_argument, NULL, 'h' },
310 { "udid", required_argument, NULL, 'u' },
311 { "network", no_argument, NULL, 'n' },
312 { "version", no_argument, NULL, 'v' },
313 { "xml", no_argument, NULL, 'x' },
314 { NULL, 0, NULL, 0}
315 };
316
317#ifndef WIN32
318 signal(SIGPIPE, SIG_IGN);
319#endif
320 /* parse cmdline args */
321 while ((c = getopt_long(argc, argv, "dhu:nvx", longopts, NULL)) != -1) {
322 switch (c) {
323 case 'd':
324 idevice_set_debug_level(1);
325 break;
326 case 'u':
327 if (!*optarg) {
328 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
329 print_usage(argc, argv, 1);
330 return 2;
331 }
332 udid = optarg;
333 break;
334 case 'n':
335 use_network = 1;
336 break;
337 case 'h':
338 print_usage(argc, argv, 0);
339 return 0;
340 case 'v':
341 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
342 return 0;
343 case 'x':
344 output_xml = 1;
345 break;
346 default:
347 print_usage(argc, argv, 1);
348 return 2;
349 }
350 }
351 argc -= optind;
352 argv += optind;
353
354 if (!argv[0]) {
355 fprintf(stderr, "ERROR: Missing command.\n");
356 print_usage(argc+optind, argv-optind, 1);
357 return 2;
358 }
359
360 i = 0;
361 if (!strcmp(argv[i], "install")) {
362 op = OP_INSTALL;
363 i++;
364 if (!argv[i] || !*argv[i]) {
365 fprintf(stderr, "Missing argument for 'install' command.\n");
366 print_usage(argc+optind, argv-optind, 1);
367 return 2;
368 }
369 param = argv[i];
370 }
371 else if (!strcmp(argv[i], "list")) {
372 op = OP_LIST;
373 }
374 else if (!strcmp(argv[i], "copy")) {
375 op = OP_COPY;
376 i++;
377 if (!argv[i] || !*argv[i]) {
378 fprintf(stderr, "Missing argument for 'copy' command.\n");
379 print_usage(argc+optind, argv-optind, 1);
380 return 2;
381 }
382 param = argv[i];
383 i++;
384 if (argv[i] && (strlen(argv[i]) > 0)) {
385 param2 = argv[i];
386 }
387 }
388 else if (!strcmp(argv[i], "remove")) {
389 op = OP_REMOVE;
390 i++;
391 if (!argv[i] || !*argv[i]) {
392 fprintf(stderr, "Missing argument for 'remove' command.\n");
393 print_usage(argc+optind, argv-optind, 1);
394 return 2;
395 }
396 param = argv[i];
397 }
398 else if (!strcmp(argv[i], "remove-all")) {
399 op = OP_REMOVE;
400 }
401 else if (!strcmp(argv[i], "dump")) {
402 op = OP_DUMP;
403 i++;
404 if (!argv[i] || !*argv[i]) {
405 fprintf(stderr, "Missing argument for 'remove' command.\n");
406 print_usage(argc+optind, argv-optind, 1);
407 return 2;
408 }
409 param = argv[i];
410 }
411 if ((op == -1) || (op >= NUM_OPS)) {
412 fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[i]);
413 print_usage(argc+optind, argv-optind, 1);
414 return 2;
415 }
416
417 if (op == OP_DUMP) {
418 unsigned char* profile_data = NULL;
419 unsigned int profile_size = 0;
420 if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
421 return -1;
422 }
423 plist_t pdata = plist_new_data((char*)profile_data, profile_size);
424 plist_t pl = profile_get_embedded_plist(pdata);
425 plist_free(pdata);
426 free(profile_data);
427
428 if (pl) {
429 if (output_xml) {
430 char* xml = NULL;
431 uint32_t xlen = 0;
432 plist_to_xml(pl, &xml, &xlen);
433 if (xml) {
434 printf("%s\n", xml);
435 free(xml);
436 }
437 } else {
438 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
439 plist_write_to_stream(pl, stdout, PLIST_FORMAT_LIMD, 0);
440 } else {
441 fprintf(stderr, "ERROR: unexpected node type in profile plist (not PLIST_DICT)\n");
442 res = -1;
443 }
444 }
445 } else {
446 fprintf(stderr, "ERROR: could not extract embedded plist from profile!\n");
447 }
448 plist_free(pl);
449
450 return res;
451 }
452
453 if (op == OP_COPY) {
454 struct stat st;
455 const char *checkdir = (param2) ? param2 : param;
456 if ((stat(checkdir, &st) < 0) || !S_ISDIR(st.st_mode)) {
457 fprintf(stderr, "ERROR: %s does not exist or is not a directory!\n", checkdir);
458 return -1;
459 }
460 }
461
462 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
463 if (ret != IDEVICE_E_SUCCESS) {
464 if (udid) {
465 printf("No device found with udid %s.\n", udid);
466 } else {
467 printf("No device found.\n");
468 }
469 return -1;
470 }
471
472 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
473 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
474 idevice_free(device);
475 return -1;
476 }
477
478 plist_t pver = NULL;
479 char *pver_s = NULL;
480 lockdownd_get_value(client, NULL, "ProductVersion", &pver);
481 if (pver && plist_get_node_type(pver) == PLIST_STRING) {
482 plist_get_string_val(pver, &pver_s);
483 }
484 plist_free(pver);
485 int product_version_major = 0;
486 int product_version_minor = 0;
487 int product_version_patch = 0;
488 if (pver_s) {
489 sscanf(pver_s, "%d.%d.%d", &product_version_major, &product_version_minor, &product_version_patch);
490 free(pver_s);
491 }
492 if (product_version_major == 0) {
493 fprintf(stderr, "ERROR: Could not determine the device's ProductVersion\n");
494 lockdownd_client_free(client);
495 idevice_free(device);
496 return -1;
497 }
498 int product_version = ((product_version_major & 0xFF) << 16) | ((product_version_minor & 0xFF) << 8) | (product_version_patch & 0xFF);
499
500 lockdownd_error_t lerr = lockdownd_start_service(client, MISAGENT_SERVICE_NAME, &service);
501 if (lerr != LOCKDOWN_E_SUCCESS) {
502 fprintf(stderr, "Could not start service %s: %s\n", MISAGENT_SERVICE_NAME, lockdownd_strerror(lerr));
503 lockdownd_client_free(client);
504 idevice_free(device);
505 return -1;
506 }
507 lockdownd_client_free(client);
508 client = NULL;
509
510 misagent_client_t mis = NULL;
511 if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) {
512 fprintf(stderr, "Could not connect to %s on device\n", MISAGENT_SERVICE_NAME);
513 if (service)
514 lockdownd_service_descriptor_free(service);
515 lockdownd_client_free(client);
516 idevice_free(device);
517 return -1;
518 }
519
520 if (service)
521 lockdownd_service_descriptor_free(service);
522
523 switch (op) {
524 case OP_INSTALL:
525 {
526 unsigned char* profile_data = NULL;
527 unsigned int profile_size = 0;
528 if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
529 break;
530 }
531
532 uint64_t psize = profile_size;
533 plist_t pdata = plist_new_data((const char*)profile_data, psize);
534 free(profile_data);
535
536 if (misagent_install(mis, pdata) == MISAGENT_E_SUCCESS) {
537 printf("Profile '%s' installed successfully.\n", param);
538 } else {
539 int sc = misagent_get_status_code(mis);
540 fprintf(stderr, "Could not install profile '%s', status code: 0x%x\n", param, sc);
541 }
542 }
543 break;
544 case OP_LIST:
545 case OP_COPY:
546 {
547 plist_t profiles = NULL;
548 misagent_error_t merr;
549 if (product_version < 0x090300) {
550 merr = misagent_copy(mis, &profiles);
551 } else {
552 merr = misagent_copy_all(mis, &profiles);
553 }
554 if (merr == MISAGENT_E_SUCCESS) {
555 int found_match = 0;
556 uint32_t num_profiles = plist_array_get_size(profiles);
557 if (op == OP_LIST || !param2) {
558 printf("Device has %d provisioning %s installed:\n", num_profiles, (num_profiles == 1) ? "profile" : "profiles");
559 }
560 uint32_t j;
561 for (j = 0; !found_match && j < num_profiles; j++) {
562 char* p_name = NULL;
563 char* p_uuid = NULL;
564 plist_t profile = plist_array_get_item(profiles, j);
565 plist_t pl = profile_get_embedded_plist(profile);
566 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
567 plist_t node;
568 node = plist_dict_get_item(pl, "Name");
569 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
570 plist_get_string_val(node, &p_name);
571 }
572 node = plist_dict_get_item(pl, "UUID");
573 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
574 plist_get_string_val(node, &p_uuid);
575 }
576 }
577 if (param2) {
578 if (p_uuid && !strcmp(p_uuid, param)) {
579 found_match = 1;
580 } else {
581 free(p_uuid);
582 free(p_name);
583 continue;
584 }
585 }
586 printf("%s - %s\n", (p_uuid) ? p_uuid : "(unknown id)", (p_name) ? p_name : "(no name)");
587 if (op == OP_COPY) {
588 char pfname[512];
589 if (p_uuid) {
590 sprintf(pfname, "%s/%s.mobileprovision", (param2) ? param2 : param, p_uuid);
591 } else {
592 sprintf(pfname, "%s/profile%d.mobileprovision", (param2) ? param2 : param, j);
593 }
594 FILE* f = fopen(pfname, "wb");
595 if (f) {
596 char* dt = NULL;
597 uint64_t ds = 0;
598 plist_get_data_val(profile, &dt, &ds);
599 fwrite(dt, 1, ds, f);
600 fclose(f);
601 printf(" => %s\n", pfname);
602 } else {
603 fprintf(stderr, "Could not open '%s' for writing: %s\n", pfname, strerror(errno));
604 }
605 }
606 free(p_uuid);
607 free(p_name);
608 }
609 if (param2 && !found_match) {
610 fprintf(stderr, "Profile '%s' was not found on the device.\n", param);
611 res = -1;
612 }
613 } else {
614 int sc = misagent_get_status_code(mis);
615 fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
616 res = -1;
617 }
618 plist_free(profiles);
619 }
620 break;
621 case OP_REMOVE:
622 if (param) {
623 /* remove specified provisioning profile */
624 if (misagent_remove(mis, param) == MISAGENT_E_SUCCESS) {
625 printf("Profile '%s' removed.\n", param);
626 } else {
627 int sc = misagent_get_status_code(mis);
628 fprintf(stderr, "Could not remove profile '%s', status code 0x%x\n", param, sc);
629 }
630 } else {
631 /* remove all provisioning profiles */
632 plist_t profiles = NULL;
633 misagent_error_t merr;
634 if (product_version < 0x090300) {
635 merr = misagent_copy(mis, &profiles);
636 } else {
637 merr = misagent_copy_all(mis, &profiles);
638 }
639 if (merr == MISAGENT_E_SUCCESS) {
640 uint32_t j;
641 uint32_t num_removed = 0;
642 for (j = 0; j < plist_array_get_size(profiles); j++) {
643 char* p_name = NULL;
644 char* p_uuid = NULL;
645 plist_t profile = plist_array_get_item(profiles, j);
646 plist_t pl = profile_get_embedded_plist(profile);
647 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
648 plist_t node;
649 node = plist_dict_get_item(pl, "Name");
650 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
651 plist_get_string_val(node, &p_name);
652 }
653 node = plist_dict_get_item(pl, "UUID");
654 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
655 plist_get_string_val(node, &p_uuid);
656 }
657 }
658 if (p_uuid) {
659 if (misagent_remove(mis, p_uuid) == MISAGENT_E_SUCCESS) {
660 printf("OK profile removed: %s - %s\n", p_uuid, (p_name) ? p_name : "(no name)");
661 num_removed++;
662 } else {
663 int sc = misagent_get_status_code(mis);
664 printf("FAIL profile not removed: %s - %s (status code 0x%x)\n", p_uuid, (p_name) ? p_name : "(no name)", sc);
665 }
666 }
667 free(p_name);
668 free(p_uuid);
669 }
670 printf("%d profiles removed.\n", num_removed);
671 } else {
672 int sc = misagent_get_status_code(mis);
673 fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
674 res = -1;
675 }
676 plist_free(profiles);
677 }
678 break;
679 default:
680 break;
681 }
682
683 misagent_client_free(mis);
684
685 idevice_free(device);
686
687 return res;
688}
689
diff --git a/tools/idevicescreenshot.c b/tools/idevicescreenshot.c
index 8567f77..0e694c7 100644
--- a/tools/idevicescreenshot.c
+++ b/tools/idevicescreenshot.c
@@ -1,113 +1,227 @@
1/** 1/*
2 * idevicescreenshot -- Gets a screenshot from a connected iPhone/iPod Touch 2 * idevicescreenshot.c
3 * Gets a screenshot from a device
3 * 4 *
4 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li> 5 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
5 * 6 *
6 * Licensed under the GNU General Public License Version 2 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.
7 * 11 *
8 * This program is free software; you can redistribute it and/or modify 12 * This library is distributed in the hope that it will be useful,
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * GNU General Public License for more profile. 15 * Lesser General Public License for more details.
17 * 16 *
18 * You should have received a copy of the GNU General Public License 17 * You should have received a copy of the GNU Lesser General Public
19 * along with this program; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * USA
22 */ 20 */
23 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define TOOL_NAME "idevicescreenshot"
27
24#include <stdio.h> 28#include <stdio.h>
25#include <string.h> 29#include <string.h>
26#include <stdlib.h> 30#include <stdlib.h>
31#include <getopt.h>
27#include <errno.h> 32#include <errno.h>
28#include <time.h> 33#include <time.h>
34#include <unistd.h>
35#ifndef WIN32
36#include <signal.h>
37#endif
29 38
30#include <libimobiledevice/libimobiledevice.h> 39#include <libimobiledevice/libimobiledevice.h>
31#include <libimobiledevice/lockdown.h> 40#include <libimobiledevice/lockdown.h>
32#include <libimobiledevice/screenshotr.h> 41#include <libimobiledevice/screenshotr.h>
33 42
34void print_usage(int argc, char **argv); 43static void get_image_filename(char *imgdata, char **filename)
44{
45 // If the provided filename already has an extension, use it as is.
46 if (*filename) {
47 char *last_dot = strrchr(*filename, '.');
48 if (last_dot && !strchr(last_dot, '/')) {
49 return;
50 }
51 }
52
53 // Find the appropriate file extension for the filename.
54 const char *fileext = NULL;
55 if (memcmp(imgdata, "\x89PNG", 4) == 0) {
56 fileext = ".png";
57 } else if (memcmp(imgdata, "MM\x00*", 4) == 0) {
58 fileext = ".tiff";
59 } else {
60 printf("WARNING: screenshot data has unexpected image format.\n");
61 fileext = ".dat";
62 }
63
64 // If a filename without an extension is provided, append the extension.
65 // Otherwise, generate a filename based on the current time.
66 char *basename = NULL;
67 if (*filename) {
68 basename = (char*)malloc(strlen(*filename) + 1);
69 strcpy(basename, *filename);
70 free(*filename);
71 *filename = NULL;
72 } else {
73 time_t now = time(NULL);
74 basename = (char*)malloc(32);
75 strftime(basename, 31, "screenshot-%Y-%m-%d-%H-%M-%S", gmtime(&now));
76 }
77
78 // Ensure the filename is unique on disk.
79 char *unique_filename = (char*)malloc(strlen(basename) + strlen(fileext) + 7);
80 sprintf(unique_filename, "%s%s", basename, fileext);
81 int i;
82 for (i = 2; i < (1 << 16); i++) {
83 if (access(unique_filename, F_OK) == -1) {
84 *filename = unique_filename;
85 break;
86 }
87 sprintf(unique_filename, "%s-%d%s", basename, i, fileext);
88 }
89 if (!*filename) {
90 free(unique_filename);
91 }
92 free(basename);
93}
94
95static void print_usage(int argc, char **argv, int is_error)
96{
97 char *name = strrchr(argv[0], '/');
98 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [FILE]\n", (name ? name + 1: argv[0]));
99 fprintf(is_error ? stderr : stdout,
100 "\n"
101 "Gets a screenshot from a connected device.\n"
102 "\n"
103 "The image is in PNG format for iOS 9+ and otherwise in TIFF format.\n"
104 "The screenshot is saved as an image with the given FILE name.\n"
105 "If FILE has no extension, FILE will be a prefix of the saved filename.\n"
106 "If FILE is not specified, \"screenshot-DATE\", will be used as a prefix\n"
107 "of the filename, e.g.:\n"
108 " ./screenshot-2013-12-31-23-59-59.tiff\n"
109 "\n"
110 "NOTE: A mounted developer disk image is required on the device, otherwise\n"
111 "the screenshotr service is not available.\n"
112 "\n"
113 " -u, --udid UDID target specific device by UDID\n"
114 " -n, --network connect to network device\n"
115 " -d, --debug enable communication debugging\n"
116 " -h, --help prints usage information\n"
117 " -v, --version prints version information\n"
118 "\n"
119 "Homepage: <" PACKAGE_URL ">\n"
120 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
121 );
122}
35 123
36int main(int argc, char **argv) 124int main(int argc, char **argv)
37{ 125{
38 idevice_t device = NULL; 126 idevice_t device = NULL;
39 lockdownd_client_t lckd = NULL; 127 lockdownd_client_t lckd = NULL;
128 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
40 screenshotr_client_t shotr = NULL; 129 screenshotr_client_t shotr = NULL;
41 uint16_t port = 0; 130 lockdownd_service_descriptor_t service = NULL;
42 int result = -1; 131 int result = -1;
43 int i; 132 const char *udid = NULL;
44 char *uuid = NULL; 133 int use_network = 0;
134 char *filename = NULL;
135 int c = 0;
136 const struct option longopts[] = {
137 { "debug", no_argument, NULL, 'd' },
138 { "help", no_argument, NULL, 'h' },
139 { "udid", required_argument, NULL, 'u' },
140 { "network", no_argument, NULL, 'n' },
141 { "version", no_argument, NULL, 'v' },
142 { NULL, 0, NULL, 0}
143 };
45 144
145#ifndef WIN32
146 signal(SIGPIPE, SIG_IGN);
147#endif
46 /* parse cmdline args */ 148 /* parse cmdline args */
47 for (i = 1; i < argc; i++) { 149
48 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 150 /* parse cmdline arguments */
151 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
152 switch (c) {
153 case 'd':
49 idevice_set_debug_level(1); 154 idevice_set_debug_level(1);
50 continue; 155 break;
51 } 156 case 'u':
52 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 157 if (!*optarg) {
53 i++; 158 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
54 if (!argv[i] || (strlen(argv[i]) != 40)) { 159 print_usage(argc, argv, 1);
55 print_usage(argc, argv); 160 return 2;
56 return 0;
57 } 161 }
58 uuid = strdup(argv[i]); 162 udid = optarg;
59 continue; 163 break;
60 } 164 case 'n':
61 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 165 use_network = 1;
62 print_usage(argc, argv); 166 break;
167 case 'h':
168 print_usage(argc, argv, 0);
63 return 0; 169 return 0;
64 } 170 case 'v':
65 else { 171 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
66 print_usage(argc, argv);
67 return 0; 172 return 0;
173 default:
174 print_usage(argc, argv, 1);
175 return 2;
68 } 176 }
69 } 177 }
178 argc -= optind;
179 argv += optind;
70 180
71 if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) { 181 if (argv[0]) {
72 printf("No device found, is it plugged in?\n"); 182 filename = strdup(argv[0]);
73 if (uuid) { 183 }
74 free(uuid); 184
185 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
186 if (udid) {
187 printf("No device found with udid %s.\n", udid);
188 } else {
189 printf("No device found.\n");
75 } 190 }
76 return -1; 191 return -1;
77 } 192 }
78 if (uuid) {
79 free(uuid);
80 }
81 193
82 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, NULL)) { 194 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
83 idevice_free(device); 195 idevice_free(device);
84 printf("Exiting.\n"); 196 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
85 return -1; 197 return -1;
86 } 198 }
87 199
88 lockdownd_start_service(lckd, "com.apple.mobile.screenshotr", &port); 200 lockdownd_error_t lerr = lockdownd_start_service(lckd, SCREENSHOTR_SERVICE_NAME, &service);
89 lockdownd_client_free(lckd); 201 lockdownd_client_free(lckd);
90 if (port > 0) { 202 if (lerr == LOCKDOWN_E_SUCCESS) {
91 if (screenshotr_client_new(device, port, &shotr) != SCREENSHOTR_E_SUCCESS) { 203 if (screenshotr_client_new(device, service, &shotr) != SCREENSHOTR_E_SUCCESS) {
92 printf("Could not connect to screenshotr!\n"); 204 printf("Could not connect to screenshotr!\n");
93 } else { 205 } else {
94 char *imgdata = NULL; 206 char *imgdata = NULL;
95 char filename[36];
96 uint64_t imgsize = 0; 207 uint64_t imgsize = 0;
97 time_t now = time(NULL);
98 strftime(filename, 36, "screenshot-%Y-%m-%d-%H-%M-%S.tiff", gmtime(&now));
99 if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) { 208 if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) {
100 FILE *f = fopen(filename, "w"); 209 get_image_filename(imgdata, &filename);
101 if (f) { 210 if (!filename) {
102 if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) { 211 printf("FATAL: Could not find a unique filename!\n");
103 printf("Screenshot saved to %s\n", filename); 212 } else {
104 result = 0; 213 FILE *f = fopen(filename, "wb");
214 if (f) {
215 if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) {
216 printf("Screenshot saved to %s\n", filename);
217 result = 0;
218 } else {
219 printf("Could not save screenshot to file %s!\n", filename);
220 }
221 fclose(f);
105 } else { 222 } else {
106 printf("Could not save screenshot to file %s!\n", filename); 223 printf("Could not open %s for writing: %s\n", filename, strerror(errno));
107 } 224 }
108 fclose(f);
109 } else {
110 printf("Could not open %s for writing: %s\n", filename, strerror(errno));
111 } 225 }
112 } else { 226 } else {
113 printf("Could not get screenshot!\n"); 227 printf("Could not get screenshot!\n");
@@ -115,25 +229,14 @@ int main(int argc, char **argv)
115 screenshotr_client_free(shotr); 229 screenshotr_client_free(shotr);
116 } 230 }
117 } else { 231 } else {
118 printf("Could not start screenshotr service! Remember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n"); 232 printf("Could not start screenshotr service: %s\nRemember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n", lockdownd_strerror(lerr));
119 } 233 }
234
235 if (service)
236 lockdownd_service_descriptor_free(service);
237
120 idevice_free(device); 238 idevice_free(device);
121 239 free(filename);
122 return result;
123}
124 240
125void print_usage(int argc, char **argv) 241 return result;
126{
127 char *name = NULL;
128
129 name = strrchr(argv[0], '/');
130 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
131 printf("Gets a screenshot from the connected iPhone/iPod Touch.\n");
132 printf("The screenshot is saved as a TIFF image in the current directory.\n");
133 printf("NOTE: A mounted developer disk image is required on the device, otherwise\n");
134 printf("the screenshotr service is not available.\n\n");
135 printf(" -d, --debug\t\tenable communication debugging\n");
136 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
137 printf(" -h, --help\t\tprints usage information\n");
138 printf("\n");
139} 242}
diff --git a/tools/idevicesetlocation.c b/tools/idevicesetlocation.c
new file mode 100644
index 0000000..69fbaf5
--- /dev/null
+++ b/tools/idevicesetlocation.c
@@ -0,0 +1,192 @@
1/*
2 * idevicesetlocation.c
3 * Simulate location on iOS device with mounted developer disk image
4 *
5 * Copyright (c) 2016-2020 Nikias Bassen, All Rights Reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#define TOOL_NAME "idevicesetlocation"
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <string.h>
31#include <errno.h>
32#include <getopt.h>
33
34#include <libimobiledevice/libimobiledevice.h>
35#include <libimobiledevice/lockdown.h>
36#include <libimobiledevice/service.h>
37
38#include <endianness.h>
39
40#define DT_SIMULATELOCATION_SERVICE "com.apple.dt.simulatelocation"
41
42enum {
43 SET_LOCATION = 0,
44 RESET_LOCATION = 1
45};
46
47static void print_usage(int argc, char **argv, int is_error)
48{
49 char *bname = strrchr(argv[0], '/');
50 bname = (bname) ? bname + 1 : argv[0];
51
52 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] -- <LAT> <LONG>\n", bname);
53 fprintf(is_error ? stderr : stdout, " %s [OPTIONS] reset\n", bname);
54 fprintf(is_error ? stderr : stdout,
55 "\n"
56 "OPTIONS:\n"
57 " -u, --udid UDID target specific device by UDID\n"
58 " -n, --network connect to network device\n"
59 " -d, --debug enable communication debugging\n"
60 " -h, --help prints usage information\n"
61 " -v, --version prints version information\n"
62 "\n"
63 "Homepage: <" PACKAGE_URL ">\n"
64 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
65 );
66}
67
68int main(int argc, char **argv)
69{
70 int c = 0;
71 const struct option longopts[] = {
72 { "help", no_argument, NULL, 'h' },
73 { "udid", required_argument, NULL, 'u' },
74 { "debug", no_argument, NULL, 'd' },
75 { "network", no_argument, NULL, 'n' },
76 { "version", no_argument, NULL, 'v' },
77 { NULL, 0, NULL, 0}
78 };
79 uint32_t mode = 0;
80 const char *udid = NULL;
81 int use_network = 0;
82
83 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
84 switch (c) {
85 case 'd':
86 idevice_set_debug_level(1);
87 break;
88 case 'u':
89 if (!*optarg) {
90 fprintf(stderr, "ERROR: UDID must not be empty!\n");
91 print_usage(argc, argv, 1);
92 return 2;
93 }
94 udid = optarg;
95 break;
96 case 'n':
97 use_network = 1;
98 break;
99 case 'h':
100 print_usage(argc, argv, 0);
101 return 0;
102 case 'v':
103 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
104 return 0;
105 default:
106 print_usage(argc, argv, 1);
107 return 2;
108 }
109 }
110
111 argc -= optind;
112 argv += optind;
113
114 if ((argc > 2) || (argc < 1)) {
115 print_usage(argc+optind, argv-optind, 1);
116 return -1;
117 }
118
119 if (argc == 2) {
120 mode = SET_LOCATION;
121 } else if (argc == 1) {
122 if (strcmp(argv[0], "reset") == 0) {
123 mode = RESET_LOCATION;
124 } else {
125 print_usage(argc+optind, argv-optind, 1);
126 return -1;
127 }
128 }
129
130 idevice_t device = NULL;
131
132 if (idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS) {
133 if (udid) {
134 printf("ERROR: Device %s not found!\n", udid);
135 } else {
136 printf("ERROR: No device found!\n");
137 }
138 return -1;
139 }
140
141 lockdownd_client_t lockdown;
142 lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
143
144 lockdownd_service_descriptor_t svc = NULL;
145 lockdownd_error_t lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc);
146 if (lerr != LOCKDOWN_E_SUCCESS) {
147 lockdownd_client_free(lockdown);
148 idevice_free(device);
149 printf("ERROR: Could not start the simulatelocation service: %s\nMake sure a developer disk image is mounted!\n", lockdownd_strerror(lerr));
150 return -1;
151 }
152 lockdownd_client_free(lockdown);
153
154 service_client_t service = NULL;
155
156 service_error_t serr = service_client_new(device, svc, &service);
157
158 lockdownd_service_descriptor_free(svc);
159
160 if (serr != SERVICE_E_SUCCESS) {
161 lockdownd_client_free(lockdown);
162 idevice_free(device);
163 printf("ERROR: Could not connect to simulatelocation service (%d)\n", serr);
164 return -1;
165 }
166
167 uint32_t l;
168 uint32_t s = 0;
169
170 l = htobe32(mode);
171 service_send(service, (const char*)&l, 4, &s);
172 if (mode == SET_LOCATION) {
173 int len = 4 + strlen(argv[0]) + 4 + strlen(argv[1]);
174 char *buf = malloc(len);
175 uint32_t latlen;
176 latlen = strlen(argv[0]);
177 l = htobe32(latlen);
178 memcpy(buf, &l, 4);
179 memcpy(buf+4, argv[0], latlen);
180 uint32_t longlen = strlen(argv[1]);
181 l = htobe32(longlen);
182 memcpy(buf+4+latlen, &l, 4);
183 memcpy(buf+4+latlen+4, argv[1], longlen);
184
185 s = 0;
186 service_send(service, buf, len, &s);
187 }
188
189 idevice_free(device);
190
191 return 0;
192}
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c
index f2b3a2f..a0e641d 100644
--- a/tools/idevicesyslog.c
+++ b/tools/idevicesyslog.c
@@ -2,169 +2,783 @@
2 * idevicesyslog.c 2 * idevicesyslog.c
3 * Relay the syslog of a device to stdout 3 * Relay the syslog of a device to stdout
4 * 4 *
5 * Copyright (c) 2010-2020 Nikias Bassen, All Rights Reserved.
5 * Copyright (c) 2009 Martin Szulecki All Rights Reserved. 6 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * 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 * 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 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21 22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#define TOOL_NAME "idevicesyslog"
28
22#include <stdio.h> 29#include <stdio.h>
23#include <string.h> 30#include <string.h>
24#include <errno.h> 31#include <errno.h>
25#include <signal.h> 32#include <signal.h>
26#include <stdlib.h> 33#include <stdlib.h>
27#include <glib.h> 34#include <unistd.h>
35#include <getopt.h>
36
37#ifdef WIN32
38#include <windows.h>
39#define sleep(x) Sleep(x*1000)
40#endif
28 41
29#include <libimobiledevice/libimobiledevice.h> 42#include <libimobiledevice/libimobiledevice.h>
30#include <libimobiledevice/lockdown.h> 43#include <libimobiledevice/syslog_relay.h>
44#include <libimobiledevice-glue/termcolors.h>
31 45
32static int quit_flag = 0; 46static int quit_flag = 0;
47static int exit_on_disconnect = 0;
48static int show_device_name = 0;
49
50static char* udid = NULL;
51static char** proc_filters = NULL;
52static int num_proc_filters = 0;
53static int proc_filter_excluding = 0;
54
55static int* pid_filters = NULL;
56static int num_pid_filters = 0;
57
58static char** msg_filters = NULL;
59static int num_msg_filters = 0;
60
61static char** trigger_filters = NULL;
62static int num_trigger_filters = 0;
63static char** untrigger_filters = NULL;
64static int num_untrigger_filters = 0;
65static int triggered = 0;
66
67static idevice_t device = NULL;
68static syslog_relay_client_t syslog = NULL;
69
70static const char QUIET_FILTER[] = "CircleJoinRequested|CommCenter|HeuristicInterpreter|MobileMail|PowerUIAgent|ProtectedCloudKeySyncing|SpringBoard|UserEventAgent|WirelessRadioManagerd|accessoryd|accountsd|aggregated|analyticsd|appstored|apsd|assetsd|assistant_service|backboardd|biometrickitd|bluetoothd|calaccessd|callservicesd|cloudd|com.apple.Safari.SafeBrowsing.Service|contextstored|corecaptured|coreduetd|corespeechd|cdpd|dasd|dataaccessd|distnoted|dprivacyd|duetexpertd|findmydeviced|fmfd|fmflocatord|gpsd|healthd|homed|identityservicesd|imagent|itunescloudd|itunesstored|kernel|locationd|maild|mDNSResponder|mediaremoted|mediaserverd|mobileassetd|nanoregistryd|nanotimekitcompaniond|navd|nsurlsessiond|passd|pasted|photoanalysisd|powerd|powerlogHelperd|ptpd|rapportd|remindd|routined|runningboardd|searchd|sharingd|suggestd|symptomsd|timed|thermalmonitord|useractivityd|vmd|wifid|wirelessproxd";
71
72static int use_network = 0;
73
74static char *line = NULL;
75static int line_buffer_size = 0;
76static int lp = 0;
77
78static void add_filter(const char* filterstr)
79{
80 int filter_len = strlen(filterstr);
81 const char* start = filterstr;
82 const char* end = filterstr + filter_len;
83 const char* p = start;
84 while (p <= end) {
85 if ((*p == '|') || (*p == '\0')) {
86 if (p-start > 0) {
87 char* procn = malloc(p-start+1);
88 if (!procn) {
89 fprintf(stderr, "ERROR: malloc() failed\n");
90 exit(EXIT_FAILURE);
91 }
92 memcpy(procn, start, p-start);
93 procn[p-start] = '\0';
94 char* endp = NULL;
95 int pid_value = (int)strtol(procn, &endp, 10);
96 if (!endp || *endp == 0) {
97 int *new_pid_filters = realloc(pid_filters, sizeof(int) * (num_pid_filters+1));
98 if (!new_pid_filters) {
99 fprintf(stderr, "ERROR: realloc() failed\n");
100 exit(EXIT_FAILURE);
101 }
102 pid_filters = new_pid_filters;
103 pid_filters[num_pid_filters] = pid_value;
104 num_pid_filters++;
105 } else {
106 char **new_proc_filters = realloc(proc_filters, sizeof(char*) * (num_proc_filters+1));
107 if (!new_proc_filters) {
108 fprintf(stderr, "ERROR: realloc() failed\n");
109 exit(EXIT_FAILURE);
110 }
111 proc_filters = new_proc_filters;
112 proc_filters[num_proc_filters] = procn;
113 num_proc_filters++;
114 }
115 }
116 start = p+1;
117 }
118 p++;
119 }
120}
121
122static int find_char(char c, char** p, const char* end)
123{
124 while ((**p != c) && (*p < end)) {
125 (*p)++;
126 }
127 return (**p == c);
128}
129
130static void stop_logging(void);
131
132static void syslog_callback(char c, void *user_data)
133{
134 if (lp >= line_buffer_size-1) {
135 line_buffer_size+=1024;
136 char* _line = realloc(line, line_buffer_size);
137 if (!_line) {
138 fprintf(stderr, "ERROR: realloc failed\n");
139 exit(EXIT_FAILURE);
140 }
141 line = _line;
142 }
143 line[lp++] = c;
144 if (c == '\0') {
145 int shall_print = 0;
146 int trigger_off = 0;
147 lp--;
148 char* linep = &line[0];
149 do {
150 if (lp < 16) {
151 shall_print = 1;
152 cprintf(FG_WHITE);
153 break;
154 }
155
156 if (line[3] == ' ' && line[6] == ' ' && line[15] == ' ') {
157 char* end = &line[lp];
158 char* p = &line[16];
159
160 /* device name */
161 char* device_name_start = p;
162 char* device_name_end = p;
163 if (!find_char(' ', &p, end)) break;
164 device_name_end = p;
165 p++;
166
167 /* check if we have any triggers/untriggers */
168 if (num_untrigger_filters > 0 && triggered) {
169 int found = 0;
170 int i;
171 for (i = 0; i < num_untrigger_filters; i++) {
172 if (strstr(device_name_end+1, untrigger_filters[i])) {
173 found = 1;
174 break;
175 }
176 }
177 if (!found) {
178 shall_print = 1;
179 } else {
180 shall_print = 1;
181 trigger_off = 1;
182 }
183 } else if (num_trigger_filters > 0 && !triggered) {
184 int found = 0;
185 int i;
186 for (i = 0; i < num_trigger_filters; i++) {
187 if (strstr(device_name_end+1, trigger_filters[i])) {
188 found = 1;
189 break;
190 }
191 }
192 if (!found) {
193 shall_print = 0;
194 break;
195 }
196 triggered = 1;
197 shall_print = 1;
198 } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) {
199 shall_print = 0;
200 quit_flag++;
201 break;
202 }
203
204 /* check message filters */
205 if (num_msg_filters > 0) {
206 int found = 0;
207 int i;
208 for (i = 0; i < num_msg_filters; i++) {
209 if (strstr(device_name_end+1, msg_filters[i])) {
210 found = 1;
211 break;
212 }
213 }
214 if (!found) {
215 shall_print = 0;
216 break;
217 }
218 shall_print = 1;
219 }
220
221 /* process name */
222 char* proc_name_start = p;
223 char* proc_name_end = p;
224 if (!find_char('[', &p, end)) break;
225 char* process_name_start = proc_name_start;
226 char* process_name_end = p;
227 char* pid_start = p+1;
228 char* pp = process_name_start;
229 if (find_char('(', &pp, p)) {
230 process_name_end = pp;
231 }
232 if (!find_char(']', &p, end)) break;
233 p++;
234 if (*p != ' ') break;
235 proc_name_end = p;
236 p++;
237
238 int proc_matched = 0;
239 if (num_pid_filters > 0) {
240 char* endp = NULL;
241 int pid_value = (int)strtol(pid_start, &endp, 10);
242 if (endp && (*endp == ']')) {
243 int found = proc_filter_excluding;
244 int i = 0;
245 for (i = 0; i < num_pid_filters; i++) {
246 if (pid_value == pid_filters[i]) {
247 found = !proc_filter_excluding;
248 break;
249 }
250 }
251 if (found) {
252 proc_matched = 1;
253 }
254 }
255 }
256 if (num_proc_filters > 0 && !proc_matched) {
257 int found = proc_filter_excluding;
258 int i = 0;
259 for (i = 0; i < num_proc_filters; i++) {
260 if (!proc_filters[i]) continue;
261 if (strncmp(proc_filters[i], process_name_start, process_name_end-process_name_start) == 0) {
262 found = !proc_filter_excluding;
263 break;
264 }
265 }
266 if (found) {
267 proc_matched = 1;
268 }
269 }
270 if (proc_matched) {
271 shall_print = 1;
272 } else {
273 if (num_pid_filters > 0 || num_proc_filters > 0) {
274 shall_print = 0;
275 break;
276 }
277 }
278
279 /* log level */
280 char* level_start = p;
281 char* level_end = p;
282 const char* level_color = NULL;
283 if (!strncmp(p, "<Notice>:", 9)) {
284 level_end += 9;
285 level_color = FG_GREEN;
286 } else if (!strncmp(p, "<Error>:", 8)) {
287 level_end += 8;
288 level_color = FG_RED;
289 } else if (!strncmp(p, "<Warning>:", 10)) {
290 level_end += 10;
291 level_color = FG_YELLOW;
292 } else if (!strncmp(p, "<Debug>:", 8)) {
293 level_end += 8;
294 level_color = FG_MAGENTA;
295 } else {
296 level_color = FG_WHITE;
297 }
298
299 /* write date and time */
300 cprintf(FG_LIGHT_GRAY);
301 fwrite(line, 1, 16, stdout);
302
303 if (show_device_name) {
304 /* write device name */
305 cprintf(FG_DARK_YELLOW);
306 fwrite(device_name_start, 1, device_name_end-device_name_start+1, stdout);
307 cprintf(COLOR_RESET);
308 }
309
310 /* write process name */
311 cprintf(FG_BRIGHT_CYAN);
312 fwrite(process_name_start, 1, process_name_end-process_name_start, stdout);
313 cprintf(FG_CYAN);
314 fwrite(process_name_end, 1, proc_name_end-process_name_end+1, stdout);
315
316 /* write log level */
317 cprintf(level_color);
318 if (level_end > level_start) {
319 fwrite(level_start, 1, level_end-level_start, stdout);
320 p = level_end;
321 }
322
323 lp -= p - linep;
324 linep = p;
325
326 cprintf(FG_WHITE);
327
328 } else {
329 shall_print = 1;
330 cprintf(FG_WHITE);
331 }
332 } while (0);
333
334 if ((num_msg_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) {
335 fwrite(linep, 1, lp, stdout);
336 cprintf(COLOR_RESET);
337 fflush(stdout);
338 if (trigger_off) {
339 triggered = 0;
340 }
341 }
342 line[0] = '\0';
343 lp = 0;
344 return;
345 }
346}
347
348static int start_logging(void)
349{
350 idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
351 if (ret != IDEVICE_E_SUCCESS) {
352 fprintf(stderr, "Device with udid %s not found!?\n", udid);
353 return -1;
354 }
355
356 lockdownd_client_t lockdown = NULL;
357 lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
358 if (lerr != LOCKDOWN_E_SUCCESS) {
359 fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
360 idevice_free(device);
361 device = NULL;
362 return -1;
363 }
364
365 /* start syslog_relay service */
366 lockdownd_service_descriptor_t svc = NULL;
367 lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc);
368 if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
369 fprintf(stderr, "*** Device is passcode protected, enter passcode on the device to continue ***\n");
370 while (!quit_flag) {
371 lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc);
372 if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) {
373 break;
374 }
375 sleep(1);
376 }
377 }
378 if (lerr != LOCKDOWN_E_SUCCESS) {
379 fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
380 idevice_free(device);
381 device = NULL;
382 return -1;
383 }
384 lockdownd_client_free(lockdown);
385
386 /* connect to syslog_relay service */
387 syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR;
388 serr = syslog_relay_client_new(device, svc, &syslog);
389 lockdownd_service_descriptor_free(svc);
390 if (serr != SYSLOG_RELAY_E_SUCCESS) {
391 fprintf(stderr, "ERROR: Could not start service com.apple.syslog_relay.\n");
392 idevice_free(device);
393 device = NULL;
394 return -1;
395 }
396
397 /* start capturing syslog */
398 serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL);
399 if (serr != SYSLOG_RELAY_E_SUCCESS) {
400 fprintf(stderr, "ERROR: Unable tot start capturing syslog.\n");
401 syslog_relay_client_free(syslog);
402 syslog = NULL;
403 idevice_free(device);
404 device = NULL;
405 return -1;
406 }
407
408 fprintf(stdout, "[connected:%s]\n", udid);
409 fflush(stdout);
410
411 return 0;
412}
413
414static void stop_logging(void)
415{
416 fflush(stdout);
33 417
34void print_usage(int argc, char **argv); 418 if (syslog) {
419 syslog_relay_client_free(syslog);
420 syslog = NULL;
421 }
422
423 if (device) {
424 idevice_free(device);
425 device = NULL;
426 }
427}
428
429static void device_event_cb(const idevice_event_t* event, void* userdata)
430{
431 if (use_network && event->conn_type != CONNECTION_NETWORK) {
432 return;
433 }
434 if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
435 return;
436 }
437 if (event->event == IDEVICE_DEVICE_ADD) {
438 if (!syslog) {
439 if (!udid) {
440 udid = strdup(event->udid);
441 }
442 if (strcmp(udid, event->udid) == 0) {
443 if (start_logging() != 0) {
444 fprintf(stderr, "Could not start logger for udid %s\n", udid);
445 }
446 }
447 }
448 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
449 if (syslog && (strcmp(udid, event->udid) == 0)) {
450 stop_logging();
451 fprintf(stdout, "[disconnected:%s]\n", udid);
452 if (exit_on_disconnect) {
453 quit_flag++;
454 }
455 }
456 }
457}
35 458
36/** 459/**
37 * signal handler function for cleaning up properly 460 * signal handler function for cleaning up properly
38 */ 461 */
39static void clean_exit(int sig) 462static void clean_exit(int sig)
40{ 463{
41 fprintf(stderr, "Exiting...\n"); 464 fprintf(stderr, "\nExiting...\n");
42 quit_flag++; 465 quit_flag++;
43} 466}
44 467
468static void print_usage(int argc, char **argv, int is_error)
469{
470 char *name = strrchr(argv[0], '/');
471 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
472 fprintf(is_error ? stderr : stdout,
473 "\n"
474 "Relay syslog of a connected device.\n"
475 "\n"
476 "OPTIONS:\n"
477 " -u, --udid UDID target specific device by UDID\n"
478 " -n, --network connect to network device\n"
479 " -x, --exit exit when device disconnects\n"
480 " -h, --help prints usage information\n"
481 " -d, --debug enable communication debugging\n"
482 " -v, --version prints version information\n"
483 " --no-colors disable colored output\n"
484 " -o, --output FILE write to FILE instead of stdout\n"
485 " (existing FILE will be overwritten)\n"
486 " --colors force writing colored output, e.g. for --output\n"
487 "\n"
488 "FILTER OPTIONS:\n"
489 " -m, --match STRING only print messages that contain STRING\n"
490 " -t, --trigger STRING start logging when matching STRING\n"
491 " -T, --untrigger STRING stop logging when matching STRING\n"
492 " -p, --process PROCESS only print messages from matching process(es)\n"
493 " -e, --exclude PROCESS print all messages except matching process(es)\n"
494 " PROCESS is a process name or multiple process names\n"
495 " separated by \"|\".\n"
496 " -q, --quiet set a filter to exclude common noisy processes\n"
497 " --quiet-list prints the list of processes for --quiet and exits\n"
498 " -k, --kernel only print kernel messages\n"
499 " -K, --no-kernel suppress kernel messages\n"
500 "\n"
501 "For filter examples consult idevicesyslog(1) man page.\n"
502 "\n"
503 "Homepage: <" PACKAGE_URL ">\n"
504 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
505 );
506}
507
45int main(int argc, char *argv[]) 508int main(int argc, char *argv[])
46{ 509{
47 lockdownd_client_t client = NULL; 510 int include_filter = 0;
48 idevice_t phone = NULL; 511 int exclude_filter = 0;
49 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 512 int include_kernel = 0;
50 int i; 513 int exclude_kernel = 0;
51 char uuid[41]; 514 int force_colors = 0;
52 uint16_t port = 0; 515 int c = 0;
53 uuid[0] = 0; 516 const struct option longopts[] = {
517 { "debug", no_argument, NULL, 'd' },
518 { "help", no_argument, NULL, 'h' },
519 { "udid", required_argument, NULL, 'u' },
520 { "network", no_argument, NULL, 'n' },
521 { "exit", no_argument, NULL, 'x' },
522 { "trigger", required_argument, NULL, 't' },
523 { "untrigger", required_argument, NULL, 'T' },
524 { "match", required_argument, NULL, 'm' },
525 { "process", required_argument, NULL, 'p' },
526 { "exclude", required_argument, NULL, 'e' },
527 { "quiet", no_argument, NULL, 'q' },
528 { "kernel", no_argument, NULL, 'k' },
529 { "no-kernel", no_argument, NULL, 'K' },
530 { "quiet-list", no_argument, NULL, 1 },
531 { "no-colors", no_argument, NULL, 2 },
532 { "colors", no_argument, NULL, 3 },
533 { "output", required_argument, NULL, 'o' },
534 { "version", no_argument, NULL, 'v' },
535 { NULL, 0, NULL, 0}
536 };
54 537
55 signal(SIGINT, clean_exit); 538 signal(SIGINT, clean_exit);
56 signal(SIGQUIT, clean_exit);
57 signal(SIGTERM, clean_exit); 539 signal(SIGTERM, clean_exit);
540#ifndef WIN32
541 signal(SIGQUIT, clean_exit);
58 signal(SIGPIPE, SIG_IGN); 542 signal(SIGPIPE, SIG_IGN);
543#endif
59 544
60 /* parse cmdline args */ 545 while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:e:p:qkKo:v", longopts, NULL)) != -1) {
61 for (i = 1; i < argc; i++) { 546 switch (c) {
62 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 547 case 'd':
63 idevice_set_debug_level(1); 548 idevice_set_debug_level(1);
64 continue; 549 break;
65 } 550 case 'u':
66 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 551 if (!*optarg) {
67 i++; 552 fprintf(stderr, "ERROR: UDID must not be empty!\n");
68 if (!argv[i] || (strlen(argv[i]) != 40)) { 553 print_usage(argc, argv, 1);
69 print_usage(argc, argv); 554 return 2;
70 return 0; 555 }
71 } 556 free(udid);
72 strcpy(uuid, argv[i]); 557 udid = strdup(optarg);
73 continue; 558 break;
74 } 559 case 'n':
75 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 560 use_network = 1;
76 print_usage(argc, argv); 561 break;
562 case 'q':
563 exclude_filter++;
564 add_filter(QUIET_FILTER);
565 break;
566 case 'p':
567 case 'e':
568 if (c == 'p') {
569 include_filter++;
570 } else if (c == 'e') {
571 exclude_filter++;
572 }
573 if (!*optarg) {
574 fprintf(stderr, "ERROR: filter string must not be empty!\n");
575 print_usage(argc, argv, 1);
576 return 2;
577 }
578 add_filter(optarg);
579 break;
580 case 'm':
581 if (!*optarg) {
582 fprintf(stderr, "ERROR: message filter string must not be empty!\n");
583 print_usage(argc, argv, 1);
584 return 2;
585 } else {
586 char **new_msg_filters = realloc(msg_filters, sizeof(char*) * (num_msg_filters+1));
587 if (!new_msg_filters) {
588 fprintf(stderr, "ERROR: realloc() failed\n");
589 exit(EXIT_FAILURE);
590 }
591 msg_filters = new_msg_filters;
592 msg_filters[num_msg_filters] = strdup(optarg);
593 num_msg_filters++;
594 }
595 break;
596 case 't':
597 if (!*optarg) {
598 fprintf(stderr, "ERROR: trigger filter string must not be empty!\n");
599 print_usage(argc, argv, 1);
600 return 2;
601 } else {
602 char **new_trigger_filters = realloc(trigger_filters, sizeof(char*) * (num_trigger_filters+1));
603 if (!new_trigger_filters) {
604 fprintf(stderr, "ERROR: realloc() failed\n");
605 exit(EXIT_FAILURE);
606 }
607 trigger_filters = new_trigger_filters;
608 trigger_filters[num_trigger_filters] = strdup(optarg);
609 num_trigger_filters++;
610 }
611 break;
612 case 'T':
613 if (!*optarg) {
614 fprintf(stderr, "ERROR: untrigger filter string must not be empty!\n");
615 print_usage(argc, argv, 1);
616 return 2;
617 } else {
618 char **new_untrigger_filters = realloc(untrigger_filters, sizeof(char*) * (num_untrigger_filters+1));
619 if (!new_untrigger_filters) {
620 fprintf(stderr, "ERROR: realloc() failed\n");
621 exit(EXIT_FAILURE);
622 }
623 untrigger_filters = new_untrigger_filters;
624 untrigger_filters[num_untrigger_filters] = strdup(optarg);
625 num_untrigger_filters++;
626 }
627 break;
628 case 'k':
629 include_kernel++;
630 break;
631 case 'K':
632 exclude_kernel++;
633 break;
634 case 'x':
635 exit_on_disconnect = 1;
636 break;
637 case 'h':
638 print_usage(argc, argv, 0);
639 return 0;
640 case 1: {
641 printf("%s\n", QUIET_FILTER);
77 return 0; 642 return 0;
78 } 643 }
79 else { 644 case 2:
80 print_usage(argc, argv); 645 term_colors_set_enabled(0);
646 break;
647 case 3:
648 force_colors = 1;
649 break;
650 case 'o':
651 if (!*optarg) {
652 fprintf(stderr, "ERROR: --output option requires an argument!\n");
653 print_usage(argc, argv, 1);
654 return 2;
655 } else {
656 if (freopen(optarg, "w", stdout) == NULL) {
657 fprintf(stderr, "ERROR: Failed to open output file '%s' for writing: %s\n", optarg, strerror(errno));
658 return 1;
659 }
660 term_colors_set_enabled(0);
661 }
662 break;
663 case 'v':
664 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
81 return 0; 665 return 0;
666 default:
667 print_usage(argc, argv, 1);
668 return 2;
82 } 669 }
83 } 670 }
84 671
85 if (uuid[0] != 0) { 672 if (force_colors) {
86 ret = idevice_new(&phone, uuid); 673 term_colors_set_enabled(1);
87 if (ret != IDEVICE_E_SUCCESS) {
88 printf("No device found with uuid %s, is it plugged in?\n", uuid);
89 return -1;
90 }
91 } 674 }
92 else 675
93 { 676 if (include_kernel > 0 && exclude_kernel > 0) {
94 ret = idevice_new(&phone, NULL); 677 fprintf(stderr, "ERROR: -k and -K cannot be used together.\n");
95 if (ret != IDEVICE_E_SUCCESS) { 678 print_usage(argc, argv, 1);
96 printf("No device found, is it plugged in?\n"); 679 return 2;
97 return -1;
98 }
99 } 680 }
100 681
101 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicesyslog")) { 682 if (include_filter > 0 && exclude_filter > 0) {
102 idevice_free(phone); 683 fprintf(stderr, "ERROR: -p and -e/-q cannot be used together.\n");
103 return -1; 684 print_usage(argc, argv, 1);
685 return 2;
686 }
687 if (include_filter > 0 && exclude_kernel > 0) {
688 fprintf(stderr, "ERROR: -p and -K cannot be used together.\n");
689 print_usage(argc, argv, 1);
690 return 2;
104 } 691 }
105 692
106 /* start syslog_relay service and retrieve port */ 693 if (exclude_filter > 0) {
107 ret = lockdownd_start_service(client, "com.apple.syslog_relay", &port); 694 proc_filter_excluding = 1;
108 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 695 if (include_kernel) {
109 lockdownd_client_free(client); 696 int i = 0;
110 697 for (i = 0; i < num_proc_filters; i++) {
111 /* connect to socket relay messages */ 698 if (!strcmp(proc_filters[i], "kernel")) {
112 idevice_connection_t conn = NULL; 699 free(proc_filters[i]);
113 if ((idevice_connect(phone, port, &conn) != IDEVICE_E_SUCCESS) || !conn) { 700 proc_filters[i] = NULL;
114 printf("ERROR: Could not open usbmux connection.\n");
115 } else {
116 while (!quit_flag) {
117 char *receive = NULL;
118 uint32_t datalen = 0, bytes = 0, recv_bytes = 0;
119
120 ret = idevice_connection_receive(conn, (char *) &datalen, sizeof(datalen), &bytes);
121 if (ret < 0) {
122 fprintf(stderr, "Error receiving data. Exiting...\n");
123 break;
124 } 701 }
702 }
703 } else if (exclude_kernel) {
704 add_filter("kernel");
705 }
706 } else {
707 if (include_kernel) {
708 add_filter("kernel");
709 } else if (exclude_kernel) {
710 proc_filter_excluding = 1;
711 add_filter("kernel");
712 }
713 }
125 714
126 datalen = GUINT32_FROM_BE(datalen); 715 if (num_untrigger_filters > 0 && num_trigger_filters == 0) {
716 triggered = 1;
717 }
127 718
128 if (datalen == 0) 719 argc -= optind;
129 continue; 720 argv += optind;
130 721
131 recv_bytes += bytes; 722 int num = 0;
132 receive = (char *) malloc(sizeof(char) * datalen); 723 idevice_info_t *devices = NULL;
724 idevice_get_device_list_extended(&devices, &num);
725 idevice_device_list_extended_free(devices);
726 if (num == 0) {
727 if (!udid) {
728 fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
729 return -1;
730 }
133 731
134 while (!quit_flag && (recv_bytes <= datalen)) { 732 fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
135 ret = idevice_connection_receive(conn, receive, datalen, &bytes); 733 }
136 734
137 if (bytes == 0) 735 line_buffer_size = 1024;
138 break; 736 line = malloc(line_buffer_size);
139 737
140 recv_bytes += bytes; 738 idevice_subscription_context_t context = NULL;
739 idevice_events_subscribe(&context, device_event_cb, NULL);
141 740
142 fwrite(receive, sizeof(char), bytes, stdout); 741 while (!quit_flag) {
143 } 742 sleep(1);
743 }
744 idevice_events_unsubscribe(context);
745 stop_logging();
144 746
145 free(receive); 747 if (num_proc_filters > 0) {
146 } 748 int i;
749 for (i = 0; i < num_proc_filters; i++) {
750 free(proc_filters[i]);
147 } 751 }
148 idevice_disconnect(conn); 752 free(proc_filters);
149 } else { 753 }
150 printf("ERROR: Could not start service com.apple.syslog_relay.\n"); 754 if (num_pid_filters > 0) {
755 free(pid_filters);
756 }
757 if (num_msg_filters > 0) {
758 int i;
759 for (i = 0; i < num_msg_filters; i++) {
760 free(msg_filters[i]);
761 }
762 free(msg_filters);
763 }
764 if (num_trigger_filters > 0) {
765 int i;
766 for (i = 0; i < num_trigger_filters; i++) {
767 free(trigger_filters[i]);
768 }
769 free(trigger_filters);
770 }
771 if (num_untrigger_filters > 0) {
772 int i;
773 for (i = 0; i < num_untrigger_filters; i++) {
774 free(untrigger_filters[i]);
775 }
776 free(untrigger_filters);
151 } 777 }
152 778
153 idevice_free(phone); 779 free(line);
154 780
155 return 0; 781 free(udid);
156}
157 782
158void print_usage(int argc, char **argv) 783 return 0;
159{
160 char *name = NULL;
161
162 name = strrchr(argv[0], '/');
163 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
164 printf("Relay syslog of a connected iPhone/iPod Touch.\n\n");
165 printf(" -d, --debug\t\tenable communication debugging\n");
166 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
167 printf(" -h, --help\t\tprints usage information\n");
168 printf("\n");
169} 784}
170