summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am139
-rw-r--r--tools/afcclient.c1648
-rw-r--r--tools/idevice_id.c169
-rw-r--r--tools/idevicebackup.c675
-rw-r--r--tools/idevicebackup2.c2910
-rw-r--r--tools/idevicebtlogger.c458
-rw-r--r--tools/idevicecrashreport.c545
-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.c858
-rw-r--r--tools/ideviceinfo.c363
-rw-r--r--tools/idevicename.c159
-rw-r--r--tools/idevicenotificationproxy.c305
-rw-r--r--tools/idevicepair.c473
-rw-r--r--tools/ideviceprovision.c670
-rw-r--r--tools/idevicescreenshot.c259
-rw-r--r--tools/idevicesetlocation.c202
-rw-r--r--tools/idevicesyslog.c1332
22 files changed, 11985 insertions, 1379 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 0a47fdc..24cfc66 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)
16idevicepair_LDADD = ../src/libimobiledevice.la 55idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS) $(ssl_lib_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) $(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) $(limd_glue_CFLAGS)
139afcclient_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
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..c2e2ce3
--- /dev/null
+++ b/tools/afcclient.c
@@ -0,0 +1,1648 @@
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#include <sys/stat.h>
42
43#ifdef _WIN32
44#include <windows.h>
45#include <sys/time.h>
46#include <conio.h>
47#define sleep(x) Sleep(x*1000)
48#define S_IFMT 0170000 /* [XSI] type of file mask */
49#define S_IFIFO 0010000 /* [XSI] named pipe (fifo) */
50#define S_IFCHR 0020000 /* [XSI] character special */
51#define S_IFBLK 0060000 /* [XSI] block special */
52#define S_IFLNK 0120000 /* [XSI] symbolic link */
53#define S_IFSOCK 0140000 /* [XSI] socket */
54#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* block special */
55#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* char special */
56#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* fifo or socket */
57#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* symbolic link */
58#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* socket */
59#else
60#include <sys/time.h>
61#include <termios.h>
62#endif
63
64#ifdef HAVE_READLINE
65#include <readline/readline.h>
66#include <readline/history.h>
67#endif
68
69#include <libimobiledevice/libimobiledevice.h>
70#include <libimobiledevice/lockdown.h>
71#include <libimobiledevice/house_arrest.h>
72#include <libimobiledevice/afc.h>
73#include <plist/plist.h>
74
75#include <libimobiledevice-glue/termcolors.h>
76#include <libimobiledevice-glue/utils.h>
77
78#undef st_mtime
79#undef st_birthtime
80struct afc_file_stat {
81 uint16_t st_mode;
82 uint16_t st_nlink;
83 uint64_t st_size;
84 uint64_t st_mtime;
85 uint64_t st_birthtime;
86 uint32_t st_blocks;
87};
88
89static char* udid = NULL;
90static int connected = 0;
91static int use_network = 0;
92static idevice_subscription_context_t context = NULL;
93static char* curdir = NULL;
94static size_t curdir_len = 0;
95
96static int file_exists(const char* path)
97{
98 struct stat tst;
99#ifdef _WIN32
100 return (stat(path, &tst) == 0);
101#else
102 return (lstat(path, &tst) == 0);
103#endif
104}
105
106static int is_directory(const char* path)
107{
108 struct stat tst;
109#ifdef _WIN32
110 return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
111#else
112 return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
113#endif
114}
115
116static void print_usage(int argc, char **argv, int is_error)
117{
118 char *name = strrchr(argv[0], '/');
119 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
120 fprintf(is_error ? stderr : stdout,
121 "\n"
122 "Interact with AFC/HouseArrest service on a connected device.\n"
123 "\n"
124 "OPTIONS:\n"
125 " -u, --udid UDID target specific device by UDID\n"
126 " -n, --network connect to network device (not recommended!)\n"
127 " --container <appid> Access container of given app\n"
128 " --documents <appid> Access Documents directory of given app\n"
129 " -h, --help prints usage information\n" \
130 " -d, --debug enable communication debugging\n" \
131 " -v, --version prints version information\n" \
132 "\n"
133 );
134 fprintf(is_error ? stderr : stdout,
135 "\n" \
136 "Homepage: <" PACKAGE_URL ">\n"
137 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
138 );
139}
140
141#ifndef HAVE_READLINE
142#ifdef _WIN32
143#define BS_CC '\b'
144#define getch _getch
145#else
146#define BS_CC 0x7f
147#define getch getchar
148#endif
149static void get_input(char *buf, int maxlen)
150{
151 int len = 0;
152 int c;
153
154 while ((c = getch())) {
155 if ((c == '\r') || (c == '\n')) {
156 break;
157 }
158 if (isprint(c)) {
159 if (len < maxlen-1) {
160 buf[len++] = c;
161#ifdef _WIN32
162 fputc(c, stdout);
163#endif
164 }
165 } else if (c == BS_CC) {
166 if (len > 0) {
167 fputs("\b \b", stdout);
168 len--;
169 } else {
170 fputc(0x07, stdout);
171 }
172 }
173 }
174 buf[len] = 0;
175}
176#endif
177
178#define OPT_DOCUMENTS 1
179#define OPT_CONTAINER 2
180
181int stop_requested = 0;
182
183static void handle_signal(int sig)
184{
185 stop_requested++;
186#ifdef _WIN32
187 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
188#else
189 kill(getpid(), SIGINT);
190#endif
191}
192
193static void handle_help(afc_client_t afc, int argc, char** argv)
194{
195 printf("Available commands:\n");
196 printf("help - print list of available commands\n");
197 printf("devinfo - print device information\n");
198 printf("info PATH - print file attributes of file at PATH\n");
199 printf("ls [-l] PATH - print directory contents of PATH\n");
200 printf("mv OLD NEW - rename file OLD to NEW\n");
201 printf("mkdir PATH - create directory at PATH\n");
202 printf("ln [-s] FILE [LINK] - create a (symbolic) link to file named LINKNAME\n");
203 printf(" NOTE: This feature has been disabled in newer versions of iOS.\n");
204 printf("rm PATH - remove item at PATH\n");
205 printf("get [-rf] PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n");
206 printf("put [-rf] LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n");
207 printf("\n");
208}
209
210static const char* path_get_basename(const char* path)
211{
212 const char *p = strrchr(path, '/');
213 return p ? p + 1 : path;
214}
215
216static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y)
217{
218 /* Perform the carry for the later subtraction by updating y. */
219 if (x->tv_usec < y->tv_usec) {
220 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
221 y->tv_usec -= 1000000 * nsec;
222 y->tv_sec += nsec;
223 }
224 if (x->tv_usec - y->tv_usec > 1000000) {
225 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
226 y->tv_usec += 1000000 * nsec;
227 y->tv_sec -= nsec;
228 }
229 /* Compute the time remaining to wait.
230 tv_usec is certainly positive. */
231 result->tv_sec = x->tv_sec - y->tv_sec;
232 result->tv_usec = x->tv_usec - y->tv_usec;
233 /* Return 1 if result is negative. */
234 return x->tv_sec < y->tv_sec;
235}
236
237struct str_item {
238 size_t len;
239 char* str;
240};
241
242static char* get_absolute_path(const char *path)
243{
244 if (*path == '/') {
245 return strdup(path);
246 } else {
247 size_t len = curdir_len + 1 + strlen(path) + 1;
248 char* result = (char*)malloc(len);
249 if (!strcmp(curdir, "/")) {
250 snprintf(result, len, "/%s", path);
251 } else {
252 snprintf(result, len, "%s/%s", curdir, path);
253 }
254 return result;
255 }
256}
257
258static char* get_realpath(const char* path)
259{
260 if (!path) return NULL;
261
262 int is_absolute = 0;
263 if (*path == '/') {
264 is_absolute = 1;
265 }
266
267 const char* p = path;
268 if (is_absolute) {
269 while (*p == '/') p++;
270 }
271 if (*p == '\0') {
272 return strdup("/");
273 }
274
275 int c_count = 1;
276 const char* start = p;
277 const char* end = p;
278 struct str_item* comps = NULL;
279
280 while (*p) {
281 if (*p == '/') {
282 p++;
283 end = p-1;
284 while (*p == '/') p++;
285 if (*p == '\0') break;
286 struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
287 if (!newcomps) {
288 free(comps);
289 printf("%s: out of memory?!\n", __func__);
290 return NULL;
291 }
292 comps = newcomps;
293 char *comp = (char*)malloc(end-start+1);
294 strncpy(comp, start, end-start);
295 comp[end-start] = '\0';
296 comps[c_count-1].len = end-start;
297 comps[c_count-1].str = comp;
298 c_count++;
299 start = p;
300 end = p;
301 }
302 p++;
303 }
304 if (p > start) {
305 if (start == end) {
306 end = p;
307 }
308 struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
309 if (!newcomps) {
310 free(comps);
311 printf("%s: out of memory?!\n", __func__);
312 return NULL;
313 }
314 comps = newcomps;
315 char *comp = (char*)malloc(end-start+1);
316 strncpy(comp, start, end-start);
317 comp[end-start] = '\0';
318 comps[c_count-1].len = end-start;
319 comps[c_count-1].str = comp;
320 }
321
322 struct str_item* comps_final = (struct str_item*)malloc(sizeof(struct str_item)*(c_count+1));
323 int o = 1;
324 if (is_absolute) {
325 comps_final[0].len = 1;
326 comps_final[0].str = (char*)"/";
327 } else {
328 comps_final[0].len = curdir_len;
329 comps_final[0].str = curdir;
330 }
331 size_t o_len = comps_final[0].len;
332
333 for (int i = 0; i < c_count; i++) {
334 if (!strcmp(comps[i].str, "..")) {
335 if (o > 1) {
336 o--;
337 }
338 continue;
339 } else if (!strcmp(comps[i].str, ".")) {
340 continue;
341 }
342 o_len += comps[i].len;
343 comps_final[o].str = comps[i].str;
344 comps_final[o].len = comps[i].len;
345 o++;
346 }
347
348 o_len += o;
349 char* result = (char*)malloc(o_len);
350 char* presult = result;
351 for (int i = 0; i < o; i++) {
352 if (i > 0 && strcmp(comps_final[i-1].str, "/") != 0) {
353 *presult = '/';
354 presult++;
355 }
356 strncpy(presult, comps_final[i].str, comps_final[i].len);
357 presult+=comps_final[i].len;
358 *presult = '\0';
359 }
360 if (presult == result) {
361 *presult = '/';
362 presult++;
363 *presult = 0;
364 }
365
366 for (int i = 0; i < c_count; i++) {
367 free(comps[i].str);
368 }
369 free(comps);
370 free(comps_final);
371
372 return result;
373}
374
375static void handle_devinfo(afc_client_t afc, int argc, char** argv)
376{
377 plist_t info = NULL;
378 afc_error_t err = afc_get_device_info_plist(afc, &info);
379 if (err == AFC_E_SUCCESS && info) {
380 if (argc > 0 && !strcmp(argv[0], "--plain")) {
381 plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
382 } else {
383 plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
384 }
385 } else {
386 printf("Error: Failed to get device info: %s (%d)\n", afc_strerror(err), err);
387 }
388 plist_free(info);
389}
390
391static int get_file_info_stat(afc_client_t afc, const char* path, struct afc_file_stat *stbuf)
392{
393 plist_t info = NULL;
394 afc_error_t ret = afc_get_file_info_plist(afc, path, &info);
395 memset(stbuf, 0, sizeof(struct afc_file_stat));
396 if (ret != AFC_E_SUCCESS) {
397 return -1;
398 } else if (!info) {
399 return -1;
400 }
401 stbuf->st_size = plist_dict_get_uint(info, "st_size");
402 stbuf->st_blocks = plist_dict_get_uint(info, "st_blocks");
403 const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
404 if (s_ifmt) {
405 if (!strcmp(s_ifmt, "S_IFREG")) {
406 stbuf->st_mode = S_IFREG;
407 } else if (!strcmp(s_ifmt, "S_IFDIR")) {
408 stbuf->st_mode = S_IFDIR;
409 } else if (!strcmp(s_ifmt, "S_IFLNK")) {
410 stbuf->st_mode = S_IFLNK;
411 } else if (!strcmp(s_ifmt, "S_IFBLK")) {
412 stbuf->st_mode = S_IFBLK;
413 } else if (!strcmp(s_ifmt, "S_IFCHR")) {
414 stbuf->st_mode = S_IFCHR;
415 } else if (!strcmp(s_ifmt, "S_IFIFO")) {
416 stbuf->st_mode = S_IFIFO;
417 } else if (!strcmp(s_ifmt, "S_IFSOCK")) {
418 stbuf->st_mode = S_IFSOCK;
419 }
420 }
421 stbuf->st_nlink = plist_dict_get_uint(info, "st_nlink");
422 stbuf->st_mtime = (time_t)(plist_dict_get_uint(info, "st_mtime") / 1000000000);
423 /* available on iOS 7+ */
424 stbuf->st_birthtime = (time_t)(plist_dict_get_uint(info, "st_birthtime") / 1000000000);
425 plist_free(info);
426 return 0;
427}
428
429static void handle_file_info(afc_client_t afc, int argc, char** argv)
430{
431 if (argc < 1) {
432 printf("Error: Missing PATH.\n");
433 return;
434 }
435
436 plist_t info = NULL;
437 char* abspath = get_absolute_path(argv[0]);
438 if (!abspath) {
439 printf("Error: Invalid argument\n");
440 return;
441 }
442 afc_error_t err = afc_get_file_info_plist(afc, abspath, &info);
443 if (err == AFC_E_SUCCESS && info) {
444 if (argc > 1 && !strcmp(argv[1], "--plain")) {
445 plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
446 } else {
447 plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
448 }
449 } else {
450 printf("Error: Failed to get file info for %s: %s (%d)\n", argv[0], afc_strerror(err), err);
451 }
452 plist_free(info);
453 free(abspath);
454}
455
456static void print_file_info(afc_client_t afc, const char* path, int list_verbose)
457{
458 struct afc_file_stat st;
459 get_file_info_stat(afc, path, &st);
460 if (list_verbose) {
461 char timebuf[64];
462 time_t t = st.st_mtime;
463 if (S_ISDIR(st.st_mode)) {
464 printf("drwxr-xr-x");
465 } else if (S_ISLNK(st.st_mode)) {
466 printf("lrwxrwxrwx");
467 } else {
468 if (S_ISFIFO(st.st_mode)) {
469 printf("f");
470 } else if (S_ISBLK(st.st_mode)) {
471 printf("b");
472 } else if (S_ISCHR(st.st_mode)) {
473 printf("c");
474 } else if (S_ISSOCK(st.st_mode)) {
475 printf("s");
476 } else {
477 printf("-");
478 }
479 printf("rw-r--r--");
480 }
481 printf(" ");
482 printf("%4d", st.st_nlink);
483 printf(" ");
484 printf("mobile");
485 printf(" ");
486 printf("mobile");
487 printf(" ");
488 printf("%10lld", (long long)st.st_size);
489 printf(" ");
490#ifdef _WIN32
491 strftime(timebuf, 64, "%d %b %Y %H:%M:%S", localtime(&t));
492#else
493 strftime(timebuf, 64, "%d %h %Y %H:%M:%S", localtime(&t));
494#endif
495 printf("%s", timebuf);
496 printf(" ");
497 }
498 if (S_ISDIR(st.st_mode)) {
499 cprintf(FG_CYAN);
500 } else if (S_ISLNK(st.st_mode)) {
501 cprintf(FG_MAGENTA);
502 } else if (S_ISREG(st.st_mode)) {
503 cprintf(FG_DEFAULT);
504 } else {
505 cprintf(FG_YELLOW);
506 }
507 cprintf("%s" COLOR_RESET "\n", path_get_basename(path));
508}
509
510static void handle_list(afc_client_t afc, int argc, char** argv)
511{
512 const char* path = NULL;
513 int list_verbose = 0;
514 if (argc < 1) {
515 path = curdir;
516 } else {
517 if (!strcmp(argv[0], "-l")) {
518 list_verbose = 1;
519 if (argc == 2) {
520 path = argv[1];
521 } else {
522 path = curdir;
523 }
524 } else {
525 path = argv[0];
526 }
527 }
528 char* abspath = get_absolute_path(path);
529 if (!abspath) {
530 printf("Error: Invalid argument\n");
531 return;
532 }
533 int abspath_is_root = strcmp(abspath, "/") == 0;
534 size_t abspath_len = (abspath_is_root) ? 0 : strlen(abspath);
535 char** entries = NULL;
536 afc_error_t err = afc_read_directory(afc, abspath, &entries);
537 if (err == AFC_E_READ_ERROR) {
538 print_file_info(afc, abspath, list_verbose);
539 return;
540 } else if (err != AFC_E_SUCCESS) {
541 printf("Error: Failed to list '%s': %s (%d)\n", path, afc_strerror(err), err);
542 free(abspath);
543 return;
544 }
545
546 char** p = entries;
547 while (p && *p) {
548 if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
549 p++;
550 continue;
551 }
552 size_t len = abspath_len + 1 + strlen(*p) + 1;
553 char* testpath = (char*)malloc(len);
554 if (abspath_is_root) {
555 snprintf(testpath, len, "/%s", *p);
556 } else {
557 snprintf(testpath, len, "%s/%s", abspath, *p);
558 }
559 print_file_info(afc, testpath, list_verbose);
560 free(testpath);
561 p++;
562 }
563 afc_dictionary_free(entries);
564 free(abspath);
565}
566
567static void handle_rename(afc_client_t afc, int argc, char** argv)
568{
569 if (argc != 2) {
570 printf("Error: Invalid number of arguments\n");
571 return;
572 }
573 char* srcpath = get_absolute_path(argv[0]);
574 if (!srcpath) {
575 printf("Error: Invalid argument\n");
576 return;
577 }
578 char* dstpath = get_absolute_path(argv[1]);
579 if (!dstpath) {
580 free(srcpath);
581 printf("Error: Invalid argument\n");
582 return;
583 }
584 afc_error_t err = afc_rename_path(afc, srcpath, dstpath);
585 if (err != AFC_E_SUCCESS) {
586 printf("Error: Failed to rename '%s' -> '%s': %s (%d)\n", argv[0], argv[1], afc_strerror(err), err);
587 }
588 free(srcpath);
589 free(dstpath);
590}
591
592static void handle_mkdir(afc_client_t afc, int argc, char** argv)
593{
594 for (int i = 0; i < argc; i++) {
595 char* abspath = get_absolute_path(argv[i]);
596 if (!abspath) {
597 printf("Error: Invalid argument '%s'\n", argv[i]);
598 continue;
599 }
600 afc_error_t err = afc_make_directory(afc, abspath);
601 if (err != AFC_E_SUCCESS) {
602 printf("Error: Failed to create directory '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
603 }
604 free(abspath);
605 }
606}
607
608static void handle_link(afc_client_t afc, int argc, char** argv)
609{
610 if (argc < 2) {
611 printf("Error: Invalid number of arguments\n");
612 return;
613 }
614 afc_link_type_t link_type = AFC_HARDLINK;
615 if (!strcmp(argv[0], "-s")) {
616 argc--;
617 argv++;
618 link_type = AFC_SYMLINK;
619 }
620 if (argc < 1 || argc > 2) {
621 printf("Error: Invalid number of arguments\n");
622 return;
623 }
624 const char *link_name = (argc == 1) ? path_get_basename(argv[0]) : argv[1];
625 char* abs_link_name = get_absolute_path(link_name);
626 if (!abs_link_name) {
627 printf("Error: Invalid argument\n");
628 return;
629 }
630 afc_error_t err = afc_make_link(afc, link_type, argv[0], link_name);
631 if (err != AFC_E_SUCCESS) {
632 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);
633 }
634}
635
636static int ask_yesno(const char* prompt)
637{
638 int ret = 0;
639#ifdef HAVE_READLINE
640 char* result = readline(prompt);
641 if (result && result[0] == 'y') {
642 ret = 1;
643 }
644#else
645 char cmdbuf[2] = {0, };
646 printf("%s", prompt);
647 fflush(stdout);
648 get_input(cmdbuf, sizeof(cmdbuf));
649 if (cmdbuf[0] == 'y') {
650 ret = 1;
651 }
652#endif
653#ifdef HAVE_READLINE
654 free(result);
655#endif
656 return ret;
657}
658
659static void handle_remove(afc_client_t afc, int argc, char** argv)
660{
661 int recursive = 0;
662 int force = 0;
663 int i = 0;
664 for (i = 0; i < argc; i++) {
665 if (!strcmp(argv[i], "--")) {
666 i++;
667 break;
668 } else if (!strcmp(argv[i], "-r")) {
669 recursive = 1;
670 } else if (!strcmp(argv[i], "-f")) {
671 force = 1;
672 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
673 recursive = 1;
674 force = 1;
675 } else {
676 break;
677 }
678 }
679 if (recursive && !force) {
680 if (!ask_yesno("WARNING: This operation will remove all contents of the given path(s). Continue? [y/N] ")) {
681 printf("Aborted.\n");
682 return;
683 }
684 }
685 for ( ; i < argc; i++) {
686 char* abspath = get_absolute_path(argv[i]);
687 if (!abspath) {
688 printf("Error: Invalid argument '%s'\n", argv[i]);
689 continue;
690 }
691 afc_error_t err;
692 if (recursive) {
693 err = afc_remove_path_and_contents(afc, abspath);
694 } else {
695 err = afc_remove_path(afc, abspath);
696 }
697 if (err != AFC_E_SUCCESS) {
698 printf("Error: Failed to remove '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
699 }
700 free(abspath);
701 }
702}
703
704static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size, uint8_t force_overwrite)
705{
706 uint64_t fh = 0;
707 afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh);
708 if (err != AFC_E_SUCCESS) {
709 printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
710 return 0;
711 }
712 if (file_exists(dstpath) && !force_overwrite) {
713 printf("Error: Failed to overwrite existing file without '-f' option: %s\n", dstpath);
714 return 0;
715 }
716 FILE *f = fopen(dstpath, "wb");
717 if (!f) {
718 printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno));
719 return 0;
720 }
721 struct timeval t1;
722 struct timeval t2;
723 struct timeval tdiff;
724 size_t bufsize = 0x100000;
725 char *buf = malloc(bufsize);
726 size_t total = 0;
727 int progress = 0;
728 int lastprog = 0;
729 if (file_size > 0x400000) {
730 progress = 1;
731 gettimeofday(&t1, NULL);
732 }
733 uint8_t succeed = 1;
734 while (err == AFC_E_SUCCESS) {
735 uint32_t bytes_read = 0;
736 size_t chunk = 0;
737 err = afc_file_read(afc, fh, buf, bufsize, &bytes_read);
738 if (bytes_read == 0) {
739 break;
740 }
741 while (chunk < bytes_read) {
742 size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f);
743 if (wr == 0) {
744 if (progress) {
745 printf("\n");
746 }
747 printf("Error: Failed to write to local file\n");
748 succeed = 0;
749 break;
750 }
751 chunk += wr;
752 }
753 total += chunk;
754 if (progress) {
755 int prog = (int) ((double) total / (double) file_size * 100.0f);
756 if (prog > lastprog) {
757 gettimeofday(&t2, NULL);
758 timeval_subtract(&tdiff, &t2, &t1);
759 double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
760 printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
761 fflush(stdout);
762 lastprog = prog;
763 }
764 }
765 }
766 if (progress) {
767 printf("\n");
768 }
769 if (err != AFC_E_SUCCESS) {
770 printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
771 succeed = 0;
772 }
773 free(buf);
774 fclose(f);
775 afc_file_close(afc, fh);
776 return succeed;
777}
778
779static int __mkdir(const char* path)
780{
781#ifdef _WIN32
782 return mkdir(path);
783#else
784 return mkdir(path, 0755);
785#endif
786}
787
788static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get)
789{
790 plist_t info = NULL;
791 uint64_t file_size = 0;
792 afc_error_t err = afc_get_file_info_plist(afc, srcpath, &info);
793 if (err == AFC_E_OBJECT_NOT_FOUND) {
794 printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
795 return 0;
796 }
797 uint8_t is_dir = 0;
798 if (info) {
799 file_size = plist_dict_get_uint(info, "st_size");
800 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
801 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
802 plist_free(info);
803 }
804 uint8_t succeed = 1;
805 if (is_dir) {
806 if (!recursive_get) {
807 printf("Error: Failed to get a directory without '-r' option: %s\n", srcpath);
808 return 0;
809 }
810 char **entries = NULL;
811 err = afc_read_directory(afc, srcpath, &entries);
812 if (err != AFC_E_SUCCESS) {
813 printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
814 return 0;
815 }
816 char **p = entries;
817 size_t srcpath_len = strlen(srcpath);
818 uint8_t srcpath_is_root = strcmp(srcpath, "/") == 0;
819 // if directory exists, check force_overwrite flag
820 if (is_directory(dstpath)) {
821 if (!force_overwrite) {
822 printf("Error: Failed to write into existing directory without '-f': %s\n", dstpath);
823 return 0;
824 }
825 } else if (__mkdir(dstpath) != 0) {
826 printf("Error: Failed to create directory '%s': %s\n", dstpath, strerror(errno));
827 afc_dictionary_free(entries);
828 return 0;
829 }
830 while (p && *p) {
831 if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
832 p++;
833 continue;
834 }
835 size_t len = srcpath_is_root ? (strlen(*p) + 2) : (srcpath_len + 1 + strlen(*p) + 1);
836 char *testpath = (char *) malloc(len);
837 if (srcpath_is_root) {
838 snprintf(testpath, len, "/%s", *p);
839 } else {
840 snprintf(testpath, len, "%s/%s", srcpath, *p);
841 }
842 uint8_t dst_is_root = strcmp(srcpath, "/") == 0;
843 size_t dst_len = dst_is_root ? (strlen(*p) + 2) : (strlen(dstpath) + 1 + strlen(*p) + 1);
844 char *newdst = (char *) malloc(dst_len);
845 if (dst_is_root) {
846 snprintf(newdst, dst_len, "/%s", *p);
847 } else {
848 snprintf(newdst, dst_len, "%s/%s", dstpath, *p);
849 }
850 if (!get_file(afc, testpath, newdst, force_overwrite, recursive_get)) {
851 succeed = 0;
852 break;
853 }
854 free(testpath);
855 free(newdst);
856 p++;
857 }
858 afc_dictionary_free(entries);
859 } else {
860 succeed = get_single_file(afc, srcpath, dstpath, file_size, force_overwrite);
861 }
862 return succeed;
863}
864
865static void handle_get(afc_client_t afc, int argc, char **argv)
866{
867 if (argc < 1) {
868 printf("Error: Invalid number of arguments\n");
869 return;
870 }
871 uint8_t force_overwrite = 0, recursive_get = 0;
872 char *srcpath = NULL;
873 char *dstpath = NULL;
874 int i = 0;
875 for ( ; i < argc; i++) {
876 if (!strcmp(argv[i], "--")) {
877 i++;
878 break;
879 } else if (!strcmp(argv[i], "-r")) {
880 recursive_get = 1;
881 } else if (!strcmp(argv[i], "-f")) {
882 force_overwrite = 1;
883 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
884 recursive_get = 1;
885 force_overwrite = 1;
886 } else {
887 break;
888 }
889 }
890 if (argc - i == 1) {
891 char *tmp = strdup(argv[i]);
892 size_t src_len = strlen(tmp);
893 if (src_len > 1 && tmp[src_len - 1] == '/') {
894 tmp[src_len - 1] = '\0';
895 }
896 srcpath = get_absolute_path(tmp);
897 dstpath = strdup(path_get_basename(tmp));
898 free(tmp);
899 } else if (argc - i == 2) {
900 char *tmp = strdup(argv[i]);
901 size_t src_len = strlen(tmp);
902 if (src_len > 1 && tmp[src_len - 1] == '/') {
903 tmp[src_len - 1] = '\0';
904 }
905 srcpath = get_absolute_path(tmp);
906 dstpath = strdup(argv[i + 1]);
907 size_t dst_len = strlen(dstpath);
908 if (dst_len > 1 && dstpath[dst_len - 1] == '/') {
909 dstpath[dst_len - 1] = '\0';
910 }
911 free(tmp);
912 } else {
913 printf("Error: Invalid number of arguments\n");
914 return;
915 }
916
917 // target is a directory, put file under this target
918 if (is_directory(dstpath)) {
919 const char *basen = path_get_basename(srcpath);
920 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
921 size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
922 char *newdst = (char *) malloc(len);
923 if (dst_is_root) {
924 snprintf(newdst, len, "/%s", basen);
925 } else {
926 snprintf(newdst, len, "%s/%s", dstpath, basen);
927 }
928 get_file(afc, srcpath, newdst, force_overwrite, recursive_get);
929 free(srcpath);
930 free(newdst);
931 free(dstpath);
932 } else {
933 // target is not a dir or does not exist, just try to create or rewrite it
934 get_file(afc, srcpath, dstpath, force_overwrite, recursive_get);
935 free(srcpath);
936 free(dstpath);
937 }
938}
939
940static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite)
941{
942 plist_t info = NULL;
943 afc_error_t ret = afc_get_file_info_plist(afc, dstpath, &info);
944 // file exists, only overwrite with '-f' option was set
945 if (ret == AFC_E_SUCCESS && info) {
946 plist_free(info);
947 if (!force_overwrite) {
948 printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath);
949 return 0;
950 }
951 }
952 FILE *f = fopen(srcpath, "rb");
953 if (!f) {
954 printf("Error: Failed to open local file '%s': %s\n", srcpath, strerror(errno));
955 return 0;
956 }
957 struct timeval t1;
958 struct timeval t2;
959 struct timeval tdiff;
960 struct stat fst;
961 int progress = 0;
962 size_t bufsize = 0x100000;
963 char *buf = malloc(bufsize);
964
965 fstat(fileno(f), &fst);
966 if (fst.st_size >= 0x400000) {
967 progress = 1;
968 gettimeofday(&t1, NULL);
969 }
970 size_t total = 0;
971 int lastprog = 0;
972 uint64_t fh = 0;
973 afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh);
974 uint8_t succeed = 1;
975 while (err == AFC_E_SUCCESS) {
976 uint32_t bytes_read = fread(buf, 1, bufsize, f);
977 if (bytes_read == 0) {
978 if (!feof(f)) {
979 if (progress) {
980 printf("\n");
981 }
982 printf("Error: Failed to read from local file\n");
983 succeed = 0;
984 }
985 break;
986 }
987 uint32_t chunk = 0;
988 while (chunk < bytes_read) {
989 uint32_t bytes_written = 0;
990 err = afc_file_write(afc, fh, buf + chunk, bytes_read - chunk, &bytes_written);
991 if (err != AFC_E_SUCCESS) {
992 if (progress) {
993 printf("\n");
994 }
995 printf("Error: Failed to write to device file\n");
996 succeed = 0;
997 break;
998 }
999 chunk += bytes_written;
1000 }
1001 total += chunk;
1002 if (progress) {
1003 int prog = (int) ((double) total / (double) fst.st_size * 100.0f);
1004 if (prog > lastprog) {
1005 gettimeofday(&t2, NULL);
1006 timeval_subtract(&tdiff, &t2, &t1);
1007 double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
1008 printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
1009 fflush(stdout);
1010 lastprog = prog;
1011 }
1012 }
1013 }
1014 if (progress) {
1015 printf("\n");
1016 }
1017 free(buf);
1018 afc_file_close(afc, fh);
1019 fclose(f);
1020 return succeed;
1021}
1022
1023static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_put)
1024{
1025 if (is_directory(srcpath)) {
1026 if (!recursive_put) {
1027 printf("Error: Failed to put directory without '-r' option: %s\n", srcpath);
1028 return 0;
1029 }
1030 plist_t info = NULL;
1031 afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
1032 //create if target directory does not exist
1033 plist_free(info);
1034 info = NULL;
1035 if (err == AFC_E_OBJECT_NOT_FOUND) {
1036 err = afc_make_directory(afc, dstpath);
1037 if (err != AFC_E_SUCCESS) {
1038 printf("Error: Failed to create directory '%s': %s (%d)\n", dstpath, afc_strerror(err), err);
1039 return 0;
1040 }
1041 } else if (!force_overwrite) {
1042 printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath);
1043 return 0;
1044 }
1045 afc_get_file_info_plist(afc, dstpath, &info);
1046 uint8_t is_dir = 0;
1047 if (info) {
1048 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
1049 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
1050 plist_free(info);
1051 }
1052 if (!is_dir) {
1053 printf("Error: Failed to create or access directory: '%s'\n", dstpath);
1054 return 0;
1055 }
1056
1057 // walk dir recursively to put files
1058 DIR *cur_dir = opendir(srcpath);
1059 if (cur_dir) {
1060 struct dirent *ep;
1061 while ((ep = readdir(cur_dir))) {
1062 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1063 continue;
1064 }
1065 char *fpath = string_build_path(srcpath, ep->d_name, NULL);
1066 if (fpath) {
1067 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
1068 size_t len = dst_is_root ? (strlen(ep->d_name) + 2) : (strlen(dstpath) + 1 + strlen(ep->d_name) + 1);
1069 char *newdst = (char *) malloc(len);
1070 if (dst_is_root) {
1071 snprintf(newdst, len, "/%s", ep->d_name);
1072 } else {
1073 snprintf(newdst, len, "%s/%s", dstpath, ep->d_name);
1074 }
1075 if (!put_file(afc, fpath, newdst, force_overwrite, recursive_put)) {
1076 free(newdst);
1077 free(fpath);
1078 return 0;
1079 }
1080 free(newdst);
1081 free(fpath);
1082 }
1083 }
1084 closedir(cur_dir);
1085 } else {
1086 printf("Error: Failed to visit directory: '%s': %s\n", srcpath, strerror(errno));
1087 return 0;
1088 }
1089 } else {
1090 return put_single_file(afc, srcpath, dstpath, force_overwrite);
1091 }
1092 return 1;
1093}
1094
1095static void handle_put(afc_client_t afc, int argc, char **argv)
1096{
1097 if (argc < 1) {
1098 printf("Error: Invalid number of arguments\n");
1099 return;
1100 }
1101 int i = 0;
1102 uint8_t force_overwrite = 0, recursive_put = 0;
1103 for ( ; i < argc; i++) {
1104 if (!strcmp(argv[i], "--")) {
1105 i++;
1106 break;
1107 } else if (!strcmp(argv[i], "-r")) {
1108 recursive_put = 1;
1109 } else if (!strcmp(argv[i], "-f")) {
1110 force_overwrite = 1;
1111 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
1112 recursive_put = 1;
1113 force_overwrite = 1;
1114 } else {
1115 break;
1116 }
1117 }
1118 if (i >= argc) {
1119 printf("Error: Invalid number of arguments\n");
1120 return;
1121 }
1122 char *srcpath = strdup(argv[i]);
1123 size_t src_len = strlen(srcpath);
1124 if (src_len > 1 && srcpath[src_len - 1] == '/') {
1125 srcpath[src_len - 1] = '\0';
1126 }
1127 char *dstpath = NULL;
1128 if (argc - i == 1) {
1129 dstpath = get_absolute_path(path_get_basename(srcpath));
1130 } else if (argc - i == 2) {
1131 char *tmp = strdup(argv[i + 1]);
1132 size_t dst_len = strlen(tmp);
1133 if (dst_len > 1 && tmp[dst_len - 1] == '/') {
1134 tmp[dst_len - 1] = '\0';
1135 }
1136 dstpath = get_absolute_path(tmp);
1137 free(tmp);
1138 } else {
1139 printf("Error: Invalid number of arguments\n");
1140 return;
1141 }
1142 plist_t info = NULL;
1143 afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
1144 // target does not exist, put directly
1145 if (err == AFC_E_OBJECT_NOT_FOUND) {
1146 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1147 free(srcpath);
1148 free(dstpath);
1149 } else {
1150 uint8_t is_dir = 0;
1151 if (info) {
1152 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
1153 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
1154 plist_free(info);
1155 }
1156 // target is a directory, try to put under this directory
1157 if (is_dir) {
1158 const char *basen = path_get_basename(srcpath);
1159 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
1160 size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
1161 char *newdst = (char *) malloc(len);
1162 if (dst_is_root) {
1163 snprintf(newdst, len, "/%s", basen);
1164 } else {
1165 snprintf(newdst, len, "%s/%s", dstpath, basen);
1166 }
1167 free(dstpath);
1168 dstpath = get_absolute_path(newdst);
1169 free(newdst);
1170 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1171 } else {
1172 //target is common file, rewrite it
1173 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1174 }
1175 free(srcpath);
1176 free(dstpath);
1177 }
1178}
1179
1180static void handle_pwd(afc_client_t afc, int argc, char** argv)
1181{
1182 printf("%s\n", curdir);
1183}
1184
1185static void handle_cd(afc_client_t afc, int argc, char** argv)
1186{
1187 if (argc != 1) {
1188 printf("Error: Invalid number of arguments\n");
1189 return;
1190 }
1191
1192 if (!strcmp(argv[0], ".")) {
1193 return;
1194 }
1195
1196 if (!strcmp(argv[0], "..")) {
1197 if (!strcmp(curdir, "/")) {
1198 return;
1199 }
1200 char *p = strrchr(curdir, '/');
1201 if (!p) {
1202 strcpy(curdir, "/");
1203 return;
1204 }
1205 if (p == curdir) {
1206 *(p+1) = '\0';
1207 curdir_len = 1;
1208 } else {
1209 *p = '\0';
1210 curdir_len = strlen(curdir);
1211 }
1212 return;
1213 }
1214
1215 char* path = get_realpath(argv[0]);
1216 int is_dir = 0;
1217 plist_t info = NULL;
1218 afc_error_t err = afc_get_file_info_plist(afc, path, &info);
1219 if (err == AFC_E_SUCCESS && info) {
1220 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
1221 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
1222 plist_free(info);
1223 } else {
1224 printf("Error: Failed to get file info for %s: %s (%d)\n", path, afc_strerror(err), err);
1225 free(path);
1226 return;
1227 }
1228
1229 if (!is_dir) {
1230 printf("Error: '%s' is not a valid directory\n", path);
1231 free(path);
1232 return;
1233 }
1234 free(curdir);
1235 curdir = path;
1236 curdir_len = strlen(curdir);
1237}
1238
1239static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline)
1240{
1241 char **argv = NULL;
1242 int argc = 0;
1243 size_t maxlen = strlen(cmdline);
1244 const char* pos = cmdline;
1245 const char* qpos = NULL;
1246 char *tmpbuf = NULL;
1247 int tmplen = 0;
1248 int is_error = 0;
1249
1250 /* skip initial whitespace */
1251 while (isspace(*pos)) pos++;
1252 maxlen -= (pos - cmdline);
1253
1254 tmpbuf = (char*)malloc(maxlen+1);
1255
1256 while (!is_error) {
1257 if (*pos == '\\') {
1258 pos++;
1259 switch (*pos) {
1260 case '"':
1261 case '\'':
1262 case '\\':
1263 case ' ':
1264 tmpbuf[tmplen++] = *pos;
1265 pos++;
1266 break;
1267 default:
1268 printf("Error: Invalid escape sequence\n");
1269 is_error++;
1270 break;
1271 }
1272 } else if (*pos == '"' || *pos == '\'') {
1273 if (!qpos) {
1274 qpos = pos;
1275 } else {
1276 qpos = NULL;
1277 }
1278 pos++;
1279 } else if (*pos == '\0' || (!qpos && isspace(*pos))) {
1280 tmpbuf[tmplen] = '\0';
1281 if (*pos == '\0' && qpos) {
1282 printf("Error: Unmatched `%c`\n", *qpos);
1283 is_error++;
1284 break;
1285 }
1286 char** new_argv = (char**)realloc(argv, (argc+1)*sizeof(char*));
1287 if (new_argv == NULL) {
1288 printf("Error: Out of memory?!\n");
1289 is_error++;
1290 break;
1291 }
1292 argv = new_argv;
1293 /* shrink buffer to actual argument size */
1294 argv[argc] = (char*)realloc(tmpbuf, tmplen+1);
1295 if (!argv[argc]) {
1296 printf("Error: Out of memory?!\n");
1297 is_error++;
1298 break;
1299 }
1300 argc++;
1301 tmpbuf = NULL;
1302 if (*pos == '\0') {
1303 break;
1304 }
1305 maxlen -= tmplen;
1306 tmpbuf = (char*)malloc(maxlen+1);
1307 tmplen = 0;
1308 while (isspace(*pos)) pos++;
1309 } else {
1310 tmpbuf[tmplen++] = *pos;
1311 pos++;
1312 }
1313 }
1314 if (tmpbuf) {
1315 free(tmpbuf);
1316 }
1317 if (is_error) {
1318 int i;
1319 for (i = 0; argv && i < argc; i++) free(argv[i]);
1320 free(argv);
1321 return;
1322 }
1323
1324 *p_argv = argv;
1325 *p_argc = argc;
1326}
1327
1328static int process_args(afc_client_t afc, int argc, char** argv)
1329{
1330 if (!strcmp(argv[0], "q") || !strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
1331 return -1;
1332 }
1333 else if (!strcmp(argv[0], "help")) {
1334 handle_help(afc, argc, argv);
1335 }
1336 else if (!strcmp(argv[0], "devinfo") || !strcmp(argv[0], "deviceinfo")) {
1337 handle_devinfo(afc, argc-1, argv+1);
1338 }
1339 else if (!strcmp(argv[0], "info")) {
1340 handle_file_info(afc, argc-1, argv+1);
1341 }
1342 else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) {
1343 handle_list(afc, argc-1, argv+1);
1344 }
1345 else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) {
1346 handle_rename(afc, argc-1, argv+1);
1347 }
1348 else if (!strcmp(argv[0], "mkdir")) {
1349 handle_mkdir(afc, argc-1, argv+1);
1350 }
1351 else if (!strcmp(argv[0], "ln")) {
1352 handle_link(afc, argc-1, argv+1);
1353 }
1354 else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) {
1355 handle_remove(afc, argc-1, argv+1);
1356 }
1357 else if (!strcmp(argv[0], "get")) {
1358 handle_get(afc, argc-1, argv+1);
1359 }
1360 else if (!strcmp(argv[0], "put")) {
1361 handle_put(afc, argc-1, argv+1);
1362 }
1363 else if (!strcmp(argv[0], "pwd")) {
1364 handle_pwd(afc, argc-1, argv+1);
1365 }
1366 else if (!strcmp(argv[0], "cd")) {
1367 handle_cd(afc, argc-1, argv+1);
1368 }
1369 else {
1370 printf("Unknown command '%s'. Type 'help' to get a list of available commands.\n", argv[0]);
1371 }
1372 return 0;
1373}
1374
1375static void start_cmdline(afc_client_t afc)
1376{
1377 while (!stop_requested) {
1378 int argc = 0;
1379 char **argv = NULL;
1380 char prompt[128];
1381 int plen = curdir_len;
1382 char *ppath = curdir;
1383 int plim = (int)(sizeof(prompt)/2)-8;
1384 if (plen > plim) {
1385 ppath = curdir + (plen - plim);
1386 plen = plim;
1387 }
1388 snprintf(prompt, 128, FG_BLACK BG_LIGHT_GRAY "afc:" COLOR_RESET FG_BRIGHT_YELLOW BG_BLUE "%.*s" COLOR_RESET " > ", plen, ppath);
1389#ifdef HAVE_READLINE
1390 char* cmd = readline(prompt);
1391 if (!cmd || !*cmd) {
1392 free(cmd);
1393 continue;
1394 }
1395 add_history(cmd);
1396 parse_cmdline(&argc, &argv, cmd);
1397#else
1398 char cmdbuf[4096];
1399 printf("%s", prompt);
1400 fflush(stdout);
1401 get_input(cmdbuf, sizeof(cmdbuf));
1402 parse_cmdline(&argc, &argv, cmdbuf);
1403#endif
1404#ifdef HAVE_READLINE
1405 free(cmd);
1406#endif
1407 /* process arguments */
1408 if (argv && argv[0]) {
1409 if (process_args(afc, argc, argv) < 0) {
1410 break;
1411 }
1412 }
1413 }
1414}
1415
1416static void device_event_cb(const idevice_event_t* event, void* userdata)
1417{
1418 if (use_network && event->conn_type != CONNECTION_NETWORK) {
1419 return;
1420 } else if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
1421 return;
1422 }
1423 if (event->event == IDEVICE_DEVICE_ADD) {
1424 if (!udid) {
1425 udid = strdup(event->udid);
1426 }
1427 if (strcmp(udid, event->udid) == 0) {
1428 connected = 1;
1429 }
1430 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
1431 if (strcmp(udid, event->udid) == 0) {
1432 connected = 0;
1433 printf("\n[disconnected]\n");
1434 handle_signal(SIGINT);
1435 }
1436 }
1437}
1438
1439int main(int argc, char** argv)
1440{
1441 const char* appid = NULL;
1442 int ret = 0;
1443 idevice_t device = NULL;
1444 lockdownd_client_t lockdown = NULL;
1445 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
1446 lockdownd_service_descriptor_t service = NULL;
1447 afc_client_t afc = NULL;
1448 house_arrest_client_t house_arrest = NULL;
1449 const char* service_name = AFC_SERVICE_NAME;
1450 int use_container = 0;
1451
1452 int c = 0;
1453 const struct option longopts[] = {
1454 { "udid", required_argument, NULL, 'u' },
1455 { "network", no_argument, NULL, 'n' },
1456 { "help", no_argument, NULL, 'h' },
1457 { "debug", no_argument, NULL, 'd' },
1458 { "version", no_argument, NULL, 'v' },
1459 { "documents", required_argument, NULL, OPT_DOCUMENTS },
1460 { "container", required_argument, NULL, OPT_CONTAINER },
1461 { NULL, 0, NULL, 0}
1462 };
1463
1464 signal(SIGTERM, handle_signal);
1465#ifndef _WIN32
1466 signal(SIGQUIT, handle_signal);
1467 signal(SIGPIPE, SIG_IGN);
1468#endif
1469
1470 while ((c = getopt_long(argc, argv, "du:nhv", longopts, NULL)) != -1) {
1471 switch (c) {
1472 case 'd':
1473 idevice_set_debug_level(1);
1474 break;
1475 case 'u':
1476 if (!*optarg) {
1477 fprintf(stderr, "ERROR: UDID must not be empty!\n");
1478 print_usage(argc, argv, 1);
1479 return 2;
1480 }
1481 udid = strdup(optarg);
1482 break;
1483 case 'n':
1484 use_network = 1;
1485 break;
1486 case 'h':
1487 print_usage(argc, argv, 0);
1488 return 0;
1489 case 'v':
1490 printf("%s %s", TOOL_NAME, PACKAGE_VERSION);
1491#ifdef HAVE_READLINE
1492 printf(" (readline)");
1493#endif
1494 printf("\n");
1495 return 0;
1496 case OPT_DOCUMENTS:
1497 if (!*optarg) {
1498 fprintf(stderr, "ERROR: '--documents' requires a non-empty app ID!\n");
1499 print_usage(argc, argv, 1);
1500 return 2;
1501 }
1502 appid = optarg;
1503 use_container = 0;
1504 break;
1505 case OPT_CONTAINER:
1506 if (!*optarg) {
1507 fprintf(stderr, "ERROR: '--container' requires a not-empty app ID!\n");
1508 print_usage(argc, argv, 1);
1509 return 2;
1510 }
1511 appid = optarg;
1512 use_container = 1;
1513 break;
1514 default:
1515 print_usage(argc, argv, 1);
1516 return 2;
1517 }
1518 }
1519
1520 argc -= optind;
1521 argv += optind;
1522
1523 int num = 0;
1524 idevice_info_t *devices = NULL;
1525 idevice_get_device_list_extended(&devices, &num);
1526 int count = 0;
1527 for (int i = 0; i < num; i++) {
1528 if (devices[i]->conn_type == CONNECTION_NETWORK && use_network) {
1529 count++;
1530 } else if (devices[i]->conn_type == CONNECTION_USBMUXD) {
1531 count++;
1532 }
1533 }
1534 idevice_device_list_extended_free(devices);
1535 if (count == 0) {
1536 fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
1537 return 1;
1538 }
1539
1540 idevice_events_subscribe(&context, device_event_cb, NULL);
1541
1542 while (!connected && !stop_requested) {
1543#ifdef _WIN32
1544 Sleep(100);
1545#else
1546 usleep(100000);
1547#endif
1548 }
1549 if (stop_requested) {
1550 return 0;
1551 }
1552
1553 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
1554 if (ret != IDEVICE_E_SUCCESS) {
1555 if (udid) {
1556 fprintf(stderr, "ERROR: Device %s not found!\n", udid);
1557 } else {
1558 fprintf(stderr, "ERROR: No device found!\n");
1559 }
1560 return 1;
1561 }
1562
1563 do {
1564 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
1565 fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
1566 ret = 1;
1567 break;
1568 }
1569
1570 if (appid) {
1571 service_name = HOUSE_ARREST_SERVICE_NAME;
1572 }
1573
1574 ldret = lockdownd_start_service(lockdown, service_name, &service);
1575 if (ldret != LOCKDOWN_E_SUCCESS) {
1576 fprintf(stderr, "ERROR: Failed to start service %s: %s (%d)\n", service_name, lockdownd_strerror(ldret), ldret);
1577 ret = 1;
1578 break;
1579 }
1580
1581 if (appid) {
1582 house_arrest_client_new(device, service, &house_arrest);
1583 if (!house_arrest) {
1584 fprintf(stderr, "Could not start document sharing service!\n");
1585 ret = 1;
1586 break;
1587 }
1588
1589 if (house_arrest_send_command(house_arrest, use_container ? "VendContainer": "VendDocuments", appid) != HOUSE_ARREST_E_SUCCESS) {
1590 fprintf(stderr, "Could not send house_arrest command!\n");
1591 ret = 1;
1592 break;
1593 }
1594
1595 plist_t dict = NULL;
1596 if (house_arrest_get_result(house_arrest, &dict) != HOUSE_ARREST_E_SUCCESS) {
1597 fprintf(stderr, "Could not get result from document sharing service!\n");
1598 break;
1599 }
1600 plist_t node = plist_dict_get_item(dict, "Error");
1601 if (node) {
1602 char *str = NULL;
1603 plist_get_string_val(node, &str);
1604 fprintf(stderr, "ERROR: %s\n", str);
1605 if (str && !strcmp(str, "InstallationLookupFailed")) {
1606 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);
1607 }
1608 free(str);
1609 plist_free(dict);
1610 break;
1611 }
1612 plist_free(dict);
1613 afc_client_new_from_house_arrest_client(house_arrest, &afc);
1614 } else {
1615 afc_client_new(device, service, &afc);
1616 }
1617 lockdownd_service_descriptor_free(service);
1618 lockdownd_client_free(lockdown);
1619 lockdown = NULL;
1620
1621 if (appid && !use_container) {
1622 curdir = strdup("/Documents");
1623 curdir_len = 10;
1624 } else {
1625 curdir = strdup("/");
1626 curdir_len = 1;
1627 }
1628
1629 if (argc > 0) {
1630 // command line mode
1631 process_args(afc, argc, argv);
1632 } else {
1633 // interactive mode
1634 start_cmdline(afc);
1635 }
1636
1637 } while (0);
1638
1639 if (afc) {
1640 afc_client_free(afc);
1641 }
1642 if (lockdown) {
1643 lockdownd_client_free(lockdown);
1644 }
1645 idevice_free(device);
1646
1647 return ret;
1648}
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..363abad 100644
--- a/tools/idevicebackup.c
+++ b/tools/idevicebackup.c
@@ -9,31 +9,42 @@
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>
38#include <sys/stat.h>
31 39
32#include <libimobiledevice/libimobiledevice.h> 40#include <libimobiledevice/libimobiledevice.h>
33#include <libimobiledevice/lockdown.h> 41#include <libimobiledevice/lockdown.h>
34#include <libimobiledevice/mobilebackup.h> 42#include <libimobiledevice/mobilebackup.h>
35#include <libimobiledevice/notification_proxy.h> 43#include <libimobiledevice/notification_proxy.h>
36#include <libimobiledevice/afc.h> 44#include <libimobiledevice/afc.h>
45#include <libimobiledevice-glue/sha.h>
46#include <libimobiledevice-glue/utils.h>
47#include <plist/plist.h>
37 48
38#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup" 49#define MOBILEBACKUP_SERVICE_NAME "com.apple.mobilebackup"
39#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy" 50#define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
@@ -41,9 +52,14 @@
41#define LOCK_ATTEMPTS 50 52#define LOCK_ATTEMPTS 50
42#define LOCK_WAIT 200000 53#define LOCK_WAIT 200000
43 54
55#ifdef _WIN32
56#include <windows.h>
57#define sleep(x) Sleep(x*1000)
58#endif
59
44static mobilebackup_client_t mobilebackup = NULL; 60static mobilebackup_client_t mobilebackup = NULL;
45static lockdownd_client_t client = NULL; 61static lockdownd_client_t client = NULL;
46static idevice_t phone = NULL; 62static idevice_t device = NULL;
47 63
48static int quit_flag = 0; 64static int quit_flag = 0;
49 65
@@ -53,22 +69,12 @@ enum cmd_mode {
53 CMD_LEAVE 69 CMD_LEAVE
54}; 70};
55 71
56enum plist_format_t {
57 PLIST_FORMAT_XML,
58 PLIST_FORMAT_BINARY
59};
60
61enum device_link_file_status_t { 72enum device_link_file_status_t {
62 DEVICE_LINK_FILE_STATUS_NONE = 0, 73 DEVICE_LINK_FILE_STATUS_NONE = 0,
63 DEVICE_LINK_FILE_STATUS_HUNK, 74 DEVICE_LINK_FILE_STATUS_HUNK,
64 DEVICE_LINK_FILE_STATUS_LAST_HUNK 75 DEVICE_LINK_FILE_STATUS_LAST_HUNK
65}; 76};
66 77
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) 78static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, int hash_len)
73{ 79{
74 int i; 80 int i;
@@ -82,51 +88,47 @@ static int compare_hash(const unsigned char *hash1, const unsigned char *hash2,
82 88
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) 89static 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{ 90{
85 gcry_md_hd_t hd = NULL; 91 sha1_context sha1;
86 gcry_md_open(&hd, GCRY_MD_SHA1, 0); 92 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"); 93 FILE *f = fopen(path, "rb");
94 if (f) { 94 if (f) {
95 unsigned char buf[16384]; 95 unsigned char buf[16384];
96 size_t len; 96 size_t len;
97 while ((len = fread(buf, 1, 16384, f)) > 0) { 97 while ((len = fread(buf, 1, 16384, f)) > 0) {
98 gcry_md_write(hd, buf, len); 98 sha1_update(&sha1, buf, len);
99 } 99 }
100 fclose(f); 100 fclose(f);
101 gcry_md_write(hd, destpath, strlen(destpath)); 101 sha1_update(&sha1, destpath, strlen(destpath));
102 gcry_md_write(hd, ";", 1); 102 sha1_update(&sha1, ";", 1);
103
103 if (greylist == 1) { 104 if (greylist == 1) {
104 gcry_md_write(hd, "true", 4); 105 sha1_update(&sha1, "true", 4);
105 } else { 106 } else {
106 gcry_md_write(hd, "false", 5); 107 sha1_update(&sha1, "false", 5);
107 } 108 }
108 gcry_md_write(hd, ";", 1); 109 sha1_update(&sha1, ";", 1);
110
109 if (domain) { 111 if (domain) {
110 gcry_md_write(hd, domain, strlen(domain)); 112 sha1_update(&sha1, domain, strlen(domain));
111 } else { 113 } else {
112 gcry_md_write(hd, "(null)", 6); 114 sha1_update(&sha1, "(null)", 6);
113 } 115 }
114 gcry_md_write(hd, ";", 1); 116 sha1_update(&sha1, ";", 1);
117
115 if (appid) { 118 if (appid) {
116 gcry_md_write(hd, appid, strlen(appid)); 119 sha1_update(&sha1, appid, strlen(appid));
117 } else { 120 } else {
118 gcry_md_write(hd, "(null)", 6); 121 sha1_update(&sha1, "(null)", 6);
119 } 122 }
120 gcry_md_write(hd, ";", 1); 123 sha1_update(&sha1, ";", 1);
124
121 if (version) { 125 if (version) {
122 gcry_md_write(hd, version, strlen(version)); 126 sha1_update(&sha1, version, strlen(version));
123 } else { 127 } else {
124 gcry_md_write(hd, "(null)", 6); 128 sha1_update(&sha1, "(null)", 6);
125 } 129 }
126 unsigned char *newhash = gcry_md_read(hd, GCRY_MD_SHA1); 130 sha1_final(&sha1, hash_out);
127 memcpy(hash_out, newhash, 20);
128 } 131 }
129 gcry_md_close(hd);
130} 132}
131 133
132static void print_hash(const unsigned char *hash, int len) 134static void print_hash(const unsigned char *hash, int len)
@@ -147,14 +149,12 @@ static void notify_cb(const char *notification, void *userdata)
147 } 149 }
148} 150}
149 151
150static plist_t mobilebackup_factory_info_plist_new() 152static plist_t mobilebackup_factory_info_plist_new(const char* udid)
151{ 153{
152 /* gather data from lockdown */ 154 /* gather data from lockdown */
153 GTimeVal tv = {0, 0};
154 plist_t value_node = NULL; 155 plist_t value_node = NULL;
155 plist_t root_node = NULL; 156 plist_t root_node = NULL;
156 char *uuid = NULL; 157 char *udid_uppercase = NULL;
157 char *uuid_uppercase = NULL;
158 158
159 plist_t ret = plist_new_dict(); 159 plist_t ret = plist_new_dict();
160 160
@@ -163,45 +163,48 @@ static plist_t mobilebackup_factory_info_plist_new()
163 163
164 /* set fields we understand */ 164 /* set fields we understand */
165 value_node = plist_dict_get_item(root_node, "BuildVersion"); 165 value_node = plist_dict_get_item(root_node, "BuildVersion");
166 plist_dict_insert_item(ret, "Build Version", plist_copy(value_node)); 166 plist_dict_set_item(ret, "Build Version", plist_copy(value_node));
167 167
168 value_node = plist_dict_get_item(root_node, "DeviceName"); 168 value_node = plist_dict_get_item(root_node, "DeviceName");
169 plist_dict_insert_item(ret, "Device Name", plist_copy(value_node)); 169 plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
170 plist_dict_insert_item(ret, "Display Name", plist_copy(value_node)); 170 plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
171 171
172 /* FIXME: How is the GUID generated? */ 172 /* FIXME: How is the GUID generated? */
173 plist_dict_insert_item(ret, "GUID", plist_new_string("---")); 173 plist_dict_set_item(ret, "GUID", plist_new_string("---"));
174 174
175 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity"); 175 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
176 if (value_node) 176 if (value_node)
177 plist_dict_insert_item(ret, "IMEI", plist_copy(value_node)); 177 plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
178 178
179 g_get_current_time(&tv); 179 plist_dict_set_item(ret, "Last Backup Date",
180 plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(tv.tv_sec, tv.tv_usec)); 180#ifdef HAVE_PLIST_UNIX_DATE
181 plist_new_unix_date(time(NULL))
182#else
183 plist_new_date(time(NULL) - MAC_EPOCH, 0)
184#endif
185 );
181 186
182 value_node = plist_dict_get_item(root_node, "ProductType"); 187 value_node = plist_dict_get_item(root_node, "ProductType");
183 plist_dict_insert_item(ret, "Product Type", plist_copy(value_node)); 188 plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
184 189
185 value_node = plist_dict_get_item(root_node, "ProductVersion"); 190 value_node = plist_dict_get_item(root_node, "ProductVersion");
186 plist_dict_insert_item(ret, "Product Version", plist_copy(value_node)); 191 plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
187 192
188 value_node = plist_dict_get_item(root_node, "SerialNumber"); 193 value_node = plist_dict_get_item(root_node, "SerialNumber");
189 plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node)); 194 plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
190 195
191 value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); 196 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
192 idevice_get_uuid(phone, &uuid); 197 plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
193 plist_dict_insert_item(ret, "Target Identifier", plist_new_string(uuid));
194 198
195 /* uppercase */ 199 /* uppercase */
196 uuid_uppercase = g_ascii_strup(uuid, -1); 200 udid_uppercase = string_toupper((char*)udid);
197 plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(uuid_uppercase)); 201 plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
198 free(uuid_uppercase); 202 free(udid_uppercase);
199 free(uuid);
200 203
201 /* FIXME: Embed files as <data> nodes */ 204 /* FIXME: Embed files as <data> nodes */
202 plist_t files = plist_new_dict(); 205 plist_t files = plist_new_dict();
203 plist_dict_insert_item(ret, "iTunes Files", files); 206 plist_dict_set_item(ret, "iTunes Files", files);
204 plist_dict_insert_item(ret, "iTunes Version", plist_new_string("9.0.2")); 207 plist_dict_set_item(ret, "iTunes Version", plist_new_string("9.0.2"));
205 208
206 plist_free(root_node); 209 plist_free(root_node);
207 210
@@ -210,102 +213,21 @@ static plist_t mobilebackup_factory_info_plist_new()
210 213
211static void mobilebackup_info_update_last_backup_date(plist_t info_plist) 214static void mobilebackup_info_update_last_backup_date(plist_t info_plist)
212{ 215{
213 GTimeVal tv = {0, 0};
214 plist_t node = NULL; 216 plist_t node = NULL;
215 217
216 if (!info_plist) 218 if (!info_plist)
217 return; 219 return;
218 220
219 g_get_current_time(&tv);
220 node = plist_dict_get_item(info_plist, "Last Backup Date"); 221 node = plist_dict_get_item(info_plist, "Last Backup Date");
221 plist_set_date_val(node, tv.tv_sec, tv.tv_usec); 222#ifdef HAVE_PLIST_UNIX_DATE
223 plist_set_unix_date_val(node, time(NULL));
224#else
225 plist_set_date_val(node, time(NULL) - MAC_EPOCH, 0);
226#endif
222 227
223 node = NULL; 228 node = NULL;
224} 229}
225 230
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) 231static int plist_strcmp(plist_t node, const char *str)
310{ 232{
311 char *buffer = NULL; 233 char *buffer = NULL;
@@ -321,11 +243,14 @@ static int plist_strcmp(plist_t node, const char *str)
321 return ret; 243 return ret;
322} 244}
323 245
324static gchar *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension) 246static char *mobilebackup_build_path(const char *backup_directory, const char *name, const char *extension)
325{ 247{
326 gchar *filename = g_strconcat(name, extension, NULL); 248 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); 249 strcpy(filename, name);
328 g_free(filename); 250 if (extension != NULL)
251 strcat(filename, extension);
252 char *path = string_build_path(backup_directory, filename, NULL);
253 free(filename);
329 return path; 254 return path;
330} 255}
331 256
@@ -333,28 +258,28 @@ static void mobilebackup_write_status(const char *path, int status)
333{ 258{
334 struct stat st; 259 struct stat st;
335 plist_t status_plist = plist_new_dict(); 260 plist_t status_plist = plist_new_dict();
336 plist_dict_insert_item(status_plist, "Backup Success", plist_new_bool(status)); 261 plist_dict_set_item(status_plist, "Backup Success", plist_new_bool(status));
337 gchar *file_path = mobilebackup_build_path(path, "Status", ".plist"); 262 char *file_path = mobilebackup_build_path(path, "Status", ".plist");
338 263
339 if (stat(file_path, &st) == 0) 264 if (stat(file_path, &st) == 0)
340 remove(file_path); 265 remove(file_path);
341 266
342 plist_write_to_filename(status_plist, file_path, PLIST_FORMAT_XML); 267 plist_write_to_file(status_plist, file_path, PLIST_FORMAT_XML, 0);
343 268
344 plist_free(status_plist); 269 plist_free(status_plist);
345 status_plist = NULL; 270 status_plist = NULL;
346 271
347 g_free(file_path); 272 free(file_path);
348} 273}
349 274
350static int mobilebackup_read_status(const char *path) 275static int mobilebackup_read_status(const char *path)
351{ 276{
352 int ret = -1; 277 int ret = -1;
353 plist_t status_plist = NULL; 278 plist_t status_plist = NULL;
354 gchar *file_path = mobilebackup_build_path(path, "Status", ".plist"); 279 char *file_path = mobilebackup_build_path(path, "Status", ".plist");
355 280
356 plist_read_from_filename(&status_plist, file_path); 281 plist_read_from_file(file_path, &status_plist, NULL);
357 g_free(file_path); 282 free(file_path);
358 if (!status_plist) { 283 if (!status_plist) {
359 printf("Could not read Status.plist!\n"); 284 printf("Could not read Status.plist!\n");
360 return ret; 285 return ret;
@@ -387,7 +312,7 @@ static int mobilebackup_info_is_current_device(plist_t info)
387 /* get basic device information in one go */ 312 /* get basic device information in one go */
388 lockdownd_get_value(client, NULL, NULL, &root_node); 313 lockdownd_get_value(client, NULL, NULL, &root_node);
389 314
390 /* verify UUID */ 315 /* verify UDID */
391 value_node = plist_dict_get_item(root_node, "UniqueDeviceID"); 316 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
392 node = plist_dict_get_item(info, "Target Identifier"); 317 node = plist_dict_get_item(info, "Target Identifier");
393 318
@@ -435,14 +360,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) 360static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory, const char *hash)
436{ 361{
437 int ret = 0; 362 int ret = 0;
438 gchar *path = mobilebackup_build_path(backup_directory, hash, ".mddata"); 363 char *path = mobilebackup_build_path(backup_directory, hash, ".mddata");
439 printf("Removing \"%s\" ", path); 364 printf("Removing \"%s\" ", path);
440 if (!remove( path )) 365 if (!remove( path ))
441 ret = 1; 366 ret = 1;
442 else 367 else
443 ret = 0; 368 ret = 0;
444 369
445 g_free(path); 370 free(path);
446 371
447 if (!ret) 372 if (!ret)
448 return ret; 373 return ret;
@@ -454,7 +379,7 @@ static int mobilebackup_delete_backup_file_by_hash(const char *backup_directory,
454 else 379 else
455 ret = 0; 380 ret = 0;
456 381
457 g_free(path); 382 free(path);
458 383
459 return ret; 384 return ret;
460} 385}
@@ -476,7 +401,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
476 } 401 }
477 402
478 infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo"); 403 infopath = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
479 plist_read_from_filename(&mdinfo, infopath); 404 plist_read_from_file(infopath, &mdinfo, NULL);
480 free(infopath); 405 free(infopath);
481 if (!mdinfo) { 406 if (!mdinfo) {
482 printf("\r\n"); 407 printf("\r\n");
@@ -521,13 +446,13 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
521 446
522 char *version = NULL; 447 char *version = NULL;
523 node = plist_dict_get_item(metadata, "Version"); 448 node = plist_dict_get_item(metadata, "Version");
524 if (node && (plist_get_node_type(node) == PLIST_STRING)) { 449 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
525 plist_get_string_val(node, &version); 450 plist_get_string_val(node, &version);
526 } 451 }
527 452
528 char *destpath = NULL; 453 char *destpath = NULL;
529 node = plist_dict_get_item(metadata, "Path"); 454 node = plist_dict_get_item(metadata, "Path");
530 if (node && (plist_get_node_type(node) == PLIST_STRING)) { 455 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
531 plist_get_string_val(node, &destpath); 456 plist_get_string_val(node, &destpath);
532 } 457 }
533 458
@@ -539,7 +464,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
539 464
540 char *domain = NULL; 465 char *domain = NULL;
541 node = plist_dict_get_item(metadata, "Domain"); 466 node = plist_dict_get_item(metadata, "Domain");
542 if (node && (plist_get_node_type(node) == PLIST_STRING)) { 467 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
543 plist_get_string_val(node, &domain); 468 plist_get_string_val(node, &domain);
544 } 469 }
545 470
@@ -550,14 +475,14 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
550 unsigned char fnhash[20]; 475 unsigned char fnhash[20];
551 char fnamehash[41]; 476 char fnamehash[41];
552 char *p = fnamehash; 477 char *p = fnamehash;
553 sha1_of_data(fnstr, strlen(fnstr), fnhash); 478 sha1((const unsigned char*)fnstr, strlen(fnstr), fnhash);
554 free(fnstr); 479 free(fnstr);
555 int i; 480 int i;
556 for ( i = 0; i < 20; i++, p += 2 ) { 481 for ( i = 0; i < 20; i++, p += 2 ) {
557 snprintf (p, 3, "%02x", (unsigned char)fnhash[i] ); 482 snprintf (p, 3, "%02x", (unsigned char)fnhash[i] );
558 } 483 }
559 if (strcmp(fnamehash, hash)) { 484 if (strcmp(fnamehash, hash) != 0) {
560 printf("\r\n"); 485 printf("\r\n");
561 printf("WARNING: filename hash does not match for entry '%s'\n", hash); 486 printf("WARNING: filename hash does not match for entry '%s'\n", hash);
562 } 487 }
563 488
@@ -567,7 +492,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
567 plist_get_string_val(node, &auth_version); 492 plist_get_string_val(node, &auth_version);
568 } 493 }
569 494
570 if (strcmp(auth_version, "1.0")) { 495 if (strcmp(auth_version, "1.0") != 0) {
571 printf("\r\n"); 496 printf("\r\n");
572 printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version); 497 printf("WARNING: Unknown AuthVersion '%s', DataHash cannot be verified!\n", auth_version);
573 } 498 }
@@ -591,9 +516,9 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
591 hash_ok = 1; 516 hash_ok = 1;
592 } 517 }
593 518
594 g_free(domain); 519 free(domain);
595 g_free(version); 520 free(version);
596 g_free(destpath); 521 free(destpath);
597 522
598 if (!hash_ok) { 523 if (!hash_ok) {
599 printf("\r\n"); 524 printf("\r\n");
@@ -605,31 +530,36 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const
605 printf("\n"); 530 printf("\n");
606 res = 0; 531 res = 0;
607 } 532 }
608 g_free(data_hash); 533 free(data_hash);
609 plist_free(mdinfo); 534 plist_free(mdinfo);
610 return res; 535 return res;
611} 536}
612 537
613static void do_post_notification(const char *notification) 538static void do_post_notification(const char *notification)
614{ 539{
615 uint16_t nport = 0; 540 lockdownd_service_descriptor_t service = NULL;
616 np_client_t np; 541 np_client_t np;
617 542
618 if (!client) { 543 if (!client) {
619 if (lockdownd_client_new_with_handshake(phone, &client, "idevicebackup") != LOCKDOWN_E_SUCCESS) { 544 if (lockdownd_client_new_with_handshake(device, &client, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
620 return; 545 return;
621 } 546 }
622 } 547 }
623 548
624 lockdownd_start_service(client, NP_SERVICE_NAME, &nport); 549 lockdownd_error_t ldret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
625 if (nport) { 550 if (ldret == LOCKDOWN_E_SUCCESS) {
626 np_client_new(phone, nport, &np); 551 np_client_new(device, service, &np);
627 if (np) { 552 if (np) {
628 np_post_notification(np, notification); 553 np_post_notification(np, notification);
629 np_client_free(np); 554 np_client_free(np);
630 } 555 }
631 } else { 556 } else {
632 printf("Could not start %s\n", NP_SERVICE_NAME); 557 printf("Could not start %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
558 }
559
560 if (service) {
561 lockdownd_service_descriptor_free(service);
562 service = NULL;
633 } 563 }
634} 564}
635 565
@@ -665,29 +595,38 @@ static void clean_exit(int sig)
665 quit_flag++; 595 quit_flag++;
666} 596}
667 597
668static void print_usage(int argc, char **argv) 598static void print_usage(int argc, char **argv, int is_error)
669{ 599{
670 char *name = NULL; 600 char *name = strrchr(argv[0], '/');
671 name = strrchr(argv[0], '/'); 601 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])); 602 fprintf(is_error ? stderr : stdout,
673 printf("Create or restore backup from the current or specified directory.\n\n"); 603 "\n"
674 printf("commands:\n"); 604 "Create or restore backup in/from the specified directory.\n"
675 printf(" backup\tSaves a device backup into DIRECTORY\n"); 605 "\n"
676 printf(" restore\tRestores a device backup from DIRECTORY.\n\n"); 606 "CMD:\n"
677 printf("options:\n"); 607 " backup Saves a device backup into DIRECTORY\n"
678 printf(" -d, --debug\t\tenable communication debugging\n"); 608 " restore Restores a device backup from DIRECTORY.\n"
679 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); 609 "\n"
680 printf(" -h, --help\t\tprints usage information\n"); 610 "OPTIONS:\n"
681 printf("\n"); 611 " -u, --udid UDID target specific device by UDID\n"
612 " -n, --network connect to network device\n"
613 " -d, --debug enable communication debugging\n"
614 " -h, --help prints usage information\n"
615 " -v, --version prints version information\n"
616 "\n"
617 "Homepage: <" PACKAGE_URL ">\n"
618 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
619 );
682} 620}
683 621
684int main(int argc, char *argv[]) 622int main(int argc, char *argv[])
685{ 623{
686 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 624 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
625 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
687 int i; 626 int i;
688 char uuid[41]; 627 char* udid = NULL;
689 uint16_t port = 0; 628 int use_network = 0;
690 uuid[0] = 0; 629 lockdownd_service_descriptor_t service = NULL;
691 int cmd = -1; 630 int cmd = -1;
692 int is_full_backup = 0; 631 int is_full_backup = 0;
693 char *backup_directory = NULL; 632 char *backup_directory = NULL;
@@ -701,60 +640,77 @@ int main(int argc, char *argv[])
701 uint64_t length = 0; 640 uint64_t length = 0;
702 uint64_t backup_total_size = 0; 641 uint64_t backup_total_size = 0;
703 enum device_link_file_status_t file_status = DEVICE_LINK_FILE_STATUS_NONE; 642 enum device_link_file_status_t file_status = DEVICE_LINK_FILE_STATUS_NONE;
704 uint64_t c = 0; 643 int c = 0;
644 const struct option longopts[] = {
645 { "debug", no_argument, NULL, 'd' },
646 { "help", no_argument, NULL, 'h' },
647 { "udid", required_argument, NULL, 'u' },
648 { "network", no_argument, NULL, 'n' },
649 { "version", no_argument, NULL, 'v' },
650 { NULL, 0, NULL, 0}
651 };
705 652
706 /* we need to exit cleanly on running backups and restores or we cause havok */ 653 /* we need to exit cleanly on running backups and restores or we cause havok */
707 signal(SIGINT, clean_exit); 654 signal(SIGINT, clean_exit);
708 signal(SIGQUIT, clean_exit);
709 signal(SIGTERM, clean_exit); 655 signal(SIGTERM, clean_exit);
656#ifndef _WIN32
657 signal(SIGQUIT, clean_exit);
710 signal(SIGPIPE, SIG_IGN); 658 signal(SIGPIPE, SIG_IGN);
659#endif
711 660
712 /* parse cmdline args */ 661 /* parse cmdline args */
713 for (i = 1; i < argc; i++) { 662 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
714 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 663 switch (c) {
664 case 'd':
715 idevice_set_debug_level(1); 665 idevice_set_debug_level(1);
716 continue; 666 break;
717 } 667 case 'u':
718 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 668 if (!*optarg) {
719 i++; 669 fprintf(stderr, "ERROR: UDID must not be empty!\n");
720 if (!argv[i] || (strlen(argv[i]) != 40)) { 670 print_usage(argc, argv, 1);
721 print_usage(argc, argv); 671 return 2;
722 return 0;
723 } 672 }
724 strcpy(uuid, argv[i]); 673 udid = strdup(optarg);
725 continue; 674 break;
726 } 675 case 'n':
727 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 676 use_network = 1;
728 print_usage(argc, argv); 677 break;
678 case 'h':
679 print_usage(argc, argv, 0);
729 return 0; 680 return 0;
730 } 681 case 'v':
731 else if (!strcmp(argv[i], "backup")) { 682 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; 683 return 0;
684 default:
685 print_usage(argc, argv, 1);
686 return 2;
743 } 687 }
744 } 688 }
689 argc -= optind;
690 argv += optind;
745 691
746 /* verify options */ 692 if (argc < 1) {
747 if (cmd == -1) { 693 fprintf(stderr, "ERROR: Missing command.\n");
748 printf("No command specified.\n"); 694 print_usage(argc+optind, argv-optind, 1);
749 print_usage(argc, argv); 695 return 2;
750 return -1;
751 } 696 }
752 697
753 if (backup_directory == NULL) { 698 if (!strcmp(argv[0], "backup")) {
754 printf("No target backup directory specified.\n"); 699 cmd = CMD_BACKUP;
755 print_usage(argc, argv); 700 } else if (!strcmp(argv[0], "restore")) {
756 return -1; 701 cmd = CMD_RESTORE;
702 } else {
703 fprintf(stderr, "ERROR: Invalid command '%s'.\n", argv[0]);
704 print_usage(argc+optind, argv-optind, 1);
705 return 2;
706 }
707
708 if (argc < 2) {
709 fprintf(stderr, "No target backup directory specified.\n");
710 print_usage(argc+optind, argv-optind, 1);
711 return 2;
757 } 712 }
713 backup_directory = argv[1];
758 714
759 /* verify if passed backup directory exists */ 715 /* verify if passed backup directory exists */
760 if (stat(backup_directory, &st) != 0) { 716 if (stat(backup_directory, &st) != 0) {
@@ -766,7 +722,7 @@ int main(int argc, char *argv[])
766 char *info_path = mobilebackup_build_path(backup_directory, "Info", ".plist"); 722 char *info_path = mobilebackup_build_path(backup_directory, "Info", ".plist");
767 if (cmd == CMD_RESTORE) { 723 if (cmd == CMD_RESTORE) {
768 if (stat(info_path, &st) != 0) { 724 if (stat(info_path, &st) != 0) {
769 g_free(info_path); 725 free(info_path);
770 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory); 726 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found.\n", backup_directory);
771 return -1; 727 return -1;
772 } 728 }
@@ -774,32 +730,54 @@ int main(int argc, char *argv[])
774 730
775 printf("Backup directory is \"%s\"\n", backup_directory); 731 printf("Backup directory is \"%s\"\n", backup_directory);
776 732
777 if (uuid[0] != 0) { 733 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
778 ret = idevice_new(&phone, uuid); 734 if (ret != IDEVICE_E_SUCCESS) {
779 if (ret != IDEVICE_E_SUCCESS) { 735 if (udid) {
780 printf("No device found with uuid %s, is it plugged in?\n", uuid); 736 printf("No device found with udid %s.\n", udid);
781 return -1; 737 } else {
738 printf("No device found.\n");
782 } 739 }
740 return -1;
783 } 741 }
784 else 742
785 { 743 if (!udid) {
786 ret = idevice_new(&phone, NULL); 744 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 } 745 }
792 746
793 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicebackup")) { 747 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
794 idevice_free(phone); 748 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
749 idevice_free(device);
750 free(udid);
795 return -1; 751 return -1;
796 } 752 }
797 753
754 node = NULL;
755 lockdownd_get_value(client, NULL, "ProductVersion", &node);
756 if (node) {
757 char* str = NULL;
758 if (plist_get_node_type(node) == PLIST_STRING) {
759 plist_get_string_val(node, &str);
760 }
761 plist_free(node);
762 node = NULL;
763 if (str) {
764 int maj = strtol(str, NULL, 10);
765 free(str);
766 if (maj > 3) {
767 printf("ERROR: This tool is only compatible with iOS 3 or below. For newer iOS versions please use the idevicebackup2 tool.\n");
768 lockdownd_client_free(client);
769 idevice_free(device);
770 free(udid);
771 return -1;
772 }
773 }
774 }
775
798 /* start notification_proxy */ 776 /* start notification_proxy */
799 np_client_t np = NULL; 777 np_client_t np = NULL;
800 ret = lockdownd_start_service(client, NP_SERVICE_NAME, &port); 778 ldret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
801 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 779 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
802 np_client_new(phone, port, &np); 780 np_client_new(device, service, &np);
803 np_set_notify_callback(np, notify_cb, NULL); 781 np_set_notify_callback(np, notify_cb, NULL);
804 const char *noties[5] = { 782 const char *noties[5] = {
805 NP_SYNC_CANCEL_REQUEST, 783 NP_SYNC_CANCEL_REQUEST,
@@ -810,25 +788,37 @@ int main(int argc, char *argv[])
810 }; 788 };
811 np_observe_notifications(np, noties); 789 np_observe_notifications(np, noties);
812 } else { 790 } else {
813 printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME); 791 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
814 } 792 }
815 793
816 afc_client_t afc = NULL; 794 afc_client_t afc = NULL;
817 if (cmd == CMD_BACKUP) { 795 if (cmd == CMD_BACKUP) {
818 /* start AFC, we need this for the lock file */ 796 /* start AFC, we need this for the lock file */
819 port = 0; 797 service->port = 0;
820 ret = lockdownd_start_service(client, "com.apple.afc", &port); 798 service->ssl_enabled = 0;
821 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 799 ldret = lockdownd_start_service(client, AFC_SERVICE_NAME, &service);
822 afc_client_new(phone, port, &afc); 800 if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
801 afc_client_new(device, service, &afc);
802 } else {
803 printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
823 } 804 }
824 } 805 }
825 806
807 if (service) {
808 lockdownd_service_descriptor_free(service);
809 service = NULL;
810 }
811
826 /* start mobilebackup service and retrieve port */ 812 /* start mobilebackup service and retrieve port */
827 port = 0; 813 ldret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &service);
828 ret = lockdownd_start_service(client, MOBILEBACKUP_SERVICE_NAME, &port); 814 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
829 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 815 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); 816 printf("%d\n", mobilebackup_client_new(device, service, &mobilebackup));
831 mobilebackup_client_new(phone, port, &mobilebackup); 817
818 if (service) {
819 lockdownd_service_descriptor_free(service);
820 service = NULL;
821 }
832 822
833 /* check abort conditions */ 823 /* check abort conditions */
834 if (quit_flag > 0) { 824 if (quit_flag > 0) {
@@ -839,7 +829,7 @@ int main(int argc, char *argv[])
839 /* verify existing Info.plist */ 829 /* verify existing Info.plist */
840 if (stat(info_path, &st) == 0) { 830 if (stat(info_path, &st) == 0) {
841 printf("Reading Info.plist from backup.\n"); 831 printf("Reading Info.plist from backup.\n");
842 plist_read_from_filename(&info_plist, info_path); 832 plist_read_from_file(info_path, &info_plist, NULL);
843 833
844 if (!info_plist) { 834 if (!info_plist) {
845 printf("Could not read Info.plist\n"); 835 printf("Could not read Info.plist\n");
@@ -850,7 +840,7 @@ int main(int argc, char *argv[])
850 /* update the last backup time within Info.plist */ 840 /* update the last backup time within Info.plist */
851 mobilebackup_info_update_last_backup_date(info_plist); 841 mobilebackup_info_update_last_backup_date(info_plist);
852 remove(info_path); 842 remove(info_path);
853 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); 843 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
854 } else { 844 } else {
855 printf("Aborting backup. Backup is not compatible with the current device.\n"); 845 printf("Aborting backup. Backup is not compatible with the current device.\n");
856 cmd = CMD_LEAVE; 846 cmd = CMD_LEAVE;
@@ -883,15 +873,16 @@ int main(int argc, char *argv[])
883 if (aerr == AFC_E_SUCCESS) { 873 if (aerr == AFC_E_SUCCESS) {
884 do_post_notification(NP_SYNC_DID_START); 874 do_post_notification(NP_SYNC_DID_START);
885 break; 875 break;
886 } else if (aerr == AFC_E_OP_WOULD_BLOCK) { 876 }
877 if (aerr == AFC_E_OP_WOULD_BLOCK) {
887 usleep(LOCK_WAIT); 878 usleep(LOCK_WAIT);
888 continue; 879 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 } 880 }
881
882 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
883 afc_file_close(afc, lockfile);
884 lockfile = 0;
885 cmd = CMD_LEAVE;
895 } 886 }
896 if (i == LOCK_ATTEMPTS) { 887 if (i == LOCK_ATTEMPTS) {
897 fprintf(stderr, "ERROR: timeout while locking for sync\n"); 888 fprintf(stderr, "ERROR: timeout while locking for sync\n");
@@ -910,12 +901,12 @@ int main(int argc, char *argv[])
910 case CMD_BACKUP: 901 case CMD_BACKUP:
911 printf("Starting backup...\n"); 902 printf("Starting backup...\n");
912 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */ 903 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
913 /* TODO: verify battery on AC enough battery remaining */ 904 /* TODO: verify battery on AC enough battery remaining */
914 905
915 /* read the last Manifest.plist */ 906 /* read the last Manifest.plist */
916 if (!is_full_backup) { 907 if (!is_full_backup) {
917 printf("Reading existing Manifest.\n"); 908 printf("Reading existing Manifest.\n");
918 plist_read_from_filename(&manifest_plist, manifest_path); 909 plist_read_from_file(manifest_path, &manifest_plist, NULL);
919 if (!manifest_plist) { 910 if (!manifest_plist) {
920 printf("Could not read Manifest.plist, switching to full backup mode.\n"); 911 printf("Could not read Manifest.plist, switching to full backup mode.\n");
921 is_full_backup = 1; 912 is_full_backup = 1;
@@ -932,10 +923,10 @@ int main(int argc, char *argv[])
932 } 923 }
933 remove(info_path); 924 remove(info_path);
934 printf("Creating Info.plist for new backup.\n"); 925 printf("Creating Info.plist for new backup.\n");
935 info_plist = mobilebackup_factory_info_plist_new(); 926 info_plist = mobilebackup_factory_info_plist_new(udid);
936 plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML); 927 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
937 } 928 }
938 g_free(info_path); 929 free(info_path);
939 930
940 plist_free(info_plist); 931 plist_free(info_plist);
941 info_plist = NULL; 932 info_plist = NULL;
@@ -965,7 +956,7 @@ int main(int argc, char *argv[])
965 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) { 956 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
966 printf("ERROR: Could not start backup process: device refused to start the backup process.\n"); 957 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
967 } else { 958 } else {
968 printf("ERROR: Could not start backup process: unspecified error occured\n"); 959 printf("ERROR: Could not start backup process: unspecified error occurred (%d)\n", err);
969 } 960 }
970 break; 961 break;
971 } 962 }
@@ -985,8 +976,9 @@ int main(int argc, char *argv[])
985 char *filename_mddata = NULL; 976 char *filename_mddata = NULL;
986 char *filename_source = NULL; 977 char *filename_source = NULL;
987 char *format_size = NULL; 978 char *format_size = NULL;
988 gboolean is_manifest = FALSE; 979 int is_manifest = 0;
989 uint8_t b = 0; 980 uint8_t b = 0;
981 uint64_t u64val = 0;
990 982
991 /* process series of DLSendFile messages */ 983 /* process series of DLSendFile messages */
992 do { 984 do {
@@ -996,7 +988,7 @@ int main(int argc, char *argv[])
996 sleep(2); 988 sleep(2);
997 goto files_out; 989 goto files_out;
998 } 990 }
999 991
1000 node = plist_array_get_item(message, 0); 992 node = plist_array_get_item(message, 0);
1001 993
1002 /* get out if we don't get a DLSendFile */ 994 /* get out if we don't get a DLSendFile */
@@ -1010,16 +1002,16 @@ int main(int argc, char *argv[])
1010 node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey"); 1002 node = plist_dict_get_item(node_tmp, "BackupTotalSizeKey");
1011 if (node) { 1003 if (node) {
1012 plist_get_uint_val(node, &backup_total_size); 1004 plist_get_uint_val(node, &backup_total_size);
1013 format_size = g_format_size_for_display(backup_total_size); 1005 format_size = string_format_size(backup_total_size);
1014 printf("Backup data requires %s on the disk.\n", format_size); 1006 printf("Backup data requires %s on the disk.\n", format_size);
1015 g_free(format_size); 1007 free(format_size);
1016 } 1008 }
1017 } 1009 }
1018 1010
1019 /* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */ 1011 /* check DLFileStatusKey (codes: 1 = Hunk, 2 = Last Hunk) */
1020 node = plist_dict_get_item(node_tmp, "DLFileStatusKey"); 1012 node = plist_dict_get_item(node_tmp, "DLFileStatusKey");
1021 plist_get_uint_val(node, &c); 1013 plist_get_uint_val(node, &u64val);
1022 file_status = c; 1014 file_status = u64val;
1023 1015
1024 /* get source filename */ 1016 /* get source filename */
1025 node = plist_dict_get_item(node_tmp, "BackupManifestKey"); 1017 node = plist_dict_get_item(node_tmp, "BackupManifestKey");
@@ -1027,7 +1019,7 @@ int main(int argc, char *argv[])
1027 if (node) { 1019 if (node) {
1028 plist_get_bool_val(node, &b); 1020 plist_get_bool_val(node, &b);
1029 } 1021 }
1030 is_manifest = (b == 1) ? TRUE: FALSE; 1022 is_manifest = (b == 1) ? 1 : 0;
1031 1023
1032 if ((hunk_index == 0) && (!is_manifest)) { 1024 if ((hunk_index == 0) && (!is_manifest)) {
1033 /* get source filename */ 1025 /* get source filename */
@@ -1040,17 +1032,17 @@ int main(int argc, char *argv[])
1040 plist_get_uint_val(node, &file_size); 1032 plist_get_uint_val(node, &file_size);
1041 backup_real_size += file_size; 1033 backup_real_size += file_size;
1042 1034
1043 format_size = g_format_size_for_display(backup_real_size); 1035 format_size = string_format_size(backup_real_size);
1044 printf("(%s", format_size); 1036 printf("(%s", format_size);
1045 g_free(format_size); 1037 free(format_size);
1046 1038
1047 format_size = g_format_size_for_display(backup_total_size); 1039 format_size = string_format_size(backup_total_size);
1048 printf("/%s): ", format_size); 1040 printf("/%s): ", format_size);
1049 g_free(format_size); 1041 free(format_size);
1050 1042
1051 format_size = g_format_size_for_display(file_size); 1043 format_size = string_format_size(file_size);
1052 printf("Receiving file %s (%s)... \n", filename_source, format_size); 1044 printf("Receiving file %s (%s)... \n", filename_source, format_size);
1053 g_free(format_size); 1045 free(format_size);
1054 1046
1055 if (filename_source) 1047 if (filename_source)
1056 free(filename_source); 1048 free(filename_source);
@@ -1071,9 +1063,9 @@ int main(int argc, char *argv[])
1071 remove(filename_mdinfo); 1063 remove(filename_mdinfo);
1072 1064
1073 node = plist_dict_get_item(node_tmp, "BackupFileInfo"); 1065 node = plist_dict_get_item(node_tmp, "BackupFileInfo");
1074 plist_write_to_filename(node, filename_mdinfo, PLIST_FORMAT_BINARY); 1066 plist_write_to_file(node, filename_mdinfo, PLIST_FORMAT_BINARY, 0);
1075 1067
1076 g_free(filename_mdinfo); 1068 free(filename_mdinfo);
1077 } 1069 }
1078 1070
1079 file_index++; 1071 file_index++;
@@ -1107,15 +1099,14 @@ int main(int argc, char *argv[])
1107 free(buffer); 1099 free(buffer);
1108 buffer = NULL; 1100 buffer = NULL;
1109 1101
1110 g_free(filename_mddata); 1102 free(filename_mddata);
1111 } 1103 }
1112 1104
1113 if ((!is_manifest)) { 1105 if ((!is_manifest)) {
1114 if (hunk_index == 0 && file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) { 1106 if (hunk_index == 0 && file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) {
1115 print_progress(100); 1107 print_progress(100);
1116 } else { 1108 } else if (file_size > 0) {
1117 if (file_size > 0) 1109 print_progress((double)(file_size_current*100)/file_size);
1118 print_progress((double)((file_size_current*100)/file_size));
1119 } 1110 }
1120 } 1111 }
1121 1112
@@ -1145,7 +1136,7 @@ files_out:
1145 1136
1146 /* remove any atomic Manifest.plist.tmp */ 1137 /* remove any atomic Manifest.plist.tmp */
1147 if (manifest_path) 1138 if (manifest_path)
1148 g_free(manifest_path); 1139 free(manifest_path);
1149 1140
1150 manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp"); 1141 manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
1151 if (stat(manifest_path, &st) == 0) 1142 if (stat(manifest_path, &st) == 0)
@@ -1184,7 +1175,7 @@ files_out:
1184 if (manifest_plist) { 1175 if (manifest_plist) {
1185 remove(manifest_path); 1176 remove(manifest_path);
1186 printf("Storing Manifest.plist...\n"); 1177 printf("Storing Manifest.plist...\n");
1187 plist_write_to_filename(manifest_plist, manifest_path, PLIST_FORMAT_XML); 1178 plist_write_to_file(manifest_plist, manifest_path, PLIST_FORMAT_XML, 0);
1188 } 1179 }
1189 1180
1190 backup_ok = 1; 1181 backup_ok = 1;
@@ -1215,21 +1206,21 @@ files_out:
1215 } 1206 }
1216 /* now make sure backup integrity is ok! verify all files */ 1207 /* now make sure backup integrity is ok! verify all files */
1217 printf("Reading existing Manifest.\n"); 1208 printf("Reading existing Manifest.\n");
1218 plist_read_from_filename(&manifest_plist, manifest_path); 1209 plist_read_from_file(manifest_path, &manifest_plist, NULL);
1219 if (!manifest_plist) { 1210 if (!manifest_plist) {
1220 printf("Could not read Manifest.plist. Aborting.\n"); 1211 printf("Could not read Manifest.plist. Aborting.\n");
1221 break; 1212 break;
1222 } 1213 }
1223 1214
1224 printf("Verifying backup integrity, please wait.\n"); 1215 printf("Verifying backup integrity, please wait.\n");
1225 char *bin = NULL; 1216 unsigned char *bin = NULL;
1226 uint64_t binsize = 0; 1217 uint64_t binsize = 0;
1227 node = plist_dict_get_item(manifest_plist, "Data"); 1218 node = plist_dict_get_item(manifest_plist, "Data");
1228 if (!node || (plist_get_node_type(node) != PLIST_DATA)) { 1219 if (!node || (plist_get_node_type(node) != PLIST_DATA)) {
1229 printf("Could not read Data key from Manifest.plist!\n"); 1220 printf("Could not read Data key from Manifest.plist!\n");
1230 break; 1221 break;
1231 } 1222 }
1232 plist_get_data_val(node, &bin, &binsize); 1223 plist_get_data_val(node, (char**)&bin, &binsize);
1233 plist_t backup_data = NULL; 1224 plist_t backup_data = NULL;
1234 if (bin) { 1225 if (bin) {
1235 char *auth_ver = NULL; 1226 char *auth_ver = NULL;
@@ -1246,7 +1237,7 @@ files_out:
1246 if (auth_sig && (auth_sig_len == 20)) { 1237 if (auth_sig && (auth_sig_len == 20)) {
1247 /* calculate the sha1, then compare */ 1238 /* calculate the sha1, then compare */
1248 unsigned char data_sha1[20]; 1239 unsigned char data_sha1[20];
1249 sha1_of_data(bin, binsize, data_sha1); 1240 sha1(bin, binsize, data_sha1);
1250 if (compare_hash(auth_sig, data_sha1, 20)) { 1241 if (compare_hash(auth_sig, data_sha1, 20)) {
1251 printf("AuthSignature is valid\n"); 1242 printf("AuthSignature is valid\n");
1252 } else { 1243 } else {
@@ -1255,12 +1246,12 @@ files_out:
1255 } else { 1246 } else {
1256 printf("Could not get AuthSignature from manifest!\n"); 1247 printf("Could not get AuthSignature from manifest!\n");
1257 } 1248 }
1258 g_free(auth_sig); 1249 free(auth_sig);
1259 } else if (auth_ver) { 1250 } else if (auth_ver) {
1260 printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver); 1251 printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver);
1261 } 1252 }
1262 plist_from_bin(bin, (uint32_t)binsize, &backup_data); 1253 plist_from_bin((char*)bin, (uint32_t)binsize, &backup_data);
1263 g_free(bin); 1254 free(bin);
1264 } 1255 }
1265 if (!backup_data) { 1256 if (!backup_data) {
1266 printf("Could not read plist from Manifest.plist Data key!\n"); 1257 printf("Could not read plist from Manifest.plist Data key!\n");
@@ -1312,7 +1303,7 @@ files_out:
1312 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) { 1303 } else if (err == MOBILEBACKUP_E_REPLY_NOT_OK) {
1313 printf("ERROR: Could not start restore process: device refused to start the restore process.\n"); 1304 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
1314 } else { 1305 } else {
1315 printf("ERROR: Could not start restore process: unspecified error occured (%d)\n", err); 1306 printf("ERROR: Could not start restore process: unspecified error occurred (%d)\n", err);
1316 } 1307 }
1317 plist_free(backup_data); 1308 plist_free(backup_data);
1318 break; 1309 break;
@@ -1342,7 +1333,7 @@ files_out:
1342 while (node) { 1333 while (node) {
1343 /* TODO: read mddata/mdinfo files and send to device using DLSendFile */ 1334 /* TODO: read mddata/mdinfo files and send to device using DLSendFile */
1344 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mdinfo"); 1335 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mdinfo");
1345 plist_read_from_filename(&file_info, file_info_path); 1336 plist_read_from_file(file_info_path, &file_info, NULL);
1346 1337
1347 /* get encryption state */ 1338 /* get encryption state */
1348 tmp_node = plist_dict_get_item(file_info, "IsEncrypted"); 1339 tmp_node = plist_dict_get_item(file_info, "IsEncrypted");
@@ -1360,42 +1351,62 @@ files_out:
1360 printf("Restoring file %s %d/%d (%d%%)... ", file_path, cur_file, total_files, (cur_file*100/total_files)); 1351 printf("Restoring file %s %d/%d (%d%%)... ", file_path, cur_file, total_files, (cur_file*100/total_files));
1361 1352
1362 /* add additional device link file information keys */ 1353 /* add additional device link file information keys */
1363 plist_dict_insert_item(file_info, "DLFileAttributesKey", plist_copy(node)); 1354 plist_dict_set_item(file_info, "DLFileAttributesKey", plist_copy(node));
1364 plist_dict_insert_item(file_info, "DLFileSource", plist_new_string(file_info_path)); 1355 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")); 1356 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)); 1357 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)); 1358 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)); 1359 plist_dict_set_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1369 1360
1370 /* read data from file */ 1361 /* read data from file */
1371 free(file_info_path); 1362 free(file_info_path);
1372 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata"); 1363 file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata");
1373 buffer_read_from_filename(file_info_path, &buffer, &length); 1364
1365 /* determine file size */
1366#ifdef _WIN32
1367 struct _stati64 fst;
1368 if (_stati64(file_info_path, &fst) != 0)
1369#else
1370 struct stat fst;
1371 if (stat(file_info_path, &fst) != 0)
1372#endif
1373 {
1374 printf("ERROR: stat() failed for '%s': %s\n", file_info_path, strerror(errno));
1375 free(file_info_path);
1376 break;
1377 }
1378 length = fst.st_size;
1379
1380 FILE *f = fopen(file_info_path, "rb");
1381 if (!f) {
1382 printf("ERROR: could not open local file '%s': %s\n", file_info_path, strerror(errno));
1383 free(file_info_path);
1384 break;
1385 }
1374 free(file_info_path); 1386 free(file_info_path);
1375 1387
1376 /* send DLSendFile messages */ 1388 /* send DLSendFile messages */
1377 file_offset = 0; 1389 file_offset = 0;
1378 do { 1390 do {
1379 if ((length-file_offset) <= 8192) 1391 char buf[8192];
1392 size_t len = fread(buf, 1, sizeof(buf), f);
1393
1394 if ((length-file_offset) <= sizeof(buf))
1380 file_status = DEVICE_LINK_FILE_STATUS_LAST_HUNK; 1395 file_status = DEVICE_LINK_FILE_STATUS_LAST_HUNK;
1381 else 1396 else
1382 file_status = DEVICE_LINK_FILE_STATUS_HUNK; 1397 file_status = DEVICE_LINK_FILE_STATUS_HUNK;
1383 1398
1384 plist_dict_remove_item(file_info, "DLFileOffsetKey"); 1399 plist_dict_remove_item(file_info, "DLFileOffsetKey");
1385 plist_dict_insert_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset)); 1400 plist_dict_set_item(file_info, "DLFileOffsetKey", plist_new_uint(file_offset));
1386 1401
1387 plist_dict_remove_item(file_info, "DLFileStatusKey"); 1402 plist_dict_remove_item(file_info, "DLFileStatusKey");
1388 plist_dict_insert_item(file_info, "DLFileStatusKey", plist_new_uint(file_status)); 1403 plist_dict_set_item(file_info, "DLFileStatusKey", plist_new_uint(file_status));
1389 1404
1390 send_file_node = plist_new_array(); 1405 send_file_node = plist_new_array();
1391 1406
1392 plist_array_append_item(send_file_node, plist_new_string("DLSendFile")); 1407 plist_array_append_item(send_file_node, plist_new_string("DLSendFile"));
1393 1408
1394 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) 1409 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)); 1410 plist_array_append_item(send_file_node, plist_copy(file_info));
1400 1411
1401 err = mobilebackup_send(mobilebackup, send_file_node); 1412 err = mobilebackup_send(mobilebackup, send_file_node);
@@ -1413,13 +1424,13 @@ files_out:
1413 } 1424 }
1414 } 1425 }
1415 1426
1416 file_offset += 8192; 1427 file_offset += len;
1417 1428
1418 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK) 1429 if (file_status == DEVICE_LINK_FILE_STATUS_LAST_HUNK)
1419 printf("DONE\n"); 1430 printf("DONE\n");
1420 1431
1421 plist_free(send_file_node); 1432 plist_free(send_file_node);
1422 1433
1423 if (file_status == DEVICE_LINK_FILE_STATUS_NONE) 1434 if (file_status == DEVICE_LINK_FILE_STATUS_NONE)
1424 break; 1435 break;
1425 1436
@@ -1466,8 +1477,8 @@ files_out:
1466 tmp_node = plist_dict_get_item(node, "AppInfo"); 1477 tmp_node = plist_dict_get_item(node, "AppInfo");
1467 1478
1468 dict = plist_new_dict(); 1479 dict = plist_new_dict();
1469 plist_dict_insert_item(dict, "AppInfo", plist_copy(tmp_node)); 1480 plist_dict_set_item(dict, "AppInfo", plist_copy(tmp_node));
1470 plist_dict_insert_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageRestoreApplicationSent")); 1481 plist_dict_set_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageRestoreApplicationSent"));
1471 1482
1472 array = plist_new_array(); 1483 array = plist_new_array();
1473 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage")); 1484 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage"));
@@ -1540,9 +1551,9 @@ files_out:
1540 do_post_notification(NP_SYNC_DID_FINISH); 1551 do_post_notification(NP_SYNC_DID_FINISH);
1541 } 1552 }
1542 if (manifest_path) 1553 if (manifest_path)
1543 g_free(manifest_path); 1554 free(manifest_path);
1544 } else { 1555 } else {
1545 printf("ERROR: Could not start service %s.\n", MOBILEBACKUP_SERVICE_NAME); 1556 printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP_SERVICE_NAME, lockdownd_strerror(ldret));
1546 lockdownd_client_free(client); 1557 lockdownd_client_free(client);
1547 client = NULL; 1558 client = NULL;
1548 } 1559 }
@@ -1561,7 +1572,9 @@ files_out:
1561 if (mobilebackup) 1572 if (mobilebackup)
1562 mobilebackup_client_free(mobilebackup); 1573 mobilebackup_client_free(mobilebackup);
1563 1574
1564 idevice_free(phone); 1575 idevice_free(device);
1576
1577 free(udid);
1565 1578
1566 return 0; 1579 return 0;
1567} 1580}
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
new file mode 100644
index 0000000..f5eb1e4
--- /dev/null
+++ b/tools/idevicebackup2.c
@@ -0,0 +1,2910 @@
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 <stdarg.h>
33#include <stdlib.h>
34#include <signal.h>
35#include <unistd.h>
36#include <dirent.h>
37#include <libgen.h>
38#include <ctype.h>
39#include <time.h>
40#include <getopt.h>
41
42#include <libimobiledevice/libimobiledevice.h>
43#include <libimobiledevice/lockdown.h>
44#include <libimobiledevice/mobilebackup2.h>
45#include <libimobiledevice/notification_proxy.h>
46#include <libimobiledevice/afc.h>
47#include <libimobiledevice/installation_proxy.h>
48#include <libimobiledevice/sbservices.h>
49#include <libimobiledevice/diagnostics_relay.h>
50#include <libimobiledevice-glue/utils.h>
51#include <plist/plist.h>
52
53#include <endianness.h>
54
55#define LOCK_ATTEMPTS 50
56#define LOCK_WAIT 200000
57
58#ifdef _WIN32
59#include <windows.h>
60#include <conio.h>
61#define sleep(x) Sleep(x*1000)
62#ifndef ELOOP
63#define ELOOP 114
64#endif
65#else
66#include <termios.h>
67#include <sys/statvfs.h>
68#endif
69#include <sys/stat.h>
70
71#define CODE_SUCCESS 0x00
72#define CODE_ERROR_LOCAL 0x06
73#define CODE_ERROR_REMOTE 0x0b
74#define CODE_FILE_DATA 0x0c
75
76static int verbose = 1;
77static int quit_flag = 0;
78static int passcode_requested = 0;
79
80#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
81
82enum cmd_mode {
83 CMD_BACKUP,
84 CMD_RESTORE,
85 CMD_INFO,
86 CMD_LIST,
87 CMD_UNBACK,
88 CMD_CHANGEPW,
89 CMD_LEAVE,
90 CMD_CLOUD
91};
92
93enum cmd_flags {
94 CMD_FLAG_RESTORE_SYSTEM_FILES = (1 << 1),
95 CMD_FLAG_RESTORE_NO_REBOOT = (1 << 2),
96 CMD_FLAG_RESTORE_COPY_BACKUP = (1 << 3),
97 CMD_FLAG_RESTORE_SETTINGS = (1 << 4),
98 CMD_FLAG_RESTORE_REMOVE_ITEMS = (1 << 5),
99 CMD_FLAG_ENCRYPTION_ENABLE = (1 << 6),
100 CMD_FLAG_ENCRYPTION_DISABLE = (1 << 7),
101 CMD_FLAG_ENCRYPTION_CHANGEPW = (1 << 8),
102 CMD_FLAG_FORCE_FULL_BACKUP = (1 << 9),
103 CMD_FLAG_CLOUD_ENABLE = (1 << 10),
104 CMD_FLAG_CLOUD_DISABLE = (1 << 11),
105 CMD_FLAG_RESTORE_SKIP_APPS = (1 << 12)
106};
107
108static int backup_domain_changed = 0;
109
110static void notify_cb(const char *notification, void *userdata)
111{
112 if (strlen(notification) == 0) {
113 return;
114 }
115 if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
116 PRINT_VERBOSE(1, "User has cancelled the backup process on the device.\n");
117 quit_flag++;
118 } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
119 backup_domain_changed = 1;
120 } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.presented")) {
121 passcode_requested = 1;
122 } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.dismissed")) {
123 passcode_requested = 0;
124 } else {
125 PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
126 }
127}
128
129static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *filename, char **data, uint64_t *size)
130{
131 if (!afc || !data || !size) {
132 return;
133 }
134
135 plist_t fileinfo = NULL;
136 uint32_t fsize = 0;
137
138 afc_get_file_info_plist(afc, filename, &fileinfo);
139 if (!fileinfo) {
140 return;
141 }
142 fsize = plist_dict_get_uint(fileinfo, "st_size");
143 plist_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",
459#ifdef HAVE_PLIST_UNIX_DATE
460 plist_new_unix_date(time(NULL))
461#else
462 plist_new_date(time(NULL) - MAC_EPOCH, 0)
463#endif
464 );
465
466 value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
467 if (value_node)
468 plist_dict_set_item(ret, "MEID", plist_copy(value_node));
469
470 value_node = plist_dict_get_item(root_node, "PhoneNumber");
471 if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
472 plist_dict_set_item(ret, "Phone Number", plist_copy(value_node));
473 }
474
475 /* FIXME Product Name */
476
477 value_node = plist_dict_get_item(root_node, "ProductType");
478 plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
479
480 value_node = plist_dict_get_item(root_node, "ProductVersion");
481 plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
482
483 value_node = plist_dict_get_item(root_node, "SerialNumber");
484 plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
485
486 /* FIXME Sync Settings? */
487
488 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
489 plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
490
491 plist_dict_set_item(ret, "Target Type", plist_new_string("Device"));
492
493 /* uppercase */
494 udid_uppercase = string_toupper((char*)udid);
495 plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
496 free(udid_uppercase);
497
498 char *data_buf = NULL;
499 uint64_t data_size = 0;
500 mobilebackup_afc_get_file_contents(afc, "/Books/iBooksData2.plist", &data_buf, &data_size);
501 if (data_buf) {
502 plist_dict_set_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
503 free(data_buf);
504 }
505
506 plist_t files = plist_new_dict();
507 const char *itunesfiles[] = {
508 "ApertureAlbumPrefs",
509 "IC-Info.sidb",
510 "IC-Info.sidv",
511 "PhotosFolderAlbums",
512 "PhotosFolderName",
513 "PhotosFolderPrefs",
514 "VoiceMemos.plist",
515 "iPhotoAlbumPrefs",
516 "iTunesApplicationIDs",
517 "iTunesPrefs",
518 "iTunesPrefs.plist",
519 NULL
520 };
521 int i = 0;
522 for (i = 0; itunesfiles[i]; i++) {
523 data_buf = NULL;
524 data_size = 0;
525 char *fname = (char*)malloc(strlen("/iTunes_Control/iTunes/") + strlen(itunesfiles[i]) + 1);
526 strcpy(fname, "/iTunes_Control/iTunes/");
527 strcat(fname, itunesfiles[i]);
528 mobilebackup_afc_get_file_contents(afc, fname, &data_buf, &data_size);
529 free(fname);
530 if (data_buf) {
531 plist_dict_set_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
532 free(data_buf);
533 }
534 }
535 plist_dict_set_item(ret, "iTunes Files", files);
536
537 plist_dict_set_item(ret, "iTunes Settings", itunes_settings ? plist_copy(itunes_settings) : plist_new_dict());
538
539 /* since we usually don't have iTunes, let's get the minimum required iTunes version from the device */
540 if (min_itunes_version) {
541 plist_dict_set_item(ret, "iTunes Version", plist_copy(min_itunes_version));
542 } else {
543 plist_dict_set_item(ret, "iTunes Version", plist_new_string("10.0.1"));
544 }
545
546 plist_free(itunes_settings);
547 plist_free(min_itunes_version);
548 plist_free(root_node);
549
550 return ret;
551}
552
553static int write_restore_applications(plist_t info_plist, afc_client_t afc)
554{
555 int res = -1;
556 uint64_t restore_applications_file = 0;
557 char * applications_plist_xml = NULL;
558 uint32_t applications_plist_xml_length = 0;
559
560 plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
561 if (!applications_plist) {
562 printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
563 return 0;
564 }
565 plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
566 if (!applications_plist_xml) {
567 printf("Error preparing RestoreApplications.plist\n");
568 goto leave;
569 }
570
571 afc_error_t afc_err = 0;
572 afc_err = afc_make_directory(afc, "/iTunesRestore");
573 if (afc_err != AFC_E_SUCCESS) {
574 printf("Error creating directory /iTunesRestore, error code %d\n", afc_err);
575 goto leave;
576 }
577
578 afc_err = afc_file_open(afc, "/iTunesRestore/RestoreApplications.plist", AFC_FOPEN_WR, &restore_applications_file);
579 if (afc_err != AFC_E_SUCCESS || !restore_applications_file) {
580 printf("Error creating /iTunesRestore/RestoreApplications.plist, error code %d\n", afc_err);
581 goto leave;
582 }
583
584 uint32_t bytes_written = 0;
585 afc_err = afc_file_write(afc, restore_applications_file, applications_plist_xml, applications_plist_xml_length, &bytes_written);
586 if (afc_err != AFC_E_SUCCESS || bytes_written != applications_plist_xml_length) {
587 printf("Error writing /iTunesRestore/RestoreApplications.plist, error code %d, wrote %u of %u bytes\n", afc_err, bytes_written, applications_plist_xml_length);
588 goto leave;
589 }
590
591 afc_err = afc_file_close(afc, restore_applications_file);
592 restore_applications_file = 0;
593 if (afc_err != AFC_E_SUCCESS) {
594 goto leave;
595 }
596 /* success */
597 res = 0;
598
599leave:
600 free(applications_plist_xml);
601
602 if (restore_applications_file) {
603 afc_file_close(afc, restore_applications_file);
604 restore_applications_file = 0;
605 }
606
607 return res;
608}
609
610static int mb2_status_check_snapshot_state(const char *path, const char *udid, const char *matches)
611{
612 int ret = 0;
613 plist_t status_plist = NULL;
614 char *file_path = string_build_path(path, udid, "Status.plist", NULL);
615
616 plist_read_from_file(file_path, &status_plist, NULL);
617 free(file_path);
618 if (!status_plist) {
619 printf("Could not read Status.plist!\n");
620 return ret;
621 }
622 plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
623 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
624 char* sval = NULL;
625 plist_get_string_val(node, &sval);
626 if (sval) {
627 ret = (strcmp(sval, matches) == 0) ? 1 : 0;
628 free(sval);
629 }
630 } else {
631 printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
632 }
633 plist_free(status_plist);
634 return ret;
635}
636
637static void do_post_notification(idevice_t device, const char *notification)
638{
639 lockdownd_service_descriptor_t service = NULL;
640 np_client_t np;
641
642 lockdownd_client_t lockdown = NULL;
643
644 if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
645 return;
646 }
647
648 lockdownd_error_t ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
649 if (ldret == LOCKDOWN_E_SUCCESS) {
650 np_client_new(device, service, &np);
651 if (np) {
652 np_post_notification(np, notification);
653 np_client_free(np);
654 }
655 } else {
656 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
657 }
658
659 if (service) {
660 lockdownd_service_descriptor_free(service);
661 service = NULL;
662 }
663 lockdownd_client_free(lockdown);
664}
665
666/* ANSI escape codes for cursor manipulation */
667#define CURSOR_UP "\033[1A"
668#define CURSOR_DOWN "\033[1B"
669#define CURSOR_SAVE "\033[s"
670#define CURSOR_RESTORE "\033[u"
671#define CURSOR_HIDE "\033[?25l"
672#define CURSOR_SHOW "\033[?25h"
673#define CLEAR_LINE "\033[2K"
674#define MOVE_COL_1 "\r"
675
676#define TRANSFER_NONE 0
677#define TRANSFER_SEND 1
678#define TRANSFER_RECEIVE 2
679
680/* Progress state - 4-line reserved area display */
681static double overall_progress = 0;
682static double file_progress = 0;
683static uint64_t file_current = 0;
684static uint64_t file_total = 0;
685static int transfer_status = 0;
686static int progress_mode_active = 0;
687static char progress_status[256] = "";
688static char transfer_info[256] = "";
689
690static void draw_progress_bar(double percent, int width)
691{
692 int i;
693 double ratio = width / 100.0;
694 printf("[");
695 for (i = 0; i < width; i++) {
696 if (i < (int)(percent * ratio)) {
697 printf("#");
698 } else {
699 printf(".");
700 }
701 }
702 printf("]");
703}
704
705static void draw_transfer_bar(double percent, int width)
706{
707 int i;
708 int pos = (int)((percent / 100.0) * width);
709
710 if (pos >= width)
711 pos = width - 1;
712 if (pos < 0)
713 pos = 0;
714
715 printf("[");
716 for (i = 0; i < width; i++) {
717 if (percent >= 100.0) {
718 printf("=");
719 } else if (i < pos) {
720 printf("=");
721 } else if (i == pos) {
722 printf(">");
723 } else {
724 printf(" ");
725 }
726 }
727 printf("]");
728}
729
730/* Initialize the 4-line progress area */
731static void progress_init(void)
732{
733 if (!progress_mode_active) {
734 progress_mode_active = 1;
735 /* Use current line as line 1, and reserve 3 more lines */
736 printf("\n\n\n");
737 /* Save cursor position at line 4 */
738 printf(CURSOR_SAVE);
739 progress_status[0] = '\0';
740 transfer_info[0] = '\0';
741 }
742}
743
744/* Render the 4-line progress display using current global state */
745static void progress_render(void)
746{
747 const int bar_width = 30;
748 const char *direction_label = "Sending";
749 const char *display_name = transfer_info;
750 if (!progress_mode_active) {
751 progress_init();
752 }
753
754 /* Restore saved cursor position for line 4, then move to line 1 */
755 printf(CURSOR_RESTORE);
756 printf(CURSOR_UP CURSOR_UP CURSOR_UP);
757
758 if (transfer_status == TRANSFER_RECEIVE) {
759 direction_label = "Receiving";
760 } else if (transfer_status == TRANSFER_SEND) {
761 direction_label = "Sending";
762 } else {
763 direction_label = "";
764 }
765
766 /* Line 1: Overall backup progress */
767 printf(CLEAR_LINE MOVE_COL_1);
768 printf("%-10s ", "Backup");
769 draw_progress_bar(overall_progress, bar_width);
770 printf(" %3.0f%%", overall_progress);
771
772 /* Line 2: Current file */
773 printf("\n");
774 printf(CLEAR_LINE MOVE_COL_1);
775 printf("%-10s ", direction_label);
776 if (display_name[0]) {
777 //printf("%s%s", direction_arrow, display_name);
778 printf("%s", display_name);
779 }
780
781 /* Line 3: Sending / Receiving progress */
782 printf("\n");
783 printf(CLEAR_LINE MOVE_COL_1);
784 printf("%-10s ", "");
785 draw_transfer_bar(file_progress, bar_width);
786 printf(" %5.1f%%", file_progress);
787 if (file_current > 0 || file_total > 0) {
788 char *format_size_current = string_format_size(file_current);
789 char *format_size_total = string_format_size(file_total);
790 if (format_size_current && format_size_total) {
791 printf(" %s / %s", format_size_current, format_size_total);
792 }
793 free(format_size_current);
794 free(format_size_total);
795 }
796 printf(" ");
797
798 /* Line 4: Status message */
799 printf("\n");
800 printf(CLEAR_LINE MOVE_COL_1);
801 printf("%-10s ", "Status");
802 if (progress_status[0]) {
803 printf("%s", progress_status);
804 }
805
806 /* Save cursor position at line 4 again for the next redraw */
807 printf(CURSOR_SAVE);
808 fflush(stdout);
809}
810
811/*
812 * Print a normal log line without permanently corrupting the reserved
813 * 4-line progress area. If progress mode is active, print the log message
814 * below the progress block, reserve a fresh 4-line area there, and redraw.
815 */
816static void progress_printf(const char *fmt, ...)
817{
818 va_list ap;
819
820 if (progress_mode_active) {
821 /* Go to saved line 4 and move to a fresh line below the block */
822 printf(CURSOR_RESTORE);
823 printf("\n");
824 }
825
826 va_start(ap, fmt);
827 vprintf(fmt, ap);
828 va_end(ap);
829
830 if (progress_mode_active) {
831 /*
832 * Re-anchor the progress block on the current line:
833 * current line becomes line 1, plus 3 reserved lines below it.
834 */
835 printf("\n\n\n");
836 printf(CURSOR_SAVE);
837 progress_render();
838 }
839}
840
841#if 0
842/* Update progress values and render */
843static void progress_update(double overall, double file, uint64_t current, uint64_t total)
844{
845 overall_progress = overall;
846 file_progress = file;
847 file_current = current;
848 file_total = total;
849 progress_render();
850}
851
852/* Legacy single-line progress for simple operations */
853static void print_progress_real(double progress, int flush)
854{
855 int i = 0;
856 PRINT_VERBOSE(1, "\r[");
857 for(i = 0; i < 50; i++) {
858 if(i < progress / 2) {
859 PRINT_VERBOSE(1, "=");
860 } else {
861 PRINT_VERBOSE(1, " ");
862 }
863 }
864 PRINT_VERBOSE(1, "] %3.0f%%", progress);
865
866 if (flush > 0) {
867 fflush(stdout);
868 }
869 if (progress == 100) {
870 PRINT_VERBOSE(1, "\n");
871 }
872}
873#endif
874
875/* Called during file transfers - uses 3-line display */
876static void print_progress(uint64_t current, uint64_t total, int sending)
877{
878 //double file_progress = 0;
879 /* Don't update file progress if overall backup is complete */
880 //if (overall_progress >= 100.0)
881 // return;
882 file_current = current;
883 file_total = total;
884 if (total > 0) {
885 file_progress = ((double)current / (double)total) * 100;
886 if (file_progress > 100) file_progress = 100;
887 } else {
888 file_progress = 0;
889 }
890 //progress_update(overall_progress, file_progress, current, total);
891 transfer_status = sending;
892 progress_render();
893}
894
895/* Exit progress mode - print final newline */
896static void progress_finish(void)
897{
898 if (progress_mode_active) {
899 overall_progress = 100;
900 //transfer_status = TRANSFER_NONE;
901 progress_render();
902 progress_mode_active = 0;
903 printf("\n");
904 fflush(stdout);
905 progress_status[0] = '\0';
906 //transfer_info[0] = '\0';
907 }
908}
909
910#if 0
911static void mb2_set_overall_progress(double progress)
912{
913 if (progress > 0.0)
914 overall_progress = progress;
915}
916#endif
917
918static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
919{
920 plist_t node = NULL;
921 double progress = 0.0;
922
923 if (!strcmp(identifier, "DLMessageDownloadFiles")) {
924 node = plist_array_get_item(message, 3);
925 } else if (!strcmp(identifier, "DLMessageUploadFiles")) {
926 node = plist_array_get_item(message, 2);
927 } else if (!strcmp(identifier, "DLMessageMoveFiles") || !strcmp(identifier, "DLMessageMoveItems")) {
928 node = plist_array_get_item(message, 3);
929 } else if (!strcmp(identifier, "DLMessageRemoveFiles") || !strcmp(identifier, "DLMessageRemoveItems")) {
930 node = plist_array_get_item(message, 3);
931 }
932
933 if (node != NULL) {
934 plist_get_real_val(node, &progress);
935 //mb2_set_overall_progress(progress);
936 overall_progress = progress;
937 }
938}
939
940static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
941{
942 if (!status_dict) return;
943 plist_t filedict = plist_new_dict();
944 plist_dict_set_item(filedict, "DLFileErrorString", plist_new_string(error_message));
945 plist_dict_set_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
946 plist_dict_set_item(status_dict, path, filedict);
947}
948
949static int errno_to_device_error(int errno_value)
950{
951 switch (errno_value) {
952 case ENOENT:
953 return -6;
954 case EEXIST:
955 return -7;
956 case ENOTDIR:
957 return -8;
958 case EISDIR:
959 return -9;
960 case ELOOP:
961 return -10;
962 case EIO:
963 return -11;
964 case ENOSPC:
965 return -15;
966 default:
967 return -1;
968 }
969}
970
971static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char *backup_dir, const char *path, plist_t *errplist)
972{
973 uint32_t nlen = 0;
974 uint32_t pathlen = strlen(path);
975 uint32_t bytes = 0;
976 char *localfile = string_build_path(backup_dir, path, NULL);
977 char buf[32768];
978#ifdef _WIN32
979 struct _stati64 fst;
980#else
981 struct stat fst;
982#endif
983
984 FILE *f = NULL;
985 uint32_t slen = 0;
986 int errcode = -1;
987 int result = -1;
988 uint32_t length;
989#ifdef _WIN32
990 uint64_t total;
991 uint64_t sent;
992#else
993 off_t total;
994 off_t sent;
995#endif
996
997 mobilebackup2_error_t err;
998
999 /* send path length */
1000 nlen = htobe32(pathlen);
1001 err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), &bytes);
1002 if (err != MOBILEBACKUP2_E_SUCCESS) {
1003 goto leave_proto_err;
1004 }
1005 if (bytes != (uint32_t)sizeof(nlen)) {
1006 err = MOBILEBACKUP2_E_MUX_ERROR;
1007 goto leave_proto_err;
1008 }
1009
1010 /* send path */
1011 err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, &bytes);
1012 if (err != MOBILEBACKUP2_E_SUCCESS) {
1013 goto leave_proto_err;
1014 }
1015 if (bytes != pathlen) {
1016 err = MOBILEBACKUP2_E_MUX_ERROR;
1017 goto leave_proto_err;
1018 }
1019
1020#ifdef _WIN32
1021 if (_stati64(localfile, &fst) < 0)
1022#else
1023 if (stat(localfile, &fst) < 0)
1024#endif
1025 {
1026 if (errno != ENOENT)
1027 progress_printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
1028 errcode = errno;
1029 goto leave;
1030 }
1031
1032 total = fst.st_size;
1033
1034 //char *format_size = string_format_size(total);
1035 snprintf(transfer_info, sizeof(transfer_info), "%s", path); //"'%s' (%s)", path, format_size);
1036 //free(format_size);
1037 progress_render();
1038
1039 if (total == 0) {
1040 errcode = 0;
1041 goto leave;
1042 }
1043
1044 f = fopen(localfile, "rb");
1045 if (!f) {
1046 progress_printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
1047 errcode = errno;
1048 goto leave;
1049 }
1050
1051 sent = 0;
1052 do {
1053 length = ((total-sent) < (long long)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
1054 /* send data size (file size + 1) */
1055 nlen = htobe32(length+1);
1056 memcpy(buf, &nlen, sizeof(nlen));
1057 buf[4] = CODE_FILE_DATA;
1058 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, &bytes);
1059 if (err != MOBILEBACKUP2_E_SUCCESS) {
1060 goto leave_proto_err;
1061 }
1062 if (bytes != 5) {
1063 goto leave_proto_err;
1064 }
1065
1066 /* send file contents */
1067 size_t r = fread(buf, 1, sizeof(buf), f);
1068 if (r <= 0) {
1069 progress_printf("%s: read error\n", __func__);
1070 errcode = errno;
1071 goto leave;
1072 }
1073 err = mobilebackup2_send_raw(mobilebackup2, buf, r, &bytes);
1074 if (err != MOBILEBACKUP2_E_SUCCESS) {
1075 goto leave_proto_err;
1076 }
1077 if (bytes != (uint32_t)r) {
1078 progress_printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
1079 goto leave_proto_err;
1080 }
1081 sent += r;
1082 print_progress(sent, total, 1);
1083 } while (sent < total);
1084 fclose(f);
1085 f = NULL;
1086 errcode = 0;
1087
1088leave:
1089 if (errcode == 0) {
1090 result = 0;
1091 nlen = 1;
1092 nlen = htobe32(nlen);
1093 memcpy(buf, &nlen, 4);
1094 buf[4] = CODE_SUCCESS;
1095 mobilebackup2_send_raw(mobilebackup2, buf, 5, &bytes);
1096 } else {
1097 if (!*errplist) {
1098 *errplist = plist_new_dict();
1099 }
1100 char *errdesc = strerror(errcode);
1101 mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
1102
1103 length = strlen(errdesc);
1104 nlen = htobe32(length+1);
1105 memcpy(buf, &nlen, 4);
1106 buf[4] = CODE_ERROR_LOCAL;
1107 slen = 5;
1108 memcpy(buf+slen, errdesc, length);
1109 slen += length;
1110 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
1111 if (err != MOBILEBACKUP2_E_SUCCESS) {
1112 progress_printf("could not send message\n");
1113 }
1114 if (bytes != slen) {
1115 progress_printf("could only send %d from %d\n", bytes, slen);
1116 }
1117 }
1118
1119leave_proto_err:
1120 if (f)
1121 fclose(f);
1122 free(localfile);
1123 return result;
1124}
1125
1126static void mb2_handle_send_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1127{
1128 uint32_t cnt;
1129 uint32_t i = 0;
1130 uint32_t sent;
1131 plist_t errplist = NULL;
1132
1133 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
1134
1135 plist_t files = plist_array_get_item(message, 1);
1136 cnt = plist_array_get_size(files);
1137
1138 //snprintf(progress_status, sizeof(progress_status), "Sending files");
1139
1140 for (i = 0; i < cnt; i++) {
1141 plist_t val = plist_array_get_item(files, i);
1142 if (plist_get_node_type(val) != PLIST_STRING) {
1143 continue;
1144 }
1145 char *str = NULL;
1146 plist_get_string_val(val, &str);
1147 if (!str)
1148 continue;
1149
1150 if (mb2_handle_send_file(mobilebackup2, backup_dir, str, &errplist) < 0) {
1151 free(str);
1152 //printf("Error when sending file '%s' to device\n", str);
1153 // TODO: perhaps we can continue, we've got a multi status response?!
1154 break;
1155 }
1156 free(str);
1157 }
1158
1159 /* send terminating 0 dword */
1160 uint32_t zero = 0;
1161 mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
1162
1163 if (!errplist) {
1164 plist_t emptydict = plist_new_dict();
1165 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, emptydict);
1166 plist_free(emptydict);
1167 } else {
1168 mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
1169 plist_free(errplist);
1170 }
1171}
1172
1173static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** filename)
1174{
1175 uint32_t nlen = 0;
1176 uint32_t rlen = 0;
1177
1178 do {
1179 nlen = 0;
1180 rlen = 0;
1181 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &rlen);
1182 nlen = be32toh(nlen);
1183
1184 if ((nlen == 0) && (rlen == 4)) {
1185 // a zero length means no more files to receive
1186 return 0;
1187 }
1188 if (rlen == 0) {
1189 // device needs more time, waiting...
1190 continue;
1191 }
1192 if (nlen > 4096) {
1193 // filename length is too large
1194 progress_printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
1195 return 0;
1196 }
1197
1198 if (*filename != NULL) {
1199 free(*filename);
1200 *filename = NULL;
1201 }
1202
1203 *filename = (char*)malloc(nlen+1);
1204
1205 rlen = 0;
1206 mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
1207 if (rlen != nlen) {
1208 progress_printf("ERROR: %s: could not read filename\n", __func__);
1209 return 0;
1210 }
1211
1212 char* p = *filename;
1213 p[rlen] = 0;
1214
1215 break;
1216 } while(1 && !quit_flag);
1217
1218 return nlen;
1219}
1220
1221static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1222{
1223 uint64_t backup_real_size = 0;
1224 uint64_t backup_total_size = 0;
1225 uint32_t blocksize;
1226 uint32_t bdone;
1227 uint32_t rlen;
1228 uint32_t nlen = 0;
1229 uint32_t r;
1230 char buf[32768];
1231 char *fname = NULL;
1232 char *dname = NULL;
1233 char *bname = NULL;
1234 char code = 0;
1235 char last_code = 0;
1236 plist_t node = NULL;
1237 FILE *f = NULL;
1238 unsigned int file_count = 0;
1239 int errcode = 0;
1240 char *errdesc = NULL;
1241
1242 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
1243
1244 node = plist_array_get_item(message, 3);
1245 if (plist_get_node_type(node) == PLIST_UINT) {
1246 plist_get_uint_val(node, &backup_total_size);
1247 }
1248 if (backup_total_size > 0) {
1249 //progress_printf("Receiving files\n");
1250 snprintf(progress_status, sizeof(progress_status), "Receiving files"); /* Clear status when starting file transfers */
1251 //transfer_info[0] = '\0'; /* Clear transfer info */
1252 }
1253
1254 do {
1255 if (quit_flag)
1256 break;
1257
1258 nlen = mb2_receive_filename(mobilebackup2, &dname);
1259 if (nlen == 0) {
1260 break;
1261 }
1262 if (nlen > 68) {
1263 snprintf(transfer_info, sizeof(transfer_info), "...%s", dname + (nlen - 65));
1264 } else {
1265 snprintf(transfer_info, sizeof(transfer_info), "%s", dname);
1266 }
1267
1268 nlen = mb2_receive_filename(mobilebackup2, &fname);
1269 if (!nlen) {
1270 break;
1271 }
1272
1273 if (bname != NULL) {
1274 free(bname);
1275 bname = NULL;
1276 }
1277
1278 bname = string_build_path(backup_dir, fname, NULL);
1279
1280 if (fname != NULL) {
1281 free(fname);
1282 fname = NULL;
1283 }
1284
1285 r = 0;
1286 nlen = 0;
1287 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1288 if (r != 4) {
1289 progress_printf("ERROR: %s: could not receive code length!\n", __func__);
1290 break;
1291 }
1292 nlen = be32toh(nlen);
1293
1294 last_code = code;
1295 code = 0;
1296
1297 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1298 if (r != 1) {
1299 progress_printf("ERROR: %s: could not receive code!\n", __func__);
1300 break;
1301 }
1302
1303 /* TODO remove this */
1304 if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
1305 progress_printf("Found new flag %02x\n", code);
1306 }
1307
1308 remove_file(bname);
1309 f = fopen(bname, "wb");
1310 while (f && (code == CODE_FILE_DATA)) {
1311 blocksize = nlen-1;
1312 bdone = 0;
1313 rlen = 0;
1314 while (bdone < blocksize) {
1315 if ((blocksize - bdone) < sizeof(buf)) {
1316 rlen = blocksize - bdone;
1317 } else {
1318 rlen = sizeof(buf);
1319 }
1320 mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
1321 if ((int)r <= 0) {
1322 break;
1323 }
1324 fwrite(buf, 1, r, f);
1325 bdone += r;
1326 }
1327 if (bdone == blocksize) {
1328 backup_real_size += blocksize;
1329 }
1330 if (backup_total_size > 0) {
1331 print_progress(backup_real_size, backup_total_size, TRANSFER_RECEIVE);
1332 }
1333 if (quit_flag)
1334 break;
1335 nlen = 0;
1336 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1337 nlen = be32toh(nlen);
1338 if (nlen > 0) {
1339 last_code = code;
1340 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1341 } else {
1342 break;
1343 }
1344 }
1345 if (f) {
1346 fclose(f);
1347 file_count++;
1348 } else {
1349 errcode = errno_to_device_error(errno);
1350 errdesc = strerror(errno);
1351 progress_printf("Error opening '%s' for writing: %s\n", bname, errdesc);
1352 break;
1353 }
1354 if (nlen == 0) {
1355 break;
1356 }
1357
1358 /* check if an error message was received */
1359 if (code == CODE_ERROR_REMOTE) {
1360 /* error message */
1361 char *msg = (char*)malloc(nlen);
1362 mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
1363 msg[r] = 0;
1364 /* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
1365 if (last_code != CODE_FILE_DATA) {
1366 progress_printf("Received an error message from device: %s\n", msg);
1367 }
1368 free(msg);
1369 }
1370 } while (1);
1371
1372 if (fname != NULL)
1373 free(fname);
1374
1375 /* if there are leftovers to read, finish up cleanly */
1376 if ((int)nlen-1 > 0) {
1377 progress_printf("Discarding current data hunk.\n");
1378 fname = (char*)malloc(nlen-1);
1379 mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
1380 free(fname);
1381 remove_file(bname);
1382 }
1383
1384 /* clean up */
1385 if (bname != NULL)
1386 free(bname);
1387
1388 if (dname != NULL)
1389 free(dname);
1390
1391 plist_t empty_plist = plist_new_dict();
1392 mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_plist);
1393 plist_free(empty_plist);
1394
1395 return file_count;
1396}
1397
1398static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1399{
1400 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1401
1402 plist_t node = plist_array_get_item(message, 1);
1403 char *str = NULL;
1404 if (plist_get_node_type(node) == PLIST_STRING) {
1405 plist_get_string_val(node, &str);
1406 }
1407 if (!str) {
1408 printf("ERROR: Malformed DLContentsOfDirectory message\n");
1409 // TODO error handling
1410 return;
1411 }
1412
1413 char *path = string_build_path(backup_dir, str, NULL);
1414 free(str);
1415
1416 plist_t dirlist = plist_new_dict();
1417
1418 DIR* cur_dir = opendir(path);
1419 if (cur_dir) {
1420 struct dirent* ep;
1421 while ((ep = readdir(cur_dir))) {
1422 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1423 continue;
1424 }
1425 char *fpath = string_build_path(path, ep->d_name, NULL);
1426 if (fpath) {
1427 plist_t fdict = plist_new_dict();
1428 struct stat st;
1429 stat(fpath, &st);
1430 const char *ftype = "DLFileTypeUnknown";
1431 if (S_ISDIR(st.st_mode)) {
1432 ftype = "DLFileTypeDirectory";
1433 } else if (S_ISREG(st.st_mode)) {
1434 ftype = "DLFileTypeRegular";
1435 }
1436 plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
1437 plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
1438 plist_dict_set_item(fdict, "DLFileModificationDate",
1439#ifdef HAVE_PLIST_UNIX_DATE
1440 plist_new_unix_date(st.st_mtime)
1441#else
1442 plist_new_date(st.st_mtime - MAC_EPOCH, 0)
1443#endif
1444 );
1445
1446 plist_dict_set_item(dirlist, ep->d_name, fdict);
1447 free(fpath);
1448 }
1449 }
1450 closedir(cur_dir);
1451 }
1452 free(path);
1453
1454 /* TODO error handling */
1455 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, dirlist);
1456 plist_free(dirlist);
1457 if (err != MOBILEBACKUP2_E_SUCCESS) {
1458 printf("Could not send status response, error %d\n", err);
1459 }
1460}
1461
1462static void mb2_handle_make_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1463{
1464 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1465
1466 plist_t dir = plist_array_get_item(message, 1);
1467 char *str = NULL;
1468 int errcode = 0;
1469 char *errdesc = NULL;
1470 plist_get_string_val(dir, &str);
1471
1472 char *newpath = string_build_path(backup_dir, str, NULL);
1473 free(str);
1474
1475 if (mkdir_with_parents(newpath, 0755) < 0) {
1476 errdesc = strerror(errno);
1477 if (errno != EEXIST) {
1478 printf("mkdir: %s (%d)\n", errdesc, errno);
1479 }
1480 errcode = errno_to_device_error(errno);
1481 }
1482 free(newpath);
1483 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
1484 if (err != MOBILEBACKUP2_E_SUCCESS) {
1485 printf("Could not send status response, error %d\n", err);
1486 }
1487}
1488
1489static void mb2_copy_file_by_path(const char *src, const char *dst)
1490{
1491 FILE *from, *to;
1492 char buf[BUFSIZ];
1493 size_t length;
1494
1495 /* open source file */
1496 if ((from = fopen(src, "rb")) == NULL) {
1497 printf("Cannot open source path '%s'.\n", src);
1498 return;
1499 }
1500
1501 /* open destination file */
1502 if ((to = fopen(dst, "wb")) == NULL) {
1503 printf("Cannot open destination file '%s'.\n", dst);
1504 fclose(from);
1505 return;
1506 }
1507
1508 /* copy the file */
1509 while ((length = fread(buf, 1, BUFSIZ, from)) != 0) {
1510 fwrite(buf, 1, length, to);
1511 }
1512
1513 if(fclose(from) == EOF) {
1514 printf("Error closing source file.\n");
1515 }
1516
1517 if(fclose(to) == EOF) {
1518 printf("Error closing destination file.\n");
1519 }
1520}
1521
1522static void mb2_copy_directory_by_path(const char *src, const char *dst)
1523{
1524 if (!src || !dst) {
1525 return;
1526 }
1527
1528 struct stat st;
1529
1530 /* if src does not exist */
1531 if ((stat(src, &st) < 0) || !S_ISDIR(st.st_mode)) {
1532 printf("ERROR: Source directory does not exist '%s': %s (%d)\n", src, strerror(errno), errno);
1533 return;
1534 }
1535
1536 /* if dst directory does not exist */
1537 if ((stat(dst, &st) < 0) || !S_ISDIR(st.st_mode)) {
1538 /* create it */
1539 if (mkdir_with_parents(dst, 0755) < 0) {
1540 printf("ERROR: Unable to create destination directory '%s': %s (%d)\n", dst, strerror(errno), errno);
1541 return;
1542 }
1543 }
1544
1545 /* loop over src directory contents */
1546 DIR *cur_dir = opendir(src);
1547 if (cur_dir) {
1548 struct dirent* ep;
1549 while ((ep = readdir(cur_dir))) {
1550 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1551 continue;
1552 }
1553 char *srcpath = string_build_path(src, ep->d_name, NULL);
1554 char *dstpath = string_build_path(dst, ep->d_name, NULL);
1555 if (srcpath && dstpath) {
1556 /* copy file */
1557 mb2_copy_file_by_path(srcpath, dstpath);
1558 }
1559
1560 if (srcpath)
1561 free(srcpath);
1562 if (dstpath)
1563 free(dstpath);
1564 }
1565 closedir(cur_dir);
1566 }
1567}
1568
1569#ifdef _WIN32
1570#define BS_CC '\b'
1571#define my_getch getch
1572#else
1573#define BS_CC 0x7f
1574static int my_getch(void)
1575{
1576 struct termios oldt, newt;
1577 int ch;
1578 tcgetattr(STDIN_FILENO, &oldt);
1579 newt = oldt;
1580 newt.c_lflag &= ~(ICANON | ECHO);
1581 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1582 ch = getchar();
1583 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
1584 return ch;
1585}
1586#endif
1587
1588static void get_hidden_input(char *buf, int maxlen)
1589{
1590 int pwlen = 0;
1591 int c;
1592
1593 while ((c = my_getch())) {
1594 if ((c == '\r') || (c == '\n')) {
1595 break;
1596 }
1597 if (isprint(c)) {
1598 if (pwlen < maxlen-1)
1599 buf[pwlen++] = c;
1600 fputc('*', stderr);
1601 } else if (c == BS_CC) {
1602 if (pwlen > 0) {
1603 fputs("\b \b", stderr);
1604 pwlen--;
1605 }
1606 }
1607 }
1608 buf[pwlen] = 0;
1609}
1610
1611static char* ask_for_password(const char* msg, int type_again)
1612{
1613 char pwbuf[256];
1614
1615 fprintf(stderr, "%s: ", msg);
1616 fflush(stderr);
1617 get_hidden_input(pwbuf, 256);
1618 fputc('\n', stderr);
1619
1620 if (type_again) {
1621 char pwrep[256];
1622
1623 fprintf(stderr, "%s (repeat): ", msg);
1624 fflush(stderr);
1625 get_hidden_input(pwrep, 256);
1626 fputc('\n', stderr);
1627
1628 if (strcmp(pwbuf, pwrep) != 0) {
1629 printf("ERROR: passwords don't match\n");
1630 return NULL;
1631 }
1632 }
1633 return strdup(pwbuf);
1634}
1635
1636/**
1637 * signal handler function for cleaning up properly
1638 */
1639static void clean_exit(int sig)
1640{
1641 snprintf(progress_status, sizeof(progress_status), "Abort requested, hold on...");
1642 progress_render();
1643 //fprintf(stderr, "Exiting...\n");
1644 quit_flag++;
1645}
1646
1647static void print_usage(int argc, char **argv, int is_error)
1648{
1649 char *name = strrchr(argv[0], '/');
1650 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
1651 fprintf(is_error ? stderr : stdout,
1652 "\n"
1653 "Create or restore backup in/from the specified directory.\n"
1654 "\n"
1655 "CMD:\n"
1656 " backup create backup for the device\n"
1657 " --full force full backup from device.\n"
1658 " restore restore last backup to the device\n"
1659 " --system restore system files, too.\n"
1660 " --no-reboot do NOT reboot the device when done (default: yes).\n"
1661 " --copy create a copy of backup folder before restoring.\n"
1662 " --settings restore device settings from the backup.\n"
1663 " --remove remove items which are not being restored\n"
1664 " --skip-apps do not trigger re-installation of apps after restore\n"
1665 " --password PWD supply the password for the encrypted source backup\n"
1666 " info show details about last completed backup of device\n"
1667 " list list files of last completed backup in CSV format\n"
1668 " unback unpack a completed backup in DIRECTORY/_unback_/\n"
1669 " encryption on|off [PWD] enable or disable backup encryption\n"
1670 " changepw [OLD NEW] change backup password on target device\n"
1671 " cloud on|off enable or disable cloud use (requires iCloud account)\n"
1672 "\n"
1673 "NOTE: Passwords will be requested in interactive mode (-i) if omitted, or can\n"
1674 "be passed via environment variable BACKUP_PASSWORD/BACKUP_PASSWORD_NEW.\n"
1675 "See man page for further details.\n"
1676 "\n"
1677 "OPTIONS:\n"
1678 " -u, --udid UDID target specific device by UDID\n"
1679 " -s, --source UDID use backup data from device specified by UDID\n"
1680 " -n, --network connect to network device\n"
1681 " -i, --interactive request passwords interactively\n"
1682 " -d, --debug enable communication debugging\n"
1683 " -h, --help prints usage information\n"
1684 " -v, --version prints version information\n"
1685 "\n"
1686 "Homepage: <" PACKAGE_URL ">\n"
1687 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
1688 );
1689}
1690
1691int main(int argc, char *argv[])
1692{
1693 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
1694 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
1695 int i = 0;
1696 char* udid = NULL;
1697 char* source_udid = NULL;
1698 int use_network = 0;
1699 lockdownd_service_descriptor_t service = NULL;
1700 int cmd = -1;
1701 int cmd_flags = 0;
1702 int is_full_backup = 0;
1703 int result_code = -1;
1704 char* backup_directory = NULL;
1705 int interactive_mode = 0;
1706 char* backup_password = NULL;
1707 char* newpw = NULL;
1708 struct stat st;
1709 plist_t node_tmp = NULL;
1710 plist_t info_plist = NULL;
1711 plist_t opts = NULL;
1712
1713 idevice_t device = NULL;
1714 afc_client_t afc = NULL;
1715 np_client_t np = NULL;
1716 lockdownd_client_t lockdown = NULL;
1717 mobilebackup2_client_t mobilebackup2 = NULL;
1718 mobilebackup2_error_t err;
1719 uint64_t lockfile = 0;
1720
1721#define OPT_SYSTEM 1
1722#define OPT_REBOOT 2
1723#define OPT_NO_REBOOT 3
1724#define OPT_COPY 4
1725#define OPT_SETTINGS 5
1726#define OPT_REMOVE 6
1727#define OPT_SKIP_APPS 7
1728#define OPT_PASSWORD 8
1729#define OPT_FULL 9
1730
1731 int c = 0;
1732 const struct option longopts[] = {
1733 { "debug", no_argument, NULL, 'd' },
1734 { "help", no_argument, NULL, 'h' },
1735 { "udid", required_argument, NULL, 'u' },
1736 { "source", required_argument, NULL, 's' },
1737 { "interactive", no_argument, NULL, 'i' },
1738 { "network", no_argument, NULL, 'n' },
1739 { "version", no_argument, NULL, 'v' },
1740 // command options:
1741 { "system", no_argument, NULL, OPT_SYSTEM },
1742 { "reboot", no_argument, NULL, OPT_REBOOT },
1743 { "no-reboot", no_argument, NULL, OPT_NO_REBOOT },
1744 { "copy", no_argument, NULL, OPT_COPY },
1745 { "settings", no_argument, NULL, OPT_SETTINGS },
1746 { "remove", no_argument, NULL, OPT_REMOVE },
1747 { "skip-apps", no_argument, NULL, OPT_SKIP_APPS },
1748 { "password", required_argument, NULL, OPT_PASSWORD },
1749 { "full", no_argument, NULL, OPT_FULL },
1750 { NULL, 0, NULL, 0}
1751 };
1752
1753 /* we need to exit cleanly on running backups and restores or we cause havok */
1754 signal(SIGINT, clean_exit);
1755 signal(SIGTERM, clean_exit);
1756#ifndef _WIN32
1757 signal(SIGQUIT, clean_exit);
1758 signal(SIGPIPE, SIG_IGN);
1759#endif
1760
1761 /* parse cmdline args */
1762 while ((c = getopt_long(argc, argv, "dhu:s:inv", longopts, NULL)) != -1) {
1763 switch (c) {
1764 case 'd':
1765 idevice_set_debug_level(1);
1766 break;
1767 case 'u':
1768 if (!*optarg) {
1769 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
1770 print_usage(argc, argv, 1);
1771 return 2;
1772 }
1773 udid = strdup(optarg);
1774 break;
1775 case 's':
1776 if (!*optarg) {
1777 fprintf(stderr, "ERROR: SOURCE argument must not be empty!\n");
1778 print_usage(argc, argv, 1);
1779 return 2;
1780 }
1781 source_udid = strdup(optarg);
1782 break;
1783 case 'i':
1784 interactive_mode = 1;
1785 break;
1786 case 'n':
1787 use_network = 1;
1788 break;
1789 case 'h':
1790 print_usage(argc, argv, 0);
1791 return 0;
1792 case 'v':
1793 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
1794 return 0;
1795 case OPT_SYSTEM:
1796 cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
1797 break;
1798 case OPT_REBOOT:
1799 cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT;
1800 break;
1801 case OPT_NO_REBOOT:
1802 cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT;
1803 break;
1804 case OPT_COPY:
1805 cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
1806 break;
1807 case OPT_SETTINGS:
1808 cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
1809 break;
1810 case OPT_REMOVE:
1811 cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
1812 break;
1813 case OPT_SKIP_APPS:
1814 cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
1815 break;
1816 case OPT_PASSWORD:
1817 free(backup_password);
1818 backup_password = strdup(optarg);
1819 break;
1820 case OPT_FULL:
1821 cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
1822 break;
1823 default:
1824 print_usage(argc, argv, 1);
1825 return 2;
1826 }
1827 }
1828 argc -= optind;
1829 argv += optind;
1830
1831 if (!argv[0]) {
1832 fprintf(stderr, "ERROR: No command specified.\n");
1833 print_usage(argc+optind, argv-optind, 1);
1834 return 2;
1835 }
1836
1837 if (!strcmp(argv[0], "backup")) {
1838 cmd = CMD_BACKUP;
1839 }
1840 else if (!strcmp(argv[0], "restore")) {
1841 cmd = CMD_RESTORE;
1842 }
1843 else if (!strcmp(argv[0], "cloud")) {
1844 cmd = CMD_CLOUD;
1845 i = 1;
1846 if (!argv[i]) {
1847 fprintf(stderr, "ERROR: No argument given for cloud command; requires either 'on' or 'off'.\n");
1848 print_usage(argc+optind, argv-optind, 1);
1849 return 2;
1850 }
1851 if (!strcmp(argv[i], "on")) {
1852 cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
1853 } else if (!strcmp(argv[i], "off")) {
1854 cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
1855 } else {
1856 fprintf(stderr, "ERROR: Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
1857 print_usage(argc+optind, argv-optind, 1);
1858 return 2;
1859 }
1860 }
1861 else if (!strcmp(argv[0], "info")) {
1862 cmd = CMD_INFO;
1863 verbose = 0;
1864 }
1865 else if (!strcmp(argv[0], "list")) {
1866 cmd = CMD_LIST;
1867 verbose = 0;
1868 }
1869 else if (!strcmp(argv[0], "unback")) {
1870 cmd = CMD_UNBACK;
1871 }
1872 else if (!strcmp(argv[0], "encryption")) {
1873 cmd = CMD_CHANGEPW;
1874 i = 1;
1875 if (!argv[i]) {
1876 fprintf(stderr, "ERROR: No argument given for encryption command; requires either 'on' or 'off'.\n");
1877 print_usage(argc+optind, argv-optind, 1);
1878 return 2;
1879 }
1880 if (!strcmp(argv[i], "on")) {
1881 cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
1882 } else if (!strcmp(argv[i], "off")) {
1883 cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
1884 } else {
1885 fprintf(stderr, "ERROR: Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
1886 print_usage(argc+optind, argv-optind, 1);
1887 return 2;
1888 }
1889 // check if a password was given on the command line
1890 free(newpw);
1891 newpw = NULL;
1892 free(backup_password);
1893 backup_password = NULL;
1894 i++;
1895 if (argv[i]) {
1896 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
1897 newpw = strdup(argv[i]);
1898 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
1899 backup_password = strdup(argv[i]);
1900 }
1901 }
1902 }
1903 else if (!strcmp(argv[0], "changepw")) {
1904 cmd = CMD_CHANGEPW;
1905 cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
1906 // check if passwords were given on command line
1907 free(newpw);
1908 newpw = NULL;
1909 free(backup_password);
1910 backup_password = NULL;
1911 i = 1;
1912 if (argv[i]) {
1913 backup_password = strdup(argv[i]);
1914 i++;
1915 if (!argv[i]) {
1916 fprintf(stderr, "ERROR: Old and new passwords have to be passed as arguments for the changepw command\n");
1917 print_usage(argc+optind, argv-optind, 1);
1918 return 2;
1919 }
1920 newpw = strdup(argv[i]);
1921 }
1922 }
1923
1924 i++;
1925 if (argv[i]) {
1926 backup_directory = argv[i];
1927 }
1928
1929 /* verify options */
1930 if (cmd == -1) {
1931 fprintf(stderr, "ERROR: Unsupported command '%s'.\n", argv[0]);
1932 print_usage(argc+optind, argv-optind, 1);
1933 return 2;
1934 }
1935
1936 if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
1937 backup_directory = (char*)".this_folder_is_not_present_on_purpose";
1938 } else {
1939 if (backup_directory == NULL) {
1940 fprintf(stderr, "ERROR: No target backup directory specified.\n");
1941 print_usage(argc+optind, argv-optind, 1);
1942 return 2;
1943 }
1944
1945 /* verify if passed backup directory exists */
1946 if (stat(backup_directory, &st) != 0) {
1947 fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
1948 return -1;
1949 }
1950 }
1951
1952 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
1953 if (ret != IDEVICE_E_SUCCESS) {
1954 if (udid) {
1955 printf("No device found with udid %s.\n", udid);
1956 } else {
1957 printf("No device found.\n");
1958 }
1959 return -1;
1960 }
1961
1962 if (!udid) {
1963 idevice_get_udid(device, &udid);
1964 }
1965
1966 if (!source_udid) {
1967 source_udid = strdup(udid);
1968 }
1969
1970 uint8_t is_encrypted = 0;
1971 char *info_path = NULL;
1972 if (cmd == CMD_CHANGEPW) {
1973 if (!interactive_mode) {
1974 if (!newpw) {
1975 newpw = getenv("BACKUP_PASSWORD_NEW");
1976 if (newpw) {
1977 newpw = strdup(newpw);
1978 }
1979 }
1980 if (!backup_password) {
1981 backup_password = getenv("BACKUP_PASSWORD");
1982 if (backup_password) {
1983 backup_password = strdup(backup_password);
1984 }
1985 }
1986 }
1987 if (!interactive_mode && !backup_password && !newpw) {
1988 idevice_free(device);
1989 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");
1990 return -1;
1991 }
1992 } else if (cmd != CMD_CLOUD) {
1993 /* backup directory must contain an Info.plist */
1994 info_path = string_build_path(backup_directory, source_udid, "Info.plist", NULL);
1995 if (cmd == CMD_RESTORE || cmd == CMD_UNBACK) {
1996 if (stat(info_path, &st) != 0) {
1997 idevice_free(device);
1998 free(info_path);
1999 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, source_udid);
2000 return -1;
2001 }
2002 char* manifest_path = string_build_path(backup_directory, source_udid, "Manifest.plist", NULL);
2003 if (stat(manifest_path, &st) != 0) {
2004 free(info_path);
2005 }
2006 plist_t manifest_plist = NULL;
2007 plist_read_from_file(manifest_path, &manifest_plist, NULL);
2008 if (!manifest_plist) {
2009 idevice_free(device);
2010 free(info_path);
2011 free(manifest_path);
2012 printf("ERROR: Backup directory \"%s\" is invalid. No Manifest.plist found for UDID %s.\n", backup_directory, source_udid);
2013 return -1;
2014 }
2015 node_tmp = plist_dict_get_item(manifest_plist, "IsEncrypted");
2016 if (node_tmp && (plist_get_node_type(node_tmp) == PLIST_BOOLEAN)) {
2017 plist_get_bool_val(node_tmp, &is_encrypted);
2018 }
2019 plist_free(manifest_plist);
2020 free(manifest_path);
2021 }
2022 PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
2023 }
2024
2025 if (cmd != CMD_CLOUD && is_encrypted) {
2026 PRINT_VERBOSE(1, "This is an encrypted backup.\n");
2027 if (backup_password == NULL) {
2028 backup_password = getenv("BACKUP_PASSWORD");
2029 if (backup_password) {
2030 backup_password = strdup(backup_password);
2031 }
2032 }
2033 if (backup_password == NULL) {
2034 if (interactive_mode) {
2035 backup_password = ask_for_password("Enter backup password", 0);
2036 }
2037 if (!backup_password || (strlen(backup_password) == 0)) {
2038 if (backup_password) {
2039 free(backup_password);
2040 }
2041 idevice_free(device);
2042 if (cmd == CMD_RESTORE) {
2043 printf("ERROR: a backup password is required to restore an encrypted backup. Cannot continue.\n");
2044 } else if (cmd == CMD_UNBACK) {
2045 printf("ERROR: a backup password is required to unback an encrypted backup. Cannot continue.\n");
2046 }
2047 return -1;
2048 }
2049 }
2050 }
2051
2052 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
2053 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
2054 idevice_free(device);
2055 return -1;
2056 }
2057
2058 uint8_t willEncrypt = 0;
2059 node_tmp = NULL;
2060 lockdownd_get_value(lockdown, "com.apple.mobile.backup", "WillEncrypt", &node_tmp);
2061 if (node_tmp) {
2062 if (plist_get_node_type(node_tmp) == PLIST_BOOLEAN) {
2063 plist_get_bool_val(node_tmp, &willEncrypt);
2064 }
2065 plist_free(node_tmp);
2066 node_tmp = NULL;
2067 }
2068
2069 /* get ProductVersion */
2070 int device_version = idevice_get_device_version(device);
2071
2072 /* start notification_proxy */
2073 ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
2074 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
2075 np_client_new(device, service, &np);
2076 np_set_notify_callback(np, notify_cb, NULL);
2077 const char *noties[7] = {
2078 NP_SYNC_CANCEL_REQUEST,
2079 NP_SYNC_SUSPEND_REQUEST,
2080 NP_SYNC_RESUME_REQUEST,
2081 NP_BACKUP_DOMAIN_CHANGED,
2082 "com.apple.LocalAuthentication.ui.presented",
2083 "com.apple.LocalAuthentication.ui.dismissed",
2084 NULL
2085 };
2086 np_observe_notifications(np, noties);
2087 } else {
2088 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
2089 cmd = CMD_LEAVE;
2090 goto checkpoint;
2091 }
2092 if (service) {
2093 lockdownd_service_descriptor_free(service);
2094 service = NULL;
2095 }
2096
2097 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
2098 /* start AFC, we need this for the lock file */
2099 ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);
2100 if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
2101 afc_client_new(device, service, &afc);
2102 } else {
2103 printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
2104 cmd = CMD_LEAVE;
2105 goto checkpoint;
2106 }
2107 }
2108
2109 if (service) {
2110 lockdownd_service_descriptor_free(service);
2111 service = NULL;
2112 }
2113
2114 /* start mobilebackup service and retrieve port */
2115 ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
2116 lockdownd_client_free(lockdown);
2117 lockdown = NULL;
2118 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
2119 PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
2120 mobilebackup2_client_new(device, service, &mobilebackup2);
2121
2122 if (service) {
2123 lockdownd_service_descriptor_free(service);
2124 service = NULL;
2125 }
2126
2127 /* send Hello message */
2128 double local_versions[2] = {2.0, 2.1};
2129 double remote_version = 0.0;
2130 err = mobilebackup2_version_exchange(mobilebackup2, local_versions, 2, &remote_version);
2131 if (err != MOBILEBACKUP2_E_SUCCESS) {
2132 printf("Could not perform backup protocol version exchange, error code %d\n", err);
2133 cmd = CMD_LEAVE;
2134 goto checkpoint;
2135 }
2136
2137 PRINT_VERBOSE(1, "Negotiated Protocol Version %.1f\n", remote_version);
2138
2139 /* check abort conditions */
2140 if (quit_flag > 0) {
2141 PRINT_VERBOSE(1, "Aborting as requested by user...\n");
2142 cmd = CMD_LEAVE;
2143 goto checkpoint;
2144 }
2145
2146 /* verify existing Info.plist */
2147 if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {
2148 PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
2149 plist_read_from_file(info_path, &info_plist, NULL);
2150
2151 if (!info_plist) {
2152 printf("Could not read Info.plist\n");
2153 is_full_backup = 1;
2154 }
2155 } else {
2156 if (cmd == CMD_RESTORE) {
2157 printf("Aborting restore. Info.plist is missing.\n");
2158 cmd = CMD_LEAVE;
2159 } else {
2160 is_full_backup = 1;
2161 }
2162 }
2163
2164 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
2165 do_post_notification(device, NP_SYNC_WILL_START);
2166 afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
2167 }
2168 if (lockfile) {
2169 afc_error_t aerr;
2170 do_post_notification(device, NP_SYNC_LOCK_REQUEST);
2171 for (i = 0; i < LOCK_ATTEMPTS; i++) {
2172 aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
2173 if (aerr == AFC_E_SUCCESS) {
2174 do_post_notification(device, NP_SYNC_DID_START);
2175 break;
2176 }
2177 if (aerr == AFC_E_OP_WOULD_BLOCK) {
2178 usleep(LOCK_WAIT);
2179 continue;
2180 }
2181
2182 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
2183 afc_file_close(afc, lockfile);
2184 lockfile = 0;
2185 cmd = CMD_LEAVE;
2186 }
2187 if (i == LOCK_ATTEMPTS) {
2188 fprintf(stderr, "ERROR: timeout while locking for sync\n");
2189 afc_file_close(afc, lockfile);
2190 lockfile = 0;
2191 cmd = CMD_LEAVE;
2192 }
2193 }
2194
2195checkpoint:
2196
2197 switch(cmd) {
2198 case CMD_CLOUD:
2199 opts = plist_new_dict();
2200 plist_dict_set_item(opts, "CloudBackupState", plist_new_bool(cmd_flags & CMD_FLAG_CLOUD_ENABLE ? 1: 0));
2201 err = mobilebackup2_send_request(mobilebackup2, "EnableCloudBackup", udid, source_udid, opts);
2202 plist_free(opts);
2203 opts = NULL;
2204 if (err != MOBILEBACKUP2_E_SUCCESS) {
2205 printf("Error setting cloud backup state on device, error code %d\n", err);
2206 cmd = CMD_LEAVE;
2207 }
2208 break;
2209 case CMD_BACKUP:
2210 PRINT_VERBOSE(1, "Starting backup...\n");
2211
2212 /* make sure backup device sub-directory exists */
2213 char* devbackupdir = string_build_path(backup_directory, source_udid, NULL);
2214 __mkdir(devbackupdir, 0755);
2215 free(devbackupdir);
2216
2217 if (strcmp(source_udid, udid) != 0) {
2218 /* handle different source backup directory */
2219 // make sure target backup device sub-directory exists
2220 devbackupdir = string_build_path(backup_directory, udid, NULL);
2221 __mkdir(devbackupdir, 0755);
2222 free(devbackupdir);
2223
2224 // use Info.plist path in target backup folder */
2225 free(info_path);
2226 info_path = string_build_path(backup_directory, udid, "Info.plist", NULL);
2227 }
2228
2229 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
2230 /* TODO: verify battery on AC enough battery remaining */
2231
2232 /* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
2233 if (info_plist) {
2234 plist_free(info_plist);
2235 info_plist = NULL;
2236 }
2237 info_plist = mobilebackup_factory_info_plist_new(udid, device, afc);
2238 if (!info_plist) {
2239 fprintf(stderr, "Failed to generate Info.plist - aborting\n");
2240 cmd = CMD_LEAVE;
2241 }
2242 remove_file(info_path);
2243 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
2244 free(info_path);
2245
2246 plist_free(info_plist);
2247 info_plist = NULL;
2248
2249 if (cmd_flags & CMD_FLAG_FORCE_FULL_BACKUP) {
2250 PRINT_VERBOSE(1, "Enforcing full backup from device.\n");
2251 opts = plist_new_dict();
2252 plist_dict_set_item(opts, "ForceFullBackup", plist_new_bool(1));
2253 }
2254 /* request backup from device with manifest from last backup */
2255 if (willEncrypt) {
2256 PRINT_VERBOSE(1, "Backup will be encrypted.\n");
2257 } else {
2258 PRINT_VERBOSE(1, "Backup will be unencrypted.\n");
2259 }
2260 PRINT_VERBOSE(1, "Requesting backup from device...\n");
2261 err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, source_udid, opts);
2262 if (opts)
2263 plist_free(opts);
2264 if (err == MOBILEBACKUP2_E_SUCCESS) {
2265 if (is_full_backup) {
2266 PRINT_VERBOSE(1, "Full backup mode.\n");
2267 } else {
2268 PRINT_VERBOSE(1, "Incremental backup mode.\n");
2269 }
2270 if (device_version >= IDEVICE_DEVICE_VERSION(16,1,0)) {
2271 /* let's wait 2 second to see if the device passcode is requested */
2272 int retries = 20;
2273 while (retries-- > 0 && !passcode_requested) {
2274 usleep(100000);
2275 }
2276 if (passcode_requested) {
2277 printf("*** Waiting for passcode to be entered on the device ***\n");
2278 }
2279 }
2280 } else {
2281 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2282 printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
2283 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2284 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
2285 } else {
2286 printf("ERROR: Could not start backup process: unspecified error occurred\n");
2287 }
2288 cmd = CMD_LEAVE;
2289 }
2290 break;
2291 case CMD_RESTORE:
2292 /* TODO: verify battery on AC enough battery remaining */
2293
2294 /* verify if Status.plist says we read from an successful backup */
2295 if (!mb2_status_check_snapshot_state(backup_directory, source_udid, "finished")) {
2296 printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
2297 cmd = CMD_LEAVE;
2298 break;
2299 }
2300
2301 PRINT_VERBOSE(1, "Starting Restore...\n");
2302
2303 opts = plist_new_dict();
2304 plist_dict_set_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
2305 PRINT_VERBOSE(1, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "Yes":"No"));
2306 if (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT)
2307 plist_dict_set_item(opts, "RestoreShouldReboot", plist_new_bool(0));
2308 PRINT_VERBOSE(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT ? "No":"Yes"));
2309 if ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0)
2310 plist_dict_set_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
2311 PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
2312 plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
2313 PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
2314 plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS));
2315 PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
2316 if (backup_password != NULL) {
2317 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2318 }
2319 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2320
2321 if (cmd_flags & CMD_FLAG_RESTORE_SKIP_APPS) {
2322 PRINT_VERBOSE(1, "Not writing RestoreApplications.plist - apps will not be re-installed after restore\n");
2323 } else {
2324 /* Write /iTunesRestore/RestoreApplications.plist so that the device will start
2325 * restoring applications once the rest of the restore process is finished */
2326 if (write_restore_applications(info_plist, afc) < 0) {
2327 cmd = CMD_LEAVE;
2328 break;
2329 }
2330 PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
2331 }
2332
2333 /* Start restore */
2334 err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, source_udid, opts);
2335 plist_free(opts);
2336 if (err != MOBILEBACKUP2_E_SUCCESS) {
2337 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2338 printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
2339 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2340 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
2341 } else {
2342 printf("ERROR: Could not start restore process: unspecified error occurred\n");
2343 }
2344 cmd = CMD_LEAVE;
2345 }
2346 break;
2347 case CMD_INFO:
2348 PRINT_VERBOSE(1, "Requesting backup info from device...\n");
2349 err = mobilebackup2_send_request(mobilebackup2, "Info", udid, source_udid, NULL);
2350 if (err != MOBILEBACKUP2_E_SUCCESS) {
2351 printf("Error requesting backup info from device, error code %d\n", err);
2352 cmd = CMD_LEAVE;
2353 }
2354 break;
2355 case CMD_LIST:
2356 PRINT_VERBOSE(1, "Requesting backup list from device...\n");
2357 err = mobilebackup2_send_request(mobilebackup2, "List", udid, source_udid, NULL);
2358 if (err != MOBILEBACKUP2_E_SUCCESS) {
2359 printf("Error requesting backup list from device, error code %d\n", err);
2360 cmd = CMD_LEAVE;
2361 }
2362 break;
2363 case CMD_UNBACK:
2364 PRINT_VERBOSE(1, "Starting to unpack backup...\n");
2365 if (backup_password != NULL) {
2366 opts = plist_new_dict();
2367 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2368 }
2369 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2370 err = mobilebackup2_send_request(mobilebackup2, "Unback", udid, source_udid, opts);
2371 if (backup_password !=NULL) {
2372 plist_free(opts);
2373 }
2374 if (err != MOBILEBACKUP2_E_SUCCESS) {
2375 printf("Error requesting unback operation from device, error code %d\n", err);
2376 cmd = CMD_LEAVE;
2377 }
2378 break;
2379 case CMD_CHANGEPW:
2380 opts = plist_new_dict();
2381 plist_dict_set_item(opts, "TargetIdentifier", plist_new_string(udid));
2382 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2383 if (!willEncrypt) {
2384 if (!newpw) {
2385 newpw = getenv("BACKUP_PASSWORD");
2386 if (newpw) {
2387 newpw = strdup(newpw);
2388 }
2389 }
2390 if (!newpw) {
2391 newpw = ask_for_password("Enter new backup password", 1);
2392 }
2393 if (!newpw) {
2394 printf("No backup password given. Aborting.\n");
2395 }
2396 } else {
2397 printf("ERROR: Backup encryption is already enabled. Aborting.\n");
2398 cmd = CMD_LEAVE;
2399 if (newpw) {
2400 free(newpw);
2401 newpw = NULL;
2402 }
2403 }
2404 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2405 if (willEncrypt) {
2406 if (!backup_password) {
2407 backup_password = getenv("BACKUP_PASSWORD");
2408 if (backup_password) {
2409 backup_password = strdup(backup_password);
2410 }
2411 }
2412 if (!backup_password) {
2413 backup_password = ask_for_password("Enter current backup password", 0);
2414 }
2415 } else {
2416 printf("ERROR: Backup encryption is not enabled. Aborting.\n");
2417 cmd = CMD_LEAVE;
2418 if (backup_password) {
2419 free(backup_password);
2420 backup_password = NULL;
2421 }
2422 }
2423 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2424 if (willEncrypt) {
2425 if (!backup_password) {
2426 backup_password = ask_for_password("Enter old backup password", 0);
2427 newpw = ask_for_password("Enter new backup password", 1);
2428 }
2429 } else {
2430 printf("ERROR: Backup encryption is not enabled so can't change password. Aborting.\n");
2431 cmd = CMD_LEAVE;
2432 if (newpw) {
2433 free(newpw);
2434 newpw = NULL;
2435 }
2436 if (backup_password) {
2437 free(backup_password);
2438 backup_password = NULL;
2439 }
2440 }
2441 }
2442 if (newpw) {
2443 plist_dict_set_item(opts, "NewPassword", plist_new_string(newpw));
2444 }
2445 if (backup_password) {
2446 plist_dict_set_item(opts, "OldPassword", plist_new_string(backup_password));
2447 }
2448 if (newpw || backup_password) {
2449 mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
2450 uint8_t passcode_hint = 0;
2451 if (device_version >= IDEVICE_DEVICE_VERSION(13,0,0)) {
2452 diagnostics_relay_client_t diag = NULL;
2453 if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2454 plist_t dict = NULL;
2455 plist_t keys = plist_new_array();
2456 plist_array_append_item(keys, plist_new_string("PasswordConfigured"));
2457 if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2458 plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured");
2459 plist_get_bool_val(node, &passcode_hint);
2460 }
2461 plist_free(keys);
2462 plist_free(dict);
2463 diagnostics_relay_goodbye(diag);
2464 diagnostics_relay_client_free(diag);
2465 }
2466 }
2467 if (passcode_hint) {
2468 if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2469 PRINT_VERBOSE(1, "Please confirm changing the backup password by entering the passcode on the device.\n");
2470 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2471 PRINT_VERBOSE(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n");
2472 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2473 PRINT_VERBOSE(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n");
2474 }
2475 }
2476 /*if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2477 int retr = 10;
2478 while ((retr-- >= 0) && !backup_domain_changed) {
2479 sleep(1);
2480 }
2481 }*/
2482 } else {
2483 cmd = CMD_LEAVE;
2484 }
2485 plist_free(opts);
2486 break;
2487 default:
2488 break;
2489 }
2490
2491 if (cmd != CMD_LEAVE) {
2492 /* reset operation success status */
2493 int operation_ok = 0;
2494 plist_t message = NULL;
2495
2496 mobilebackup2_error_t mberr;
2497 char *dlmsg = NULL;
2498 int file_count = 0;
2499 int errcode = 0;
2500 const char *errdesc = NULL;
2501 int progress_finished = 0;
2502
2503 /* process series of DLMessage* operations */
2504 do {
2505 free(dlmsg);
2506 dlmsg = NULL;
2507 mberr = mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
2508 if (mberr == MOBILEBACKUP2_E_RECEIVE_TIMEOUT) {
2509 PRINT_VERBOSE(2, "Device is not ready yet, retrying...\n");
2510 goto files_out;
2511 } else if (mberr != MOBILEBACKUP2_E_SUCCESS) {
2512 PRINT_VERBOSE(0, "ERROR: Could not receive from mobilebackup2 (%d)\n", mberr);
2513 quit_flag++;
2514 goto files_out;
2515 }
2516
2517 if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
2518 /* device wants to download files from the computer */
2519 mb2_set_overall_progress_from_message(message, dlmsg);
2520 mb2_handle_send_files(mobilebackup2, message, backup_directory);
2521 //snprintf(progress_status, sizeof(progress_status), "Waiting for device...");
2522 //progress_render();
2523 } else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
2524 /* device wants to send files to the computer */
2525 mb2_set_overall_progress_from_message(message, dlmsg);
2526 file_count += mb2_handle_receive_files(mobilebackup2, message, backup_directory);
2527 } else if (!strcmp(dlmsg, "DLMessageGetFreeDiskSpace")) {
2528 /* device wants to know how much disk space is available on the computer */
2529 uint64_t freespace = 0;
2530 int res = -1;
2531#ifdef _WIN32
2532 if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
2533 res = 0;
2534 }
2535#else
2536 struct statvfs fs;
2537 memset(&fs, '\0', sizeof(fs));
2538 res = statvfs(backup_directory, &fs);
2539 if (res == 0) {
2540 freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize;
2541 }
2542#endif
2543 plist_t freespace_item = plist_new_uint(freespace);
2544 mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);
2545 plist_free(freespace_item);
2546 } else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) {
2547 /* device wants to purge disk space on the host - not supported */
2548 plist_t empty_dict = plist_new_dict();
2549 err = mobilebackup2_send_status_response(mobilebackup2, -1, "Operation not supported", empty_dict);
2550 plist_free(empty_dict);
2551 } else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
2552 /* list directory contents */
2553 mb2_handle_list_directory(mobilebackup2, message, backup_directory);
2554 } else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
2555 /* make a directory */
2556 mb2_handle_make_directory(mobilebackup2, message, backup_directory);
2557 } else if (!strcmp(dlmsg, "DLMessageMoveFiles") || !strcmp(dlmsg, "DLMessageMoveItems")) {
2558 /* perform a series of rename operations */
2559 mb2_set_overall_progress_from_message(message, dlmsg);
2560 plist_t moves = plist_array_get_item(message, 1);
2561 uint32_t cnt = plist_dict_get_size(moves);
2562 snprintf(progress_status, sizeof(progress_status), "Moving %d file%s", cnt, (cnt == 1) ? "" : "s");
2563 progress_render();
2564 plist_dict_iter iter = NULL;
2565 plist_dict_new_iter(moves, &iter);
2566 errcode = 0;
2567 errdesc = NULL;
2568 if (iter) {
2569 char *key = NULL;
2570 plist_t val = NULL;
2571 do {
2572 plist_dict_next_item(moves, iter, &key, &val);
2573 if (key && (plist_get_node_type(val) == PLIST_STRING)) {
2574 char *str = NULL;
2575 plist_get_string_val(val, &str);
2576 if (str) {
2577 char *newpath = string_build_path(backup_directory, str, NULL);
2578 free(str);
2579 char *oldpath = string_build_path(backup_directory, key, NULL);
2580
2581 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode))
2582 rmdir_recursive(newpath);
2583 else
2584 remove_file(newpath);
2585 if (rename(oldpath, newpath) < 0) {
2586 printf("Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
2587 errcode = errno_to_device_error(errno);
2588 errdesc = strerror(errno);
2589 break;
2590 }
2591 free(oldpath);
2592 free(newpath);
2593 }
2594 free(key);
2595 key = NULL;
2596 }
2597 } while (val);
2598 free(iter);
2599 } else {
2600 errcode = -1;
2601 errdesc = "Could not create dict iterator";
2602 printf("Could not create dict iterator\n");
2603 }
2604 plist_t empty_dict = plist_new_dict();
2605 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2606 plist_free(empty_dict);
2607 if (err != MOBILEBACKUP2_E_SUCCESS) {
2608 printf("Could not send status response, error %d\n", err);
2609 }
2610 } else if (!strcmp(dlmsg, "DLMessageRemoveFiles") || !strcmp(dlmsg, "DLMessageRemoveItems")) {
2611 mb2_set_overall_progress_from_message(message, dlmsg);
2612 plist_t removes = plist_array_get_item(message, 1);
2613 uint32_t cnt = plist_array_get_size(removes);
2614 snprintf(progress_status, sizeof(progress_status), "Removing %d file%s", cnt, (cnt == 1) ? "" : "s");
2615 progress_render();
2616 uint32_t ii = 0;
2617 errcode = 0;
2618 errdesc = NULL;
2619 for (ii = 0; ii < cnt; ii++) {
2620 plist_t val = plist_array_get_item(removes, ii);
2621 if (plist_get_node_type(val) == PLIST_STRING) {
2622 char *str = NULL;
2623 plist_get_string_val(val, &str);
2624 if (str) {
2625 const char *checkfile = strchr(str, '/');
2626 int suppress_warning = 0;
2627 if (checkfile) {
2628 if (strcmp(checkfile+1, "Manifest.mbdx") == 0) {
2629 suppress_warning = 1;
2630 }
2631 }
2632 char *newpath = string_build_path(backup_directory, str, NULL);
2633 free(str);
2634 int res = 0;
2635 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2636 res = rmdir_recursive(newpath);
2637 } else {
2638 res = remove_file(newpath);
2639 }
2640 if (res != 0 && res != ENOENT) {
2641 if (!suppress_warning)
2642 printf("Could not remove '%s': %s (%d)\n", newpath, strerror(res), res);
2643 errcode = errno_to_device_error(res);
2644 errdesc = strerror(res);
2645 }
2646 free(newpath);
2647 }
2648 }
2649 }
2650 plist_t empty_dict = plist_new_dict();
2651 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2652 plist_free(empty_dict);
2653 if (err != MOBILEBACKUP2_E_SUCCESS) {
2654 printf("Could not send status response, error %d\n", err);
2655 }
2656 } else if (!strcmp(dlmsg, "DLMessageCopyItem")) {
2657 plist_t srcpath = plist_array_get_item(message, 1);
2658 plist_t dstpath = plist_array_get_item(message, 2);
2659 errcode = 0;
2660 errdesc = NULL;
2661 if ((plist_get_node_type(srcpath) == PLIST_STRING) && (plist_get_node_type(dstpath) == PLIST_STRING)) {
2662 char *src = NULL;
2663 char *dst = NULL;
2664 plist_get_string_val(srcpath, &src);
2665 plist_get_string_val(dstpath, &dst);
2666 if (src && dst) {
2667 char *oldpath = string_build_path(backup_directory, src, NULL);
2668 char *newpath = string_build_path(backup_directory, dst, NULL);
2669
2670 PRINT_VERBOSE(1, "Copying '%s' to '%s'\n", src, dst);
2671
2672 /* check that src exists */
2673 if ((stat(oldpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2674 mb2_copy_directory_by_path(oldpath, newpath);
2675 } else if ((stat(oldpath, &st) == 0) && S_ISREG(st.st_mode)) {
2676 mb2_copy_file_by_path(oldpath, newpath);
2677 }
2678
2679 free(newpath);
2680 free(oldpath);
2681 }
2682 free(src);
2683 free(dst);
2684 }
2685 plist_t empty_dict = plist_new_dict();
2686 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2687 plist_free(empty_dict);
2688 if (err != MOBILEBACKUP2_E_SUCCESS) {
2689 printf("Could not send status response, error %d\n", err);
2690 }
2691 } else if (!strcmp(dlmsg, "DLMessageDisconnect")) {
2692 break;
2693 } else if (!strcmp(dlmsg, "DLMessageProcessMessage")) {
2694 node_tmp = plist_array_get_item(message, 1);
2695 if (plist_get_node_type(node_tmp) != PLIST_DICT) {
2696 printf("Unknown message received!\n");
2697 }
2698 plist_t nn;
2699 int error_code = -1;
2700 nn = plist_dict_get_item(node_tmp, "ErrorCode");
2701 if (nn && (plist_get_node_type(nn) == PLIST_UINT)) {
2702 uint64_t ec = 0;
2703 plist_get_uint_val(nn, &ec);
2704 error_code = (uint32_t)ec;
2705 if (error_code == 0) {
2706 operation_ok = 1;
2707 result_code = 0;
2708 } else {
2709 result_code = -error_code;
2710 }
2711 }
2712 nn = plist_dict_get_item(node_tmp, "ErrorDescription");
2713 char *str = NULL;
2714 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2715 plist_get_string_val(nn, &str);
2716 }
2717 if (error_code != 0) {
2718 if (str) {
2719 printf("ErrorCode %d: %s\n", error_code, str);
2720 } else {
2721 printf("ErrorCode %d: (Unknown)\n", error_code);
2722 }
2723 }
2724 if (str) {
2725 free(str);
2726 }
2727 nn = plist_dict_get_item(node_tmp, "Content");
2728 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2729 str = NULL;
2730 plist_get_string_val(nn, &str);
2731 PRINT_VERBOSE(1, "Content:\n");
2732 printf("%s", str);
2733 free(str);
2734 }
2735 break;
2736 }
2737
2738 /* print status */
2739 if ((overall_progress > 0) && !progress_finished) {
2740 if (overall_progress >= 100.0F) {
2741 progress_finished = 1;
2742 //progress_finish();
2743 //} else {
2744 // print_progress_real(overall_progress, 0);
2745 }
2746 }
2747
2748files_out:
2749 plist_free(message);
2750 message = NULL;
2751 free(dlmsg);
2752 dlmsg = NULL;
2753
2754 if (quit_flag > 0) {
2755 /* need to cancel the backup here */
2756 //mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
2757
2758 /* remove any atomic Manifest.plist.tmp */
2759
2760 /*manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
2761 if (stat(manifest_path, &st) == 0)
2762 remove(manifest_path);*/
2763 break;
2764 }
2765 } while (1);
2766
2767 plist_free(message);
2768 free(dlmsg);
2769
2770 progress_finish();
2771
2772 /* report operation status to user */
2773 switch (cmd) {
2774 case CMD_CLOUD:
2775 if (cmd_flags & CMD_FLAG_CLOUD_ENABLE) {
2776 if (operation_ok) {
2777 PRINT_VERBOSE(1, "Cloud backup has been enabled successfully.\n");
2778 } else {
2779 PRINT_VERBOSE(1, "Could not enable cloud backup.\n");
2780 }
2781 } else if (cmd_flags & CMD_FLAG_CLOUD_DISABLE) {
2782 if (operation_ok) {
2783 PRINT_VERBOSE(1, "Cloud backup has been disabled successfully.\n");
2784 } else {
2785 PRINT_VERBOSE(1, "Could not disable cloud backup.\n");
2786 }
2787 }
2788 break;
2789 case CMD_BACKUP:
2790 PRINT_VERBOSE(1, "Received %d files from device.\n", file_count);
2791 if (operation_ok && mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
2792 PRINT_VERBOSE(1, "Backup Successful.\n");
2793 } else {
2794 if (quit_flag) {
2795 PRINT_VERBOSE(1, "Backup Aborted.\n");
2796 } else {
2797 PRINT_VERBOSE(1, "Backup Failed (Error Code %d).\n", -result_code);
2798 }
2799 }
2800 break;
2801 case CMD_UNBACK:
2802 if (quit_flag) {
2803 PRINT_VERBOSE(1, "Unback Aborted.\n");
2804 } else {
2805 PRINT_VERBOSE(1, "The files can now be found in the \"_unback_\" directory.\n");
2806 PRINT_VERBOSE(1, "Unback Successful.\n");
2807 }
2808 break;
2809 case CMD_CHANGEPW:
2810 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2811 if (operation_ok) {
2812 PRINT_VERBOSE(1, "Backup encryption has been enabled successfully.\n");
2813 } else {
2814 PRINT_VERBOSE(1, "Could not enable backup encryption.\n");
2815 }
2816 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2817 if (operation_ok) {
2818 PRINT_VERBOSE(1, "Backup encryption has been disabled successfully.\n");
2819 } else {
2820 PRINT_VERBOSE(1, "Could not disable backup encryption.\n");
2821 }
2822 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2823 if (operation_ok) {
2824 PRINT_VERBOSE(1, "Backup encryption password has been changed successfully.\n");
2825 } else {
2826 PRINT_VERBOSE(1, "Could not change backup encryption password.\n");
2827 }
2828 }
2829 break;
2830 case CMD_RESTORE:
2831 if (operation_ok) {
2832 if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
2833 PRINT_VERBOSE(1, "The device should reboot now.\n");
2834 PRINT_VERBOSE(1, "Restore Successful.\n");
2835 } else {
2836 afc_remove_path(afc, "/iTunesRestore/RestoreApplications.plist");
2837 afc_remove_path(afc, "/iTunesRestore");
2838 if (quit_flag) {
2839 PRINT_VERBOSE(1, "Restore Aborted.\n");
2840 } else {
2841 PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code);
2842 }
2843 }
2844 break;
2845 case CMD_INFO:
2846 case CMD_LIST:
2847 case CMD_LEAVE:
2848 default:
2849 if (quit_flag) {
2850 PRINT_VERBOSE(1, "Operation Aborted.\n");
2851 } else if (cmd == CMD_LEAVE) {
2852 PRINT_VERBOSE(1, "Operation Failed.\n");
2853 } else {
2854 PRINT_VERBOSE(1, "Operation Successful.\n");
2855 }
2856 break;
2857 }
2858 }
2859 if (lockfile) {
2860 afc_file_lock(afc, lockfile, AFC_LOCK_UN);
2861 afc_file_close(afc, lockfile);
2862 lockfile = 0;
2863 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE)
2864 do_post_notification(device, NP_SYNC_DID_FINISH);
2865 }
2866 } else {
2867 printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));
2868 lockdownd_client_free(lockdown);
2869 lockdown = NULL;
2870 }
2871
2872 if (lockdown) {
2873 lockdownd_client_free(lockdown);
2874 lockdown = NULL;
2875 }
2876
2877 if (mobilebackup2) {
2878 mobilebackup2_client_free(mobilebackup2);
2879 mobilebackup2 = NULL;
2880 }
2881
2882 if (afc) {
2883 afc_client_free(afc);
2884 afc = NULL;
2885 }
2886
2887 if (np) {
2888 np_client_free(np);
2889 np = NULL;
2890 }
2891
2892 idevice_free(device);
2893 device = NULL;
2894
2895 if (backup_password) {
2896 free(backup_password);
2897 }
2898
2899 if (udid) {
2900 free(udid);
2901 udid = NULL;
2902 }
2903 if (source_udid) {
2904 free(source_udid);
2905 source_udid = NULL;
2906 }
2907
2908 return result_code;
2909}
2910
diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c
new file mode 100644
index 0000000..ca68b59
--- /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..b9869ae
--- /dev/null
+++ b/tools/idevicecrashreport.c
@@ -0,0 +1,545 @@
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#include <sys/stat.h>
35#ifndef _WIN32
36#include <signal.h>
37#endif
38#include <libimobiledevice-glue/utils.h>
39
40#include <libimobiledevice/libimobiledevice.h>
41#include <libimobiledevice/lockdown.h>
42#include <libimobiledevice/service.h>
43#include <libimobiledevice/afc.h>
44#include <plist/plist.h>
45
46#ifdef _WIN32
47#include <windows.h>
48#define S_IFLNK S_IFREG
49#define S_IFSOCK S_IFREG
50#endif
51
52#define CRASH_REPORT_MOVER_SERVICE "com.apple.crashreportmover"
53#define CRASH_REPORT_COPY_MOBILE_SERVICE "com.apple.crashreportcopymobile"
54
55const char* target_directory = NULL;
56static int extract_raw_crash_reports = 0;
57static int keep_crash_reports = 0;
58static int remove_all = 0;
59
60static int file_exists(const char* path)
61{
62 struct stat tst;
63#ifdef _WIN32
64 return (stat(path, &tst) == 0);
65#else
66 return (lstat(path, &tst) == 0);
67#endif
68}
69
70static int extract_raw_crash_report(const char* filename)
71{
72 int res = 0;
73 plist_t report = NULL;
74 char* raw = NULL;
75 char* raw_filename = strdup(filename);
76
77 /* create filename with '.crash' extension */
78 char* p = strrchr(raw_filename, '.');
79 if ((p == NULL) || (strcmp(p, ".plist") != 0)) {
80 free(raw_filename);
81 return res;
82 }
83 strcpy(p, ".crash");
84
85 /* read plist crash report */
86 if (plist_read_from_file(filename, &report, NULL)) {
87 plist_t description_node = plist_dict_get_item(report, "description");
88 if (description_node && plist_get_node_type(description_node) == PLIST_STRING) {
89 plist_get_string_val(description_node, &raw);
90
91 if (raw != NULL) {
92 /* write file */
93 buffer_write_to_filename(raw_filename, raw, strlen(raw));
94 free(raw);
95 res = 1;
96 }
97 }
98 }
99
100 if (report)
101 plist_free(report);
102
103 if (raw_filename)
104 free(raw_filename);
105
106 return res;
107}
108
109static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char* device_directory, const char* host_directory, const char* filename_filter)
110{
111 afc_error_t afc_error;
112 int k;
113 int res = -1;
114 int crash_report_count = 0;
115 uint64_t handle;
116 char source_filename[512];
117 char target_filename[512];
118
119 if (!afc)
120 return res;
121
122 char** list = NULL;
123 afc_error = afc_read_directory(afc, device_directory, &list);
124 if (afc_error != AFC_E_SUCCESS) {
125 fprintf(stderr, "ERROR: Could not read device directory '%s'\n", device_directory);
126 return res;
127 }
128
129 /* ensure we have a trailing slash */
130 strcpy(source_filename, device_directory);
131 if (source_filename[strlen(source_filename)-1] != '/') {
132 strcat(source_filename, "/");
133 }
134 int device_directory_length = strlen(source_filename);
135
136 /* ensure we have a trailing slash */
137 strcpy(target_filename, host_directory);
138 if (target_filename[strlen(target_filename)-1] != '/') {
139 strcat(target_filename, "/");
140 }
141 int host_directory_length = strlen(target_filename);
142
143 /* loop over file entries */
144 for (k = 0; list[k]; k++) {
145 if (!strcmp(list[k], ".") || !strcmp(list[k], "..")) {
146 continue;
147 }
148
149 plist_t fileinfo = NULL;
150 struct stat stbuf;
151 memset(&stbuf, '\0', sizeof(struct stat));
152
153 /* assemble absolute source filename */
154 strcpy(((char*)source_filename) + device_directory_length, list[k]);
155
156 /* assemble absolute target filename */
157#ifdef _WIN32
158 /* replace every ':' with '-' since ':' is an illegal character for file names in windows */
159 char* current_pos = strchr(list[k], ':');
160 while (current_pos) {
161 *current_pos = '-';
162 current_pos = strchr(current_pos, ':');
163 }
164#endif
165 char* p = strrchr(list[k], '.');
166 if (p != NULL && !strncmp(p, ".synced", 7)) {
167 /* make sure to strip ".synced" extension as seen on iOS 5 */
168 size_t newlen = p - list[k];
169 strncpy(((char*)target_filename) + host_directory_length, list[k], newlen);
170 target_filename[host_directory_length + newlen] = '\0';
171 } else {
172 strcpy(((char*)target_filename) + host_directory_length, list[k]);
173 }
174
175 /* get file information */
176 afc_get_file_info_plist(afc, source_filename, &fileinfo);
177 if (!fileinfo) {
178 printf("Failed to read information for '%s'. Skipping...\n", source_filename);
179 continue;
180 }
181
182 /* parse file information */
183 stbuf.st_size = plist_dict_get_uint(fileinfo, "st_size");
184 const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(fileinfo, "st_ifmt"), NULL);
185 if (s_ifmt) {
186 if (!strcmp(s_ifmt, "S_IFREG")) {
187 stbuf.st_mode = S_IFREG;
188 } else if (!strcmp(s_ifmt, "S_IFDIR")) {
189 stbuf.st_mode = S_IFDIR;
190 } else if (!strcmp(s_ifmt, "S_IFLNK")) {
191 stbuf.st_mode = S_IFLNK;
192 } else if (!strcmp(s_ifmt, "S_IFBLK")) {
193 stbuf.st_mode = S_IFBLK;
194 } else if (!strcmp(s_ifmt, "S_IFCHR")) {
195 stbuf.st_mode = S_IFCHR;
196 } else if (!strcmp(s_ifmt, "S_IFIFO")) {
197 stbuf.st_mode = S_IFIFO;
198 } else if (!strcmp(s_ifmt, "S_IFSOCK")) {
199 stbuf.st_mode = S_IFSOCK;
200 }
201 }
202 stbuf.st_nlink = plist_dict_get_uint(fileinfo, "st_nlink");
203 stbuf.st_mtime = (time_t)(plist_dict_get_uint(fileinfo, "st_mtime") / 1000000000);
204 const char* linktarget = plist_get_string_ptr(plist_dict_get_item(fileinfo, "LinkTarget"), NULL);
205 if (linktarget && !remove_all) {
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 const char* b = strrchr(linktarget, '/');
217 if (b == NULL) {
218 b = linktarget;
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 /* free file information */
236 plist_free(fileinfo);
237
238 /* recurse into child directories */
239 if (S_ISDIR(stbuf.st_mode)) {
240 if (!remove_all) {
241#ifdef _WIN32
242 mkdir(target_filename);
243#else
244 mkdir(target_filename, 0755);
245#endif
246 }
247 res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename, filename_filter);
248
249 /* remove directory from device */
250 if (!remove_all && !keep_crash_reports)
251 afc_remove_path(afc, source_filename);
252 } else if (S_ISREG(stbuf.st_mode)) {
253 if (filename_filter != NULL && strstr(source_filename, filename_filter) == NULL) {
254 continue;
255 }
256
257 if (remove_all) {
258 printf("Remove: %s\n", source_filename);
259 afc_remove_path(afc, source_filename);
260 continue;
261 }
262
263 /* copy file to host */
264 afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle);
265 if(afc_error != AFC_E_SUCCESS) {
266 if (afc_error == AFC_E_OBJECT_NOT_FOUND) {
267 continue;
268 }
269 fprintf(stderr, "Unable to open device file '%s' (%d). Skipping...\n", source_filename, afc_error);
270 continue;
271 }
272
273 FILE* output = fopen(target_filename, "wb");
274 if(output == NULL) {
275 fprintf(stderr, "Unable to open local file '%s'. Skipping...\n", target_filename);
276 afc_file_close(afc, handle);
277 continue;
278 }
279
280 printf("%s: %s\n", (keep_crash_reports ? "Copy": "Move") , (char*)target_filename + strlen(target_directory));
281
282 uint32_t bytes_read = 0;
283 uint32_t bytes_total = 0;
284 unsigned char data[0x1000];
285
286 afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
287 while(afc_error == AFC_E_SUCCESS && bytes_read > 0) {
288 fwrite(data, 1, bytes_read, output);
289 bytes_total += bytes_read;
290 afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
291 }
292 afc_file_close(afc, handle);
293 fclose(output);
294
295 if ((uint32_t)stbuf.st_size != bytes_total) {
296 fprintf(stderr, "File size mismatch. Skipping...\n");
297 continue;
298 }
299
300 /* remove file from device */
301 if (!keep_crash_reports) {
302 afc_remove_path(afc, source_filename);
303 }
304
305 /* extract raw crash information into separate '.crash' file */
306 if (extract_raw_crash_reports) {
307 extract_raw_crash_report(target_filename);
308 }
309
310 crash_report_count++;
311
312 res = 0;
313 }
314 }
315 afc_dictionary_free(list);
316
317 /* no reports, no error */
318 if (crash_report_count == 0)
319 res = 0;
320
321 return res;
322}
323
324static void print_usage(int argc, char **argv, int is_error)
325{
326 char *name = strrchr(argv[0], '/');
327 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
328 fprintf(is_error ? stderr : stdout,
329 "\n"
330 "Move crash reports from device to a local DIRECTORY.\n"
331 "\n"
332 "OPTIONS:\n"
333 " -u, --udid UDID target specific device by UDID\n"
334 " -n, --network connect to network device\n"
335 " -e, --extract extract raw crash report into separate '.crash' file\n"
336 " -k, --keep copy but do not remove crash reports from device\n"
337 " -d, --debug enable communication debugging\n"
338 " -f, --filter NAME filter crash reports by NAME (case sensitive)\n"
339 " -h, --help prints usage information\n"
340 " -v, --version prints version information\n"
341 " --remove-all remove all crash logs found\n"
342 "\n"
343 "Homepage: <" PACKAGE_URL ">\n"
344 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
345 );
346}
347
348int main(int argc, char* argv[])
349{
350 idevice_t device = NULL;
351 lockdownd_client_t lockdownd = NULL;
352 afc_client_t afc = NULL;
353
354 idevice_error_t device_error = IDEVICE_E_SUCCESS;
355 lockdownd_error_t lockdownd_error = LOCKDOWN_E_SUCCESS;
356 afc_error_t afc_error = AFC_E_SUCCESS;
357
358 const char* udid = NULL;
359 int use_network = 0;
360 const char* filename_filter = NULL;
361
362 int c = 0;
363 const struct option longopts[] = {
364 { "debug", no_argument, NULL, 'd' },
365 { "help", no_argument, NULL, 'h' },
366 { "udid", required_argument, NULL, 'u' },
367 { "network", no_argument, NULL, 'n' },
368 { "version", no_argument, NULL, 'v' },
369 { "filter", required_argument, NULL, 'f' },
370 { "extract", no_argument, NULL, 'e' },
371 { "keep", no_argument, NULL, 'k' },
372 { "remove-all", no_argument, NULL, 1 },
373 { NULL, 0, NULL, 0}
374 };
375
376#ifndef _WIN32
377 signal(SIGPIPE, SIG_IGN);
378#endif
379
380 /* parse cmdline args */
381 while ((c = getopt_long(argc, argv, "dhu:nvf:ek", longopts, NULL)) != -1) {
382 switch (c) {
383 case 'd':
384 idevice_set_debug_level(1);
385 break;
386 case 'u':
387 if (!*optarg) {
388 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
389 print_usage(argc, argv, 1);
390 return 2;
391 }
392 udid = optarg;
393 break;
394 case 'n':
395 use_network = 1;
396 break;
397 case 'h':
398 print_usage(argc, argv, 0);
399 return 0;
400 case 'v':
401 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
402 return 0;
403 case 'f':
404 if (!*optarg) {
405 fprintf(stderr, "ERROR: filter argument must not be empty!\n");
406 print_usage(argc, argv, 1);
407 return 2;
408 }
409 filename_filter = optarg;
410 break;
411 case 'e':
412 extract_raw_crash_reports = 1;
413 break;
414 case 'k':
415 keep_crash_reports = 1;
416 break;
417 case 1:
418 remove_all = 1;
419 break;
420 default:
421 print_usage(argc, argv, 1);
422 return 2;
423 }
424 }
425 argc -= optind;
426 argv += optind;
427
428 /* ensure a target directory was supplied */
429 if (!remove_all) {
430 if (!argv[0]) {
431 fprintf(stderr, "ERROR: missing target directory.\n");
432 print_usage(argc+optind, argv-optind, 1);
433 return 2;
434 }
435 target_directory = argv[0];
436 } else {
437 target_directory = ".";
438 }
439
440 /* check if target directory exists */
441 if (!file_exists(target_directory)) {
442 fprintf(stderr, "ERROR: Directory '%s' does not exist.\n", target_directory);
443 return 1;
444 }
445
446 device_error = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
447 if (device_error != IDEVICE_E_SUCCESS) {
448 if (udid) {
449 printf("No device found with udid %s.\n", udid);
450 } else {
451 printf("No device found.\n");
452 }
453 return -1;
454 }
455
456 lockdownd_error = lockdownd_client_new_with_handshake(device, &lockdownd, TOOL_NAME);
457 if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
458 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lockdownd_error);
459 idevice_free(device);
460 return -1;
461 }
462
463 /* start crash log mover service */
464 lockdownd_service_descriptor_t service = NULL;
465 lockdownd_error = lockdownd_start_service(lockdownd, CRASH_REPORT_MOVER_SERVICE, &service);
466 if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
467 fprintf(stderr, "ERROR: Could not start service %s: %s\n", CRASH_REPORT_MOVER_SERVICE, lockdownd_strerror(lockdownd_error));
468 lockdownd_client_free(lockdownd);
469 idevice_free(device);
470 return -1;
471 }
472
473 /* trigger move operation on device */
474 service_client_t svcmove = NULL;
475 service_error_t service_error = service_client_new(device, service, &svcmove);
476 lockdownd_service_descriptor_free(service);
477 service = NULL;
478 if (service_error != SERVICE_E_SUCCESS) {
479 lockdownd_client_free(lockdownd);
480 idevice_free(device);
481 return -1;
482 }
483
484 /* read "ping" message which indicates the crash logs have been moved to a safe harbor */
485 char *ping = malloc(4);
486 memset(ping, '\0', 4);
487 int attempts = 0;
488 while ((strncmp(ping, "ping", 4) != 0) && (attempts < 10)) {
489 uint32_t bytes = 0;
490 service_error = service_receive_with_timeout(svcmove, ping, 4, &bytes, 2000);
491 if (service_error == SERVICE_E_SUCCESS || service_error == SERVICE_E_TIMEOUT) {
492 attempts++;
493 continue;
494 }
495
496 fprintf(stderr, "ERROR: Crash logs could not be moved. Connection interrupted (%d).\n", service_error);
497 break;
498 }
499 service_client_free(svcmove);
500 free(ping);
501
502 if (device_error != IDEVICE_E_SUCCESS || attempts > 10) {
503 fprintf(stderr, "ERROR: Failed to receive ping message from crash report mover.\n");
504 lockdownd_client_free(lockdownd);
505 idevice_free(device);
506 return -1;
507 }
508
509 lockdownd_error = lockdownd_start_service(lockdownd, CRASH_REPORT_COPY_MOBILE_SERVICE, &service);
510 if (lockdownd_error != LOCKDOWN_E_SUCCESS) {
511 fprintf(stderr, "ERROR: Could not start service %s: %s\n", CRASH_REPORT_COPY_MOBILE_SERVICE, lockdownd_strerror(lockdownd_error));
512 lockdownd_client_free(lockdownd);
513 idevice_free(device);
514 return -1;
515 }
516 lockdownd_client_free(lockdownd);
517
518 afc = NULL;
519 afc_error = afc_client_new(device, service, &afc);
520 if(afc_error != AFC_E_SUCCESS) {
521 lockdownd_client_free(lockdownd);
522 idevice_free(device);
523 return -1;
524 }
525
526 if (service) {
527 lockdownd_service_descriptor_free(service);
528 service = NULL;
529 }
530
531 /* recursively copy crash reports from the device to a local directory */
532 if (afc_client_copy_and_remove_crash_reports(afc, ".", target_directory, filename_filter) < 0) {
533 fprintf(stderr, "ERROR: Failed to get crash reports from device.\n");
534 afc_client_free(afc);
535 idevice_free(device);
536 return -1;
537 }
538
539 printf("Done.\n");
540
541 afc_client_free(afc);
542 idevice_free(device);
543
544 return 0;
545}
diff --git a/tools/idevicedate.c b/tools/idevicedate.c
index e4e0a33..31b0cf7 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..3f2e289
--- /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..fb082b3
--- /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..6bf1a1c
--- /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..365c0a4
--- /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..65eb882 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..b319d05 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,303 @@
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#include <sys/stat.h>
40#ifndef _WIN32
41#include <signal.h>
42#endif
33 43
34#include <libimobiledevice/libimobiledevice.h> 44#include <libimobiledevice/libimobiledevice.h>
35#include <libimobiledevice/lockdown.h> 45#include <libimobiledevice/lockdown.h>
36#include <libimobiledevice/afc.h> 46#include <libimobiledevice/afc.h>
37#include <libimobiledevice/notification_proxy.h> 47#include <libimobiledevice/notification_proxy.h>
38#include <libimobiledevice/mobile_image_mounter.h> 48#include <libimobiledevice/mobile_image_mounter.h>
39 49#include <libimobiledevice-glue/sha.h>
40static int indent_level = 0; 50#include <libimobiledevice-glue/utils.h>
51#include <asprintf.h>
52#include <plist/plist.h>
53#include <libtatsu/tss.h>
41 54
42static int list_mode = 0; 55static int list_mode = 0;
56static int use_network = 0;
43static int xml_mode = 0; 57static int xml_mode = 0;
44static char *uuid = NULL; 58static const char *udid = NULL;
45static char *imagetype = NULL; 59static const char *imagetype = NULL;
46 60
47static const char PKG_PATH[] = "PublicStaging"; 61static const char PKG_PATH[] = "PublicStaging";
48static const char PATH_PREFIX[] = "/private/var/mobile/Media"; 62static const char PATH_PREFIX[] = "/private/var/mobile/Media";
49 63
50static void print_usage(int argc, char **argv) 64typedef enum {
65 DISK_IMAGE_UPLOAD_TYPE_AFC,
66 DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
67} disk_image_upload_type_t;
68
69enum cmd_mode {
70 CMD_NONE = 0,
71 CMD_MOUNT,
72 CMD_UNMOUNT,
73 CMD_LIST,
74 CMD_DEVMODESTATUS
75};
76
77int cmd = CMD_NONE;
78
79static void print_usage(int argc, char **argv, int is_error)
51{ 80{
52 char *name = NULL; 81 char *name = strrchr(argv[0], '/');
53 82 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0]));
54 name = strrchr(argv[0], '/'); 83 fprintf(is_error ? stderr : stdout,
55 printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0])); 84 "\n"
56 printf("Mounts the specified disk image on the device.\n\n"); 85 "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"); 86 "\n"
58 printf(" -l, --list\t\tList mount information\n"); 87 "COMMANDS:\n"
59 printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n"); 88 " mount PATH Mount the developer disk image at PATH.\n"
60 printf(" -x, --xml\t\tUse XML output\n"); 89 " For iOS 17+, PATH is a directory containing a .dmg image,\n"
61 printf(" -d, --debug\t\tenable communication debugging\n"); 90 " a BuildManifest.plist, and a Firmware sub-directory;\n"
62 printf(" -h, --help\t\tprints usage information\n"); 91 " for older versions PATH is a .dmg filename with a\n"
63 printf("\n"); 92 " .dmg.signature in the same directory, or with another\n"
93 " parameter pointing to a file elsewhere.\n"
94 " list List mounted disk images.\n"
95 " unmount PATH Unmount the image mounted at PATH.\n"
96 " devmodestatus Query the developer mode status (iOS 16+)\n"
97 "\n"
98 "OPTIONS:\n"
99 " -u, --udid UDID target specific device by UDID\n"
100 " -n, --network connect to network device\n"
101 " -t, --imagetype TYPE Image type to use, default is 'Developer'\n"
102 " -x, --xml Use XML output\n"
103 " -d, --debug enable communication debugging\n"
104 " -h, --help prints usage information\n"
105 " -v, --version prints version information\n"
106 "\n"
107 "Homepage: <" PACKAGE_URL ">\n"
108 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
109 );
64} 110}
65 111
66static void parse_opts(int argc, char **argv) 112static void parse_opts(int argc, char **argv)
67{ 113{
114 int debug_level = 0;
68 static struct option longopts[] = { 115 static struct option longopts[] = {
69 {"help", 0, NULL, 'h'}, 116 { "help", no_argument, NULL, 'h' },
70 {"uuid", 0, NULL, 'u'}, 117 { "udid", required_argument, NULL, 'u' },
71 {"list", 0, NULL, 'l'}, 118 { "network", no_argument, NULL, 'n' },
72 {"imagetype", 0, NULL, 't'}, 119 { "imagetype", required_argument, NULL, 't' },
73 {"xml", 0, NULL, 'x'}, 120 { "xml", no_argument, NULL, 'x' },
74 {"debug", 0, NULL, 'd'}, 121 { "debug", no_argument, NULL, 'd' },
75 {NULL, 0, NULL, 0} 122 { "version", no_argument, NULL, 'v' },
123 { NULL, 0, NULL, 0 }
76 }; 124 };
77 int c; 125 int c;
78 126
79 while (1) { 127 while (1) {
80 c = getopt_long(argc, argv, "hu:lt:xd", longopts, 128 c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL);
81 (int *) 0);
82 if (c == -1) { 129 if (c == -1) {
83 break; 130 break;
84 } 131 }
85 132
86 switch (c) { 133 switch (c) {
87 case 'h': 134 case 'h':
88 print_usage(argc, argv); 135 print_usage(argc, argv, 0);
89 exit(0); 136 exit(0);
90 case 'u': 137 case 'u':
91 if (strlen(optarg) != 40) { 138 if (!*optarg) {
92 printf("%s: invalid UUID specified (length != 40)\n", 139 fprintf(stderr, "ERROR: UDID must not be empty!\n");
93 argv[0]); 140 print_usage(argc, argv, 1);
94 print_usage(argc, argv);
95 exit(2); 141 exit(2);
96 } 142 }
97 uuid = strdup(optarg); 143 udid = optarg;
98 break; 144 break;
99 case 'l': 145 case 'n':
100 list_mode = 1; 146 use_network = 1;
101 break; 147 break;
102 case 't': 148 case 't':
103 imagetype = strdup(optarg); 149 imagetype = optarg;
104 break; 150 break;
105 case 'x': 151 case 'x':
106 xml_mode = 1; 152 xml_mode = 1;
107 break; 153 break;
108 case 'd': 154 case 'd':
109 idevice_set_debug_level(1); 155 debug_level++;
110 break; 156 break;
157 case 'v':
158 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
159 exit(0);
111 default: 160 default:
112 print_usage(argc, argv); 161 print_usage(argc, argv, 1);
113 exit(2); 162 exit(2);
114 } 163 }
115 } 164 }
165 idevice_set_debug_level(debug_level);
166 tss_set_debug_level(debug_level);
116} 167}
117 168
118static void plist_node_to_string(plist_t node); 169static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
119
120static void plist_array_to_string(plist_t node)
121{ 170{
122 /* iterate over items */ 171 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} 172}
250 173
251int main(int argc, char **argv) 174int main(int argc, char **argv)
252{ 175{
253 idevice_t device = NULL; 176 idevice_t device = NULL;
254 lockdownd_client_t lckd = NULL; 177 lockdownd_client_t lckd = NULL;
178 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
255 mobile_image_mounter_client_t mim = NULL; 179 mobile_image_mounter_client_t mim = NULL;
256 afc_client_t afc = NULL; 180 afc_client_t afc = NULL;
257 uint16_t port = 0; 181 lockdownd_service_descriptor_t service = NULL;
258 int res = -1; 182 int res = -1;
259 char *image_path = NULL; 183 char *image_path = NULL;
184 size_t image_size = 0;
260 char *image_sig_path = NULL; 185 char *image_sig_path = NULL;
261 186
187#ifndef _WIN32
188 signal(SIGPIPE, SIG_IGN);
189#endif
262 parse_opts(argc, argv); 190 parse_opts(argc, argv);
263 191
264 argc -= optind; 192 argc -= optind;
265 argv += optind; 193 argv += optind;
266 194
267 if (!list_mode) { 195 if (argc == 0) {
268 if (argc < 1) { 196 fprintf(stderr, "ERROR: Missing command.\n\n");
269 printf("ERROR: No IMAGE_FILE has been given!\n"); 197 print_usage(argc+optind, argv-optind, 1);
270 return -1; 198 return 2;
271 } 199 }
272 image_path = strdup(argv[0]); 200
273 if (argc >= 2) { 201 char* cmdstr = argv[0];
274 image_sig_path = strdup(argv[1]); 202
203 int optind2 = 0;
204 if (!strcmp(cmdstr, "mount")) {
205 cmd = CMD_MOUNT;
206 optind2++;
207 } else if (!strcmp(cmdstr, "list")) {
208 cmd = CMD_LIST;
209 optind2++;
210 } else if (!strcmp(cmdstr, "umount") || !strcmp(cmdstr, "unmount")) {
211 cmd = CMD_UNMOUNT;
212 optind2++;
213 } else if (!strcmp(cmdstr, "devmodestatus")) {
214 cmd = CMD_DEVMODESTATUS;
215 optind2++;
216 } else {
217 // assume mount command, unless -l / --list was specified
218 if (list_mode) {
219 cmd = CMD_LIST;
275 } else { 220 } else {
276 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { 221 cmd = CMD_MOUNT;
277 printf("Out of memory?!\n");
278 return -1;
279 }
280 } 222 }
281 } 223 }
282 224
283 if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) { 225 argc -= optind2;
284 printf("No device found, is it plugged in?\n"); 226 argv += optind2;
285 return -1; 227 optind += optind2;
228
229 switch (cmd) {
230 case CMD_MOUNT:
231 if (argc < 1) {
232 fprintf(stderr, "ERROR: Missing IMAGE_FILE for mount command\n");
233 print_usage(argc+optind, argv-optind, 1);
234 return 2;
235 }
236 image_path = strdup(argv[0]);
237 if (argc >= 2) {
238 image_sig_path = strdup(argv[1]);
239 } else {
240 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
241 printf("Out of memory?!\n");
242 return 1;
243 }
244 }
245 break;
246 case CMD_UNMOUNT:
247 if (argc != 1) {
248 fprintf(stderr, "ERROR: Missing mount path (argc = %d)\n", argc);
249 print_usage(argc+optind, argv-optind, 1);
250 return 2;
251 }
252 break;
253 default:
254 break;
286 } 255 }
287 256
288 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) { 257 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
289 printf("ERROR: could not connect to lockdown. Exiting.\n"); 258 if (udid) {
259 printf("No device found with udid %s.\n", udid);
260 } else {
261 printf("No device found.\n");
262 }
263 return 1;
264 }
265
266 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
267 printf("ERROR: Could not connect to lockdown, error code %d.\n", ldret);
290 goto leave; 268 goto leave;
291 } 269 }
292 270
293 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &port); 271 unsigned int device_version = idevice_get_device_version(device);
272
273 disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC;
274 if (device_version >= IDEVICE_DEVICE_VERSION(7,0,0)) {
275 disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
276 }
277
278 if (device_version >= IDEVICE_DEVICE_VERSION(16,0,0)) {
279 uint8_t dev_mode_status = 0;
280 plist_t val = NULL;
281 ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
282 if (ldret == LOCKDOWN_E_SUCCESS) {
283 plist_get_bool_val(val, &dev_mode_status);
284 plist_free(val);
285 }
286 if (!dev_mode_status) {
287 printf("ERROR: You have to enable Developer Mode on the given device in order to allowing mounting a developer disk image.\n");
288 goto leave;
289 }
290 }
291
292 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service);
294 293
295 if (port == 0) { 294 if (!service || service->port == 0) {
296 printf("ERROR: Could not start mobile_image_mounter service!\n"); 295 printf("ERROR: Could not start mobile_image_mounter service!\n");
297 goto leave; 296 goto leave;
298 } 297 }
299 298
300 if (mobile_image_mounter_new(device, port, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 299 if (mobile_image_mounter_new(device, service, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
301 printf("ERROR: Could not connect to mobile_image_mounter!\n"); 300 printf("ERROR: Could not connect to mobile_image_mounter!\n");
302 goto leave; 301 goto leave;
303 } 302 }
303
304 if (service) {
305 lockdownd_service_descriptor_free(service);
306 service = NULL;
307 }
304 308
305 if (!list_mode) { 309 if (cmd == CMD_MOUNT) {
306 struct stat fst; 310 struct stat fst;
307 port = 0; 311 if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
308 if ((lockdownd_start_service(lckd, "com.apple.afc", &port) != 312 if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
309 LOCKDOWN_E_SUCCESS) || !port) { 313 LOCKDOWN_E_SUCCESS) || !service || !service->port) {
310 fprintf(stderr, "Could not start com.apple.afc!\n"); 314 fprintf(stderr, "Could not start com.apple.afc!\n");
311 goto leave; 315 goto leave;
312 } 316 }
313 if (afc_client_new(device, port, &afc) != AFC_E_SUCCESS) { 317 if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) {
314 fprintf(stderr, "Could not connect to AFC!\n"); 318 fprintf(stderr, "Could not connect to AFC!\n");
315 goto leave; 319 goto leave;
320 }
321 if (service) {
322 lockdownd_service_descriptor_free(service);
323 service = NULL;
324 }
316 } 325 }
317 if (stat(image_path, &fst) != 0) { 326 if (stat(image_path, &fst) != 0) {
318 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno)); 327 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
319 goto leave; 328 goto leave;
320 } 329 }
321 if (stat(image_sig_path, &fst) != 0) { 330 image_size = fst.st_size;
331 if (device_version < IDEVICE_DEVICE_VERSION(17,0,0) && stat(image_sig_path, &fst) != 0) {
322 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno)); 332 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
323 goto leave; 333 goto leave;
324 } 334 }
@@ -327,49 +337,240 @@ int main(int argc, char **argv)
327 lockdownd_client_free(lckd); 337 lockdownd_client_free(lckd);
328 lckd = NULL; 338 lckd = NULL;
329 339
330 mobile_image_mounter_error_t err; 340 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
331 plist_t result = NULL; 341 plist_t result = NULL;
332 342
333 if (list_mode) { 343 if (cmd == CMD_LIST) {
334 /* list mounts mode */ 344 /* list mounts mode */
335 if (!imagetype) { 345 if (!imagetype) {
336 imagetype = strdup("Developer"); 346 if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) {
347 imagetype = "Developer";
348 } else {
349 imagetype = "Personalized";
350 }
337 } 351 }
338 err = mobile_image_mounter_lookup_image(mim, imagetype, &result); 352 err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
339 free(imagetype);
340 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 353 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
341 res = 0; 354 res = 0;
342 if (xml_mode) { 355 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 { 356 } else {
348 printf("Error: lookup_image returned %d\n", err); 357 printf("Error: lookup_image returned %d\n", err);
349 } 358 }
350 } else { 359 } else if (cmd == CMD_MOUNT) {
351 char sig[8192]; 360 unsigned char *sig = NULL;
352 size_t sig_length = 0; 361 size_t sig_length = 0;
353 FILE *f = fopen(image_sig_path, "r"); 362 FILE *f;
354 if (!f) { 363 struct stat fst;
355 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); 364 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 365
365 f = fopen(image_path, "r"); 366 if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) {
366 if (!f) { 367 f = fopen(image_sig_path, "rb");
367 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); 368 if (!f) {
368 goto leave; 369 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
370 goto leave;
371 }
372 if (fstat(fileno(f), &fst) != 0) {
373 fprintf(stderr, "Error: fstat: %s\n", strerror(errno));
374 goto leave;
375 }
376 sig = malloc(fst.st_size);
377 sig_length = fread(sig, 1, fst.st_size, f);
378 fclose(f);
379 if (sig_length == 0) {
380 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
381 goto leave;
382 }
383
384 f = fopen(image_path, "rb");
385 if (!f) {
386 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
387 goto leave;
388 }
389 } else {
390 if (stat(image_path, &fst) != 0) {
391 fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno));
392 goto leave;
393 }
394 if (!S_ISDIR(fst.st_mode)) {
395 fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n");
396 goto leave;
397 }
398 char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL);
399 plist_t build_manifest = NULL;
400 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) {
401 free(build_manifest_path);
402 build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL);
403 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) {
404 char* image_path_new = string_build_path(image_path, "Restore", NULL);
405 free(image_path);
406 image_path = image_path_new;
407 }
408 }
409 if (!build_manifest) {
410 fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n");
411 goto leave;
412 }
413
414 plist_t identifiers = NULL;
415 mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers);
416 if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
417 fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr);
418 goto error_out;
419 }
420
421 unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId");
422 unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID");
423
424 plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities");
425 plist_array_iter iter;
426 plist_array_new_iter(build_identities, &iter);
427 plist_t item = NULL;
428 plist_t build_identity = NULL;
429 do {
430 plist_array_next_item(build_identities, iter, &item);
431 if (!item) {
432 break;
433 }
434 unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID");
435 unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID");
436 if (bi_chip_id == chip_id && bi_board_id == board_id) {
437 build_identity = item;
438 break;
439 }
440 } while (item);
441 plist_mem_free(iter);
442 if (!build_identity) {
443 fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n");
444 goto leave;
445 }
446 plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path");
447 if (!p_tc_path) {
448 fprintf(stderr, "Error: Could not determine path for trust cache!\n");
449 goto leave;
450 }
451 plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path");
452 if (!p_dmg_path) {
453 fprintf(stderr, "Error: Could not determine path for disk image!\n");
454 goto leave;
455 }
456 char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL);
457 unsigned char* trust_cache = NULL;
458 uint64_t trust_cache_size = 0;
459 if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) {
460 fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path);
461 goto leave;
462 }
463 mount_options = plist_new_dict();
464 plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size));
465 free(trust_cache);
466 char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL);
467 free(image_path);
468 image_path = dmg_path;
469 f = fopen(image_path, "rb");
470 if (!f) {
471 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
472 goto leave;
473 }
474
475 unsigned char buf[8192];
476 unsigned char sha384_digest[48];
477 sha384_context ctx;
478 sha384_init(&ctx);
479 fstat(fileno(f), &fst);
480 image_size = fst.st_size;
481 while (!feof(f)) {
482 ssize_t fr = fread(buf, 1, sizeof(buf), f);
483 if (fr <= 0) {
484 break;
485 }
486 sha384_update(&ctx, buf, fr);
487 }
488 rewind(f);
489 sha384_final(&ctx, sha384_digest);
490 unsigned char* manifest = NULL;
491 unsigned int manifest_size = 0;
492 /* check if the device already has a personalization manifest for this image */
493 if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
494 printf("Using existing personalization manifest from device.\n");
495 } else {
496 /* we need to re-connect in this case */
497 mobile_image_mounter_free(mim);
498 mim = NULL;
499 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
500 goto error_out;
501 }
502 printf("No personalization manifest, requesting from TSS...\n");
503 unsigned char* nonce = NULL;
504 unsigned int nonce_size = 0;
505
506 /* create new TSS request and fill parameters */
507 plist_t request = tss_request_new(NULL);
508 plist_t params = plist_new_dict();
509 tss_parameters_add_from_manifest(params, build_identity, 1);
510
511 /* copy all `Ap,*` items from identifiers */
512 plist_dict_iter di = NULL;
513 plist_dict_new_iter(identifiers, &di);
514 plist_t node = NULL;
515 do {
516 char* key = NULL;
517 plist_dict_next_item(identifiers, di, &key, &node);
518 if (node) {
519 if (!strncmp(key, "Ap,", 3)) {
520 plist_dict_set_item(request, key, plist_copy(node));
521 }
522 }
523 free(key);
524 } while (node);
525 plist_mem_free(di);
526
527 plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID");
528 plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1));
529 plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1));
530 plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1));
531
532 /* query nonce from image mounter service */
533 merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size);
534 if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
535 plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size));
536 } else {
537 fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr);
538 goto error_out;
539 }
540 mobile_image_mounter_free(mim);
541 mim = NULL;
542
543 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));
544 plist_dict_set_item(params, "UID_MODE", plist_new_bool(0));
545 tss_request_add_ap_tags(request, params, NULL);
546 tss_request_add_common_tags(request, params, NULL);
547 tss_request_add_ap_img4_tags(request, params);
548 plist_free(params);
549
550 /* request IM4M from TSS */
551 plist_t response = tss_request_send(request, NULL);
552 plist_free(request);
553
554 plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket");
555 if (!PLIST_IS_DATA(p_manifest)) {
556 fprintf(stderr, "Failed to get Img4Ticket\n");
557 goto error_out;
558 }
559
560 uint64_t m4m_len = 0;
561 plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len);
562 manifest_size = m4m_len;
563 plist_free(response);
564 printf("Done.\n");
565 }
566 sig = manifest;
567 sig_length = manifest_size;
568
569 imagetype = "Personalized";
369 } 570 }
370 571
371 char *targetname = NULL; 572 char *targetname = NULL;
372 if (asprintf(&targetname, "%s/%s", PKG_PATH, basename(image_path)) < 0) { 573 if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) {
373 fprintf(stderr, "Out of memory!?\n"); 574 fprintf(stderr, "Out of memory!?\n");
374 goto leave; 575 goto leave;
375 } 576 }
@@ -379,68 +580,84 @@ int main(int argc, char **argv)
379 goto leave; 580 goto leave;
380 } 581 }
381 582
382 printf("Copying '%s' --> '%s'\n", image_path, targetname); 583 if (!imagetype) {
383 584 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 } 585 }
398 586
399 uint64_t af = 0; 587 if (!mim) {
400 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) != 588 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
401 AFC_E_SUCCESS) || !af) { 589 goto error_out;
402 fclose(f); 590 }
403 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
404 goto leave;
405 } 591 }
406 592
407 char buf[8192]; 593 switch(disk_image_upload_type) {
408 size_t amount = 0; 594 case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
409 do { 595 printf("Uploading %s\n", image_path);
410 amount = fread(buf, 1, sizeof(buf), f); 596 err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig, sig_length, mim_upload_cb, f);
411 if (amount > 0) { 597 break;
412 uint32_t written, total = 0; 598 case DISK_IMAGE_UPLOAD_TYPE_AFC:
413 while (total < amount) { 599 default:
414 written = 0; 600 printf("Uploading %s --> afc:///%s\n", image_path, targetname);
415 if (afc_file_write(afc, af, buf, amount, &written) != 601 plist_t fileinfo = NULL;
416 AFC_E_SUCCESS) { 602 if (afc_get_file_info_plist(afc, PKG_PATH, &fileinfo) != AFC_E_SUCCESS) {
417 fprintf(stderr, "AFC Write error!\n"); 603 if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
418 break; 604 fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
419 } 605 }
420 total += written;
421 } 606 }
422 if (total != amount) { 607 plist_free(fileinfo);
423 fprintf(stderr, "Error: wrote only %d of %d\n", total, 608
424 (unsigned int)amount); 609 uint64_t af = 0;
425 afc_file_close(afc, af); 610 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
611 AFC_E_SUCCESS) || !af) {
426 fclose(f); 612 fclose(f);
613 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
427 goto leave; 614 goto leave;
428 } 615 }
429 } 616
617 char buf[8192];
618 size_t amount = 0;
619 do {
620 amount = fread(buf, 1, sizeof(buf), f);
621 if (amount > 0) {
622 uint32_t written, total = 0;
623 while (total < amount) {
624 written = 0;
625 if (afc_file_write(afc, af, buf + total, amount - total, &written) !=
626 AFC_E_SUCCESS) {
627 fprintf(stderr, "AFC Write error!\n");
628 break;
629 }
630 total += written;
631 }
632 if (total != amount) {
633 fprintf(stderr, "Error: wrote only %d of %d\n", total,
634 (unsigned int)amount);
635 afc_file_close(afc, af);
636 fclose(f);
637 goto leave;
638 }
639 }
640 }
641 while (amount > 0);
642
643 afc_file_close(afc, af);
644 break;
430 } 645 }
431 while (amount > 0);
432 646
433 afc_file_close(afc, af);
434 fclose(f); 647 fclose(f);
435 648
649 if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
650 if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) {
651 printf("ERROR: Device is locked, can't mount. Unlock device and try again.\n");
652 } else {
653 printf("ERROR: Unknown error occurred, can't mount.\n");
654 }
655 goto error_out;
656 }
436 printf("done.\n"); 657 printf("done.\n");
437 658
438 printf("Mounting...\n"); 659 printf("Mounting...\n");
439 if (!imagetype) { 660 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) { 661 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
445 if (result) { 662 if (result) {
446 plist_t node = plist_dict_get_item(result, "Status"); 663 plist_t node = plist_dict_get_item(result, "Status");
@@ -453,20 +670,12 @@ int main(int argc, char **argv)
453 res = 0; 670 res = 0;
454 } else { 671 } else {
455 printf("unexpected status value:\n"); 672 printf("unexpected status value:\n");
456 if (xml_mode) { 673 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 } 674 }
462 free(status); 675 free(status);
463 } else { 676 } else {
464 printf("unexpected result:\n"); 677 printf("unexpected result:\n");
465 if (xml_mode) { 678 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 } 679 }
471 } 680 }
472 node = plist_dict_get_item(result, "Error"); 681 node = plist_dict_get_item(result, "Error");
@@ -478,31 +687,58 @@ int main(int argc, char **argv)
478 free(error); 687 free(error);
479 } else { 688 } else {
480 printf("unexpected result:\n"); 689 printf("unexpected result:\n");
481 if (xml_mode) { 690 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 } 691 }
487 692 node = plist_dict_get_item(result, "DetailedError");
488 } else { 693 if (node) {
489 if (xml_mode) { 694 printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL));
490 print_xml(result);
491 } else {
492 plist_dict_to_string(result);
493 } 695 }
696 } else {
697 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
494 } 698 }
495 } 699 }
496 } else { 700 } else {
497 printf("Error: mount_image returned %d\n", err); 701 printf("Error: mount_image returned %d\n", err);
498 702
499 } 703 }
704 } else if (cmd == CMD_UNMOUNT) {
705 err = mobile_image_mounter_unmount_image(mim, argv[0]);
706 switch (err) {
707 case MOBILE_IMAGE_MOUNTER_E_SUCCESS:
708 printf("Success\n");
709 res = 0;
710 break;
711 case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED:
712 printf("Error: '%s' is not mounted\n", argv[0]);
713 res = 1;
714 break;
715 case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED:
716 printf("Error: 'unmount' is not supported on this device\n");
717 res = 1;
718 break;
719 case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED:
720 printf("Error: device is locked\n");
721 res = 1;
722 break;
723 default:
724 printf("Error: unmount returned %d\n", err);
725 break;
726 }
727 } else if (cmd == CMD_DEVMODESTATUS) {
728 err = mobile_image_mounter_query_developer_mode_status(mim, &result);
729 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
730 res = 0;
731 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
732 } else {
733 printf("Error: query_developer_mode_status returned %d\n", err);
734 }
500 } 735 }
501 736
502 if (result) { 737 if (result) {
503 plist_free(result); 738 plist_free(result);
504 } 739 }
505 740
741error_out:
506 /* perform hangup command */ 742 /* perform hangup command */
507 mobile_image_mounter_hangup(mim); 743 mobile_image_mounter_hangup(mim);
508 /* free client */ 744 /* free client */
@@ -518,7 +754,7 @@ leave:
518 idevice_free(device); 754 idevice_free(device);
519 755
520 if (image_path) 756 if (image_path)
521 free(image_path); 757 free(image_path);
522 if (image_sig_path) 758 if (image_sig_path)
523 free(image_sig_path); 759 free(image_sig_path);
524 760
diff --git a/tools/ideviceinfo.c b/tools/ideviceinfo.c
index e05165b..20cc916 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..248bda3
--- /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..192192a
--- /dev/null
+++ b/tools/idevicenotificationproxy.c
@@ -0,0 +1,305 @@
1/*
2 * idevicenotificationproxy.c
3 * Simple client for the notification_proxy service
4 *
5 * Copyright (c) 2018-2024 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2009-2015 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 "idevicenotificationproxy"
28
29#include <stdio.h>
30#include <string.h>
31#include <stdlib.h>
32#include <getopt.h>
33#include <errno.h>
34#include <signal.h>
35
36#ifdef _WIN32
37#include <windows.h>
38#define sleep(x) Sleep(x*1000)
39#else
40#include <unistd.h>
41#endif
42
43#include <libimobiledevice/libimobiledevice.h>
44#include <libimobiledevice/lockdown.h>
45#include <libimobiledevice/notification_proxy.h>
46
47enum cmd_mode {
48 CMD_NONE = 0,
49 CMD_OBSERVE,
50 CMD_POST
51};
52
53static int quit_flag = 0;
54
55/**
56 * signal handler function for cleaning up properly
57 */
58static void clean_exit(int sig)
59{
60 fprintf(stderr, "Exiting...\n");
61 quit_flag++;
62}
63
64static void print_usage(int argc, char **argv, int is_error)
65{
66 char *name = strrchr(argv[0], '/');
67 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
68 fprintf(is_error ? stderr : stdout,
69 "\n"
70 "Post or observe notifications on a device.\n"
71 "\n"
72 "Where COMMAND is one of:\n"
73 " post ID [...] post notification IDs to device and exit\n"
74 " observe ID [...] observe notification IDs in foreground until CTRL+C\n"
75 " or signal is received\n"
76 "\n"
77 "The following OPTIONS are accepted:\n"
78 " -u, --udid UDID target specific device by UDID\n"
79 " -i, --insecure use insecure notification proxy (non-paired device)\n"
80 " -n, --network connect to network device\n"
81 " -d, --debug enable communication debugging\n"
82 " -h, --help prints usage information\n"
83 " -v, --version prints version information\n"
84 "\n"
85 "Homepage: <" PACKAGE_URL ">\n"
86 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
87 );
88}
89
90static void notify_cb(const char *notification, void *user_data)
91{
92 printf("> %s\n", notification);
93}
94
95int main(int argc, char *argv[])
96{
97 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
98 lockdownd_service_descriptor_t service = NULL;
99 lockdownd_client_t client = NULL;
100 idevice_t device = NULL;
101 np_client_t gnp = NULL;
102
103 int result = -1;
104 int i = 0;
105 const char* udid = NULL;
106 int use_network = 0;
107 int insecure = 0;
108 int cmd = CMD_NONE;
109 char* cmd_arg = NULL;
110
111 int count = 0;
112 char **nspec = NULL;
113 char **nspectmp = NULL;
114
115 int c = 0;
116 const struct option longopts[] = {
117 { "debug", no_argument, NULL, 'd' },
118 { "help", no_argument, NULL, 'h' },
119 { "udid", required_argument, NULL, 'u' },
120 { "insecure", no_argument, NULL, 'i' },
121 { "network", no_argument, NULL, 'n' },
122 { "version", no_argument, NULL, 'v' },
123 { NULL, 0, NULL, 0}
124 };
125
126 signal(SIGINT, clean_exit);
127 signal(SIGTERM, clean_exit);
128#ifndef _WIN32
129 signal(SIGQUIT, clean_exit);
130 signal(SIGPIPE, SIG_IGN);
131#endif
132
133 /* parse cmdline args */
134 while ((c = getopt_long(argc, argv, "dhu:inv", longopts, NULL)) != -1) {
135 switch (c) {
136 case 'd':
137 idevice_set_debug_level(1);
138 break;
139 case 'u':
140 if (!*optarg) {
141 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
142 print_usage(argc, argv, 1);
143 return 2;
144 }
145 udid = optarg;
146 break;
147 case 'n':
148 use_network = 1;
149 break;
150 case 'i':
151 insecure = 1;
152 break;
153 case 'h':
154 print_usage(argc, argv, 0);
155 return 0;
156 case 'v':
157 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
158 return 0;
159 default:
160 print_usage(argc, argv, 1);
161 return 2;
162 }
163 }
164 argc -= optind;
165 argv += optind;
166
167 if (!argv[i]) {
168 fprintf(stderr, "ERROR: Missing command\n");
169 print_usage(argc+optind, argv-optind, 1);
170 return 2;
171 }
172
173 if (!strcmp(argv[i], "post")) {
174 cmd = CMD_POST;
175 } else if (!strcmp(argv[i], "observe")) {
176 cmd = CMD_OBSERVE;
177 }
178
179 if (cmd == CMD_POST || cmd == CMD_OBSERVE) {
180 i++;
181 if (!argv[i]) {
182 fprintf(stderr, "ERROR: Please supply a valid notification identifier.\n");
183 print_usage(argc+optind, argv-optind, 1);
184 return 2;
185 }
186
187 count = 0;
188 nspec = malloc(sizeof(char*) * (count+1));
189
190 while(1) {
191 if (argv[i] && (strlen(argv[i]) >= 2) && (strncmp(argv[i], "-", 1) != 0)) {
192 nspectmp = realloc(nspec, sizeof(char*) * (count+1));
193 nspectmp[count] = strdup(argv[i]);
194 nspec = nspectmp;
195 count = count+1;
196 i++;
197 } else {
198 i--;
199 break;
200 }
201 }
202
203 nspectmp = realloc(nspec, sizeof(char*) * (count+1));
204 nspectmp[count] = NULL;
205 nspec = nspectmp;
206 }
207
208 /* verify options */
209 if (cmd == CMD_NONE) {
210 fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[0]);
211 print_usage(argc+optind, argv-optind, 1);
212 return 2;
213 }
214
215 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
216 if (udid) {
217 printf("No device found with udid %s.\n", udid);
218 } else {
219 printf("No device found.\n");
220 }
221 goto cleanup;
222 }
223
224 if (insecure) {
225 ret = lockdownd_client_new(device, &client, TOOL_NAME);
226 } else {
227 ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME);
228 }
229 if (LOCKDOWN_E_SUCCESS != ret) {
230 fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ret), ret);
231 goto cleanup;
232 }
233
234 ret = lockdownd_start_service(client, (insecure) ? "com.apple.mobile.insecure_notification_proxy" : NP_SERVICE_NAME, &service);
235
236 lockdownd_client_free(client);
237
238 if ((ret == LOCKDOWN_E_SUCCESS) && (service->port > 0)) {
239 if (np_client_new(device, service, &gnp) != NP_E_SUCCESS) {
240 printf("Could not connect to notification_proxy!\n");
241 result = -1;
242 } else {
243 np_set_notify_callback(gnp, notify_cb, NULL);
244
245 switch (cmd) {
246 case CMD_POST:
247 i = 0;
248 while(nspec[i] != NULL && i < (count+1)) {
249 printf("< posting \"%s\"\n", nspec[i]);
250 np_post_notification(gnp, nspec[i]);
251 i++;
252 }
253 break;
254 case CMD_OBSERVE:
255 default:
256 i = 0;
257 while(nspec[i] != NULL && i < (count+1)) {
258 printf("! observing \"%s\"\n", nspec[i]);
259 np_observe_notification(gnp, nspec[i]);
260 i++;
261 }
262
263 /* just sleep and wait for notifications */
264 while (!quit_flag) {
265 sleep(1);
266 }
267
268 break;
269 }
270
271 result = EXIT_SUCCESS;
272
273 if (gnp) {
274 np_client_free(gnp);
275 gnp = NULL;
276 }
277 }
278 } else {
279 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ret));
280 }
281
282 if (service) {
283 lockdownd_service_descriptor_free(service);
284 service = NULL;
285 }
286
287cleanup:
288 if (nspec) {
289 i = 0;
290 while(nspec[i] != NULL && i < (count+1)) {
291 free(nspec[i]);
292 i++;
293 }
294 free(nspec);
295 }
296
297 if (cmd_arg) {
298 free(cmd_arg);
299 }
300
301 if (device)
302 idevice_free(device);
303
304 return result;
305}
diff --git a/tools/idevicepair.c b/tools/idevicepair.c
index b9676b9..884c690 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..94f4ec5
--- /dev/null
+++ b/tools/ideviceprovision.c
@@ -0,0 +1,670 @@
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 <winsock2.h>
41#include <windows.h>
42#else
43#include <arpa/inet.h>
44#endif
45
46#include <libimobiledevice/libimobiledevice.h>
47#include <libimobiledevice/lockdown.h>
48#include <libimobiledevice/misagent.h>
49#include <plist/plist.h>
50
51static void print_usage(int argc, char **argv, int is_error)
52{
53 char *name = strrchr(argv[0], '/');
54 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
55 fprintf(is_error ? stderr : stdout,
56 "\n"
57 "Manage provisioning profiles on a device.\n"
58 "\n"
59 "Where COMMAND is one of:\n"
60 " install FILE Installs the provisioning profile specified by FILE.\n"
61 " A valid .mobileprovision file is expected.\n"
62 " list Get a list of all provisioning profiles on the device.\n"
63 " copy PATH Retrieves all provisioning profiles from the device and\n"
64 " stores them into the existing directory specified by PATH.\n"
65 " The files will be stored as UUID.mobileprovision\n"
66 " copy UUID PATH Retrieves the provisioning profile identified by UUID\n"
67 " from the device and stores it into the existing directory\n"
68 " specified by PATH. The file will be stored as UUID.mobileprovision.\n"
69 " remove UUID Removes the provisioning profile identified by UUID.\n"
70 " remove-all Removes all installed provisioning profiles.\n"
71 " dump FILE Prints detailed information about the provisioning profile\n"
72 " specified by FILE.\n"
73 "\n"
74 "The following OPTIONS are accepted:\n"
75 " -u, --udid UDID target specific device by UDID\n"
76 " -n, --network connect to network device\n"
77 " -x, --xml print XML output when using the 'dump' command\n"
78 " -d, --debug enable communication debugging\n"
79 " -h, --help prints usage information\n"
80 " -v, --version prints version information\n"
81 "\n"
82 "Homepage: <" PACKAGE_URL ">\n"
83 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
84 );
85}
86
87enum {
88 OP_INSTALL,
89 OP_LIST,
90 OP_COPY,
91 OP_REMOVE,
92 OP_DUMP,
93 NUM_OPS
94};
95
96#define ASN1_SEQUENCE 0x30
97#define ASN1_CONTAINER 0xA0
98#define ASN1_OBJECT_IDENTIFIER 0x06
99#define ASN1_OCTET_STRING 0x04
100
101static void asn1_next_item(unsigned char** p)
102{
103 char bsize = *(*p+1);
104 if (bsize & 0x80) {
105 *p += 2 + (bsize & 0xF);
106 } else {
107 *p += 3;
108 }
109}
110
111static size_t asn1_item_get_size(const unsigned char* p)
112{
113 size_t res = 0;
114 char bsize = *(p+1);
115 if (bsize & 0x80) {
116 uint16_t ws = 0;
117 uint32_t ds = 0;
118 switch (bsize & 0xF) {
119 case 2:
120 ws = *(uint16_t*)(p+2);
121 res = ntohs(ws);
122 break;
123 case 3:
124 ds = *(uint32_t*)(p+2);
125 res = ntohl(ds) >> 8;
126 break;
127 case 4:
128 ds = *(uint32_t*)(p+2);
129 res = ntohl(ds);
130 break;
131 default:
132 fprintf(stderr, "ERROR: Invalid or unimplemented byte size %d\n", bsize & 0xF);
133 break;
134 }
135 } else {
136 res = (int)bsize;
137 }
138 return res;
139}
140
141static void asn1_skip_item(unsigned char** p)
142{
143 size_t sz = asn1_item_get_size(*p);
144 *p += 2;
145 *p += sz;
146}
147
148static plist_t profile_get_embedded_plist(plist_t profile)
149{
150 if (plist_get_node_type(profile) != PLIST_DATA) {
151 fprintf(stderr, "%s: unexpected plist node type for profile (PLIST_DATA expected)\n", __func__);
152 return NULL;
153 }
154 char* bbuf = NULL;
155 uint64_t blen = 0;
156 plist_get_data_val(profile, &bbuf, &blen);
157 if (!bbuf) {
158 fprintf(stderr, "%s: could not get data value from plist node\n", __func__);
159 return NULL;
160 }
161
162 unsigned char* pp = (unsigned char*)bbuf;
163
164 if (*pp != ASN1_SEQUENCE) {
165 free(bbuf);
166 fprintf(stderr, "%s: unexpected profile data (0)\n", __func__);
167 return NULL;
168 }
169 size_t slen = asn1_item_get_size(pp);
170 char bsize = *(pp+1);
171 if (bsize & 0x80) {
172 slen += 2 + (bsize & 0xF);
173 } else {
174 slen += 3;
175 }
176 if (slen != blen) {
177 free(bbuf);
178 fprintf(stderr, "%s: unexpected profile data (1)\n", __func__);
179 return NULL;
180 }
181 asn1_next_item(&pp);
182
183 if (*pp != ASN1_OBJECT_IDENTIFIER) {
184 free(bbuf);
185 fprintf(stderr, "%s: unexpected profile data (2)\n", __func__);
186 return NULL;
187 }
188 asn1_skip_item(&pp);
189
190 if (*pp != ASN1_CONTAINER) {
191 free(bbuf);
192 fprintf(stderr, "%s: unexpected profile data (3)\n", __func__);
193 return NULL;
194 }
195 asn1_next_item(&pp);
196
197 if (*pp != ASN1_SEQUENCE) {
198 free(bbuf);
199 fprintf(stderr, "%s: unexpected profile data (4)\n", __func__);
200 return NULL;
201 }
202 asn1_next_item(&pp);
203
204 int k = 0;
205 // go to the 3rd element (skip 2)
206 while (k < 2) {
207 asn1_skip_item(&pp);
208 k++;
209 }
210 if (*pp != ASN1_SEQUENCE) {
211 free(bbuf);
212 fprintf(stderr, "%s: unexpected profile data (5)\n", __func__);
213 return NULL;
214 }
215 asn1_next_item(&pp);
216
217 if (*pp != ASN1_OBJECT_IDENTIFIER) {
218 free(bbuf);
219 fprintf(stderr, "%s: unexpected profile data (6)\n", __func__);
220 return NULL;
221 }
222 asn1_skip_item(&pp);
223
224 if (*pp != ASN1_CONTAINER) {
225 free(bbuf);
226 fprintf(stderr, "%s: unexpected profile data (7)\n", __func__);
227 return NULL;
228 }
229 asn1_next_item(&pp);
230
231 if (*pp != ASN1_OCTET_STRING) {
232 free(bbuf);
233 fprintf(stderr, "%s: unexpected profile data (8)\n", __func__);
234 return NULL;
235 }
236 slen = asn1_item_get_size(pp);
237 asn1_next_item(&pp);
238
239 plist_t pl = NULL;
240 plist_from_xml((char*)pp, slen, &pl);
241 free(bbuf);
242
243 return pl;
244}
245
246static int profile_read_from_file(const char* path, unsigned char **profile_data, unsigned int *profile_size)
247{
248 FILE* f = fopen(path, "rb");
249 if (!f) {
250 fprintf(stderr, "Could not open file '%s'\n", path);
251 return -1;
252 }
253 fseek(f, 0, SEEK_END);
254 long int size = ftell(f);
255 fseek(f, 0, SEEK_SET);
256
257 if (size >= 0x1000000) {
258 fprintf(stderr, "The file '%s' is too large for processing.\n", path);
259 fclose(f);
260 return -1;
261 }
262
263 unsigned char* buf = malloc(size);
264 if (!buf) {
265 fprintf(stderr, "Could not allocate memory...\n");
266 fclose(f);
267 return -1;
268 }
269
270 long int cur = 0;
271 while (cur < size) {
272 ssize_t r = fread(buf+cur, 1, 512, f);
273 if (r <= 0) {
274 break;
275 }
276 cur += r;
277 }
278 fclose(f);
279
280 if (cur != size) {
281 free(buf);
282 fprintf(stderr, "Could not read in file '%s' (size %ld read %ld)\n", path, size, cur);
283 return -1;
284 }
285
286 *profile_data = buf;
287 *profile_size = (unsigned int)size;
288
289 return 0;
290}
291
292int main(int argc, char *argv[])
293{
294 lockdownd_client_t client = NULL;
295 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
296 lockdownd_service_descriptor_t service = NULL;
297 idevice_t device = NULL;
298 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
299 int res = 0;
300 int i;
301 int op = -1;
302 int output_xml = 0;
303 const char* udid = NULL;
304 const char* param = NULL;
305 const char* param2 = NULL;
306 int use_network = 0;
307 int c = 0;
308 const struct option longopts[] = {
309 { "debug", no_argument, NULL, 'd' },
310 { "help", no_argument, NULL, 'h' },
311 { "udid", required_argument, NULL, 'u' },
312 { "network", no_argument, NULL, 'n' },
313 { "version", no_argument, NULL, 'v' },
314 { "xml", no_argument, NULL, 'x' },
315 { NULL, 0, NULL, 0}
316 };
317
318#ifndef _WIN32
319 signal(SIGPIPE, SIG_IGN);
320#endif
321 /* parse cmdline args */
322 while ((c = getopt_long(argc, argv, "dhu:nvx", longopts, NULL)) != -1) {
323 switch (c) {
324 case 'd':
325 idevice_set_debug_level(1);
326 break;
327 case 'u':
328 if (!*optarg) {
329 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
330 print_usage(argc, argv, 1);
331 return 2;
332 }
333 udid = optarg;
334 break;
335 case 'n':
336 use_network = 1;
337 break;
338 case 'h':
339 print_usage(argc, argv, 0);
340 return 0;
341 case 'v':
342 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
343 return 0;
344 case 'x':
345 output_xml = 1;
346 break;
347 default:
348 print_usage(argc, argv, 1);
349 return 2;
350 }
351 }
352 argc -= optind;
353 argv += optind;
354
355 if (!argv[0]) {
356 fprintf(stderr, "ERROR: Missing command.\n");
357 print_usage(argc+optind, argv-optind, 1);
358 return 2;
359 }
360
361 i = 0;
362 if (!strcmp(argv[i], "install")) {
363 op = OP_INSTALL;
364 i++;
365 if (!argv[i] || !*argv[i]) {
366 fprintf(stderr, "Missing argument for 'install' command.\n");
367 print_usage(argc+optind, argv-optind, 1);
368 return 2;
369 }
370 param = argv[i];
371 }
372 else if (!strcmp(argv[i], "list")) {
373 op = OP_LIST;
374 }
375 else if (!strcmp(argv[i], "copy")) {
376 op = OP_COPY;
377 i++;
378 if (!argv[i] || !*argv[i]) {
379 fprintf(stderr, "Missing argument for 'copy' command.\n");
380 print_usage(argc+optind, argv-optind, 1);
381 return 2;
382 }
383 param = argv[i];
384 i++;
385 if (argv[i] && (strlen(argv[i]) > 0)) {
386 param2 = argv[i];
387 }
388 }
389 else if (!strcmp(argv[i], "remove")) {
390 op = OP_REMOVE;
391 i++;
392 if (!argv[i] || !*argv[i]) {
393 fprintf(stderr, "Missing argument for 'remove' command.\n");
394 print_usage(argc+optind, argv-optind, 1);
395 return 2;
396 }
397 param = argv[i];
398 }
399 else if (!strcmp(argv[i], "remove-all")) {
400 op = OP_REMOVE;
401 }
402 else if (!strcmp(argv[i], "dump")) {
403 op = OP_DUMP;
404 i++;
405 if (!argv[i] || !*argv[i]) {
406 fprintf(stderr, "Missing argument for 'remove' command.\n");
407 print_usage(argc+optind, argv-optind, 1);
408 return 2;
409 }
410 param = argv[i];
411 }
412 if ((op == -1) || (op >= NUM_OPS)) {
413 fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[i]);
414 print_usage(argc+optind, argv-optind, 1);
415 return 2;
416 }
417
418 if (op == OP_DUMP) {
419 unsigned char* profile_data = NULL;
420 unsigned int profile_size = 0;
421 if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
422 return -1;
423 }
424 plist_t pdata = plist_new_data((char*)profile_data, profile_size);
425 plist_t pl = profile_get_embedded_plist(pdata);
426 plist_free(pdata);
427 free(profile_data);
428
429 if (pl) {
430 if (output_xml) {
431 char* xml = NULL;
432 uint32_t xlen = 0;
433 plist_to_xml(pl, &xml, &xlen);
434 if (xml) {
435 printf("%s\n", xml);
436 free(xml);
437 }
438 } else {
439 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
440 plist_write_to_stream(pl, stdout, PLIST_FORMAT_LIMD, 0);
441 } else {
442 fprintf(stderr, "ERROR: unexpected node type in profile plist (not PLIST_DICT)\n");
443 res = -1;
444 }
445 }
446 } else {
447 fprintf(stderr, "ERROR: could not extract embedded plist from profile!\n");
448 }
449 plist_free(pl);
450
451 return res;
452 }
453
454 if (op == OP_COPY) {
455 struct stat st;
456 const char *checkdir = (param2) ? param2 : param;
457 if ((stat(checkdir, &st) < 0) || !S_ISDIR(st.st_mode)) {
458 fprintf(stderr, "ERROR: %s does not exist or is not a directory!\n", checkdir);
459 return -1;
460 }
461 }
462
463 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
464 if (ret != IDEVICE_E_SUCCESS) {
465 if (udid) {
466 printf("No device found with udid %s.\n", udid);
467 } else {
468 printf("No device found.\n");
469 }
470 return -1;
471 }
472
473 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
474 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
475 idevice_free(device);
476 return -1;
477 }
478
479 unsigned int device_version = idevice_get_device_version(device);
480
481 lockdownd_error_t lerr = lockdownd_start_service(client, MISAGENT_SERVICE_NAME, &service);
482 if (lerr != LOCKDOWN_E_SUCCESS) {
483 fprintf(stderr, "Could not start service %s: %s\n", MISAGENT_SERVICE_NAME, lockdownd_strerror(lerr));
484 lockdownd_client_free(client);
485 idevice_free(device);
486 return -1;
487 }
488 lockdownd_client_free(client);
489 client = NULL;
490
491 misagent_client_t mis = NULL;
492 if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) {
493 fprintf(stderr, "Could not connect to %s on device\n", MISAGENT_SERVICE_NAME);
494 if (service)
495 lockdownd_service_descriptor_free(service);
496 lockdownd_client_free(client);
497 idevice_free(device);
498 return -1;
499 }
500
501 if (service)
502 lockdownd_service_descriptor_free(service);
503
504 switch (op) {
505 case OP_INSTALL:
506 {
507 unsigned char* profile_data = NULL;
508 unsigned int profile_size = 0;
509 if (profile_read_from_file(param, &profile_data, &profile_size) != 0) {
510 break;
511 }
512
513 uint64_t psize = profile_size;
514 plist_t pdata = plist_new_data((const char*)profile_data, psize);
515 free(profile_data);
516
517 if (misagent_install(mis, pdata) == MISAGENT_E_SUCCESS) {
518 printf("Profile '%s' installed successfully.\n", param);
519 } else {
520 int sc = misagent_get_status_code(mis);
521 fprintf(stderr, "Could not install profile '%s', status code: 0x%x\n", param, sc);
522 }
523 }
524 break;
525 case OP_LIST:
526 case OP_COPY:
527 {
528 plist_t profiles = NULL;
529 misagent_error_t merr;
530 if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) {
531 merr = misagent_copy(mis, &profiles);
532 } else {
533 merr = misagent_copy_all(mis, &profiles);
534 }
535 if (merr == MISAGENT_E_SUCCESS) {
536 int found_match = 0;
537 uint32_t num_profiles = plist_array_get_size(profiles);
538 if (op == OP_LIST || !param2) {
539 printf("Device has %d provisioning %s installed:\n", num_profiles, (num_profiles == 1) ? "profile" : "profiles");
540 }
541 uint32_t j;
542 for (j = 0; !found_match && j < num_profiles; j++) {
543 char* p_name = NULL;
544 char* p_uuid = NULL;
545 plist_t profile = plist_array_get_item(profiles, j);
546 plist_t pl = profile_get_embedded_plist(profile);
547 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
548 plist_t node;
549 node = plist_dict_get_item(pl, "Name");
550 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
551 plist_get_string_val(node, &p_name);
552 }
553 node = plist_dict_get_item(pl, "UUID");
554 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
555 plist_get_string_val(node, &p_uuid);
556 }
557 }
558 if (param2) {
559 if (p_uuid && !strcmp(p_uuid, param)) {
560 found_match = 1;
561 } else {
562 free(p_uuid);
563 free(p_name);
564 continue;
565 }
566 }
567 printf("%s - %s\n", (p_uuid) ? p_uuid : "(unknown id)", (p_name) ? p_name : "(no name)");
568 if (op == OP_COPY) {
569 char pfname[512];
570 if (p_uuid) {
571 sprintf(pfname, "%s/%s.mobileprovision", (param2) ? param2 : param, p_uuid);
572 } else {
573 sprintf(pfname, "%s/profile%d.mobileprovision", (param2) ? param2 : param, j);
574 }
575 FILE* f = fopen(pfname, "wb");
576 if (f) {
577 char* dt = NULL;
578 uint64_t ds = 0;
579 plist_get_data_val(profile, &dt, &ds);
580 fwrite(dt, 1, ds, f);
581 fclose(f);
582 printf(" => %s\n", pfname);
583 } else {
584 fprintf(stderr, "Could not open '%s' for writing: %s\n", pfname, strerror(errno));
585 }
586 }
587 free(p_uuid);
588 free(p_name);
589 }
590 if (param2 && !found_match) {
591 fprintf(stderr, "Profile '%s' was not found on the device.\n", param);
592 res = -1;
593 }
594 } else {
595 int sc = misagent_get_status_code(mis);
596 fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
597 res = -1;
598 }
599 plist_free(profiles);
600 }
601 break;
602 case OP_REMOVE:
603 if (param) {
604 /* remove specified provisioning profile */
605 if (misagent_remove(mis, param) == MISAGENT_E_SUCCESS) {
606 printf("Profile '%s' removed.\n", param);
607 } else {
608 int sc = misagent_get_status_code(mis);
609 fprintf(stderr, "Could not remove profile '%s', status code 0x%x\n", param, sc);
610 }
611 } else {
612 /* remove all provisioning profiles */
613 plist_t profiles = NULL;
614 misagent_error_t merr;
615 if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) {
616 merr = misagent_copy(mis, &profiles);
617 } else {
618 merr = misagent_copy_all(mis, &profiles);
619 }
620 if (merr == MISAGENT_E_SUCCESS) {
621 uint32_t j;
622 uint32_t num_removed = 0;
623 for (j = 0; j < plist_array_get_size(profiles); j++) {
624 char* p_name = NULL;
625 char* p_uuid = NULL;
626 plist_t profile = plist_array_get_item(profiles, j);
627 plist_t pl = profile_get_embedded_plist(profile);
628 if (pl && (plist_get_node_type(pl) == PLIST_DICT)) {
629 plist_t node;
630 node = plist_dict_get_item(pl, "Name");
631 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
632 plist_get_string_val(node, &p_name);
633 }
634 node = plist_dict_get_item(pl, "UUID");
635 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
636 plist_get_string_val(node, &p_uuid);
637 }
638 }
639 if (p_uuid) {
640 if (misagent_remove(mis, p_uuid) == MISAGENT_E_SUCCESS) {
641 printf("OK profile removed: %s - %s\n", p_uuid, (p_name) ? p_name : "(no name)");
642 num_removed++;
643 } else {
644 int sc = misagent_get_status_code(mis);
645 printf("FAIL profile not removed: %s - %s (status code 0x%x)\n", p_uuid, (p_name) ? p_name : "(no name)", sc);
646 }
647 }
648 free(p_name);
649 free(p_uuid);
650 }
651 printf("%d profiles removed.\n", num_removed);
652 } else {
653 int sc = misagent_get_status_code(mis);
654 fprintf(stderr, "Could not get installed profiles from device, status code: 0x%x\n", sc);
655 res = -1;
656 }
657 plist_free(profiles);
658 }
659 break;
660 default:
661 break;
662 }
663
664 misagent_client_free(mis);
665
666 idevice_free(device);
667
668 return res;
669}
670
diff --git a/tools/idevicescreenshot.c b/tools/idevicescreenshot.c
index 8567f77..bfaa059 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..dca8830
--- /dev/null
+++ b/tools/idevicesetlocation.c
@@ -0,0 +1,202 @@
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 = NULL;
142 lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
143 if (lerr != LOCKDOWN_E_SUCCESS) {
144 idevice_free(device);
145 printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(lerr), lerr);
146 return 1;
147 }
148
149 lockdownd_service_descriptor_t svc = NULL;
150 lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc);
151 if (lerr != LOCKDOWN_E_SUCCESS) {
152 unsigned int device_version = idevice_get_device_version(device);
153 lockdownd_client_free(lockdown);
154 idevice_free(device);
155 printf("ERROR: Could not start the simulatelocation service: %s\n", lockdownd_strerror(lerr));
156 if (device_version >= IDEVICE_DEVICE_VERSION(17,0,0)) {
157 printf("Note: This tool is currently not supported on iOS 17+\n");
158 } else {
159 printf("Make sure a developer disk image is mounted!\n");
160 }
161 return 1;
162 }
163 lockdownd_client_free(lockdown);
164
165 service_client_t service = NULL;
166
167 service_error_t serr = service_client_new(device, svc, &service);
168
169 lockdownd_service_descriptor_free(svc);
170
171 if (serr != SERVICE_E_SUCCESS) {
172 idevice_free(device);
173 printf("ERROR: Could not connect to simulatelocation service (%d)\n", serr);
174 return 1;
175 }
176
177 uint32_t l;
178 uint32_t s = 0;
179
180 l = htobe32(mode);
181 service_send(service, (const char*)&l, 4, &s);
182 if (mode == SET_LOCATION) {
183 int len = 4 + strlen(argv[0]) + 4 + strlen(argv[1]);
184 char *buf = malloc(len);
185 uint32_t latlen;
186 latlen = strlen(argv[0]);
187 l = htobe32(latlen);
188 memcpy(buf, &l, 4);
189 memcpy(buf+4, argv[0], latlen);
190 uint32_t longlen = strlen(argv[1]);
191 l = htobe32(longlen);
192 memcpy(buf+4+latlen, &l, 4);
193 memcpy(buf+4+latlen+4, argv[1], longlen);
194
195 s = 0;
196 service_send(service, buf, len, &s);
197 }
198
199 idevice_free(device);
200
201 return 0;
202}
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c
index f2b3a2f..88af4c1 100644
--- a/tools/idevicesyslog.c
+++ b/tools/idevicesyslog.c
@@ -2,169 +2,1309 @@
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#include <time.h>
37
38#ifdef _WIN32
39#include <windows.h>
40#define sleep(x) Sleep(x*1000)
41#endif
28 42
29#include <libimobiledevice/libimobiledevice.h> 43#include <libimobiledevice/libimobiledevice.h>
30#include <libimobiledevice/lockdown.h> 44#include <libimobiledevice/syslog_relay.h>
45#include <libimobiledevice-glue/termcolors.h>
46#include <libimobiledevice/ostrace.h>
31 47
32static int quit_flag = 0; 48static int quit_flag = 0;
49static int exit_on_disconnect = 0;
50static int show_device_name = 0;
51static int force_syslog_relay = 0;
33 52
34void print_usage(int argc, char **argv); 53static char* udid = NULL;
54static char** proc_filters = NULL;
55static int num_proc_filters = 0;
56static int proc_filter_excluding = 0;
35 57
36/** 58static int* pid_filters = NULL;
37 * signal handler function for cleaning up properly 59static int num_pid_filters = 0;
38 */ 60
39static void clean_exit(int sig) 61static char** msg_filters = NULL;
62static int num_msg_filters = 0;
63
64static char** msg_reverse_filters = NULL;
65static int num_msg_reverse_filters = 0;
66
67static char** trigger_filters = NULL;
68static int num_trigger_filters = 0;
69static char** untrigger_filters = NULL;
70static int num_untrigger_filters = 0;
71static int triggered = 0;
72
73static idevice_t device = NULL;
74static syslog_relay_client_t syslog = NULL;
75static ostrace_client_t ostrace = NULL;
76
77static 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";
78
79static int use_network = 0;
80
81static long long start_time = -1;
82static long long size_limit = -1;
83static long long age_limit = -1;
84
85static char *line = NULL;
86static int line_buffer_size = 0;
87static int lp = 0;
88
89static void add_filter(const char* filterstr)
40{ 90{
41 fprintf(stderr, "Exiting...\n"); 91 int filter_len = strlen(filterstr);
42 quit_flag++; 92 const char* start = filterstr;
93 const char* end = filterstr + filter_len;
94 const char* p = start;
95 while (p <= end) {
96 if ((*p == '|') || (*p == '\0')) {
97 if (p-start > 0) {
98 char* procn = malloc(p-start+1);
99 if (!procn) {
100 fprintf(stderr, "ERROR: malloc() failed\n");
101 exit(EXIT_FAILURE);
102 }
103 memcpy(procn, start, p-start);
104 procn[p-start] = '\0';
105 char* endp = NULL;
106 int pid_value = (int)strtol(procn, &endp, 10);
107 if (!endp || *endp == 0) {
108 int *new_pid_filters = realloc(pid_filters, sizeof(int) * (num_pid_filters+1));
109 if (!new_pid_filters) {
110 fprintf(stderr, "ERROR: realloc() failed\n");
111 exit(EXIT_FAILURE);
112 }
113 pid_filters = new_pid_filters;
114 pid_filters[num_pid_filters] = pid_value;
115 num_pid_filters++;
116 } else {
117 char **new_proc_filters = realloc(proc_filters, sizeof(char*) * (num_proc_filters+1));
118 if (!new_proc_filters) {
119 fprintf(stderr, "ERROR: realloc() failed\n");
120 exit(EXIT_FAILURE);
121 }
122 proc_filters = new_proc_filters;
123 proc_filters[num_proc_filters] = procn;
124 num_proc_filters++;
125 }
126 }
127 start = p+1;
128 }
129 p++;
130 }
43} 131}
44 132
45int main(int argc, char *argv[]) 133static int find_char(char c, char** p, const char* end)
46{ 134{
47 lockdownd_client_t client = NULL; 135 while ((**p != c) && (*p < end)) {
48 idevice_t phone = NULL; 136 (*p)++;
49 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; 137 }
50 int i; 138 return (**p == c);
51 char uuid[41]; 139}
52 uint16_t port = 0;
53 uuid[0] = 0;
54 140
55 signal(SIGINT, clean_exit); 141static void stop_logging(void);
56 signal(SIGQUIT, clean_exit);
57 signal(SIGTERM, clean_exit);
58 signal(SIGPIPE, SIG_IGN);
59 142
60 /* parse cmdline args */ 143static int message_filter_matching(const char* message)
61 for (i = 1; i < argc; i++) { 144{
62 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 145 if (num_msg_filters > 0) {
63 idevice_set_debug_level(1); 146 int found = 0;
64 continue; 147 int i;
65 } 148 for (i = 0; i < num_msg_filters; i++) {
66 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 149 if (strstr(message, msg_filters[i])) {
67 i++; 150 found = 1;
68 if (!argv[i] || (strlen(argv[i]) != 40)) { 151 break;
69 print_usage(argc, argv);
70 return 0;
71 } 152 }
72 strcpy(uuid, argv[i]);
73 continue;
74 } 153 }
75 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 154 if (!found) {
76 print_usage(argc, argv);
77 return 0; 155 return 0;
78 } 156 }
79 else { 157 }
80 print_usage(argc, argv); 158 if (num_msg_reverse_filters > 0) {
159 int found = 0;
160 int i;
161 for (i = 0; i < num_msg_reverse_filters; i++) {
162 if (strstr(message, msg_reverse_filters[i])) {
163 found = 1;
164 break;
165 }
166 }
167 if (found) {
81 return 0; 168 return 0;
82 } 169 }
83 } 170 }
171 return 1;
172}
84 173
85 if (uuid[0] != 0) { 174static int process_filter_matching(int pid, const char* process_name, int process_name_length)
86 ret = idevice_new(&phone, uuid); 175{
87 if (ret != IDEVICE_E_SUCCESS) { 176 int proc_matched = 0;
88 printf("No device found with uuid %s, is it plugged in?\n", uuid); 177 if (num_pid_filters > 0) {
89 return -1; 178 int found = proc_filter_excluding;
179 int i = 0;
180 for (i = 0; i < num_pid_filters; i++) {
181 if (pid == pid_filters[i]) {
182 found = !proc_filter_excluding;
183 break;
184 }
185 }
186 if (found) {
187 proc_matched = 1;
90 } 188 }
91 } 189 }
92 else 190 if (num_proc_filters > 0 && !proc_matched) {
93 { 191 int found = proc_filter_excluding;
94 ret = idevice_new(&phone, NULL); 192 int i = 0;
95 if (ret != IDEVICE_E_SUCCESS) { 193 for (i = 0; i < num_proc_filters; i++) {
96 printf("No device found, is it plugged in?\n"); 194 if (!proc_filters[i]) continue;
97 return -1; 195 if (strncmp(proc_filters[i], process_name, process_name_length) == 0) {
196 found = !proc_filter_excluding;
197 break;
198 }
199 }
200 if (found) {
201 proc_matched = 1;
98 } 202 }
99 } 203 }
204 return proc_matched;
205}
100 206
101 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "idevicesyslog")) { 207static void syslog_callback(char c, void *user_data)
102 idevice_free(phone); 208{
103 return -1; 209 if (lp >= line_buffer_size-1) {
210 line_buffer_size+=1024;
211 char* _line = realloc(line, line_buffer_size);
212 if (!_line) {
213 fprintf(stderr, "ERROR: realloc failed\n");
214 exit(EXIT_FAILURE);
215 }
216 line = _line;
104 } 217 }
218 line[lp++] = c;
219 if (c == '\0') {
220 int shall_print = 0;
221 int trigger_off = 0;
222 lp--;
223 char* linep = &line[0];
224 do {
225 if (lp < 16) {
226 shall_print = 1;
227 cprintf(FG_WHITE);
228 break;
229 }
105 230
106 /* start syslog_relay service and retrieve port */ 231 if (line[3] == ' ' && line[6] == ' ' && line[15] == ' ') {
107 ret = lockdownd_start_service(client, "com.apple.syslog_relay", &port); 232 char* end = &line[lp];
108 if ((ret == LOCKDOWN_E_SUCCESS) && port) { 233 char* p = &line[16];
109 lockdownd_client_free(client); 234
110 235 /* device name */
111 /* connect to socket relay messages */ 236 char* device_name_start = p;
112 idevice_connection_t conn = NULL; 237 char* device_name_end = p;
113 if ((idevice_connect(phone, port, &conn) != IDEVICE_E_SUCCESS) || !conn) { 238 if (!find_char(' ', &p, end)) break;
114 printf("ERROR: Could not open usbmux connection.\n"); 239 device_name_end = p;
115 } else { 240 p++;
116 while (!quit_flag) {
117 char *receive = NULL;
118 uint32_t datalen = 0, bytes = 0, recv_bytes = 0;
119 241
120 ret = idevice_connection_receive(conn, (char *) &datalen, sizeof(datalen), &bytes); 242 /* check if we have any triggers/untriggers */
121 if (ret < 0) { 243 if (num_untrigger_filters > 0 && triggered) {
122 fprintf(stderr, "Error receiving data. Exiting...\n"); 244 int found = 0;
245 int i;
246 for (i = 0; i < num_untrigger_filters; i++) {
247 if (strstr(device_name_end+1, untrigger_filters[i])) {
248 found = 1;
249 break;
250 }
251 }
252 if (!found) {
253 shall_print = 1;
254 } else {
255 shall_print = 1;
256 trigger_off = 1;
257 }
258 } else if (num_trigger_filters > 0 && !triggered) {
259 int found = 0;
260 int i;
261 for (i = 0; i < num_trigger_filters; i++) {
262 if (strstr(device_name_end+1, trigger_filters[i])) {
263 found = 1;
264 break;
265 }
266 }
267 if (!found) {
268 shall_print = 0;
269 break;
270 }
271 triggered = 1;
272 shall_print = 1;
273 } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) {
274 shall_print = 0;
275 quit_flag++;
123 break; 276 break;
124 } 277 }
125 278
126 datalen = GUINT32_FROM_BE(datalen); 279 /* check message filters */
280 shall_print = message_filter_matching(device_name_end+1);
281 if (!shall_print) {
282 break;
283 }
127 284
128 if (datalen == 0) 285 /* process name */
129 continue; 286 char* proc_name_start = p;
287 char* proc_name_end = p;
288 if (!find_char('[', &p, end)) break;
289 char* process_name_start = proc_name_start;
290 char* process_name_end = p;
291 char* pid_start = p+1;
292 char* pp = process_name_start;
293 if (find_char('(', &pp, p)) {
294 process_name_end = pp;
295 }
296 if (!find_char(']', &p, end)) break;
297 p++;
298 if (*p != ' ') break;
299 proc_name_end = p;
300 p++;
130 301
131 recv_bytes += bytes; 302 /* match pid / process name */
132 receive = (char *) malloc(sizeof(char) * datalen); 303 char* endp = NULL;
304 int pid_value = (int)strtol(pid_start, &endp, 10);
305 if (process_filter_matching(pid_value, process_name_start, process_name_end-process_name_start)) {
306 shall_print = 1;
307 } else {
308 if (num_pid_filters > 0 || num_proc_filters > 0) {
309 shall_print = 0;
310 break;
311 }
312 }
133 313
134 while (!quit_flag && (recv_bytes <= datalen)) { 314 /* log level */
135 ret = idevice_connection_receive(conn, receive, datalen, &bytes); 315 char* level_start = p;
316 char* level_end = p;
317 const char* level_color = NULL;
318 if (!strncmp(p, "<Notice>:", 9)) {
319 level_end += 9;
320 level_color = FG_GREEN;
321 } else if (!strncmp(p, "<Error>:", 8)) {
322 level_end += 8;
323 level_color = FG_RED;
324 } else if (!strncmp(p, "<Warning>:", 10)) {
325 level_end += 10;
326 level_color = FG_YELLOW;
327 } else if (!strncmp(p, "<Debug>:", 8)) {
328 level_end += 8;
329 level_color = FG_MAGENTA;
330 } else {
331 level_color = FG_WHITE;
332 }
136 333
137 if (bytes == 0) 334 /* write date and time */
138 break; 335 cprintf(FG_LIGHT_GRAY);
336 fwrite(line, 1, 16, stdout);
337
338 if (show_device_name) {
339 /* write device name */
340 cprintf(FG_DARK_YELLOW);
341 fwrite(device_name_start, 1, device_name_end-device_name_start+1, stdout);
342 cprintf(COLOR_RESET);
343 }
139 344
140 recv_bytes += bytes; 345 /* write process name */
346 cprintf(FG_BRIGHT_CYAN);
347 fwrite(process_name_start, 1, process_name_end-process_name_start, stdout);
348 cprintf(FG_CYAN);
349 fwrite(process_name_end, 1, proc_name_end-process_name_end+1, stdout);
141 350
142 fwrite(receive, sizeof(char), bytes, stdout); 351 /* write log level */
352 cprintf(level_color);
353 if (level_end > level_start) {
354 fwrite(level_start, 1, level_end-level_start, stdout);
355 p = level_end;
143 } 356 }
144 357
145 free(receive); 358 lp -= p - linep;
359 linep = p;
360
361 cprintf(FG_WHITE);
362
363 } else {
364 shall_print = 1;
365 cprintf(FG_WHITE);
366 }
367 } while (0);
368
369 if ((num_msg_filters == 0 && num_msg_reverse_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) {
370 fwrite(linep, 1, lp, stdout);
371 cprintf(COLOR_RESET);
372 fflush(stdout);
373 if (trigger_off) {
374 triggered = 0;
375 }
376 }
377 line[0] = '\0';
378 lp = 0;
379 return;
380 }
381}
382
383static void ostrace_syslog_callback(const void* buf, size_t len, void* user_data)
384{
385 if (len < 0x81) {
386 fprintf(stderr, "Error: not enough data in callback function?!\n");
387 return;
388 }
389
390 struct ostrace_packet_header_t *trace_hdr = (struct ostrace_packet_header_t*)buf;
391
392 if (trace_hdr->marker != 2 || (trace_hdr->type != 8 && trace_hdr->type != 2)) {
393 fprintf(stderr, "unexpected packet data %02x %08x\n", trace_hdr->marker, trace_hdr->type);
394 }
395
396 const char* dataptr = (const char*)buf + trace_hdr->header_size;
397 const char* process_name = dataptr;
398 const char* image_name = (trace_hdr->imagepath_len > 0) ? dataptr + trace_hdr->procpath_len : NULL;
399 const char* message = (trace_hdr->message_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len : NULL;
400 //const char* subsystem = (trace_hdr->subsystem_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len : NULL;
401 //const char* category = (trace_hdr->category_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len + trace_hdr->subsystem_len : NULL;
402
403 int shall_print = 1;
404 int trigger_off = 0;
405 const char* process_name_short = (process_name) ? strrchr(process_name, '/') : "";
406 process_name_short = (process_name_short) ? process_name_short+1 : process_name;
407 const char* image_name_short = (image_name) ? strrchr(image_name, '/') : NULL;
408 image_name_short = (image_name_short) ? image_name_short+1 : process_name;
409 if (image_name_short && !strcmp(image_name_short, process_name_short)) {
410 image_name_short = NULL;
411 }
412
413 do {
414 /* check if we have any triggers/untriggers */
415 if (num_untrigger_filters > 0 && triggered) {
416 int found = 0;
417 int i;
418 for (i = 0; i < num_untrigger_filters; i++) {
419 if (strstr(message, untrigger_filters[i])) {
420 found = 1;
421 break;
422 }
423 }
424 if (!found) {
425 shall_print = 1;
426 } else {
427 shall_print = 1;
428 trigger_off = 1;
429 }
430 } else if (num_trigger_filters > 0 && !triggered) {
431 int found = 0;
432 int i;
433 for (i = 0; i < num_trigger_filters; i++) {
434 if (strstr(message, trigger_filters[i])) {
435 found = 1;
436 break;
437 }
438 }
439 if (!found) {
440 shall_print = 0;
441 break;
442 }
443 triggered = 1;
444 shall_print = 1;
445 } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) {
446 shall_print = 0;
447 quit_flag++;
448 break;
449 }
450
451 /* check message filters */
452 shall_print = message_filter_matching(message);
453 if (!shall_print) {
454 break;
455 }
456
457 /* check process filters */
458 if (process_filter_matching(trace_hdr->pid, process_name_short, strlen(process_name_short))) {
459 shall_print = 1;
460 } else {
461 if (num_pid_filters > 0 || num_proc_filters > 0) {
462 shall_print = 0;
463 }
464 }
465 if (!shall_print) {
466 break;
467 }
468 } while (0);
469
470 if (!shall_print) {
471 return;
472 }
473
474 const char* level_str = "Unknown";
475 const char* level_color = FG_YELLOW;
476 switch (trace_hdr->level) {
477 case 0:
478 level_str = "Notice";
479 level_color = FG_GREEN;
480 break;
481 case 0x01:
482 level_str = "Info";
483 level_color = FG_WHITE;
484 break;
485 case 0x02:
486 level_str = "Debug";
487 level_color = FG_MAGENTA;
488 break;
489 case 0x10:
490 level_str = "Error";
491 level_color = FG_RED;
492 break;
493 case 0x11:
494 level_str = "Fault";
495 level_color = FG_RED;
496 default:
497 break;
498 }
499
500 char datebuf[24];
501 struct tm *tp;
502 time_t time_sec = (time_t)trace_hdr->time_sec;
503#ifdef HAVE_LOCALTIME_R
504 struct tm tp_ = {0, };
505 tp = localtime_r(&time_sec, &tp_);
506#else
507 tp = localtime(&time_sec);
508#endif
509#ifdef _WIN32
510 strftime(datebuf, 16, "%b %#d %H:%M:%S", tp);
511#else
512 strftime(datebuf, 16, "%b %e %H:%M:%S", tp);
513#endif
514 snprintf(datebuf+15, 9, ".%06u", trace_hdr->time_usec);
515
516 /* write date and time */
517 cprintf(FG_LIGHT_GRAY "%s ", datebuf);
518
519 if (show_device_name) {
520 /* write device name TODO do we need this? */
521 //cprintf(FG_DARK_YELLOW "%s ", device_name);
522 }
523
524 /* write process name */
525 cprintf(FG_BRIGHT_CYAN "%s" FG_CYAN, process_name_short);
526 if (image_name_short) {
527 cprintf("(%s)", image_name_short);
528 }
529 cprintf("[%d]" COLOR_RESET " ", trace_hdr->pid);
530
531 /* write log level */
532 cprintf(level_color);
533 cprintf("<%s>:" COLOR_RESET " ", level_str);
534
535 /* write message */
536 cprintf(FG_WHITE);
537 cprintf("%s" COLOR_RESET "\n", message);
538 fflush(stdout);
539
540 if (trigger_off) {
541 triggered = 0;
542 }
543}
544
545static plist_t get_pid_list()
546{
547 plist_t list = NULL;
548 ostrace_client_t ostrace_tmp = NULL;
549 ostrace_client_start_service(device, &ostrace_tmp, TOOL_NAME);
550 if (ostrace_tmp) {
551 ostrace_get_pid_list(ostrace_tmp, &list);
552 ostrace_client_free(ostrace_tmp);
553 }
554 return list;
555}
556
557static int pid_valid(int pid)
558{
559 plist_t list = get_pid_list();
560 if (!list) return 0;
561 char valbuf[16];
562 snprintf(valbuf, 16, "%d", pid);
563 return (plist_dict_get_item(list, valbuf)) ? 1 : 0;
564}
565
566static int pid_for_proc(const char* procname)
567{
568 int result = -1;
569 plist_t list = get_pid_list();
570 if (!list) {
571 return result;
572 }
573 plist_dict_iter iter = NULL;
574 plist_dict_new_iter(list, &iter);
575 if (iter) {
576 plist_t node = NULL;
577 do {
578 char* key = NULL;
579 node = NULL;
580 plist_dict_next_item(list, iter, &key, &node);
581 if (!key) {
582 break;
583 }
584 if (PLIST_IS_DICT(node)) {
585 plist_t pname = plist_dict_get_item(node, "ProcessName");
586 if (PLIST_IS_STRING(pname)) {
587 if (!strcmp(plist_get_string_ptr(pname, NULL), procname)) {
588 result = (int)strtol(key, NULL, 10);
589 }
590 }
591 }
592 free(key);
593 } while (node);
594 plist_mem_free(iter);
595 }
596 plist_free(list);
597 return result;
598}
599
600static int connect_service(int ostrace_required)
601{
602 if (!device) {
603 idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
604 if (ret != IDEVICE_E_SUCCESS) {
605 fprintf(stderr, "Device with udid %s not found!?\n", udid);
606 return -1;
607 }
608 }
609
610 lockdownd_client_t lockdown = NULL;
611 lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
612 if (lerr != LOCKDOWN_E_SUCCESS) {
613 fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
614 idevice_free(device);
615 device = NULL;
616 return -1;
617 }
618 lockdownd_service_descriptor_t svc = NULL;
619
620 const char* service_name = OSTRACE_SERVICE_NAME;
621 int use_ostrace = 1;
622 if (idevice_get_device_version(device) < IDEVICE_DEVICE_VERSION(9,0,0) || force_syslog_relay) {
623 service_name = SYSLOG_RELAY_SERVICE_NAME;
624 use_ostrace = 0;
625 }
626 if (ostrace_required && !use_ostrace) {
627 fprintf(stderr, "ERROR: This operation requires iOS 9 or later.\n");
628 lockdownd_client_free(lockdown);
629 idevice_free(device);
630 device = NULL;
631 return -1;
632 }
633
634 /* start syslog_relay/os_trace_relay service */
635 lerr = lockdownd_start_service(lockdown, service_name, &svc);
636 if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
637 fprintf(stderr, "*** Device is passcode protected, enter passcode on the device to continue ***\n");
638 while (!quit_flag) {
639 lerr = lockdownd_start_service(lockdown, service_name, &svc);
640 if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) {
641 break;
642 }
643 sleep(1);
644 }
645 }
646 if (lerr != LOCKDOWN_E_SUCCESS) {
647 fprintf(stderr, "ERROR: Could not start %s service: %s (%d)\n", service_name, lockdownd_strerror(lerr), lerr);
648 idevice_free(device);
649 device = NULL;
650 return -1;
651 }
652 lockdownd_client_free(lockdown);
653
654 if (use_ostrace) {
655 /* connect to os_trace_relay service */
656 ostrace_error_t serr = OSTRACE_E_UNKNOWN_ERROR;
657 serr = ostrace_client_new(device, svc, &ostrace);
658 lockdownd_service_descriptor_free(svc);
659 if (serr != OSTRACE_E_SUCCESS) {
660 fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr);
661 idevice_free(device);
662 device = NULL;
663 return -1;
664 }
665 } else {
666 /* connect to syslog_relay service */
667 syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR;
668 serr = syslog_relay_client_new(device, svc, &syslog);
669 lockdownd_service_descriptor_free(svc);
670 if (serr != SYSLOG_RELAY_E_SUCCESS) {
671 fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr);
672 idevice_free(device);
673 device = NULL;
674 return -1;
675 }
676 }
677 return 0;
678}
679
680static int start_logging(void)
681{
682 if (connect_service(0) < 0) {
683 return -1;
684 }
685
686 /* start capturing syslog */
687 if (ostrace) {
688 plist_t options = plist_new_dict();
689 if (num_proc_filters == 0 && num_pid_filters == 1 && !proc_filter_excluding) {
690 if (pid_filters[0] > 0) {
691 if (!pid_valid(pid_filters[0])) {
692 fprintf(stderr, "NOTE: A process with pid doesn't exists!\n");
693 }
694 }
695 plist_dict_set_item(options, "Pid", plist_new_int(pid_filters[0]));
696 } else if (num_proc_filters == 1 && num_pid_filters == 0 && !proc_filter_excluding) {
697 int pid = pid_for_proc(proc_filters[0]);
698 if (!strcmp(proc_filters[0], "kernel")) {
699 pid = 0;
146 } 700 }
701 if (pid >= 0) {
702 plist_dict_set_item(options, "Pid", plist_new_int(pid));
703 }
704 }
705 ostrace_error_t serr = ostrace_start_activity(ostrace, options, ostrace_syslog_callback, NULL);
706 if (serr != OSTRACE_E_SUCCESS) {
707 fprintf(stderr, "ERROR: Unable to start capturing syslog.\n");
708 ostrace_client_free(ostrace);
709 ostrace = NULL;
710 idevice_free(device);
711 device = NULL;
712 return -1;
713 }
714 } else if (syslog) {
715 syslog_relay_error_t serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL);
716 if (serr != SYSLOG_RELAY_E_SUCCESS) {
717 fprintf(stderr, "ERROR: Unable to start capturing syslog.\n");
718 syslog_relay_client_free(syslog);
719 syslog = NULL;
720 idevice_free(device);
721 device = NULL;
722 return -1;
147 } 723 }
148 idevice_disconnect(conn);
149 } else { 724 } else {
150 printf("ERROR: Could not start service com.apple.syslog_relay.\n"); 725 return -1;
151 } 726 }
152 727
153 idevice_free(phone); 728 fprintf(stdout, "[connected:%s]\n", udid);
729 fflush(stdout);
154 730
155 return 0; 731 return 0;
156} 732}
157 733
158void print_usage(int argc, char **argv) 734static void stop_logging(void)
159{ 735{
160 char *name = NULL; 736 fflush(stdout);
161 737
162 name = strrchr(argv[0], '/'); 738 if (syslog) {
163 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0])); 739 syslog_relay_client_free(syslog);
164 printf("Relay syslog of a connected iPhone/iPod Touch.\n\n"); 740 syslog = NULL;
165 printf(" -d, --debug\t\tenable communication debugging\n"); 741 }
166 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); 742 if (ostrace) {
167 printf(" -h, --help\t\tprints usage information\n"); 743 ostrace_stop_activity(ostrace);
168 printf("\n"); 744 ostrace_client_free(ostrace);
745 ostrace = NULL;
746 }
747
748 if (device) {
749 idevice_free(device);
750 device = NULL;
751 }
752}
753
754static int write_callback(const void* buf, size_t len, void *user_data)
755{
756 FILE* f = (FILE*)user_data;
757 ssize_t res = fwrite(buf, 1, len, f);
758 if (res < 0) {
759 return -1;
760 }
761 if (quit_flag > 0) {
762 return -1;
763 }
764 return 0;
765}
766
767static void print_sorted_pidlist(plist_t list)
768{
769 struct listelem;
770 struct listelem {
771 int val;
772 struct listelem *next;
773 };
774 struct listelem* sortedlist = NULL;
775
776 plist_dict_iter iter = NULL;
777 plist_dict_new_iter(list, &iter);
778 if (iter) {
779 plist_t node = NULL;
780 do {
781 char* key = NULL;
782 node = NULL;
783 plist_dict_next_item(list, iter, &key, &node);
784 if (key) {
785 int pidval = (int)strtol(key, NULL, 10);
786 struct listelem* elem = (struct listelem*)malloc(sizeof(struct listelem));
787 elem->val = pidval;
788 elem->next = NULL;
789 struct listelem* prev = NULL;
790 struct listelem* curr = sortedlist;
791
792 while (curr && pidval > curr->val) {
793 prev = curr;
794 curr = curr->next;
795 }
796
797 elem->next = curr;
798 if (prev == NULL) {
799 sortedlist = elem;
800 } else {
801 prev->next = elem;
802 }
803 free(key);
804 }
805 } while (node);
806 plist_mem_free(iter);
807 }
808 struct listelem *listp = sortedlist;
809 char pidstr[16];
810 while (listp) {
811 snprintf(pidstr, 16, "%d", listp->val);
812 plist_t node = plist_dict_get_item(list, pidstr);
813 if (PLIST_IS_DICT(node)) {
814 plist_t pname = plist_dict_get_item(node, "ProcessName");
815 if (PLIST_IS_STRING(pname)) {
816 printf("%d %s\n", listp->val, plist_get_string_ptr(pname, NULL));
817 }
818 }
819 struct listelem *curr = listp;
820 listp = listp->next;
821 free(curr);
822 }
823}
824
825static void device_event_cb(const idevice_event_t* event, void* userdata)
826{
827 if (use_network && event->conn_type != CONNECTION_NETWORK) {
828 return;
829 }
830 if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
831 return;
832 }
833 if (event->event == IDEVICE_DEVICE_ADD) {
834 if (!syslog && !ostrace) {
835 if (!udid) {
836 udid = strdup(event->udid);
837 }
838 if (strcmp(udid, event->udid) == 0) {
839 if (start_logging() != 0) {
840 fprintf(stderr, "Could not start logger for udid %s\n", udid);
841 }
842 }
843 }
844 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
845 if ((syslog || ostrace) && (strcmp(udid, event->udid) == 0)) {
846 stop_logging();
847 fprintf(stdout, "[disconnected:%s]\n", udid);
848 if (exit_on_disconnect) {
849 quit_flag++;
850 }
851 }
852 }
169} 853}
170 854
855/**
856 * signal handler function for cleaning up properly
857 */
858static void clean_exit(int sig)
859{
860 fprintf(stderr, "\nExiting...\n");
861 quit_flag++;
862}
863
864static void print_usage(int argc, char **argv, int is_error)
865{
866 char *name = strrchr(argv[0], '/');
867 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
868 fprintf(is_error ? stderr : stdout,
869 "\n"
870 "Relay syslog of a connected device.\n"
871 "\n"
872 "OPTIONS:\n"
873 " -u, --udid UDID target specific device by UDID\n"
874 " -n, --network connect to network device\n"
875 " -x, --exit exit when device disconnects\n"
876 " -h, --help prints usage information\n"
877 " -d, --debug enable communication debugging\n"
878 " -v, --version prints version information\n"
879 " --no-colors disable colored output\n"
880 " -o, --output FILE write to FILE instead of stdout\n"
881 " (existing FILE will be overwritten)\n"
882 " --colors force writing colored output, e.g. for --output\n"
883 " --syslog-relay force use of syslog_relay service\n"
884 "\n"
885 "COMMANDS:\n"
886 " pidlist Print pid and name of all running processes.\n"
887 " archive PATH Request a logarchive and write it to PATH.\n"
888 " Output can be piped to another process using - as PATH.\n"
889 " The file data will be in .tar format.\n"
890 " --start-time VALUE start time of the log data as UNIX timestamp\n"
891 " --age-limit VALUE maximum age of the log data\n"
892 " --size-limit VALUE limit the size of the archive\n"
893 "\n"
894 "FILTER OPTIONS:\n"
895 " -m, --match STRING only print messages that contain STRING\n"
896 " -M, --unmatch STRING print messages that not contain STRING\n"
897 " -t, --trigger STRING start logging when matching STRING\n"
898 " -T, --untrigger STRING stop logging when matching STRING\n"
899 " -p, --process PROCESS only print messages from matching process(es)\n"
900 " -e, --exclude PROCESS print all messages except matching process(es)\n"
901 " PROCESS is a process name or multiple process names\n"
902 " separated by \"|\".\n"
903 " -q, --quiet set a filter to exclude common noisy processes\n"
904 " --quiet-list prints the list of processes for --quiet and exits\n"
905 " -k, --kernel only print kernel messages\n"
906 " -K, --no-kernel suppress kernel messages\n"
907 "\n"
908 "For filter examples consult idevicesyslog(1) man page.\n"
909 "\n"
910 "Homepage: <" PACKAGE_URL ">\n"
911 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
912 );
913}
914
915int main(int argc, char *argv[])
916{
917 int include_filter = 0;
918 int exclude_filter = 0;
919 int include_kernel = 0;
920 int exclude_kernel = 0;
921 int force_colors = 0;
922 int c = 0;
923 const struct option longopts[] = {
924 { "debug", no_argument, NULL, 'd' },
925 { "help", no_argument, NULL, 'h' },
926 { "udid", required_argument, NULL, 'u' },
927 { "network", no_argument, NULL, 'n' },
928 { "exit", no_argument, NULL, 'x' },
929 { "trigger", required_argument, NULL, 't' },
930 { "untrigger", required_argument, NULL, 'T' },
931 { "match", required_argument, NULL, 'm' },
932 { "process", required_argument, NULL, 'p' },
933 { "exclude", required_argument, NULL, 'e' },
934 { "quiet", no_argument, NULL, 'q' },
935 { "kernel", no_argument, NULL, 'k' },
936 { "no-kernel", no_argument, NULL, 'K' },
937 { "quiet-list", no_argument, NULL, 1 },
938 { "no-colors", no_argument, NULL, 2 },
939 { "colors", no_argument, NULL, 3 },
940 { "syslog_relay", no_argument, NULL, 4 },
941 { "syslog-relay", no_argument, NULL, 4 },
942 { "legacy", no_argument, NULL, 4 },
943 { "start-time", required_argument, NULL, 5 },
944 { "size-limit", required_argument, NULL, 6 },
945 { "age-limit", required_argument, NULL, 7 },
946 { "output", required_argument, NULL, 'o' },
947 { "version", no_argument, NULL, 'v' },
948 { NULL, 0, NULL, 0}
949 };
950
951 signal(SIGINT, clean_exit);
952 signal(SIGTERM, clean_exit);
953#ifndef _WIN32
954 signal(SIGQUIT, clean_exit);
955 signal(SIGPIPE, SIG_IGN);
956#endif
957
958 while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:M:e:p:qkKo:v", longopts, NULL)) != -1) {
959 switch (c) {
960 case 'd':
961 idevice_set_debug_level(1);
962 break;
963 case 'u':
964 if (!*optarg) {
965 fprintf(stderr, "ERROR: UDID must not be empty!\n");
966 print_usage(argc, argv, 1);
967 return 2;
968 }
969 free(udid);
970 udid = strdup(optarg);
971 break;
972 case 'n':
973 use_network = 1;
974 break;
975 case 'q':
976 exclude_filter++;
977 add_filter(QUIET_FILTER);
978 break;
979 case 'p':
980 case 'e':
981 if (c == 'p') {
982 include_filter++;
983 } else if (c == 'e') {
984 exclude_filter++;
985 }
986 if (!*optarg) {
987 fprintf(stderr, "ERROR: filter string must not be empty!\n");
988 print_usage(argc, argv, 1);
989 return 2;
990 }
991 add_filter(optarg);
992 break;
993 case 'm':
994 if (!*optarg) {
995 fprintf(stderr, "ERROR: message filter string must not be empty!\n");
996 print_usage(argc, argv, 1);
997 return 2;
998 } else {
999 char **new_msg_filters = realloc(msg_filters, sizeof(char*) * (num_msg_filters+1));
1000 if (!new_msg_filters) {
1001 fprintf(stderr, "ERROR: realloc() failed\n");
1002 exit(EXIT_FAILURE);
1003 }
1004 msg_filters = new_msg_filters;
1005 msg_filters[num_msg_filters] = strdup(optarg);
1006 num_msg_filters++;
1007 }
1008 break;
1009 case 'M':
1010 if (!*optarg) {
1011 fprintf(stderr, "ERROR: reverse message filter string must not be empty!\n");
1012 print_usage(argc, argv, 1);
1013 return 2;
1014 } else {
1015 char **new_msg_filters = realloc(msg_reverse_filters, sizeof(char*) * (num_msg_reverse_filters+1));
1016 if (!new_msg_filters) {
1017 fprintf(stderr, "ERROR: realloc() failed\n");
1018 exit(EXIT_FAILURE);
1019 }
1020 msg_reverse_filters = new_msg_filters;
1021 msg_reverse_filters[num_msg_reverse_filters] = strdup(optarg);
1022 num_msg_reverse_filters++;
1023 }
1024 break;
1025 case 't':
1026 if (!*optarg) {
1027 fprintf(stderr, "ERROR: trigger filter string must not be empty!\n");
1028 print_usage(argc, argv, 1);
1029 return 2;
1030 } else {
1031 char **new_trigger_filters = realloc(trigger_filters, sizeof(char*) * (num_trigger_filters+1));
1032 if (!new_trigger_filters) {
1033 fprintf(stderr, "ERROR: realloc() failed\n");
1034 exit(EXIT_FAILURE);
1035 }
1036 trigger_filters = new_trigger_filters;
1037 trigger_filters[num_trigger_filters] = strdup(optarg);
1038 num_trigger_filters++;
1039 }
1040 break;
1041 case 'T':
1042 if (!*optarg) {
1043 fprintf(stderr, "ERROR: untrigger filter string must not be empty!\n");
1044 print_usage(argc, argv, 1);
1045 return 2;
1046 } else {
1047 char **new_untrigger_filters = realloc(untrigger_filters, sizeof(char*) * (num_untrigger_filters+1));
1048 if (!new_untrigger_filters) {
1049 fprintf(stderr, "ERROR: realloc() failed\n");
1050 exit(EXIT_FAILURE);
1051 }
1052 untrigger_filters = new_untrigger_filters;
1053 untrigger_filters[num_untrigger_filters] = strdup(optarg);
1054 num_untrigger_filters++;
1055 }
1056 break;
1057 case 'k':
1058 include_kernel++;
1059 break;
1060 case 'K':
1061 exclude_kernel++;
1062 break;
1063 case 'x':
1064 exit_on_disconnect = 1;
1065 break;
1066 case 'h':
1067 print_usage(argc, argv, 0);
1068 return 0;
1069 case 1: {
1070 printf("%s\n", QUIET_FILTER);
1071 return 0;
1072 }
1073 case 2:
1074 term_colors_set_enabled(0);
1075 break;
1076 case 3:
1077 force_colors = 1;
1078 break;
1079 case 4:
1080 force_syslog_relay = 1;
1081 break;
1082 case 5:
1083 start_time = strtoll(optarg, NULL, 10);
1084 break;
1085 case 6:
1086 size_limit = strtoll(optarg, NULL, 10);
1087 break;
1088 case 7:
1089 age_limit = strtoll(optarg, NULL, 10);
1090 break;
1091 case 'o':
1092 if (!*optarg) {
1093 fprintf(stderr, "ERROR: --output option requires an argument!\n");
1094 print_usage(argc, argv, 1);
1095 return 2;
1096 } else {
1097 if (freopen(optarg, "w", stdout) == NULL) {
1098 fprintf(stderr, "ERROR: Failed to open output file '%s' for writing: %s\n", optarg, strerror(errno));
1099 return 1;
1100 }
1101 term_colors_set_enabled(0);
1102 }
1103 break;
1104 case 'v':
1105 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
1106 return 0;
1107 default:
1108 print_usage(argc, argv, 1);
1109 return 2;
1110 }
1111 }
1112
1113 if (force_colors) {
1114 term_colors_set_enabled(1);
1115 }
1116
1117 if (include_kernel > 0 && exclude_kernel > 0) {
1118 fprintf(stderr, "ERROR: -k and -K cannot be used together.\n");
1119 print_usage(argc, argv, 1);
1120 return 2;
1121 }
1122
1123 if (include_filter > 0 && exclude_filter > 0) {
1124 fprintf(stderr, "ERROR: -p and -e/-q cannot be used together.\n");
1125 print_usage(argc, argv, 1);
1126 return 2;
1127 }
1128 if (include_filter > 0 && exclude_kernel > 0) {
1129 fprintf(stderr, "ERROR: -p and -K cannot be used together.\n");
1130 print_usage(argc, argv, 1);
1131 return 2;
1132 }
1133
1134 if (exclude_filter > 0) {
1135 proc_filter_excluding = 1;
1136 if (include_kernel) {
1137 int i = 0;
1138 for (i = 0; i < num_proc_filters; i++) {
1139 if (!strcmp(proc_filters[i], "kernel")) {
1140 free(proc_filters[i]);
1141 proc_filters[i] = NULL;
1142 }
1143 }
1144 } else if (exclude_kernel) {
1145 add_filter("kernel");
1146 }
1147 } else {
1148 if (include_kernel) {
1149 add_filter("kernel");
1150 } else if (exclude_kernel) {
1151 proc_filter_excluding = 1;
1152 add_filter("kernel");
1153 }
1154 }
1155
1156 if (num_untrigger_filters > 0 && num_trigger_filters == 0) {
1157 triggered = 1;
1158 }
1159
1160 argc -= optind;
1161 argv += optind;
1162
1163 if (argc > 0) {
1164 if (!strcmp(argv[0], "pidlist")) {
1165 if (connect_service(1) < 0) {
1166 return 1;
1167 }
1168 plist_t list = NULL;
1169 ostrace_get_pid_list(ostrace, &list);
1170 ostrace_client_free(ostrace);
1171 ostrace = NULL;
1172 idevice_free(device);
1173 device = NULL;
1174 if (!list) {
1175 return 1;
1176 }
1177 print_sorted_pidlist(list);
1178 plist_free(list);
1179 return 0;
1180 } else if (!strcmp(argv[0], "archive")) {
1181 if (force_syslog_relay) {
1182 force_syslog_relay = 0;
1183 }
1184 if (argc < 2) {
1185 fprintf(stderr, "Please specify an output filename.\n");
1186 return 1;
1187 }
1188 FILE* outf = NULL;
1189 if (!strcmp(argv[1], "-")) {
1190 if (isatty(1)) {
1191 fprintf(stderr, "Refusing to directly write to stdout. Pipe the output to another process.\n");
1192 return 1;
1193 }
1194 outf = stdout;
1195 } else {
1196 outf = fopen(argv[1], "w");
1197 }
1198 if (!outf) {
1199 fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno));
1200 return 1;
1201 }
1202 if (connect_service(1) < 0) {
1203 if (outf != stdout) {
1204 fclose(outf);
1205 }
1206 return 1;
1207 }
1208 plist_t options = plist_new_dict();
1209 if (start_time > 0) {
1210 plist_dict_set_item(options, "StartTime", plist_new_int(start_time));
1211 }
1212 if (size_limit > 0) {
1213 plist_dict_set_item(options, "SizeLimit", plist_new_int(size_limit));
1214 }
1215 if (age_limit > 0) {
1216 plist_dict_set_item(options, "AgeLimit", plist_new_int(age_limit));
1217 }
1218 ostrace_create_archive(ostrace, options, write_callback, outf);
1219 ostrace_client_free(ostrace);
1220 ostrace = NULL;
1221 idevice_free(device);
1222 device = NULL;
1223 if (outf != stdout) {
1224 fclose(outf);
1225 }
1226 return 0;
1227 } else {
1228 fprintf(stderr, "Unknown command '%s'. See --help for valid commands.\n", argv[0]);
1229 return 1;
1230 }
1231 }
1232
1233 int num = 0;
1234 idevice_info_t *devices = NULL;
1235 idevice_get_device_list_extended(&devices, &num);
1236 int count = 0;
1237 for (int i = 0; i < num; i++) {
1238 if (devices[i]->conn_type == CONNECTION_NETWORK && use_network) {
1239 count++;
1240 } else if (devices[i]->conn_type == CONNECTION_USBMUXD) {
1241 count++;
1242 }
1243 }
1244 idevice_device_list_extended_free(devices);
1245 if (count == 0) {
1246 if (!udid) {
1247 fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to become available.\n");
1248 return 1;
1249 }
1250
1251 fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
1252 }
1253
1254 line_buffer_size = 1024;
1255 line = malloc(line_buffer_size);
1256
1257 idevice_subscription_context_t context = NULL;
1258 idevice_events_subscribe(&context, device_event_cb, NULL);
1259
1260 while (!quit_flag) {
1261 sleep(1);
1262 }
1263 idevice_events_unsubscribe(context);
1264 stop_logging();
1265
1266 if (num_proc_filters > 0) {
1267 int i;
1268 for (i = 0; i < num_proc_filters; i++) {
1269 free(proc_filters[i]);
1270 }
1271 free(proc_filters);
1272 }
1273 if (num_pid_filters > 0) {
1274 free(pid_filters);
1275 }
1276 if (num_msg_filters > 0) {
1277 int i;
1278 for (i = 0; i < num_msg_filters; i++) {
1279 free(msg_filters[i]);
1280 }
1281 free(msg_filters);
1282 }
1283 if (num_msg_reverse_filters > 0) {
1284 int i;
1285 for (i = 0; i < num_msg_reverse_filters; i++) {
1286 free(msg_reverse_filters[i]);
1287 }
1288 free(msg_reverse_filters);
1289 }
1290 if (num_trigger_filters > 0) {
1291 int i;
1292 for (i = 0; i < num_trigger_filters; i++) {
1293 free(trigger_filters[i]);
1294 }
1295 free(trigger_filters);
1296 }
1297 if (num_untrigger_filters > 0) {
1298 int i;
1299 for (i = 0; i < num_untrigger_filters; i++) {
1300 free(untrigger_filters[i]);
1301 }
1302 free(untrigger_filters);
1303 }
1304
1305 free(line);
1306
1307 free(udid);
1308
1309 return 0;
1310}