summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am139
-rw-r--r--tools/afcclient.c1630
-rw-r--r--tools/idevice_id.c169
-rw-r--r--tools/idevicebackup.c675
-rw-r--r--tools/idevicebackup2.c2688
-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, 11745 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..a958c23
--- /dev/null
+++ b/tools/afcclient.c
@@ -0,0 +1,1630 @@
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#else
145#define BS_CC 0x7f
146#define getch getchar
147#endif
148static void get_input(char *buf, int maxlen)
149{
150 int len = 0;
151 int c;
152
153 while ((c = getch())) {
154 if ((c == '\r') || (c == '\n')) {
155 break;
156 }
157 if (isprint(c)) {
158 if (len < maxlen-1)
159 buf[len++] = c;
160 } else if (c == BS_CC) {
161 if (len > 0) {
162 fputs("\b \b", stdout);
163 len--;
164 }
165 }
166 }
167 buf[len] = 0;
168}
169#endif
170
171#define OPT_DOCUMENTS 1
172#define OPT_CONTAINER 2
173
174int stop_requested = 0;
175
176static void handle_signal(int sig)
177{
178 stop_requested++;
179#ifdef _WIN32
180 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
181#else
182 kill(getpid(), SIGINT);
183#endif
184}
185
186static void handle_help(afc_client_t afc, int argc, char** argv)
187{
188 printf("Available commands:\n");
189 printf("help - print list of available commands\n");
190 printf("devinfo - print device information\n");
191 printf("info PATH - print file attributes of file at PATH\n");
192 printf("ls [-l] PATH - print directory contents of PATH\n");
193 printf("mv OLD NEW - rename file OLD to NEW\n");
194 printf("mkdir PATH - create directory at PATH\n");
195 printf("ln [-s] FILE [LINK] - create a (symbolic) link to file named LINKNAME\n");
196 printf(" NOTE: This feature has been disabled in newer versions of iOS.\n");
197 printf("rm PATH - remove item at PATH\n");
198 printf("get [-rf] PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n");
199 printf("put [-rf] LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n");
200 printf("\n");
201}
202
203static const char* path_get_basename(const char* path)
204{
205 const char *p = strrchr(path, '/');
206 return p ? p + 1 : path;
207}
208
209static int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y)
210{
211 /* Perform the carry for the later subtraction by updating y. */
212 if (x->tv_usec < y->tv_usec) {
213 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
214 y->tv_usec -= 1000000 * nsec;
215 y->tv_sec += nsec;
216 }
217 if (x->tv_usec - y->tv_usec > 1000000) {
218 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
219 y->tv_usec += 1000000 * nsec;
220 y->tv_sec -= nsec;
221 }
222 /* Compute the time remaining to wait.
223 tv_usec is certainly positive. */
224 result->tv_sec = x->tv_sec - y->tv_sec;
225 result->tv_usec = x->tv_usec - y->tv_usec;
226 /* Return 1 if result is negative. */
227 return x->tv_sec < y->tv_sec;
228}
229
230struct str_item {
231 size_t len;
232 char* str;
233};
234
235static char* get_absolute_path(const char *path)
236{
237 if (*path == '/') {
238 return strdup(path);
239 } else {
240 size_t len = curdir_len + 1 + strlen(path) + 1;
241 char* result = (char*)malloc(len);
242 if (!strcmp(curdir, "/")) {
243 snprintf(result, len, "/%s", path);
244 } else {
245 snprintf(result, len, "%s/%s", curdir, path);
246 }
247 return result;
248 }
249}
250
251static char* get_realpath(const char* path)
252{
253 if (!path) return NULL;
254
255 int is_absolute = 0;
256 if (*path == '/') {
257 is_absolute = 1;
258 }
259
260 const char* p = path;
261 if (is_absolute) {
262 while (*p == '/') p++;
263 }
264 if (*p == '\0') {
265 return strdup("/");
266 }
267
268 int c_count = 1;
269 const char* start = p;
270 const char* end = p;
271 struct str_item* comps = NULL;
272
273 while (*p) {
274 if (*p == '/') {
275 p++;
276 end = p-1;
277 while (*p == '/') p++;
278 if (*p == '\0') break;
279 struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
280 if (!newcomps) {
281 free(comps);
282 printf("%s: out of memory?!\n", __func__);
283 return NULL;
284 }
285 comps = newcomps;
286 char *comp = (char*)malloc(end-start+1);
287 strncpy(comp, start, end-start);
288 comp[end-start] = '\0';
289 comps[c_count-1].len = end-start;
290 comps[c_count-1].str = comp;
291 c_count++;
292 start = p;
293 end = p;
294 }
295 p++;
296 }
297 if (p > start) {
298 if (start == end) {
299 end = p;
300 }
301 struct str_item* newcomps = (struct str_item*)realloc(comps, sizeof(struct str_item)*c_count);
302 if (!newcomps) {
303 free(comps);
304 printf("%s: out of memory?!\n", __func__);
305 return NULL;
306 }
307 comps = newcomps;
308 char *comp = (char*)malloc(end-start+1);
309 strncpy(comp, start, end-start);
310 comp[end-start] = '\0';
311 comps[c_count-1].len = end-start;
312 comps[c_count-1].str = comp;
313 }
314
315 struct str_item* comps_final = (struct str_item*)malloc(sizeof(struct str_item)*(c_count+1));
316 int o = 1;
317 if (is_absolute) {
318 comps_final[0].len = 1;
319 comps_final[0].str = (char*)"/";
320 } else {
321 comps_final[0].len = curdir_len;
322 comps_final[0].str = curdir;
323 }
324 size_t o_len = comps_final[0].len;
325
326 for (int i = 0; i < c_count; i++) {
327 if (!strcmp(comps[i].str, "..")) {
328 o--;
329 continue;
330 } else if (!strcmp(comps[i].str, ".")) {
331 continue;
332 }
333 o_len += comps[i].len;
334 comps_final[o].str = comps[i].str;
335 comps_final[o].len = comps[i].len;
336 o++;
337 }
338
339 o_len += o;
340 char* result = (char*)malloc(o_len);
341 char* presult = result;
342 for (int i = 0; i < o; i++) {
343 if (i > 0 && strcmp(comps_final[i-1].str, "/") != 0) {
344 *presult = '/';
345 presult++;
346 }
347 strncpy(presult, comps_final[i].str, comps_final[i].len);
348 presult+=comps_final[i].len;
349 *presult = '\0';
350 }
351 if (presult == result) {
352 *presult = '/';
353 presult++;
354 *presult = 0;
355 }
356
357 for (int i = 0; i < c_count; i++) {
358 free(comps[i].str);
359 }
360 free(comps);
361 free(comps_final);
362
363 return result;
364}
365
366static void handle_devinfo(afc_client_t afc, int argc, char** argv)
367{
368 plist_t info = NULL;
369 afc_error_t err = afc_get_device_info_plist(afc, &info);
370 if (err == AFC_E_SUCCESS && info) {
371 if (argc > 0 && !strcmp(argv[0], "--plain")) {
372 plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
373 } else {
374 plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
375 }
376 } else {
377 printf("Error: Failed to get device info: %s (%d)\n", afc_strerror(err), err);
378 }
379 plist_free(info);
380}
381
382static int get_file_info_stat(afc_client_t afc, const char* path, struct afc_file_stat *stbuf)
383{
384 plist_t info = NULL;
385 afc_error_t ret = afc_get_file_info_plist(afc, path, &info);
386 memset(stbuf, 0, sizeof(struct afc_file_stat));
387 if (ret != AFC_E_SUCCESS) {
388 return -1;
389 } else if (!info) {
390 return -1;
391 }
392 stbuf->st_size = plist_dict_get_uint(info, "st_size");
393 stbuf->st_blocks = plist_dict_get_uint(info, "st_blocks");
394 const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
395 if (s_ifmt) {
396 if (!strcmp(s_ifmt, "S_IFREG")) {
397 stbuf->st_mode = S_IFREG;
398 } else if (!strcmp(s_ifmt, "S_IFDIR")) {
399 stbuf->st_mode = S_IFDIR;
400 } else if (!strcmp(s_ifmt, "S_IFLNK")) {
401 stbuf->st_mode = S_IFLNK;
402 } else if (!strcmp(s_ifmt, "S_IFBLK")) {
403 stbuf->st_mode = S_IFBLK;
404 } else if (!strcmp(s_ifmt, "S_IFCHR")) {
405 stbuf->st_mode = S_IFCHR;
406 } else if (!strcmp(s_ifmt, "S_IFIFO")) {
407 stbuf->st_mode = S_IFIFO;
408 } else if (!strcmp(s_ifmt, "S_IFSOCK")) {
409 stbuf->st_mode = S_IFSOCK;
410 }
411 }
412 stbuf->st_nlink = plist_dict_get_uint(info, "st_nlink");
413 stbuf->st_mtime = (time_t)(plist_dict_get_uint(info, "st_mtime") / 1000000000);
414 /* available on iOS 7+ */
415 stbuf->st_birthtime = (time_t)(plist_dict_get_uint(info, "st_birthtime") / 1000000000);
416 plist_free(info);
417 return 0;
418}
419
420static void handle_file_info(afc_client_t afc, int argc, char** argv)
421{
422 if (argc < 1) {
423 printf("Error: Missing PATH.\n");
424 return;
425 }
426
427 plist_t info = NULL;
428 char* abspath = get_absolute_path(argv[0]);
429 if (!abspath) {
430 printf("Error: Invalid argument\n");
431 return;
432 }
433 afc_error_t err = afc_get_file_info_plist(afc, abspath, &info);
434 if (err == AFC_E_SUCCESS && info) {
435 if (argc > 1 && !strcmp(argv[1], "--plain")) {
436 plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
437 } else {
438 plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
439 }
440 } else {
441 printf("Error: Failed to get file info for %s: %s (%d)\n", argv[0], afc_strerror(err), err);
442 }
443 plist_free(info);
444 free(abspath);
445}
446
447static void print_file_info(afc_client_t afc, const char* path, int list_verbose)
448{
449 struct afc_file_stat st;
450 get_file_info_stat(afc, path, &st);
451 if (list_verbose) {
452 char timebuf[64];
453 time_t t = st.st_mtime;
454 if (S_ISDIR(st.st_mode)) {
455 printf("drwxr-xr-x");
456 } else if (S_ISLNK(st.st_mode)) {
457 printf("lrwxrwxrwx");
458 } else {
459 if (S_ISFIFO(st.st_mode)) {
460 printf("f");
461 } else if (S_ISBLK(st.st_mode)) {
462 printf("b");
463 } else if (S_ISCHR(st.st_mode)) {
464 printf("c");
465 } else if (S_ISSOCK(st.st_mode)) {
466 printf("s");
467 } else {
468 printf("-");
469 }
470 printf("rw-r--r--");
471 }
472 printf(" ");
473 printf("%4d", st.st_nlink);
474 printf(" ");
475 printf("mobile");
476 printf(" ");
477 printf("mobile");
478 printf(" ");
479 printf("%10lld", (long long)st.st_size);
480 printf(" ");
481#ifdef _WIN32
482 strftime(timebuf, 64, "%d %b %Y %H:%M:%S", localtime(&t));
483#else
484 strftime(timebuf, 64, "%d %h %Y %H:%M:%S", localtime(&t));
485#endif
486 printf("%s", timebuf);
487 printf(" ");
488 }
489 if (S_ISDIR(st.st_mode)) {
490 cprintf(FG_CYAN);
491 } else if (S_ISLNK(st.st_mode)) {
492 cprintf(FG_MAGENTA);
493 } else if (S_ISREG(st.st_mode)) {
494 cprintf(FG_DEFAULT);
495 } else {
496 cprintf(FG_YELLOW);
497 }
498 cprintf("%s" COLOR_RESET "\n", path_get_basename(path));
499}
500
501static void handle_list(afc_client_t afc, int argc, char** argv)
502{
503 const char* path = NULL;
504 int list_verbose = 0;
505 if (argc < 1) {
506 path = curdir;
507 } else {
508 if (!strcmp(argv[0], "-l")) {
509 list_verbose = 1;
510 if (argc == 2) {
511 path = argv[1];
512 } else {
513 path = curdir;
514 }
515 } else {
516 path = argv[0];
517 }
518 }
519 char* abspath = get_absolute_path(path);
520 if (!abspath) {
521 printf("Error: Invalid argument\n");
522 return;
523 }
524 int abspath_is_root = strcmp(abspath, "/") == 0;
525 size_t abspath_len = (abspath_is_root) ? 0 : strlen(abspath);
526 char** entries = NULL;
527 afc_error_t err = afc_read_directory(afc, abspath, &entries);
528 if (err == AFC_E_READ_ERROR) {
529 print_file_info(afc, abspath, list_verbose);
530 return;
531 } else if (err != AFC_E_SUCCESS) {
532 printf("Error: Failed to list '%s': %s (%d)\n", path, afc_strerror(err), err);
533 free(abspath);
534 return;
535 }
536
537 char** p = entries;
538 while (p && *p) {
539 if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
540 p++;
541 continue;
542 }
543 size_t len = abspath_len + 1 + strlen(*p) + 1;
544 char* testpath = (char*)malloc(len);
545 if (abspath_is_root) {
546 snprintf(testpath, len, "/%s", *p);
547 } else {
548 snprintf(testpath, len, "%s/%s", abspath, *p);
549 }
550 print_file_info(afc, testpath, list_verbose);
551 free(testpath);
552 p++;
553 }
554 afc_dictionary_free(entries);
555 free(abspath);
556}
557
558static void handle_rename(afc_client_t afc, int argc, char** argv)
559{
560 if (argc != 2) {
561 printf("Error: Invalid number of arguments\n");
562 return;
563 }
564 char* srcpath = get_absolute_path(argv[0]);
565 if (!srcpath) {
566 printf("Error: Invalid argument\n");
567 return;
568 }
569 char* dstpath = get_absolute_path(argv[1]);
570 if (!dstpath) {
571 free(srcpath);
572 printf("Error: Invalid argument\n");
573 return;
574 }
575 afc_error_t err = afc_rename_path(afc, srcpath, dstpath);
576 if (err != AFC_E_SUCCESS) {
577 printf("Error: Failed to rename '%s' -> '%s': %s (%d)\n", argv[0], argv[1], afc_strerror(err), err);
578 }
579 free(srcpath);
580 free(dstpath);
581}
582
583static void handle_mkdir(afc_client_t afc, int argc, char** argv)
584{
585 for (int i = 0; i < argc; i++) {
586 char* abspath = get_absolute_path(argv[i]);
587 if (!abspath) {
588 printf("Error: Invalid argument '%s'\n", argv[i]);
589 continue;
590 }
591 afc_error_t err = afc_make_directory(afc, abspath);
592 if (err != AFC_E_SUCCESS) {
593 printf("Error: Failed to create directory '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
594 }
595 free(abspath);
596 }
597}
598
599static void handle_link(afc_client_t afc, int argc, char** argv)
600{
601 if (argc < 2) {
602 printf("Error: Invalid number of arguments\n");
603 return;
604 }
605 afc_link_type_t link_type = AFC_HARDLINK;
606 if (!strcmp(argv[0], "-s")) {
607 argc--;
608 argv++;
609 link_type = AFC_SYMLINK;
610 }
611 if (argc < 1 || argc > 2) {
612 printf("Error: Invalid number of arguments\n");
613 return;
614 }
615 const char *link_name = (argc == 1) ? path_get_basename(argv[0]) : argv[1];
616 char* abs_link_name = get_absolute_path(link_name);
617 if (!abs_link_name) {
618 printf("Error: Invalid argument\n");
619 return;
620 }
621 afc_error_t err = afc_make_link(afc, link_type, argv[0], link_name);
622 if (err != AFC_E_SUCCESS) {
623 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);
624 }
625}
626
627static int ask_yesno(const char* prompt)
628{
629 int ret = 0;
630#ifdef HAVE_READLINE
631 char* result = readline(prompt);
632 if (result && result[0] == 'y') {
633 ret = 1;
634 }
635#else
636 char cmdbuf[2] = {0, };
637 printf("%s", prompt);
638 fflush(stdout);
639 get_input(cmdbuf, sizeof(cmdbuf));
640 if (cmdbuf[0] == 'y') {
641 ret = 1;
642 }
643#endif
644#ifdef HAVE_READLINE
645 free(result);
646#endif
647 return ret;
648}
649
650static void handle_remove(afc_client_t afc, int argc, char** argv)
651{
652 int recursive = 0;
653 int force = 0;
654 int i = 0;
655 for (i = 0; i < argc; i++) {
656 if (!strcmp(argv[i], "--")) {
657 i++;
658 break;
659 } else if (!strcmp(argv[i], "-r")) {
660 recursive = 1;
661 } else if (!strcmp(argv[i], "-f")) {
662 force = 1;
663 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
664 recursive = 1;
665 force = 1;
666 } else {
667 break;
668 }
669 }
670 if (recursive && !force) {
671 if (!ask_yesno("WARNING: This operation will remove all contents of the given path(s). Continue? [y/N] ")) {
672 printf("Aborted.\n");
673 return;
674 }
675 }
676 for ( ; i < argc; i++) {
677 char* abspath = get_absolute_path(argv[i]);
678 if (!abspath) {
679 printf("Error: Invalid argument '%s'\n", argv[i]);
680 continue;
681 }
682 afc_error_t err;
683 if (recursive) {
684 err = afc_remove_path_and_contents(afc, abspath);
685 } else {
686 err = afc_remove_path(afc, abspath);
687 }
688 if (err != AFC_E_SUCCESS) {
689 printf("Error: Failed to remove '%s': %s (%d)\n", argv[i], afc_strerror(err), err);
690 }
691 free(abspath);
692 }
693}
694
695static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size, uint8_t force_overwrite)
696{
697 uint64_t fh = 0;
698 afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh);
699 if (err != AFC_E_SUCCESS) {
700 printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
701 return 0;
702 }
703 if (file_exists(dstpath) && !force_overwrite) {
704 printf("Error: Failed to overwrite existing file without '-f' option: %s\n", dstpath);
705 return 0;
706 }
707 FILE *f = fopen(dstpath, "wb");
708 if (!f) {
709 printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno));
710 return 0;
711 }
712 struct timeval t1;
713 struct timeval t2;
714 struct timeval tdiff;
715 size_t bufsize = 0x100000;
716 char *buf = malloc(bufsize);
717 size_t total = 0;
718 int progress = 0;
719 int lastprog = 0;
720 if (file_size > 0x400000) {
721 progress = 1;
722 gettimeofday(&t1, NULL);
723 }
724 uint8_t succeed = 1;
725 while (err == AFC_E_SUCCESS) {
726 uint32_t bytes_read = 0;
727 size_t chunk = 0;
728 err = afc_file_read(afc, fh, buf, bufsize, &bytes_read);
729 if (bytes_read == 0) {
730 break;
731 }
732 while (chunk < bytes_read) {
733 size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f);
734 if (wr == 0) {
735 if (progress) {
736 printf("\n");
737 }
738 printf("Error: Failed to write to local file\n");
739 succeed = 0;
740 break;
741 }
742 chunk += wr;
743 }
744 total += chunk;
745 if (progress) {
746 int prog = (int) ((double) total / (double) file_size * 100.0f);
747 if (prog > lastprog) {
748 gettimeofday(&t2, NULL);
749 timeval_subtract(&tdiff, &t2, &t1);
750 double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
751 printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
752 fflush(stdout);
753 lastprog = prog;
754 }
755 }
756 }
757 if (progress) {
758 printf("\n");
759 }
760 if (err != AFC_E_SUCCESS) {
761 printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
762 succeed = 0;
763 }
764 free(buf);
765 fclose(f);
766 afc_file_close(afc, fh);
767 return succeed;
768}
769
770static int __mkdir(const char* path)
771{
772#ifdef _WIN32
773 return mkdir(path);
774#else
775 return mkdir(path, 0755);
776#endif
777}
778
779static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get)
780{
781 plist_t info = NULL;
782 uint64_t file_size = 0;
783 afc_error_t err = afc_get_file_info_plist(afc, srcpath, &info);
784 if (err == AFC_E_OBJECT_NOT_FOUND) {
785 printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
786 return 0;
787 }
788 uint8_t is_dir = 0;
789 if (info) {
790 file_size = plist_dict_get_uint(info, "st_size");
791 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
792 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
793 plist_free(info);
794 }
795 uint8_t succeed = 1;
796 if (is_dir) {
797 if (!recursive_get) {
798 printf("Error: Failed to get a directory without '-r' option: %s\n", srcpath);
799 return 0;
800 }
801 char **entries = NULL;
802 err = afc_read_directory(afc, srcpath, &entries);
803 if (err != AFC_E_SUCCESS) {
804 printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
805 return 0;
806 }
807 char **p = entries;
808 size_t srcpath_len = strlen(srcpath);
809 uint8_t srcpath_is_root = strcmp(srcpath, "/") == 0;
810 // if directory exists, check force_overwrite flag
811 if (is_directory(dstpath)) {
812 if (!force_overwrite) {
813 printf("Error: Failed to write into existing directory without '-f': %s\n", dstpath);
814 return 0;
815 }
816 } else if (__mkdir(dstpath) != 0) {
817 printf("Error: Failed to create directory '%s': %s\n", dstpath, strerror(errno));
818 afc_dictionary_free(entries);
819 return 0;
820 }
821 while (p && *p) {
822 if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) {
823 p++;
824 continue;
825 }
826 size_t len = srcpath_is_root ? (strlen(*p) + 2) : (srcpath_len + 1 + strlen(*p) + 1);
827 char *testpath = (char *) malloc(len);
828 if (srcpath_is_root) {
829 snprintf(testpath, len, "/%s", *p);
830 } else {
831 snprintf(testpath, len, "%s/%s", srcpath, *p);
832 }
833 uint8_t dst_is_root = strcmp(srcpath, "/") == 0;
834 size_t dst_len = dst_is_root ? (strlen(*p) + 2) : (strlen(dstpath) + 1 + strlen(*p) + 1);
835 char *newdst = (char *) malloc(dst_len);
836 if (dst_is_root) {
837 snprintf(newdst, dst_len, "/%s", *p);
838 } else {
839 snprintf(newdst, dst_len, "%s/%s", dstpath, *p);
840 }
841 if (!get_file(afc, testpath, newdst, force_overwrite, recursive_get)) {
842 succeed = 0;
843 break;
844 }
845 free(testpath);
846 free(newdst);
847 p++;
848 }
849 afc_dictionary_free(entries);
850 } else {
851 succeed = get_single_file(afc, srcpath, dstpath, file_size, force_overwrite);
852 }
853 return succeed;
854}
855
856static void handle_get(afc_client_t afc, int argc, char **argv)
857{
858 if (argc < 1) {
859 printf("Error: Invalid number of arguments\n");
860 return;
861 }
862 uint8_t force_overwrite = 0, recursive_get = 0;
863 char *srcpath = NULL;
864 char *dstpath = NULL;
865 int i = 0;
866 for ( ; i < argc; i++) {
867 if (!strcmp(argv[i], "--")) {
868 i++;
869 break;
870 } else if (!strcmp(argv[i], "-r")) {
871 recursive_get = 1;
872 } else if (!strcmp(argv[i], "-f")) {
873 force_overwrite = 1;
874 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
875 recursive_get = 1;
876 force_overwrite = 1;
877 } else {
878 break;
879 }
880 }
881 if (argc - i == 1) {
882 char *tmp = strdup(argv[i]);
883 size_t src_len = strlen(tmp);
884 if (src_len > 1 && tmp[src_len - 1] == '/') {
885 tmp[src_len - 1] = '\0';
886 }
887 srcpath = get_absolute_path(tmp);
888 dstpath = strdup(path_get_basename(tmp));
889 free(tmp);
890 } else if (argc - i == 2) {
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(argv[i + 1]);
898 size_t dst_len = strlen(dstpath);
899 if (dst_len > 1 && dstpath[dst_len - 1] == '/') {
900 dstpath[dst_len - 1] = '\0';
901 }
902 free(tmp);
903 } else {
904 printf("Error: Invalid number of arguments\n");
905 return;
906 }
907
908 // target is a directory, put file under this target
909 if (is_directory(dstpath)) {
910 const char *basen = path_get_basename(srcpath);
911 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
912 size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
913 char *newdst = (char *) malloc(len);
914 if (dst_is_root) {
915 snprintf(newdst, len, "/%s", basen);
916 } else {
917 snprintf(newdst, len, "%s/%s", dstpath, basen);
918 }
919 get_file(afc, srcpath, newdst, force_overwrite, recursive_get);
920 free(srcpath);
921 free(newdst);
922 free(dstpath);
923 } else {
924 // target is not a dir or does not exist, just try to create or rewrite it
925 get_file(afc, srcpath, dstpath, force_overwrite, recursive_get);
926 free(srcpath);
927 free(dstpath);
928 }
929}
930
931static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite)
932{
933 plist_t info = NULL;
934 afc_error_t ret = afc_get_file_info_plist(afc, dstpath, &info);
935 // file exists, only overwrite with '-f' option was set
936 if (ret == AFC_E_SUCCESS && info) {
937 plist_free(info);
938 if (!force_overwrite) {
939 printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath);
940 return 0;
941 }
942 }
943 FILE *f = fopen(srcpath, "rb");
944 if (!f) {
945 printf("Error: Failed to open local file '%s': %s\n", srcpath, strerror(errno));
946 return 0;
947 }
948 struct timeval t1;
949 struct timeval t2;
950 struct timeval tdiff;
951 struct stat fst;
952 int progress = 0;
953 size_t bufsize = 0x100000;
954 char *buf = malloc(bufsize);
955
956 fstat(fileno(f), &fst);
957 if (fst.st_size >= 0x400000) {
958 progress = 1;
959 gettimeofday(&t1, NULL);
960 }
961 size_t total = 0;
962 int lastprog = 0;
963 uint64_t fh = 0;
964 afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh);
965 uint8_t succeed = 1;
966 while (err == AFC_E_SUCCESS) {
967 uint32_t bytes_read = fread(buf, 1, bufsize, f);
968 if (bytes_read == 0) {
969 if (!feof(f)) {
970 if (progress) {
971 printf("\n");
972 }
973 printf("Error: Failed to read from local file\n");
974 succeed = 0;
975 }
976 break;
977 }
978 uint32_t chunk = 0;
979 while (chunk < bytes_read) {
980 uint32_t bytes_written = 0;
981 err = afc_file_write(afc, fh, buf + chunk, bytes_read - chunk, &bytes_written);
982 if (err != AFC_E_SUCCESS) {
983 if (progress) {
984 printf("\n");
985 }
986 printf("Error: Failed to write to device file\n");
987 succeed = 0;
988 break;
989 }
990 chunk += bytes_written;
991 }
992 total += chunk;
993 if (progress) {
994 int prog = (int) ((double) total / (double) fst.st_size * 100.0f);
995 if (prog > lastprog) {
996 gettimeofday(&t2, NULL);
997 timeval_subtract(&tdiff, &t2, &t1);
998 double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000;
999 printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec);
1000 fflush(stdout);
1001 lastprog = prog;
1002 }
1003 }
1004 }
1005 free(buf);
1006 afc_file_close(afc, fh);
1007 fclose(f);
1008 return succeed;
1009}
1010
1011static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_put)
1012{
1013 if (is_directory(srcpath)) {
1014 if (!recursive_put) {
1015 printf("Error: Failed to put directory without '-r' option: %s\n", srcpath);
1016 return 0;
1017 }
1018 plist_t info = NULL;
1019 afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
1020 //create if target directory does not exist
1021 plist_free(info);
1022 info = NULL;
1023 if (err == AFC_E_OBJECT_NOT_FOUND) {
1024 err = afc_make_directory(afc, dstpath);
1025 if (err != AFC_E_SUCCESS) {
1026 printf("Error: Failed to create directory '%s': %s (%d)\n", dstpath, afc_strerror(err), err);
1027 return 0;
1028 }
1029 } else if (!force_overwrite) {
1030 printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath);
1031 return 0;
1032 }
1033 afc_get_file_info_plist(afc, dstpath, &info);
1034 uint8_t is_dir = 0;
1035 if (info) {
1036 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
1037 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
1038 plist_free(info);
1039 }
1040 if (!is_dir) {
1041 printf("Error: Failed to create or access directory: '%s'\n", dstpath);
1042 return 0;
1043 }
1044
1045 // walk dir recursively to put files
1046 DIR *cur_dir = opendir(srcpath);
1047 if (cur_dir) {
1048 struct dirent *ep;
1049 while ((ep = readdir(cur_dir))) {
1050 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1051 continue;
1052 }
1053 char *fpath = string_build_path(srcpath, ep->d_name, NULL);
1054 if (fpath) {
1055 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
1056 size_t len = dst_is_root ? (strlen(ep->d_name) + 2) : (strlen(dstpath) + 1 + strlen(ep->d_name) + 1);
1057 char *newdst = (char *) malloc(len);
1058 if (dst_is_root) {
1059 snprintf(newdst, len, "/%s", ep->d_name);
1060 } else {
1061 snprintf(newdst, len, "%s/%s", dstpath, ep->d_name);
1062 }
1063 if (!put_file(afc, fpath, newdst, force_overwrite, recursive_put)) {
1064 free(newdst);
1065 free(fpath);
1066 return 0;
1067 }
1068 free(newdst);
1069 free(fpath);
1070 }
1071 }
1072 closedir(cur_dir);
1073 } else {
1074 printf("Error: Failed to visit directory: '%s': %s\n", srcpath, strerror(errno));
1075 return 0;
1076 }
1077 } else {
1078 return put_single_file(afc, srcpath, dstpath, force_overwrite);
1079 }
1080 return 1;
1081}
1082
1083static void handle_put(afc_client_t afc, int argc, char **argv)
1084{
1085 if (argc < 1) {
1086 printf("Error: Invalid number of arguments\n");
1087 return;
1088 }
1089 int i = 0;
1090 uint8_t force_overwrite = 0, recursive_put = 0;
1091 for ( ; i < argc; i++) {
1092 if (!strcmp(argv[i], "--")) {
1093 i++;
1094 break;
1095 } else if (!strcmp(argv[i], "-r")) {
1096 recursive_put = 1;
1097 } else if (!strcmp(argv[i], "-f")) {
1098 force_overwrite = 1;
1099 } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) {
1100 recursive_put = 1;
1101 force_overwrite = 1;
1102 } else {
1103 break;
1104 }
1105 }
1106 if (i >= argc) {
1107 printf("Error: Invalid number of arguments\n");
1108 return;
1109 }
1110 char *srcpath = strdup(argv[i]);
1111 size_t src_len = strlen(srcpath);
1112 if (src_len > 1 && srcpath[src_len - 1] == '/') {
1113 srcpath[src_len - 1] = '\0';
1114 }
1115 char *dstpath = NULL;
1116 if (argc - i == 1) {
1117 dstpath = get_absolute_path(path_get_basename(srcpath));
1118 } else if (argc - i == 2) {
1119 char *tmp = strdup(argv[i + 1]);
1120 size_t dst_len = strlen(tmp);
1121 if (dst_len > 1 && tmp[dst_len - 1] == '/') {
1122 tmp[dst_len - 1] = '\0';
1123 }
1124 dstpath = get_absolute_path(tmp);
1125 free(tmp);
1126 } else {
1127 printf("Error: Invalid number of arguments\n");
1128 return;
1129 }
1130 plist_t info = NULL;
1131 afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
1132 // target does not exist, put directly
1133 if (err == AFC_E_OBJECT_NOT_FOUND) {
1134 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1135 free(srcpath);
1136 free(dstpath);
1137 } else {
1138 uint8_t is_dir = 0;
1139 if (info) {
1140 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
1141 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
1142 plist_free(info);
1143 }
1144 // target is a directory, try to put under this directory
1145 if (is_dir) {
1146 const char *basen = path_get_basename(srcpath);
1147 uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
1148 size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
1149 char *newdst = (char *) malloc(len);
1150 if (dst_is_root) {
1151 snprintf(newdst, len, "/%s", basen);
1152 } else {
1153 snprintf(newdst, len, "%s/%s", dstpath, basen);
1154 }
1155 free(dstpath);
1156 dstpath = get_absolute_path(newdst);
1157 free(newdst);
1158 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1159 } else {
1160 //target is common file, rewrite it
1161 put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
1162 }
1163 free(srcpath);
1164 free(dstpath);
1165 }
1166}
1167
1168static void handle_pwd(afc_client_t afc, int argc, char** argv)
1169{
1170 printf("%s\n", curdir);
1171}
1172
1173static void handle_cd(afc_client_t afc, int argc, char** argv)
1174{
1175 if (argc != 1) {
1176 printf("Error: Invalid number of arguments\n");
1177 return;
1178 }
1179
1180 if (!strcmp(argv[0], ".")) {
1181 return;
1182 }
1183
1184 if (!strcmp(argv[0], "..")) {
1185 if (!strcmp(curdir, "/")) {
1186 return;
1187 }
1188 char *p = strrchr(curdir, '/');
1189 if (!p) {
1190 strcpy(curdir, "/");
1191 return;
1192 }
1193 if (p == curdir) {
1194 *(p+1) = '\0';
1195 } else {
1196 *p = '\0';
1197 }
1198 return;
1199 }
1200
1201 char* path = get_realpath(argv[0]);
1202 int is_dir = 0;
1203 plist_t info = NULL;
1204 afc_error_t err = afc_get_file_info_plist(afc, path, &info);
1205 if (err == AFC_E_SUCCESS && info) {
1206 const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
1207 is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
1208 plist_free(info);
1209 } else {
1210 printf("Error: Failed to get file info for %s: %s (%d)\n", path, afc_strerror(err), err);
1211 free(path);
1212 return;
1213 }
1214
1215 if (!is_dir) {
1216 printf("Error: '%s' is not a valid directory\n", path);
1217 free(path);
1218 return;
1219 }
1220
1221 free(curdir);
1222 curdir = path;
1223 curdir_len = strlen(curdir);
1224}
1225
1226static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline)
1227{
1228 char **argv = NULL;
1229 int argc = 0;
1230 size_t maxlen = strlen(cmdline);
1231 const char* pos = cmdline;
1232 const char* qpos = NULL;
1233 char *tmpbuf = NULL;
1234 int tmplen = 0;
1235 int is_error = 0;
1236
1237 /* skip initial whitespace */
1238 while (isspace(*pos)) pos++;
1239 maxlen -= (pos - cmdline);
1240
1241 tmpbuf = (char*)malloc(maxlen+1);
1242
1243 while (!is_error) {
1244 if (*pos == '\\') {
1245 pos++;
1246 switch (*pos) {
1247 case '"':
1248 case '\'':
1249 case '\\':
1250 case ' ':
1251 tmpbuf[tmplen++] = *pos;
1252 pos++;
1253 break;
1254 default:
1255 printf("Error: Invalid escape sequence\n");
1256 is_error++;
1257 break;
1258 }
1259 } else if (*pos == '"' || *pos == '\'') {
1260 if (!qpos) {
1261 qpos = pos;
1262 } else {
1263 qpos = NULL;
1264 }
1265 pos++;
1266 } else if (*pos == '\0' || (!qpos && isspace(*pos))) {
1267 tmpbuf[tmplen] = '\0';
1268 if (*pos == '\0' && qpos) {
1269 printf("Error: Unmatched `%c`\n", *qpos);
1270 is_error++;
1271 break;
1272 }
1273 char** new_argv = (char**)realloc(argv, (argc+1)*sizeof(char*));
1274 if (new_argv == NULL) {
1275 printf("Error: Out of memory?!\n");
1276 is_error++;
1277 break;
1278 }
1279 argv = new_argv;
1280 /* shrink buffer to actual argument size */
1281 argv[argc] = (char*)realloc(tmpbuf, tmplen+1);
1282 if (!argv[argc]) {
1283 printf("Error: Out of memory?!\n");
1284 is_error++;
1285 break;
1286 }
1287 argc++;
1288 tmpbuf = NULL;
1289 if (*pos == '\0') {
1290 break;
1291 }
1292 maxlen -= tmplen;
1293 tmpbuf = (char*)malloc(maxlen+1);
1294 tmplen = 0;
1295 while (isspace(*pos)) pos++;
1296 } else {
1297 tmpbuf[tmplen++] = *pos;
1298 pos++;
1299 }
1300 }
1301 if (tmpbuf) {
1302 free(tmpbuf);
1303 }
1304 if (is_error) {
1305 int i;
1306 for (i = 0; argv && i < argc; i++) free(argv[i]);
1307 free(argv);
1308 return;
1309 }
1310
1311 *p_argv = argv;
1312 *p_argc = argc;
1313}
1314
1315static int process_args(afc_client_t afc, int argc, char** argv)
1316{
1317 if (!strcmp(argv[0], "q") || !strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
1318 return -1;
1319 }
1320 else if (!strcmp(argv[0], "help")) {
1321 handle_help(afc, argc, argv);
1322 }
1323 else if (!strcmp(argv[0], "devinfo") || !strcmp(argv[0], "deviceinfo")) {
1324 handle_devinfo(afc, argc-1, argv+1);
1325 }
1326 else if (!strcmp(argv[0], "info")) {
1327 handle_file_info(afc, argc-1, argv+1);
1328 }
1329 else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) {
1330 handle_list(afc, argc-1, argv+1);
1331 }
1332 else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) {
1333 handle_rename(afc, argc-1, argv+1);
1334 }
1335 else if (!strcmp(argv[0], "mkdir")) {
1336 handle_mkdir(afc, argc-1, argv+1);
1337 }
1338 else if (!strcmp(argv[0], "ln")) {
1339 handle_link(afc, argc-1, argv+1);
1340 }
1341 else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) {
1342 handle_remove(afc, argc-1, argv+1);
1343 }
1344 else if (!strcmp(argv[0], "get")) {
1345 handle_get(afc, argc-1, argv+1);
1346 }
1347 else if (!strcmp(argv[0], "put")) {
1348 handle_put(afc, argc-1, argv+1);
1349 }
1350 else if (!strcmp(argv[0], "pwd")) {
1351 handle_pwd(afc, argc-1, argv+1);
1352 }
1353 else if (!strcmp(argv[0], "cd")) {
1354 handle_cd(afc, argc-1, argv+1);
1355 }
1356 else {
1357 printf("Unknown command '%s'. Type 'help' to get a list of available commands.\n", argv[0]);
1358 }
1359 return 0;
1360}
1361
1362static void start_cmdline(afc_client_t afc)
1363{
1364 while (!stop_requested) {
1365 int argc = 0;
1366 char **argv = NULL;
1367 char prompt[128];
1368 int plen = curdir_len;
1369 char *ppath = curdir;
1370 int plim = (int)(sizeof(prompt)/2)-8;
1371 if (plen > plim) {
1372 ppath = curdir + (plen - plim);
1373 plen = plim;
1374 }
1375 snprintf(prompt, 128, FG_BLACK BG_LIGHT_GRAY "afc:" COLOR_RESET FG_BRIGHT_YELLOW BG_BLUE "%.*s" COLOR_RESET " > ", plen, ppath);
1376#ifdef HAVE_READLINE
1377 char* cmd = readline(prompt);
1378 if (!cmd || !*cmd) {
1379 free(cmd);
1380 continue;
1381 }
1382 add_history(cmd);
1383 parse_cmdline(&argc, &argv, cmd);
1384#else
1385 char cmdbuf[4096];
1386 printf("%s", prompt);
1387 fflush(stdout);
1388 get_input(cmdbuf, sizeof(cmdbuf));
1389 parse_cmdline(&argc, &argv, cmdbuf);
1390#endif
1391#ifdef HAVE_READLINE
1392 free(cmd);
1393#endif
1394 /* process arguments */
1395 if (argv && argv[0]) {
1396 if (process_args(afc, argc, argv) < 0) {
1397 break;
1398 }
1399 }
1400 }
1401}
1402
1403static void device_event_cb(const idevice_event_t* event, void* userdata)
1404{
1405 if (use_network && event->conn_type != CONNECTION_NETWORK) {
1406 return;
1407 } else if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
1408 return;
1409 }
1410 if (event->event == IDEVICE_DEVICE_ADD) {
1411 if (!udid) {
1412 udid = strdup(event->udid);
1413 }
1414 if (strcmp(udid, event->udid) == 0) {
1415 connected = 1;
1416 }
1417 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
1418 if (strcmp(udid, event->udid) == 0) {
1419 connected = 0;
1420 printf("\n[disconnected]\n");
1421 handle_signal(SIGINT);
1422 }
1423 }
1424}
1425
1426int main(int argc, char** argv)
1427{
1428 const char* appid = NULL;
1429 int ret = 0;
1430 idevice_t device = NULL;
1431 lockdownd_client_t lockdown = NULL;
1432 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
1433 lockdownd_service_descriptor_t service = NULL;
1434 afc_client_t afc = NULL;
1435 house_arrest_client_t house_arrest = NULL;
1436 const char* service_name = AFC_SERVICE_NAME;
1437 int use_container = 0;
1438
1439 int c = 0;
1440 const struct option longopts[] = {
1441 { "udid", required_argument, NULL, 'u' },
1442 { "network", no_argument, NULL, 'n' },
1443 { "help", no_argument, NULL, 'h' },
1444 { "debug", no_argument, NULL, 'd' },
1445 { "version", no_argument, NULL, 'v' },
1446 { "documents", required_argument, NULL, OPT_DOCUMENTS },
1447 { "container", required_argument, NULL, OPT_CONTAINER },
1448 { NULL, 0, NULL, 0}
1449 };
1450
1451 signal(SIGTERM, handle_signal);
1452#ifndef _WIN32
1453 signal(SIGQUIT, handle_signal);
1454 signal(SIGPIPE, SIG_IGN);
1455#endif
1456
1457 while ((c = getopt_long(argc, argv, "du:nhv", longopts, NULL)) != -1) {
1458 switch (c) {
1459 case 'd':
1460 idevice_set_debug_level(1);
1461 break;
1462 case 'u':
1463 if (!*optarg) {
1464 fprintf(stderr, "ERROR: UDID must not be empty!\n");
1465 print_usage(argc, argv, 1);
1466 return 2;
1467 }
1468 udid = strdup(optarg);
1469 break;
1470 case 'n':
1471 use_network = 1;
1472 break;
1473 case 'h':
1474 print_usage(argc, argv, 0);
1475 return 0;
1476 case 'v':
1477 printf("%s %s", TOOL_NAME, PACKAGE_VERSION);
1478#ifdef HAVE_READLINE
1479 printf(" (readline)");
1480#endif
1481 printf("\n");
1482 return 0;
1483 case OPT_DOCUMENTS:
1484 if (!*optarg) {
1485 fprintf(stderr, "ERROR: '--documents' requires a non-empty app ID!\n");
1486 print_usage(argc, argv, 1);
1487 return 2;
1488 }
1489 appid = optarg;
1490 use_container = 0;
1491 break;
1492 case OPT_CONTAINER:
1493 if (!*optarg) {
1494 fprintf(stderr, "ERROR: '--container' requires a not-empty app ID!\n");
1495 print_usage(argc, argv, 1);
1496 return 2;
1497 }
1498 appid = optarg;
1499 use_container = 1;
1500 break;
1501 default:
1502 print_usage(argc, argv, 1);
1503 return 2;
1504 }
1505 }
1506
1507 argc -= optind;
1508 argv += optind;
1509
1510 int num = 0;
1511 idevice_info_t *devices = NULL;
1512 idevice_get_device_list_extended(&devices, &num);
1513 int count = 0;
1514 for (int i = 0; i < num; i++) {
1515 if (devices[i]->conn_type == CONNECTION_NETWORK && use_network) {
1516 count++;
1517 } else if (devices[i]->conn_type == CONNECTION_USBMUXD) {
1518 count++;
1519 }
1520 }
1521 idevice_device_list_extended_free(devices);
1522 if (count == 0) {
1523 fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
1524 return 1;
1525 }
1526
1527 idevice_events_subscribe(&context, device_event_cb, NULL);
1528
1529 while (!connected && !stop_requested) {
1530#ifdef _WIN32
1531 Sleep(100);
1532#else
1533 usleep(100000);
1534#endif
1535 }
1536 if (stop_requested) {
1537 return 0;
1538 }
1539
1540 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
1541 if (ret != IDEVICE_E_SUCCESS) {
1542 if (udid) {
1543 fprintf(stderr, "ERROR: Device %s not found!\n", udid);
1544 } else {
1545 fprintf(stderr, "ERROR: No device found!\n");
1546 }
1547 return 1;
1548 }
1549
1550 do {
1551 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
1552 fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ldret), ldret);
1553 ret = 1;
1554 break;
1555 }
1556
1557 if (appid) {
1558 service_name = HOUSE_ARREST_SERVICE_NAME;
1559 }
1560
1561 ldret = lockdownd_start_service(lockdown, service_name, &service);
1562 if (ldret != LOCKDOWN_E_SUCCESS) {
1563 fprintf(stderr, "ERROR: Failed to start service %s: %s (%d)\n", service_name, lockdownd_strerror(ldret), ldret);
1564 ret = 1;
1565 break;
1566 }
1567
1568 if (appid) {
1569 house_arrest_client_new(device, service, &house_arrest);
1570 if (!house_arrest) {
1571 fprintf(stderr, "Could not start document sharing service!\n");
1572 ret = 1;
1573 break;
1574 }
1575
1576 if (house_arrest_send_command(house_arrest, use_container ? "VendContainer": "VendDocuments", appid) != HOUSE_ARREST_E_SUCCESS) {
1577 fprintf(stderr, "Could not send house_arrest command!\n");
1578 ret = 1;
1579 break;
1580 }
1581
1582 plist_t dict = NULL;
1583 if (house_arrest_get_result(house_arrest, &dict) != HOUSE_ARREST_E_SUCCESS) {
1584 fprintf(stderr, "Could not get result from document sharing service!\n");
1585 break;
1586 }
1587 plist_t node = plist_dict_get_item(dict, "Error");
1588 if (node) {
1589 char *str = NULL;
1590 plist_get_string_val(node, &str);
1591 fprintf(stderr, "ERROR: %s\n", str);
1592 if (str && !strcmp(str, "InstallationLookupFailed")) {
1593 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);
1594 }
1595 free(str);
1596 plist_free(dict);
1597 break;
1598 }
1599 plist_free(dict);
1600 afc_client_new_from_house_arrest_client(house_arrest, &afc);
1601 } else {
1602 afc_client_new(device, service, &afc);
1603 }
1604 lockdownd_service_descriptor_free(service);
1605 lockdownd_client_free(lockdown);
1606 lockdown = NULL;
1607
1608 curdir = strdup("/");
1609 curdir_len = 1;
1610
1611 if (argc > 0) {
1612 // command line mode
1613 process_args(afc, argc, argv);
1614 } else {
1615 // interactive mode
1616 start_cmdline(afc);
1617 }
1618
1619 } while (0);
1620
1621 if (afc) {
1622 afc_client_free(afc);
1623 }
1624 if (lockdown) {
1625 lockdownd_client_free(lockdown);
1626 }
1627 idevice_free(device);
1628
1629 return ret;
1630}
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..12d6083
--- /dev/null
+++ b/tools/idevicebackup2.c
@@ -0,0 +1,2688 @@
1/*
2 * idevicebackup2.c
3 * Command line interface to use the device's backup and restore service
4 *
5 * Copyright (c) 2010-2022 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2009-2010 Martin Szulecki, All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#define TOOL_NAME "idevicebackup2"
28
29#include <stdio.h>
30#include <string.h>
31#include <errno.h>
32#include <stdlib.h>
33#include <signal.h>
34#include <unistd.h>
35#include <dirent.h>
36#include <libgen.h>
37#include <ctype.h>
38#include <time.h>
39#include <getopt.h>
40
41#include <libimobiledevice/libimobiledevice.h>
42#include <libimobiledevice/lockdown.h>
43#include <libimobiledevice/mobilebackup2.h>
44#include <libimobiledevice/notification_proxy.h>
45#include <libimobiledevice/afc.h>
46#include <libimobiledevice/installation_proxy.h>
47#include <libimobiledevice/sbservices.h>
48#include <libimobiledevice/diagnostics_relay.h>
49#include <libimobiledevice-glue/utils.h>
50#include <plist/plist.h>
51
52#include <endianness.h>
53
54#define LOCK_ATTEMPTS 50
55#define LOCK_WAIT 200000
56
57#ifdef _WIN32
58#include <windows.h>
59#include <conio.h>
60#define sleep(x) Sleep(x*1000)
61#ifndef ELOOP
62#define ELOOP 114
63#endif
64#else
65#include <termios.h>
66#include <sys/statvfs.h>
67#endif
68#include <sys/stat.h>
69
70#define CODE_SUCCESS 0x00
71#define CODE_ERROR_LOCAL 0x06
72#define CODE_ERROR_REMOTE 0x0b
73#define CODE_FILE_DATA 0x0c
74
75static int verbose = 1;
76static int quit_flag = 0;
77static int passcode_requested = 0;
78
79#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
80
81enum cmd_mode {
82 CMD_BACKUP,
83 CMD_RESTORE,
84 CMD_INFO,
85 CMD_LIST,
86 CMD_UNBACK,
87 CMD_CHANGEPW,
88 CMD_LEAVE,
89 CMD_CLOUD
90};
91
92enum cmd_flags {
93 CMD_FLAG_RESTORE_SYSTEM_FILES = (1 << 1),
94 CMD_FLAG_RESTORE_NO_REBOOT = (1 << 2),
95 CMD_FLAG_RESTORE_COPY_BACKUP = (1 << 3),
96 CMD_FLAG_RESTORE_SETTINGS = (1 << 4),
97 CMD_FLAG_RESTORE_REMOVE_ITEMS = (1 << 5),
98 CMD_FLAG_ENCRYPTION_ENABLE = (1 << 6),
99 CMD_FLAG_ENCRYPTION_DISABLE = (1 << 7),
100 CMD_FLAG_ENCRYPTION_CHANGEPW = (1 << 8),
101 CMD_FLAG_FORCE_FULL_BACKUP = (1 << 9),
102 CMD_FLAG_CLOUD_ENABLE = (1 << 10),
103 CMD_FLAG_CLOUD_DISABLE = (1 << 11),
104 CMD_FLAG_RESTORE_SKIP_APPS = (1 << 12)
105};
106
107static int backup_domain_changed = 0;
108
109static void notify_cb(const char *notification, void *userdata)
110{
111 if (strlen(notification) == 0) {
112 return;
113 }
114 if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
115 PRINT_VERBOSE(1, "User has cancelled the backup process on the device.\n");
116 quit_flag++;
117 } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
118 backup_domain_changed = 1;
119 } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.presented")) {
120 passcode_requested = 1;
121 } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.dismissed")) {
122 passcode_requested = 0;
123 } else {
124 PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
125 }
126}
127
128static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *filename, char **data, uint64_t *size)
129{
130 if (!afc || !data || !size) {
131 return;
132 }
133
134 plist_t fileinfo = NULL;
135 uint32_t fsize = 0;
136
137 afc_get_file_info_plist(afc, filename, &fileinfo);
138 if (!fileinfo) {
139 return;
140 }
141 fsize = plist_dict_get_uint(fileinfo, "st_size");
142 plist_free(fileinfo);
143
144 if (fsize == 0) {
145 return;
146 }
147
148 uint64_t f = 0;
149 afc_file_open(afc, filename, AFC_FOPEN_RDONLY, &f);
150 if (!f) {
151 return;
152 }
153 char *buf = (char*)malloc((uint32_t)fsize);
154 uint32_t done = 0;
155 while (done < fsize) {
156 uint32_t bread = 0;
157 afc_file_read(afc, f, buf+done, 65536, &bread);
158 if (bread > 0) {
159 done += bread;
160 } else {
161 break;
162 }
163 }
164 if (done == fsize) {
165 *size = fsize;
166 *data = buf;
167 } else {
168 free(buf);
169 }
170 afc_file_close(afc, f);
171}
172
173static int __mkdir(const char* path, int mode)
174{
175#ifdef _WIN32
176 return mkdir(path);
177#else
178 return mkdir(path, mode);
179#endif
180}
181
182static int mkdir_with_parents(const char *dir, int mode)
183{
184 if (!dir) return -1;
185 if (__mkdir(dir, mode) == 0) {
186 return 0;
187 }
188 if (errno == EEXIST) return 0;
189 int res;
190 char *parent = strdup(dir);
191 char *parentdir = dirname(parent);
192 if (parentdir) {
193 res = mkdir_with_parents(parentdir, mode);
194 } else {
195 res = -1;
196 }
197 free(parent);
198 if (res == 0) {
199 mkdir_with_parents(dir, mode);
200 }
201 return res;
202}
203
204#ifdef _WIN32
205static int win32err_to_errno(int err_value)
206{
207 switch (err_value) {
208 case ERROR_FILE_NOT_FOUND:
209 return ENOENT;
210 case ERROR_ALREADY_EXISTS:
211 return EEXIST;
212 default:
213 return EFAULT;
214 }
215}
216#endif
217
218static int remove_file(const char* path)
219{
220 int e = 0;
221#ifdef _WIN32
222 if (!DeleteFile(path)) {
223 e = win32err_to_errno(GetLastError());
224 }
225#else
226 if (remove(path) < 0) {
227 e = errno;
228 }
229#endif
230 return e;
231}
232
233static int remove_directory(const char* path)
234{
235 int e = 0;
236#ifdef _WIN32
237 if (!RemoveDirectory(path)) {
238 e = win32err_to_errno(GetLastError());
239 }
240#else
241 if (remove(path) < 0) {
242 e = errno;
243 }
244#endif
245 return e;
246}
247
248struct entry {
249 char *name;
250 struct entry *next;
251};
252
253static void scan_directory(const char *path, struct entry **files, struct entry **directories)
254{
255 DIR* cur_dir = opendir(path);
256 if (cur_dir) {
257 struct dirent* ep;
258 while ((ep = readdir(cur_dir))) {
259 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
260 continue;
261 }
262 char *fpath = string_build_path(path, ep->d_name, NULL);
263 if (fpath) {
264#ifdef HAVE_DIRENT_D_TYPE
265 if (ep->d_type & DT_DIR) {
266#else
267 struct stat st;
268 if (stat(fpath, &st) != 0) return;
269 if (S_ISDIR(st.st_mode)) {
270#endif
271 struct entry *ent = malloc(sizeof(struct entry));
272 if (!ent) return;
273 ent->name = fpath;
274 ent->next = *directories;
275 *directories = ent;
276 scan_directory(fpath, files, directories);
277 fpath = NULL;
278 } else {
279 struct entry *ent = malloc(sizeof(struct entry));
280 if (!ent) return;
281 ent->name = fpath;
282 ent->next = *files;
283 *files = ent;
284 fpath = NULL;
285 }
286 }
287 }
288 closedir(cur_dir);
289 }
290}
291
292static int rmdir_recursive(const char* path)
293{
294 int res = 0;
295 struct entry *files = NULL;
296 struct entry *directories = NULL;
297 struct entry *ent;
298
299 ent = malloc(sizeof(struct entry));
300 if (!ent) return ENOMEM;
301 ent->name = strdup(path);
302 ent->next = NULL;
303 directories = ent;
304
305 scan_directory(path, &files, &directories);
306
307 ent = files;
308 while (ent) {
309 struct entry *del = ent;
310 res = remove_file(ent->name);
311 free(ent->name);
312 ent = ent->next;
313 free(del);
314 }
315 ent = directories;
316 while (ent) {
317 struct entry *del = ent;
318 res = remove_directory(ent->name);
319 free(ent->name);
320 ent = ent->next;
321 free(del);
322 }
323
324 return res;
325}
326
327static char* get_uuid()
328{
329 const char *chars = "ABCDEF0123456789";
330 int i = 0;
331 char *uuid = (char*)malloc(sizeof(char) * 33);
332
333 srand(time(NULL));
334
335 for (i = 0; i < 32; i++) {
336 uuid[i] = chars[rand() % 16];
337 }
338
339 uuid[32] = '\0';
340
341 return uuid;
342}
343
344static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t device, afc_client_t afc)
345{
346 /* gather data from lockdown */
347 plist_t value_node = NULL;
348 plist_t root_node = NULL;
349 plist_t itunes_settings = NULL;
350 plist_t min_itunes_version = NULL;
351 char *udid_uppercase = NULL;
352
353 lockdownd_client_t lockdown = NULL;
354 if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
355 return NULL;
356 }
357
358 plist_t ret = plist_new_dict();
359
360 /* get basic device information in one go */
361 lockdownd_get_value(lockdown, NULL, NULL, &root_node);
362
363 /* get iTunes settings */
364 lockdownd_get_value(lockdown, "com.apple.iTunes", NULL, &itunes_settings);
365
366 /* get minimum iTunes version */
367 lockdownd_get_value(lockdown, "com.apple.mobile.iTunes", "MinITunesVersion", &min_itunes_version);
368
369 lockdownd_client_free(lockdown);
370
371 /* get a list of installed user applications */
372 plist_t app_dict = plist_new_dict();
373 plist_t installed_apps = plist_new_array();
374 instproxy_client_t ip = NULL;
375 if (instproxy_client_start_service(device, &ip, TOOL_NAME) == INSTPROXY_E_SUCCESS) {
376 plist_t client_opts = instproxy_client_options_new();
377 instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
378 instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata", NULL);
379
380 plist_t apps = NULL;
381 instproxy_browse(ip, client_opts, &apps);
382
383 sbservices_client_t sbs = NULL;
384 if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {
385 printf("Couldn't establish sbservices connection. Continuing anyway.\n");
386 }
387
388 if (apps && (plist_get_node_type(apps) == PLIST_ARRAY)) {
389 uint32_t app_count = plist_array_get_size(apps);
390 uint32_t i;
391 for (i = 0; i < app_count; i++) {
392 plist_t app_entry = plist_array_get_item(apps, i);
393 plist_t bundle_id = plist_dict_get_item(app_entry, "CFBundleIdentifier");
394 if (bundle_id) {
395 char *bundle_id_str = NULL;
396 plist_array_append_item(installed_apps, plist_copy(bundle_id));
397
398 plist_get_string_val(bundle_id, &bundle_id_str);
399 plist_t sinf = plist_dict_get_item(app_entry, "ApplicationSINF");
400 plist_t meta = plist_dict_get_item(app_entry, "iTunesMetadata");
401 if (sinf && meta) {
402 plist_t adict = plist_new_dict();
403 plist_dict_set_item(adict, "ApplicationSINF", plist_copy(sinf));
404 if (sbs) {
405 char *pngdata = NULL;
406 uint64_t pngsize = 0;
407 sbservices_get_icon_pngdata(sbs, bundle_id_str, &pngdata, &pngsize);
408 if (pngdata) {
409 plist_dict_set_item(adict, "PlaceholderIcon", plist_new_data(pngdata, pngsize));
410 free(pngdata);
411 }
412 }
413 plist_dict_set_item(adict, "iTunesMetadata", plist_copy(meta));
414 plist_dict_set_item(app_dict, bundle_id_str, adict);
415 }
416 free(bundle_id_str);
417 }
418 }
419 }
420 plist_free(apps);
421
422 if (sbs) {
423 sbservices_client_free(sbs);
424 }
425
426 instproxy_client_options_free(client_opts);
427
428 instproxy_client_free(ip);
429 }
430
431 /* Applications */
432 plist_dict_set_item(ret, "Applications", app_dict);
433
434 /* set fields we understand */
435 value_node = plist_dict_get_item(root_node, "BuildVersion");
436 plist_dict_set_item(ret, "Build Version", plist_copy(value_node));
437
438 value_node = plist_dict_get_item(root_node, "DeviceName");
439 plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
440 plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
441
442 char *uuid = get_uuid();
443 plist_dict_set_item(ret, "GUID", plist_new_string(uuid));
444 free(uuid);
445
446 value_node = plist_dict_get_item(root_node, "IntegratedCircuitCardIdentity");
447 if (value_node)
448 plist_dict_set_item(ret, "ICCID", plist_copy(value_node));
449
450 value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
451 if (value_node)
452 plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
453
454 /* Installed Applications */
455 plist_dict_set_item(ret, "Installed Applications", installed_apps);
456
457 plist_dict_set_item(ret, "Last Backup Date",
458#ifdef HAVE_PLIST_UNIX_DATE
459 plist_new_unix_date(time(NULL))
460#else
461 plist_new_date(time(NULL) - MAC_EPOCH, 0)
462#endif
463 );
464
465 value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
466 if (value_node)
467 plist_dict_set_item(ret, "MEID", plist_copy(value_node));
468
469 value_node = plist_dict_get_item(root_node, "PhoneNumber");
470 if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
471 plist_dict_set_item(ret, "Phone Number", plist_copy(value_node));
472 }
473
474 /* FIXME Product Name */
475
476 value_node = plist_dict_get_item(root_node, "ProductType");
477 plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
478
479 value_node = plist_dict_get_item(root_node, "ProductVersion");
480 plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
481
482 value_node = plist_dict_get_item(root_node, "SerialNumber");
483 plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
484
485 /* FIXME Sync Settings? */
486
487 value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
488 plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
489
490 plist_dict_set_item(ret, "Target Type", plist_new_string("Device"));
491
492 /* uppercase */
493 udid_uppercase = string_toupper((char*)udid);
494 plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
495 free(udid_uppercase);
496
497 char *data_buf = NULL;
498 uint64_t data_size = 0;
499 mobilebackup_afc_get_file_contents(afc, "/Books/iBooksData2.plist", &data_buf, &data_size);
500 if (data_buf) {
501 plist_dict_set_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
502 free(data_buf);
503 }
504
505 plist_t files = plist_new_dict();
506 const char *itunesfiles[] = {
507 "ApertureAlbumPrefs",
508 "IC-Info.sidb",
509 "IC-Info.sidv",
510 "PhotosFolderAlbums",
511 "PhotosFolderName",
512 "PhotosFolderPrefs",
513 "VoiceMemos.plist",
514 "iPhotoAlbumPrefs",
515 "iTunesApplicationIDs",
516 "iTunesPrefs",
517 "iTunesPrefs.plist",
518 NULL
519 };
520 int i = 0;
521 for (i = 0; itunesfiles[i]; i++) {
522 data_buf = NULL;
523 data_size = 0;
524 char *fname = (char*)malloc(strlen("/iTunes_Control/iTunes/") + strlen(itunesfiles[i]) + 1);
525 strcpy(fname, "/iTunes_Control/iTunes/");
526 strcat(fname, itunesfiles[i]);
527 mobilebackup_afc_get_file_contents(afc, fname, &data_buf, &data_size);
528 free(fname);
529 if (data_buf) {
530 plist_dict_set_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
531 free(data_buf);
532 }
533 }
534 plist_dict_set_item(ret, "iTunes Files", files);
535
536 plist_dict_set_item(ret, "iTunes Settings", itunes_settings ? plist_copy(itunes_settings) : plist_new_dict());
537
538 /* since we usually don't have iTunes, let's get the minimum required iTunes version from the device */
539 if (min_itunes_version) {
540 plist_dict_set_item(ret, "iTunes Version", plist_copy(min_itunes_version));
541 } else {
542 plist_dict_set_item(ret, "iTunes Version", plist_new_string("10.0.1"));
543 }
544
545 plist_free(itunes_settings);
546 plist_free(min_itunes_version);
547 plist_free(root_node);
548
549 return ret;
550}
551
552static int write_restore_applications(plist_t info_plist, afc_client_t afc)
553{
554 int res = -1;
555 uint64_t restore_applications_file = 0;
556 char * applications_plist_xml = NULL;
557 uint32_t applications_plist_xml_length = 0;
558
559 plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
560 if (!applications_plist) {
561 printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
562 return 0;
563 }
564 plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
565 if (!applications_plist_xml) {
566 printf("Error preparing RestoreApplications.plist\n");
567 goto leave;
568 }
569
570 afc_error_t afc_err = 0;
571 afc_err = afc_make_directory(afc, "/iTunesRestore");
572 if (afc_err != AFC_E_SUCCESS) {
573 printf("Error creating directory /iTunesRestore, error code %d\n", afc_err);
574 goto leave;
575 }
576
577 afc_err = afc_file_open(afc, "/iTunesRestore/RestoreApplications.plist", AFC_FOPEN_WR, &restore_applications_file);
578 if (afc_err != AFC_E_SUCCESS || !restore_applications_file) {
579 printf("Error creating /iTunesRestore/RestoreApplications.plist, error code %d\n", afc_err);
580 goto leave;
581 }
582
583 uint32_t bytes_written = 0;
584 afc_err = afc_file_write(afc, restore_applications_file, applications_plist_xml, applications_plist_xml_length, &bytes_written);
585 if (afc_err != AFC_E_SUCCESS || bytes_written != applications_plist_xml_length) {
586 printf("Error writing /iTunesRestore/RestoreApplications.plist, error code %d, wrote %u of %u bytes\n", afc_err, bytes_written, applications_plist_xml_length);
587 goto leave;
588 }
589
590 afc_err = afc_file_close(afc, restore_applications_file);
591 restore_applications_file = 0;
592 if (afc_err != AFC_E_SUCCESS) {
593 goto leave;
594 }
595 /* success */
596 res = 0;
597
598leave:
599 free(applications_plist_xml);
600
601 if (restore_applications_file) {
602 afc_file_close(afc, restore_applications_file);
603 restore_applications_file = 0;
604 }
605
606 return res;
607}
608
609static int mb2_status_check_snapshot_state(const char *path, const char *udid, const char *matches)
610{
611 int ret = 0;
612 plist_t status_plist = NULL;
613 char *file_path = string_build_path(path, udid, "Status.plist", NULL);
614
615 plist_read_from_file(file_path, &status_plist, NULL);
616 free(file_path);
617 if (!status_plist) {
618 printf("Could not read Status.plist!\n");
619 return ret;
620 }
621 plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
622 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
623 char* sval = NULL;
624 plist_get_string_val(node, &sval);
625 if (sval) {
626 ret = (strcmp(sval, matches) == 0) ? 1 : 0;
627 free(sval);
628 }
629 } else {
630 printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
631 }
632 plist_free(status_plist);
633 return ret;
634}
635
636static void do_post_notification(idevice_t device, const char *notification)
637{
638 lockdownd_service_descriptor_t service = NULL;
639 np_client_t np;
640
641 lockdownd_client_t lockdown = NULL;
642
643 if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
644 return;
645 }
646
647 lockdownd_error_t ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
648 if (ldret == LOCKDOWN_E_SUCCESS) {
649 np_client_new(device, service, &np);
650 if (np) {
651 np_post_notification(np, notification);
652 np_client_free(np);
653 }
654 } else {
655 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
656 }
657
658 if (service) {
659 lockdownd_service_descriptor_free(service);
660 service = NULL;
661 }
662 lockdownd_client_free(lockdown);
663}
664
665static void print_progress_real(double progress, int flush)
666{
667 int i = 0;
668 PRINT_VERBOSE(1, "\r[");
669 for(i = 0; i < 50; i++) {
670 if(i < progress / 2) {
671 PRINT_VERBOSE(1, "=");
672 } else {
673 PRINT_VERBOSE(1, " ");
674 }
675 }
676 PRINT_VERBOSE(1, "] %3.0f%%", progress);
677
678 if (flush > 0) {
679 fflush(stdout);
680 if (progress == 100)
681 PRINT_VERBOSE(1, "\n");
682 }
683}
684
685static void print_progress(uint64_t current, uint64_t total)
686{
687 char *format_size = NULL;
688 double progress = ((double)current/(double)total)*100;
689 if (progress < 0)
690 return;
691
692 if (progress > 100)
693 progress = 100;
694
695 print_progress_real((double)progress, 0);
696
697 format_size = string_format_size(current);
698 PRINT_VERBOSE(1, " (%s", format_size);
699 free(format_size);
700 format_size = string_format_size(total);
701 PRINT_VERBOSE(1, "/%s) ", format_size);
702 free(format_size);
703
704 fflush(stdout);
705 if (progress == 100)
706 PRINT_VERBOSE(1, "\n");
707}
708
709static double overall_progress = 0;
710
711static void mb2_set_overall_progress(double progress)
712{
713 if (progress > 0.0)
714 overall_progress = progress;
715}
716
717static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
718{
719 plist_t node = NULL;
720 double progress = 0.0;
721
722 if (!strcmp(identifier, "DLMessageDownloadFiles")) {
723 node = plist_array_get_item(message, 3);
724 } else if (!strcmp(identifier, "DLMessageUploadFiles")) {
725 node = plist_array_get_item(message, 2);
726 } else if (!strcmp(identifier, "DLMessageMoveFiles") || !strcmp(identifier, "DLMessageMoveItems")) {
727 node = plist_array_get_item(message, 3);
728 } else if (!strcmp(identifier, "DLMessageRemoveFiles") || !strcmp(identifier, "DLMessageRemoveItems")) {
729 node = plist_array_get_item(message, 3);
730 }
731
732 if (node != NULL) {
733 plist_get_real_val(node, &progress);
734 mb2_set_overall_progress(progress);
735 }
736}
737
738static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
739{
740 if (!status_dict) return;
741 plist_t filedict = plist_new_dict();
742 plist_dict_set_item(filedict, "DLFileErrorString", plist_new_string(error_message));
743 plist_dict_set_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
744 plist_dict_set_item(status_dict, path, filedict);
745}
746
747static int errno_to_device_error(int errno_value)
748{
749 switch (errno_value) {
750 case ENOENT:
751 return -6;
752 case EEXIST:
753 return -7;
754 case ENOTDIR:
755 return -8;
756 case EISDIR:
757 return -9;
758 case ELOOP:
759 return -10;
760 case EIO:
761 return -11;
762 case ENOSPC:
763 return -15;
764 default:
765 return -1;
766 }
767}
768
769static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char *backup_dir, const char *path, plist_t *errplist)
770{
771 uint32_t nlen = 0;
772 uint32_t pathlen = strlen(path);
773 uint32_t bytes = 0;
774 char *localfile = string_build_path(backup_dir, path, NULL);
775 char buf[32768];
776#ifdef _WIN32
777 struct _stati64 fst;
778#else
779 struct stat fst;
780#endif
781
782 FILE *f = NULL;
783 uint32_t slen = 0;
784 int errcode = -1;
785 int result = -1;
786 uint32_t length;
787#ifdef _WIN32
788 uint64_t total;
789 uint64_t sent;
790#else
791 off_t total;
792 off_t sent;
793#endif
794
795 mobilebackup2_error_t err;
796
797 /* send path length */
798 nlen = htobe32(pathlen);
799 err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), &bytes);
800 if (err != MOBILEBACKUP2_E_SUCCESS) {
801 goto leave_proto_err;
802 }
803 if (bytes != (uint32_t)sizeof(nlen)) {
804 err = MOBILEBACKUP2_E_MUX_ERROR;
805 goto leave_proto_err;
806 }
807
808 /* send path */
809 err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, &bytes);
810 if (err != MOBILEBACKUP2_E_SUCCESS) {
811 goto leave_proto_err;
812 }
813 if (bytes != pathlen) {
814 err = MOBILEBACKUP2_E_MUX_ERROR;
815 goto leave_proto_err;
816 }
817
818#ifdef _WIN32
819 if (_stati64(localfile, &fst) < 0)
820#else
821 if (stat(localfile, &fst) < 0)
822#endif
823 {
824 if (errno != ENOENT)
825 printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
826 errcode = errno;
827 goto leave;
828 }
829
830 total = fst.st_size;
831
832 char *format_size = string_format_size(total);
833 PRINT_VERBOSE(1, "Sending '%s' (%s)\n", path, format_size);
834 free(format_size);
835
836 if (total == 0) {
837 errcode = 0;
838 goto leave;
839 }
840
841 f = fopen(localfile, "rb");
842 if (!f) {
843 printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
844 errcode = errno;
845 goto leave;
846 }
847
848 sent = 0;
849 do {
850 length = ((total-sent) < (long long)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
851 /* send data size (file size + 1) */
852 nlen = htobe32(length+1);
853 memcpy(buf, &nlen, sizeof(nlen));
854 buf[4] = CODE_FILE_DATA;
855 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, &bytes);
856 if (err != MOBILEBACKUP2_E_SUCCESS) {
857 goto leave_proto_err;
858 }
859 if (bytes != 5) {
860 goto leave_proto_err;
861 }
862
863 /* send file contents */
864 size_t r = fread(buf, 1, sizeof(buf), f);
865 if (r <= 0) {
866 printf("%s: read error\n", __func__);
867 errcode = errno;
868 goto leave;
869 }
870 err = mobilebackup2_send_raw(mobilebackup2, buf, r, &bytes);
871 if (err != MOBILEBACKUP2_E_SUCCESS) {
872 goto leave_proto_err;
873 }
874 if (bytes != (uint32_t)r) {
875 printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
876 goto leave_proto_err;
877 }
878 sent += r;
879 } while (sent < total);
880 fclose(f);
881 f = NULL;
882 errcode = 0;
883
884leave:
885 if (errcode == 0) {
886 result = 0;
887 nlen = 1;
888 nlen = htobe32(nlen);
889 memcpy(buf, &nlen, 4);
890 buf[4] = CODE_SUCCESS;
891 mobilebackup2_send_raw(mobilebackup2, buf, 5, &bytes);
892 } else {
893 if (!*errplist) {
894 *errplist = plist_new_dict();
895 }
896 char *errdesc = strerror(errcode);
897 mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
898
899 length = strlen(errdesc);
900 nlen = htobe32(length+1);
901 memcpy(buf, &nlen, 4);
902 buf[4] = CODE_ERROR_LOCAL;
903 slen = 5;
904 memcpy(buf+slen, errdesc, length);
905 slen += length;
906 err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
907 if (err != MOBILEBACKUP2_E_SUCCESS) {
908 printf("could not send message\n");
909 }
910 if (bytes != slen) {
911 printf("could only send %d from %d\n", bytes, slen);
912 }
913 }
914
915leave_proto_err:
916 if (f)
917 fclose(f);
918 free(localfile);
919 return result;
920}
921
922static void mb2_handle_send_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
923{
924 uint32_t cnt;
925 uint32_t i = 0;
926 uint32_t sent;
927 plist_t errplist = NULL;
928
929 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
930
931 plist_t files = plist_array_get_item(message, 1);
932 cnt = plist_array_get_size(files);
933
934 for (i = 0; i < cnt; i++) {
935 plist_t val = plist_array_get_item(files, i);
936 if (plist_get_node_type(val) != PLIST_STRING) {
937 continue;
938 }
939 char *str = NULL;
940 plist_get_string_val(val, &str);
941 if (!str)
942 continue;
943
944 if (mb2_handle_send_file(mobilebackup2, backup_dir, str, &errplist) < 0) {
945 free(str);
946 //printf("Error when sending file '%s' to device\n", str);
947 // TODO: perhaps we can continue, we've got a multi status response?!
948 break;
949 }
950 free(str);
951 }
952
953 /* send terminating 0 dword */
954 uint32_t zero = 0;
955 mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
956
957 if (!errplist) {
958 plist_t emptydict = plist_new_dict();
959 mobilebackup2_send_status_response(mobilebackup2, 0, NULL, emptydict);
960 plist_free(emptydict);
961 } else {
962 mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
963 plist_free(errplist);
964 }
965}
966
967static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** filename)
968{
969 uint32_t nlen = 0;
970 uint32_t rlen = 0;
971
972 do {
973 nlen = 0;
974 rlen = 0;
975 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &rlen);
976 nlen = be32toh(nlen);
977
978 if ((nlen == 0) && (rlen == 4)) {
979 // a zero length means no more files to receive
980 return 0;
981 }
982 if (rlen == 0) {
983 // device needs more time, waiting...
984 continue;
985 }
986 if (nlen > 4096) {
987 // filename length is too large
988 printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
989 return 0;
990 }
991
992 if (*filename != NULL) {
993 free(*filename);
994 *filename = NULL;
995 }
996
997 *filename = (char*)malloc(nlen+1);
998
999 rlen = 0;
1000 mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
1001 if (rlen != nlen) {
1002 printf("ERROR: %s: could not read filename\n", __func__);
1003 return 0;
1004 }
1005
1006 char* p = *filename;
1007 p[rlen] = 0;
1008
1009 break;
1010 } while(1 && !quit_flag);
1011
1012 return nlen;
1013}
1014
1015static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1016{
1017 uint64_t backup_real_size = 0;
1018 uint64_t backup_total_size = 0;
1019 uint32_t blocksize;
1020 uint32_t bdone;
1021 uint32_t rlen;
1022 uint32_t nlen = 0;
1023 uint32_t r;
1024 char buf[32768];
1025 char *fname = NULL;
1026 char *dname = NULL;
1027 char *bname = NULL;
1028 char code = 0;
1029 char last_code = 0;
1030 plist_t node = NULL;
1031 FILE *f = NULL;
1032 unsigned int file_count = 0;
1033 int errcode = 0;
1034 char *errdesc = NULL;
1035
1036 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
1037
1038 node = plist_array_get_item(message, 3);
1039 if (plist_get_node_type(node) == PLIST_UINT) {
1040 plist_get_uint_val(node, &backup_total_size);
1041 }
1042 if (backup_total_size > 0) {
1043 PRINT_VERBOSE(1, "Receiving files\n");
1044 }
1045
1046 do {
1047 if (quit_flag)
1048 break;
1049
1050 nlen = mb2_receive_filename(mobilebackup2, &dname);
1051 if (nlen == 0) {
1052 break;
1053 }
1054
1055 nlen = mb2_receive_filename(mobilebackup2, &fname);
1056 if (!nlen) {
1057 break;
1058 }
1059
1060 if (bname != NULL) {
1061 free(bname);
1062 bname = NULL;
1063 }
1064
1065 bname = string_build_path(backup_dir, fname, NULL);
1066
1067 if (fname != NULL) {
1068 free(fname);
1069 fname = NULL;
1070 }
1071
1072 r = 0;
1073 nlen = 0;
1074 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1075 if (r != 4) {
1076 printf("ERROR: %s: could not receive code length!\n", __func__);
1077 break;
1078 }
1079 nlen = be32toh(nlen);
1080
1081 last_code = code;
1082 code = 0;
1083
1084 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1085 if (r != 1) {
1086 printf("ERROR: %s: could not receive code!\n", __func__);
1087 break;
1088 }
1089
1090 /* TODO remove this */
1091 if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
1092 PRINT_VERBOSE(1, "Found new flag %02x\n", code);
1093 }
1094
1095 remove_file(bname);
1096 f = fopen(bname, "wb");
1097 while (f && (code == CODE_FILE_DATA)) {
1098 blocksize = nlen-1;
1099 bdone = 0;
1100 rlen = 0;
1101 while (bdone < blocksize) {
1102 if ((blocksize - bdone) < sizeof(buf)) {
1103 rlen = blocksize - bdone;
1104 } else {
1105 rlen = sizeof(buf);
1106 }
1107 mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
1108 if ((int)r <= 0) {
1109 break;
1110 }
1111 fwrite(buf, 1, r, f);
1112 bdone += r;
1113 }
1114 if (bdone == blocksize) {
1115 backup_real_size += blocksize;
1116 }
1117 if (backup_total_size > 0) {
1118 print_progress(backup_real_size, backup_total_size);
1119 }
1120 if (quit_flag)
1121 break;
1122 nlen = 0;
1123 mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
1124 nlen = be32toh(nlen);
1125 if (nlen > 0) {
1126 last_code = code;
1127 mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
1128 } else {
1129 break;
1130 }
1131 }
1132 if (f) {
1133 fclose(f);
1134 file_count++;
1135 } else {
1136 errcode = errno_to_device_error(errno);
1137 errdesc = strerror(errno);
1138 printf("Error opening '%s' for writing: %s\n", bname, errdesc);
1139 break;
1140 }
1141 if (nlen == 0) {
1142 break;
1143 }
1144
1145 /* check if an error message was received */
1146 if (code == CODE_ERROR_REMOTE) {
1147 /* error message */
1148 char *msg = (char*)malloc(nlen);
1149 mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
1150 msg[r] = 0;
1151 /* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
1152 if (last_code != CODE_FILE_DATA) {
1153 fprintf(stdout, "\nReceived an error message from device: %s\n", msg);
1154 }
1155 free(msg);
1156 }
1157 } while (1);
1158
1159 if (fname != NULL)
1160 free(fname);
1161
1162 /* if there are leftovers to read, finish up cleanly */
1163 if ((int)nlen-1 > 0) {
1164 PRINT_VERBOSE(1, "\nDiscarding current data hunk.\n");
1165 fname = (char*)malloc(nlen-1);
1166 mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
1167 free(fname);
1168 remove_file(bname);
1169 }
1170
1171 /* clean up */
1172 if (bname != NULL)
1173 free(bname);
1174
1175 if (dname != NULL)
1176 free(dname);
1177
1178 plist_t empty_plist = plist_new_dict();
1179 mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_plist);
1180 plist_free(empty_plist);
1181
1182 return file_count;
1183}
1184
1185static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1186{
1187 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1188
1189 plist_t node = plist_array_get_item(message, 1);
1190 char *str = NULL;
1191 if (plist_get_node_type(node) == PLIST_STRING) {
1192 plist_get_string_val(node, &str);
1193 }
1194 if (!str) {
1195 printf("ERROR: Malformed DLContentsOfDirectory message\n");
1196 // TODO error handling
1197 return;
1198 }
1199
1200 char *path = string_build_path(backup_dir, str, NULL);
1201 free(str);
1202
1203 plist_t dirlist = plist_new_dict();
1204
1205 DIR* cur_dir = opendir(path);
1206 if (cur_dir) {
1207 struct dirent* ep;
1208 while ((ep = readdir(cur_dir))) {
1209 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1210 continue;
1211 }
1212 char *fpath = string_build_path(path, ep->d_name, NULL);
1213 if (fpath) {
1214 plist_t fdict = plist_new_dict();
1215 struct stat st;
1216 stat(fpath, &st);
1217 const char *ftype = "DLFileTypeUnknown";
1218 if (S_ISDIR(st.st_mode)) {
1219 ftype = "DLFileTypeDirectory";
1220 } else if (S_ISREG(st.st_mode)) {
1221 ftype = "DLFileTypeRegular";
1222 }
1223 plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
1224 plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
1225 plist_dict_set_item(fdict, "DLFileModificationDate",
1226#ifdef HAVE_PLIST_UNIX_DATE
1227 plist_new_unix_date(st.st_mtime)
1228#else
1229 plist_new_date(st.st_mtime - MAC_EPOCH, 0)
1230#endif
1231 );
1232
1233 plist_dict_set_item(dirlist, ep->d_name, fdict);
1234 free(fpath);
1235 }
1236 }
1237 closedir(cur_dir);
1238 }
1239 free(path);
1240
1241 /* TODO error handling */
1242 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, dirlist);
1243 plist_free(dirlist);
1244 if (err != MOBILEBACKUP2_E_SUCCESS) {
1245 printf("Could not send status response, error %d\n", err);
1246 }
1247}
1248
1249static void mb2_handle_make_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
1250{
1251 if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
1252
1253 plist_t dir = plist_array_get_item(message, 1);
1254 char *str = NULL;
1255 int errcode = 0;
1256 char *errdesc = NULL;
1257 plist_get_string_val(dir, &str);
1258
1259 char *newpath = string_build_path(backup_dir, str, NULL);
1260 free(str);
1261
1262 if (mkdir_with_parents(newpath, 0755) < 0) {
1263 errdesc = strerror(errno);
1264 if (errno != EEXIST) {
1265 printf("mkdir: %s (%d)\n", errdesc, errno);
1266 }
1267 errcode = errno_to_device_error(errno);
1268 }
1269 free(newpath);
1270 mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
1271 if (err != MOBILEBACKUP2_E_SUCCESS) {
1272 printf("Could not send status response, error %d\n", err);
1273 }
1274}
1275
1276static void mb2_copy_file_by_path(const char *src, const char *dst)
1277{
1278 FILE *from, *to;
1279 char buf[BUFSIZ];
1280 size_t length;
1281
1282 /* open source file */
1283 if ((from = fopen(src, "rb")) == NULL) {
1284 printf("Cannot open source path '%s'.\n", src);
1285 return;
1286 }
1287
1288 /* open destination file */
1289 if ((to = fopen(dst, "wb")) == NULL) {
1290 printf("Cannot open destination file '%s'.\n", dst);
1291 fclose(from);
1292 return;
1293 }
1294
1295 /* copy the file */
1296 while ((length = fread(buf, 1, BUFSIZ, from)) != 0) {
1297 fwrite(buf, 1, length, to);
1298 }
1299
1300 if(fclose(from) == EOF) {
1301 printf("Error closing source file.\n");
1302 }
1303
1304 if(fclose(to) == EOF) {
1305 printf("Error closing destination file.\n");
1306 }
1307}
1308
1309static void mb2_copy_directory_by_path(const char *src, const char *dst)
1310{
1311 if (!src || !dst) {
1312 return;
1313 }
1314
1315 struct stat st;
1316
1317 /* if src does not exist */
1318 if ((stat(src, &st) < 0) || !S_ISDIR(st.st_mode)) {
1319 printf("ERROR: Source directory does not exist '%s': %s (%d)\n", src, strerror(errno), errno);
1320 return;
1321 }
1322
1323 /* if dst directory does not exist */
1324 if ((stat(dst, &st) < 0) || !S_ISDIR(st.st_mode)) {
1325 /* create it */
1326 if (mkdir_with_parents(dst, 0755) < 0) {
1327 printf("ERROR: Unable to create destination directory '%s': %s (%d)\n", dst, strerror(errno), errno);
1328 return;
1329 }
1330 }
1331
1332 /* loop over src directory contents */
1333 DIR *cur_dir = opendir(src);
1334 if (cur_dir) {
1335 struct dirent* ep;
1336 while ((ep = readdir(cur_dir))) {
1337 if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
1338 continue;
1339 }
1340 char *srcpath = string_build_path(src, ep->d_name, NULL);
1341 char *dstpath = string_build_path(dst, ep->d_name, NULL);
1342 if (srcpath && dstpath) {
1343 /* copy file */
1344 mb2_copy_file_by_path(srcpath, dstpath);
1345 }
1346
1347 if (srcpath)
1348 free(srcpath);
1349 if (dstpath)
1350 free(dstpath);
1351 }
1352 closedir(cur_dir);
1353 }
1354}
1355
1356#ifdef _WIN32
1357#define BS_CC '\b'
1358#define my_getch getch
1359#else
1360#define BS_CC 0x7f
1361static int my_getch(void)
1362{
1363 struct termios oldt, newt;
1364 int ch;
1365 tcgetattr(STDIN_FILENO, &oldt);
1366 newt = oldt;
1367 newt.c_lflag &= ~(ICANON | ECHO);
1368 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1369 ch = getchar();
1370 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
1371 return ch;
1372}
1373#endif
1374
1375static void get_hidden_input(char *buf, int maxlen)
1376{
1377 int pwlen = 0;
1378 int c;
1379
1380 while ((c = my_getch())) {
1381 if ((c == '\r') || (c == '\n')) {
1382 break;
1383 }
1384 if (isprint(c)) {
1385 if (pwlen < maxlen-1)
1386 buf[pwlen++] = c;
1387 fputc('*', stderr);
1388 } else if (c == BS_CC) {
1389 if (pwlen > 0) {
1390 fputs("\b \b", stderr);
1391 pwlen--;
1392 }
1393 }
1394 }
1395 buf[pwlen] = 0;
1396}
1397
1398static char* ask_for_password(const char* msg, int type_again)
1399{
1400 char pwbuf[256];
1401
1402 fprintf(stderr, "%s: ", msg);
1403 fflush(stderr);
1404 get_hidden_input(pwbuf, 256);
1405 fputc('\n', stderr);
1406
1407 if (type_again) {
1408 char pwrep[256];
1409
1410 fprintf(stderr, "%s (repeat): ", msg);
1411 fflush(stderr);
1412 get_hidden_input(pwrep, 256);
1413 fputc('\n', stderr);
1414
1415 if (strcmp(pwbuf, pwrep) != 0) {
1416 printf("ERROR: passwords don't match\n");
1417 return NULL;
1418 }
1419 }
1420 return strdup(pwbuf);
1421}
1422
1423/**
1424 * signal handler function for cleaning up properly
1425 */
1426static void clean_exit(int sig)
1427{
1428 fprintf(stderr, "Exiting...\n");
1429 quit_flag++;
1430}
1431
1432static void print_usage(int argc, char **argv, int is_error)
1433{
1434 char *name = strrchr(argv[0], '/');
1435 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
1436 fprintf(is_error ? stderr : stdout,
1437 "\n"
1438 "Create or restore backup in/from the specified directory.\n"
1439 "\n"
1440 "CMD:\n"
1441 " backup create backup for the device\n"
1442 " --full force full backup from device.\n"
1443 " restore restore last backup to the device\n"
1444 " --system restore system files, too.\n"
1445 " --no-reboot do NOT reboot the device when done (default: yes).\n"
1446 " --copy create a copy of backup folder before restoring.\n"
1447 " --settings restore device settings from the backup.\n"
1448 " --remove remove items which are not being restored\n"
1449 " --skip-apps do not trigger re-installation of apps after restore\n"
1450 " --password PWD supply the password for the encrypted source backup\n"
1451 " info show details about last completed backup of device\n"
1452 " list list files of last completed backup in CSV format\n"
1453 " unback unpack a completed backup in DIRECTORY/_unback_/\n"
1454 " encryption on|off [PWD] enable or disable backup encryption\n"
1455 " changepw [OLD NEW] change backup password on target device\n"
1456 " cloud on|off enable or disable cloud use (requires iCloud account)\n"
1457 "\n"
1458 "NOTE: Passwords will be requested in interactive mode (-i) if omitted, or can\n"
1459 "be passed via environment variable BACKUP_PASSWORD/BACKUP_PASSWORD_NEW.\n"
1460 "See man page for further details.\n"
1461 "\n"
1462 "OPTIONS:\n"
1463 " -u, --udid UDID target specific device by UDID\n"
1464 " -s, --source UDID use backup data from device specified by UDID\n"
1465 " -n, --network connect to network device\n"
1466 " -i, --interactive request passwords interactively\n"
1467 " -d, --debug enable communication debugging\n"
1468 " -h, --help prints usage information\n"
1469 " -v, --version prints version information\n"
1470 "\n"
1471 "Homepage: <" PACKAGE_URL ">\n"
1472 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
1473 );
1474}
1475
1476int main(int argc, char *argv[])
1477{
1478 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
1479 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
1480 int i = 0;
1481 char* udid = NULL;
1482 char* source_udid = NULL;
1483 int use_network = 0;
1484 lockdownd_service_descriptor_t service = NULL;
1485 int cmd = -1;
1486 int cmd_flags = 0;
1487 int is_full_backup = 0;
1488 int result_code = -1;
1489 char* backup_directory = NULL;
1490 int interactive_mode = 0;
1491 char* backup_password = NULL;
1492 char* newpw = NULL;
1493 struct stat st;
1494 plist_t node_tmp = NULL;
1495 plist_t info_plist = NULL;
1496 plist_t opts = NULL;
1497
1498 idevice_t device = NULL;
1499 afc_client_t afc = NULL;
1500 np_client_t np = NULL;
1501 lockdownd_client_t lockdown = NULL;
1502 mobilebackup2_client_t mobilebackup2 = NULL;
1503 mobilebackup2_error_t err;
1504 uint64_t lockfile = 0;
1505
1506#define OPT_SYSTEM 1
1507#define OPT_REBOOT 2
1508#define OPT_NO_REBOOT 3
1509#define OPT_COPY 4
1510#define OPT_SETTINGS 5
1511#define OPT_REMOVE 6
1512#define OPT_SKIP_APPS 7
1513#define OPT_PASSWORD 8
1514#define OPT_FULL 9
1515
1516 int c = 0;
1517 const struct option longopts[] = {
1518 { "debug", no_argument, NULL, 'd' },
1519 { "help", no_argument, NULL, 'h' },
1520 { "udid", required_argument, NULL, 'u' },
1521 { "source", required_argument, NULL, 's' },
1522 { "interactive", no_argument, NULL, 'i' },
1523 { "network", no_argument, NULL, 'n' },
1524 { "version", no_argument, NULL, 'v' },
1525 // command options:
1526 { "system", no_argument, NULL, OPT_SYSTEM },
1527 { "reboot", no_argument, NULL, OPT_REBOOT },
1528 { "no-reboot", no_argument, NULL, OPT_NO_REBOOT },
1529 { "copy", no_argument, NULL, OPT_COPY },
1530 { "settings", no_argument, NULL, OPT_SETTINGS },
1531 { "remove", no_argument, NULL, OPT_REMOVE },
1532 { "skip-apps", no_argument, NULL, OPT_SKIP_APPS },
1533 { "password", required_argument, NULL, OPT_PASSWORD },
1534 { "full", no_argument, NULL, OPT_FULL },
1535 { NULL, 0, NULL, 0}
1536 };
1537
1538 /* we need to exit cleanly on running backups and restores or we cause havok */
1539 signal(SIGINT, clean_exit);
1540 signal(SIGTERM, clean_exit);
1541#ifndef _WIN32
1542 signal(SIGQUIT, clean_exit);
1543 signal(SIGPIPE, SIG_IGN);
1544#endif
1545
1546 /* parse cmdline args */
1547 while ((c = getopt_long(argc, argv, "dhu:s:inv", longopts, NULL)) != -1) {
1548 switch (c) {
1549 case 'd':
1550 idevice_set_debug_level(1);
1551 break;
1552 case 'u':
1553 if (!*optarg) {
1554 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
1555 print_usage(argc, argv, 1);
1556 return 2;
1557 }
1558 udid = strdup(optarg);
1559 break;
1560 case 's':
1561 if (!*optarg) {
1562 fprintf(stderr, "ERROR: SOURCE argument must not be empty!\n");
1563 print_usage(argc, argv, 1);
1564 return 2;
1565 }
1566 source_udid = strdup(optarg);
1567 break;
1568 case 'i':
1569 interactive_mode = 1;
1570 break;
1571 case 'n':
1572 use_network = 1;
1573 break;
1574 case 'h':
1575 print_usage(argc, argv, 0);
1576 return 0;
1577 case 'v':
1578 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
1579 return 0;
1580 case OPT_SYSTEM:
1581 cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
1582 break;
1583 case OPT_REBOOT:
1584 cmd_flags &= ~CMD_FLAG_RESTORE_NO_REBOOT;
1585 break;
1586 case OPT_NO_REBOOT:
1587 cmd_flags |= CMD_FLAG_RESTORE_NO_REBOOT;
1588 break;
1589 case OPT_COPY:
1590 cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
1591 break;
1592 case OPT_SETTINGS:
1593 cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
1594 break;
1595 case OPT_REMOVE:
1596 cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
1597 break;
1598 case OPT_SKIP_APPS:
1599 cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
1600 break;
1601 case OPT_PASSWORD:
1602 free(backup_password);
1603 backup_password = strdup(optarg);
1604 break;
1605 case OPT_FULL:
1606 cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
1607 break;
1608 default:
1609 print_usage(argc, argv, 1);
1610 return 2;
1611 }
1612 }
1613 argc -= optind;
1614 argv += optind;
1615
1616 if (!argv[0]) {
1617 fprintf(stderr, "ERROR: No command specified.\n");
1618 print_usage(argc+optind, argv-optind, 1);
1619 return 2;
1620 }
1621
1622 if (!strcmp(argv[0], "backup")) {
1623 cmd = CMD_BACKUP;
1624 }
1625 else if (!strcmp(argv[0], "restore")) {
1626 cmd = CMD_RESTORE;
1627 }
1628 else if (!strcmp(argv[0], "cloud")) {
1629 cmd = CMD_CLOUD;
1630 i = 1;
1631 if (!argv[i]) {
1632 fprintf(stderr, "ERROR: No argument given for cloud command; requires either 'on' or 'off'.\n");
1633 print_usage(argc+optind, argv-optind, 1);
1634 return 2;
1635 }
1636 if (!strcmp(argv[i], "on")) {
1637 cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
1638 } else if (!strcmp(argv[i], "off")) {
1639 cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
1640 } else {
1641 fprintf(stderr, "ERROR: Invalid argument '%s' for cloud command; must be either 'on' or 'off'.\n", argv[i]);
1642 print_usage(argc+optind, argv-optind, 1);
1643 return 2;
1644 }
1645 }
1646 else if (!strcmp(argv[0], "info")) {
1647 cmd = CMD_INFO;
1648 verbose = 0;
1649 }
1650 else if (!strcmp(argv[0], "list")) {
1651 cmd = CMD_LIST;
1652 verbose = 0;
1653 }
1654 else if (!strcmp(argv[0], "unback")) {
1655 cmd = CMD_UNBACK;
1656 }
1657 else if (!strcmp(argv[0], "encryption")) {
1658 cmd = CMD_CHANGEPW;
1659 i = 1;
1660 if (!argv[i]) {
1661 fprintf(stderr, "ERROR: No argument given for encryption command; requires either 'on' or 'off'.\n");
1662 print_usage(argc+optind, argv-optind, 1);
1663 return 2;
1664 }
1665 if (!strcmp(argv[i], "on")) {
1666 cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
1667 } else if (!strcmp(argv[i], "off")) {
1668 cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
1669 } else {
1670 fprintf(stderr, "ERROR: Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
1671 print_usage(argc+optind, argv-optind, 1);
1672 return 2;
1673 }
1674 // check if a password was given on the command line
1675 free(newpw);
1676 newpw = NULL;
1677 free(backup_password);
1678 backup_password = NULL;
1679 i++;
1680 if (argv[i]) {
1681 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
1682 newpw = strdup(argv[i]);
1683 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
1684 backup_password = strdup(argv[i]);
1685 }
1686 }
1687 }
1688 else if (!strcmp(argv[0], "changepw")) {
1689 cmd = CMD_CHANGEPW;
1690 cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
1691 // check if passwords were given on command line
1692 free(newpw);
1693 newpw = NULL;
1694 free(backup_password);
1695 backup_password = NULL;
1696 i = 1;
1697 if (argv[i]) {
1698 backup_password = strdup(argv[i]);
1699 i++;
1700 if (!argv[i]) {
1701 fprintf(stderr, "ERROR: Old and new passwords have to be passed as arguments for the changepw command\n");
1702 print_usage(argc+optind, argv-optind, 1);
1703 return 2;
1704 }
1705 newpw = strdup(argv[i]);
1706 }
1707 }
1708
1709 i++;
1710 if (argv[i]) {
1711 backup_directory = argv[i];
1712 }
1713
1714 /* verify options */
1715 if (cmd == -1) {
1716 fprintf(stderr, "ERROR: Unsupported command '%s'.\n", argv[0]);
1717 print_usage(argc+optind, argv-optind, 1);
1718 return 2;
1719 }
1720
1721 if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
1722 backup_directory = (char*)".this_folder_is_not_present_on_purpose";
1723 } else {
1724 if (backup_directory == NULL) {
1725 fprintf(stderr, "ERROR: No target backup directory specified.\n");
1726 print_usage(argc+optind, argv-optind, 1);
1727 return 2;
1728 }
1729
1730 /* verify if passed backup directory exists */
1731 if (stat(backup_directory, &st) != 0) {
1732 fprintf(stderr, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
1733 return -1;
1734 }
1735 }
1736
1737 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
1738 if (ret != IDEVICE_E_SUCCESS) {
1739 if (udid) {
1740 printf("No device found with udid %s.\n", udid);
1741 } else {
1742 printf("No device found.\n");
1743 }
1744 return -1;
1745 }
1746
1747 if (!udid) {
1748 idevice_get_udid(device, &udid);
1749 }
1750
1751 if (!source_udid) {
1752 source_udid = strdup(udid);
1753 }
1754
1755 uint8_t is_encrypted = 0;
1756 char *info_path = NULL;
1757 if (cmd == CMD_CHANGEPW) {
1758 if (!interactive_mode) {
1759 if (!newpw) {
1760 newpw = getenv("BACKUP_PASSWORD_NEW");
1761 if (newpw) {
1762 newpw = strdup(newpw);
1763 }
1764 }
1765 if (!backup_password) {
1766 backup_password = getenv("BACKUP_PASSWORD");
1767 if (backup_password) {
1768 backup_password = strdup(backup_password);
1769 }
1770 }
1771 }
1772 if (!interactive_mode && !backup_password && !newpw) {
1773 idevice_free(device);
1774 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");
1775 return -1;
1776 }
1777 } else if (cmd != CMD_CLOUD) {
1778 /* backup directory must contain an Info.plist */
1779 info_path = string_build_path(backup_directory, source_udid, "Info.plist", NULL);
1780 if (cmd == CMD_RESTORE || cmd == CMD_UNBACK) {
1781 if (stat(info_path, &st) != 0) {
1782 idevice_free(device);
1783 free(info_path);
1784 printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, source_udid);
1785 return -1;
1786 }
1787 char* manifest_path = string_build_path(backup_directory, source_udid, "Manifest.plist", NULL);
1788 if (stat(manifest_path, &st) != 0) {
1789 free(info_path);
1790 }
1791 plist_t manifest_plist = NULL;
1792 plist_read_from_file(manifest_path, &manifest_plist, NULL);
1793 if (!manifest_plist) {
1794 idevice_free(device);
1795 free(info_path);
1796 free(manifest_path);
1797 printf("ERROR: Backup directory \"%s\" is invalid. No Manifest.plist found for UDID %s.\n", backup_directory, source_udid);
1798 return -1;
1799 }
1800 node_tmp = plist_dict_get_item(manifest_plist, "IsEncrypted");
1801 if (node_tmp && (plist_get_node_type(node_tmp) == PLIST_BOOLEAN)) {
1802 plist_get_bool_val(node_tmp, &is_encrypted);
1803 }
1804 plist_free(manifest_plist);
1805 free(manifest_path);
1806 }
1807 PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
1808 }
1809
1810 if (cmd != CMD_CLOUD && is_encrypted) {
1811 PRINT_VERBOSE(1, "This is an encrypted backup.\n");
1812 if (backup_password == NULL) {
1813 backup_password = getenv("BACKUP_PASSWORD");
1814 if (backup_password) {
1815 backup_password = strdup(backup_password);
1816 }
1817 }
1818 if (backup_password == NULL) {
1819 if (interactive_mode) {
1820 backup_password = ask_for_password("Enter backup password", 0);
1821 }
1822 if (!backup_password || (strlen(backup_password) == 0)) {
1823 if (backup_password) {
1824 free(backup_password);
1825 }
1826 idevice_free(device);
1827 if (cmd == CMD_RESTORE) {
1828 printf("ERROR: a backup password is required to restore an encrypted backup. Cannot continue.\n");
1829 } else if (cmd == CMD_UNBACK) {
1830 printf("ERROR: a backup password is required to unback an encrypted backup. Cannot continue.\n");
1831 }
1832 return -1;
1833 }
1834 }
1835 }
1836
1837 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
1838 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
1839 idevice_free(device);
1840 return -1;
1841 }
1842
1843 uint8_t willEncrypt = 0;
1844 node_tmp = NULL;
1845 lockdownd_get_value(lockdown, "com.apple.mobile.backup", "WillEncrypt", &node_tmp);
1846 if (node_tmp) {
1847 if (plist_get_node_type(node_tmp) == PLIST_BOOLEAN) {
1848 plist_get_bool_val(node_tmp, &willEncrypt);
1849 }
1850 plist_free(node_tmp);
1851 node_tmp = NULL;
1852 }
1853
1854 /* get ProductVersion */
1855 int device_version = idevice_get_device_version(device);
1856
1857 /* start notification_proxy */
1858 ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
1859 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
1860 np_client_new(device, service, &np);
1861 np_set_notify_callback(np, notify_cb, NULL);
1862 const char *noties[7] = {
1863 NP_SYNC_CANCEL_REQUEST,
1864 NP_SYNC_SUSPEND_REQUEST,
1865 NP_SYNC_RESUME_REQUEST,
1866 NP_BACKUP_DOMAIN_CHANGED,
1867 "com.apple.LocalAuthentication.ui.presented",
1868 "com.apple.LocalAuthentication.ui.dismissed",
1869 NULL
1870 };
1871 np_observe_notifications(np, noties);
1872 } else {
1873 printf("ERROR: Could not start service %s: %s\n", NP_SERVICE_NAME, lockdownd_strerror(ldret));
1874 cmd = CMD_LEAVE;
1875 goto checkpoint;
1876 }
1877 if (service) {
1878 lockdownd_service_descriptor_free(service);
1879 service = NULL;
1880 }
1881
1882 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
1883 /* start AFC, we need this for the lock file */
1884 ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);
1885 if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
1886 afc_client_new(device, service, &afc);
1887 } else {
1888 printf("ERROR: Could not start service %s: %s\n", AFC_SERVICE_NAME, lockdownd_strerror(ldret));
1889 cmd = CMD_LEAVE;
1890 goto checkpoint;
1891 }
1892 }
1893
1894 if (service) {
1895 lockdownd_service_descriptor_free(service);
1896 service = NULL;
1897 }
1898
1899 /* start mobilebackup service and retrieve port */
1900 ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
1901 lockdownd_client_free(lockdown);
1902 lockdown = NULL;
1903 if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
1904 PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
1905 mobilebackup2_client_new(device, service, &mobilebackup2);
1906
1907 if (service) {
1908 lockdownd_service_descriptor_free(service);
1909 service = NULL;
1910 }
1911
1912 /* send Hello message */
1913 double local_versions[2] = {2.0, 2.1};
1914 double remote_version = 0.0;
1915 err = mobilebackup2_version_exchange(mobilebackup2, local_versions, 2, &remote_version);
1916 if (err != MOBILEBACKUP2_E_SUCCESS) {
1917 printf("Could not perform backup protocol version exchange, error code %d\n", err);
1918 cmd = CMD_LEAVE;
1919 goto checkpoint;
1920 }
1921
1922 PRINT_VERBOSE(1, "Negotiated Protocol Version %.1f\n", remote_version);
1923
1924 /* check abort conditions */
1925 if (quit_flag > 0) {
1926 PRINT_VERBOSE(1, "Aborting as requested by user...\n");
1927 cmd = CMD_LEAVE;
1928 goto checkpoint;
1929 }
1930
1931 /* verify existing Info.plist */
1932 if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {
1933 PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
1934 plist_read_from_file(info_path, &info_plist, NULL);
1935
1936 if (!info_plist) {
1937 printf("Could not read Info.plist\n");
1938 is_full_backup = 1;
1939 }
1940 } else {
1941 if (cmd == CMD_RESTORE) {
1942 printf("Aborting restore. Info.plist is missing.\n");
1943 cmd = CMD_LEAVE;
1944 } else {
1945 is_full_backup = 1;
1946 }
1947 }
1948
1949 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
1950 do_post_notification(device, NP_SYNC_WILL_START);
1951 afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
1952 }
1953 if (lockfile) {
1954 afc_error_t aerr;
1955 do_post_notification(device, NP_SYNC_LOCK_REQUEST);
1956 for (i = 0; i < LOCK_ATTEMPTS; i++) {
1957 aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
1958 if (aerr == AFC_E_SUCCESS) {
1959 do_post_notification(device, NP_SYNC_DID_START);
1960 break;
1961 }
1962 if (aerr == AFC_E_OP_WOULD_BLOCK) {
1963 usleep(LOCK_WAIT);
1964 continue;
1965 }
1966
1967 fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
1968 afc_file_close(afc, lockfile);
1969 lockfile = 0;
1970 cmd = CMD_LEAVE;
1971 }
1972 if (i == LOCK_ATTEMPTS) {
1973 fprintf(stderr, "ERROR: timeout while locking for sync\n");
1974 afc_file_close(afc, lockfile);
1975 lockfile = 0;
1976 cmd = CMD_LEAVE;
1977 }
1978 }
1979
1980checkpoint:
1981
1982 switch(cmd) {
1983 case CMD_CLOUD:
1984 opts = plist_new_dict();
1985 plist_dict_set_item(opts, "CloudBackupState", plist_new_bool(cmd_flags & CMD_FLAG_CLOUD_ENABLE ? 1: 0));
1986 err = mobilebackup2_send_request(mobilebackup2, "EnableCloudBackup", udid, source_udid, opts);
1987 plist_free(opts);
1988 opts = NULL;
1989 if (err != MOBILEBACKUP2_E_SUCCESS) {
1990 printf("Error setting cloud backup state on device, error code %d\n", err);
1991 cmd = CMD_LEAVE;
1992 }
1993 break;
1994 case CMD_BACKUP:
1995 PRINT_VERBOSE(1, "Starting backup...\n");
1996
1997 /* make sure backup device sub-directory exists */
1998 char* devbackupdir = string_build_path(backup_directory, source_udid, NULL);
1999 __mkdir(devbackupdir, 0755);
2000 free(devbackupdir);
2001
2002 if (strcmp(source_udid, udid) != 0) {
2003 /* handle different source backup directory */
2004 // make sure target backup device sub-directory exists
2005 devbackupdir = string_build_path(backup_directory, udid, NULL);
2006 __mkdir(devbackupdir, 0755);
2007 free(devbackupdir);
2008
2009 // use Info.plist path in target backup folder */
2010 free(info_path);
2011 info_path = string_build_path(backup_directory, udid, "Info.plist", NULL);
2012 }
2013
2014 /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
2015 /* TODO: verify battery on AC enough battery remaining */
2016
2017 /* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
2018 if (info_plist) {
2019 plist_free(info_plist);
2020 info_plist = NULL;
2021 }
2022 info_plist = mobilebackup_factory_info_plist_new(udid, device, afc);
2023 if (!info_plist) {
2024 fprintf(stderr, "Failed to generate Info.plist - aborting\n");
2025 cmd = CMD_LEAVE;
2026 }
2027 remove_file(info_path);
2028 plist_write_to_file(info_plist, info_path, PLIST_FORMAT_XML, 0);
2029 free(info_path);
2030
2031 plist_free(info_plist);
2032 info_plist = NULL;
2033
2034 if (cmd_flags & CMD_FLAG_FORCE_FULL_BACKUP) {
2035 PRINT_VERBOSE(1, "Enforcing full backup from device.\n");
2036 opts = plist_new_dict();
2037 plist_dict_set_item(opts, "ForceFullBackup", plist_new_bool(1));
2038 }
2039 /* request backup from device with manifest from last backup */
2040 if (willEncrypt) {
2041 PRINT_VERBOSE(1, "Backup will be encrypted.\n");
2042 } else {
2043 PRINT_VERBOSE(1, "Backup will be unencrypted.\n");
2044 }
2045 PRINT_VERBOSE(1, "Requesting backup from device...\n");
2046 err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, source_udid, opts);
2047 if (opts)
2048 plist_free(opts);
2049 if (err == MOBILEBACKUP2_E_SUCCESS) {
2050 if (is_full_backup) {
2051 PRINT_VERBOSE(1, "Full backup mode.\n");
2052 } else {
2053 PRINT_VERBOSE(1, "Incremental backup mode.\n");
2054 }
2055 if (device_version >= IDEVICE_DEVICE_VERSION(16,1,0)) {
2056 /* let's wait 2 second to see if the device passcode is requested */
2057 int retries = 20;
2058 while (retries-- > 0 && !passcode_requested) {
2059 usleep(100000);
2060 }
2061 if (passcode_requested) {
2062 printf("*** Waiting for passcode to be entered on the device ***\n");
2063 }
2064 }
2065 } else {
2066 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2067 printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
2068 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2069 printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
2070 } else {
2071 printf("ERROR: Could not start backup process: unspecified error occurred\n");
2072 }
2073 cmd = CMD_LEAVE;
2074 }
2075 break;
2076 case CMD_RESTORE:
2077 /* TODO: verify battery on AC enough battery remaining */
2078
2079 /* verify if Status.plist says we read from an successful backup */
2080 if (!mb2_status_check_snapshot_state(backup_directory, source_udid, "finished")) {
2081 printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
2082 cmd = CMD_LEAVE;
2083 break;
2084 }
2085
2086 PRINT_VERBOSE(1, "Starting Restore...\n");
2087
2088 opts = plist_new_dict();
2089 plist_dict_set_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
2090 PRINT_VERBOSE(1, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "Yes":"No"));
2091 if (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT)
2092 plist_dict_set_item(opts, "RestoreShouldReboot", plist_new_bool(0));
2093 PRINT_VERBOSE(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT ? "No":"Yes"));
2094 if ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0)
2095 plist_dict_set_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
2096 PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
2097 plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
2098 PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
2099 plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS));
2100 PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
2101 if (backup_password != NULL) {
2102 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2103 }
2104 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2105
2106 if (cmd_flags & CMD_FLAG_RESTORE_SKIP_APPS) {
2107 PRINT_VERBOSE(1, "Not writing RestoreApplications.plist - apps will not be re-installed after restore\n");
2108 } else {
2109 /* Write /iTunesRestore/RestoreApplications.plist so that the device will start
2110 * restoring applications once the rest of the restore process is finished */
2111 if (write_restore_applications(info_plist, afc) < 0) {
2112 cmd = CMD_LEAVE;
2113 break;
2114 }
2115 PRINT_VERBOSE(1, "Wrote RestoreApplications.plist\n");
2116 }
2117
2118 /* Start restore */
2119 err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, source_udid, opts);
2120 plist_free(opts);
2121 if (err != MOBILEBACKUP2_E_SUCCESS) {
2122 if (err == MOBILEBACKUP2_E_BAD_VERSION) {
2123 printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
2124 } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
2125 printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
2126 } else {
2127 printf("ERROR: Could not start restore process: unspecified error occurred\n");
2128 }
2129 cmd = CMD_LEAVE;
2130 }
2131 break;
2132 case CMD_INFO:
2133 PRINT_VERBOSE(1, "Requesting backup info from device...\n");
2134 err = mobilebackup2_send_request(mobilebackup2, "Info", udid, source_udid, NULL);
2135 if (err != MOBILEBACKUP2_E_SUCCESS) {
2136 printf("Error requesting backup info from device, error code %d\n", err);
2137 cmd = CMD_LEAVE;
2138 }
2139 break;
2140 case CMD_LIST:
2141 PRINT_VERBOSE(1, "Requesting backup list from device...\n");
2142 err = mobilebackup2_send_request(mobilebackup2, "List", udid, source_udid, NULL);
2143 if (err != MOBILEBACKUP2_E_SUCCESS) {
2144 printf("Error requesting backup list from device, error code %d\n", err);
2145 cmd = CMD_LEAVE;
2146 }
2147 break;
2148 case CMD_UNBACK:
2149 PRINT_VERBOSE(1, "Starting to unpack backup...\n");
2150 if (backup_password != NULL) {
2151 opts = plist_new_dict();
2152 plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
2153 }
2154 PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
2155 err = mobilebackup2_send_request(mobilebackup2, "Unback", udid, source_udid, opts);
2156 if (backup_password !=NULL) {
2157 plist_free(opts);
2158 }
2159 if (err != MOBILEBACKUP2_E_SUCCESS) {
2160 printf("Error requesting unback operation from device, error code %d\n", err);
2161 cmd = CMD_LEAVE;
2162 }
2163 break;
2164 case CMD_CHANGEPW:
2165 opts = plist_new_dict();
2166 plist_dict_set_item(opts, "TargetIdentifier", plist_new_string(udid));
2167 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2168 if (!willEncrypt) {
2169 if (!newpw) {
2170 newpw = getenv("BACKUP_PASSWORD");
2171 if (newpw) {
2172 newpw = strdup(newpw);
2173 }
2174 }
2175 if (!newpw) {
2176 newpw = ask_for_password("Enter new backup password", 1);
2177 }
2178 if (!newpw) {
2179 printf("No backup password given. Aborting.\n");
2180 }
2181 } else {
2182 printf("ERROR: Backup encryption is already enabled. Aborting.\n");
2183 cmd = CMD_LEAVE;
2184 if (newpw) {
2185 free(newpw);
2186 newpw = NULL;
2187 }
2188 }
2189 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2190 if (willEncrypt) {
2191 if (!backup_password) {
2192 backup_password = getenv("BACKUP_PASSWORD");
2193 if (backup_password) {
2194 backup_password = strdup(backup_password);
2195 }
2196 }
2197 if (!backup_password) {
2198 backup_password = ask_for_password("Enter current backup password", 0);
2199 }
2200 } else {
2201 printf("ERROR: Backup encryption is not enabled. Aborting.\n");
2202 cmd = CMD_LEAVE;
2203 if (backup_password) {
2204 free(backup_password);
2205 backup_password = NULL;
2206 }
2207 }
2208 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2209 if (willEncrypt) {
2210 if (!backup_password) {
2211 backup_password = ask_for_password("Enter old backup password", 0);
2212 newpw = ask_for_password("Enter new backup password", 1);
2213 }
2214 } else {
2215 printf("ERROR: Backup encryption is not enabled so can't change password. Aborting.\n");
2216 cmd = CMD_LEAVE;
2217 if (newpw) {
2218 free(newpw);
2219 newpw = NULL;
2220 }
2221 if (backup_password) {
2222 free(backup_password);
2223 backup_password = NULL;
2224 }
2225 }
2226 }
2227 if (newpw) {
2228 plist_dict_set_item(opts, "NewPassword", plist_new_string(newpw));
2229 }
2230 if (backup_password) {
2231 plist_dict_set_item(opts, "OldPassword", plist_new_string(backup_password));
2232 }
2233 if (newpw || backup_password) {
2234 mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
2235 uint8_t passcode_hint = 0;
2236 if (device_version >= IDEVICE_DEVICE_VERSION(13,0,0)) {
2237 diagnostics_relay_client_t diag = NULL;
2238 if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2239 plist_t dict = NULL;
2240 plist_t keys = plist_new_array();
2241 plist_array_append_item(keys, plist_new_string("PasswordConfigured"));
2242 if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) {
2243 plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured");
2244 plist_get_bool_val(node, &passcode_hint);
2245 }
2246 plist_free(keys);
2247 plist_free(dict);
2248 diagnostics_relay_goodbye(diag);
2249 diagnostics_relay_client_free(diag);
2250 }
2251 }
2252 if (passcode_hint) {
2253 if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2254 PRINT_VERBOSE(1, "Please confirm changing the backup password by entering the passcode on the device.\n");
2255 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2256 PRINT_VERBOSE(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n");
2257 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2258 PRINT_VERBOSE(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n");
2259 }
2260 }
2261 /*if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2262 int retr = 10;
2263 while ((retr-- >= 0) && !backup_domain_changed) {
2264 sleep(1);
2265 }
2266 }*/
2267 } else {
2268 cmd = CMD_LEAVE;
2269 }
2270 plist_free(opts);
2271 break;
2272 default:
2273 break;
2274 }
2275
2276 if (cmd != CMD_LEAVE) {
2277 /* reset operation success status */
2278 int operation_ok = 0;
2279 plist_t message = NULL;
2280
2281 mobilebackup2_error_t mberr;
2282 char *dlmsg = NULL;
2283 int file_count = 0;
2284 int errcode = 0;
2285 const char *errdesc = NULL;
2286 int progress_finished = 0;
2287
2288 /* process series of DLMessage* operations */
2289 do {
2290 free(dlmsg);
2291 dlmsg = NULL;
2292 mberr = mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
2293 if (mberr == MOBILEBACKUP2_E_RECEIVE_TIMEOUT) {
2294 PRINT_VERBOSE(2, "Device is not ready yet, retrying...\n");
2295 goto files_out;
2296 } else if (mberr != MOBILEBACKUP2_E_SUCCESS) {
2297 PRINT_VERBOSE(0, "ERROR: Could not receive from mobilebackup2 (%d)\n", mberr);
2298 quit_flag++;
2299 goto files_out;
2300 }
2301
2302 if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
2303 /* device wants to download files from the computer */
2304 mb2_set_overall_progress_from_message(message, dlmsg);
2305 mb2_handle_send_files(mobilebackup2, message, backup_directory);
2306 } else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
2307 /* device wants to send files to the computer */
2308 mb2_set_overall_progress_from_message(message, dlmsg);
2309 file_count += mb2_handle_receive_files(mobilebackup2, message, backup_directory);
2310 } else if (!strcmp(dlmsg, "DLMessageGetFreeDiskSpace")) {
2311 /* device wants to know how much disk space is available on the computer */
2312 uint64_t freespace = 0;
2313 int res = -1;
2314#ifdef _WIN32
2315 if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
2316 res = 0;
2317 }
2318#else
2319 struct statvfs fs;
2320 memset(&fs, '\0', sizeof(fs));
2321 res = statvfs(backup_directory, &fs);
2322 if (res == 0) {
2323 freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize;
2324 }
2325#endif
2326 plist_t freespace_item = plist_new_uint(freespace);
2327 mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);
2328 plist_free(freespace_item);
2329 } else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) {
2330 /* device wants to purge disk space on the host - not supported */
2331 plist_t empty_dict = plist_new_dict();
2332 err = mobilebackup2_send_status_response(mobilebackup2, -1, "Operation not supported", empty_dict);
2333 plist_free(empty_dict);
2334 } else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
2335 /* list directory contents */
2336 mb2_handle_list_directory(mobilebackup2, message, backup_directory);
2337 } else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
2338 /* make a directory */
2339 mb2_handle_make_directory(mobilebackup2, message, backup_directory);
2340 } else if (!strcmp(dlmsg, "DLMessageMoveFiles") || !strcmp(dlmsg, "DLMessageMoveItems")) {
2341 /* perform a series of rename operations */
2342 mb2_set_overall_progress_from_message(message, dlmsg);
2343 plist_t moves = plist_array_get_item(message, 1);
2344 uint32_t cnt = plist_dict_get_size(moves);
2345 PRINT_VERBOSE(1, "Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
2346 plist_dict_iter iter = NULL;
2347 plist_dict_new_iter(moves, &iter);
2348 errcode = 0;
2349 errdesc = NULL;
2350 if (iter) {
2351 char *key = NULL;
2352 plist_t val = NULL;
2353 do {
2354 plist_dict_next_item(moves, iter, &key, &val);
2355 if (key && (plist_get_node_type(val) == PLIST_STRING)) {
2356 char *str = NULL;
2357 plist_get_string_val(val, &str);
2358 if (str) {
2359 char *newpath = string_build_path(backup_directory, str, NULL);
2360 free(str);
2361 char *oldpath = string_build_path(backup_directory, key, NULL);
2362
2363 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode))
2364 rmdir_recursive(newpath);
2365 else
2366 remove_file(newpath);
2367 if (rename(oldpath, newpath) < 0) {
2368 printf("Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
2369 errcode = errno_to_device_error(errno);
2370 errdesc = strerror(errno);
2371 break;
2372 }
2373 free(oldpath);
2374 free(newpath);
2375 }
2376 free(key);
2377 key = NULL;
2378 }
2379 } while (val);
2380 free(iter);
2381 } else {
2382 errcode = -1;
2383 errdesc = "Could not create dict iterator";
2384 printf("Could not create dict iterator\n");
2385 }
2386 plist_t empty_dict = plist_new_dict();
2387 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2388 plist_free(empty_dict);
2389 if (err != MOBILEBACKUP2_E_SUCCESS) {
2390 printf("Could not send status response, error %d\n", err);
2391 }
2392 } else if (!strcmp(dlmsg, "DLMessageRemoveFiles") || !strcmp(dlmsg, "DLMessageRemoveItems")) {
2393 mb2_set_overall_progress_from_message(message, dlmsg);
2394 plist_t removes = plist_array_get_item(message, 1);
2395 uint32_t cnt = plist_array_get_size(removes);
2396 PRINT_VERBOSE(1, "Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
2397 uint32_t ii = 0;
2398 errcode = 0;
2399 errdesc = NULL;
2400 for (ii = 0; ii < cnt; ii++) {
2401 plist_t val = plist_array_get_item(removes, ii);
2402 if (plist_get_node_type(val) == PLIST_STRING) {
2403 char *str = NULL;
2404 plist_get_string_val(val, &str);
2405 if (str) {
2406 const char *checkfile = strchr(str, '/');
2407 int suppress_warning = 0;
2408 if (checkfile) {
2409 if (strcmp(checkfile+1, "Manifest.mbdx") == 0) {
2410 suppress_warning = 1;
2411 }
2412 }
2413 char *newpath = string_build_path(backup_directory, str, NULL);
2414 free(str);
2415 int res = 0;
2416 if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2417 res = rmdir_recursive(newpath);
2418 } else {
2419 res = remove_file(newpath);
2420 }
2421 if (res != 0 && res != ENOENT) {
2422 if (!suppress_warning)
2423 printf("Could not remove '%s': %s (%d)\n", newpath, strerror(res), res);
2424 errcode = errno_to_device_error(res);
2425 errdesc = strerror(res);
2426 }
2427 free(newpath);
2428 }
2429 }
2430 }
2431 plist_t empty_dict = plist_new_dict();
2432 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2433 plist_free(empty_dict);
2434 if (err != MOBILEBACKUP2_E_SUCCESS) {
2435 printf("Could not send status response, error %d\n", err);
2436 }
2437 } else if (!strcmp(dlmsg, "DLMessageCopyItem")) {
2438 plist_t srcpath = plist_array_get_item(message, 1);
2439 plist_t dstpath = plist_array_get_item(message, 2);
2440 errcode = 0;
2441 errdesc = NULL;
2442 if ((plist_get_node_type(srcpath) == PLIST_STRING) && (plist_get_node_type(dstpath) == PLIST_STRING)) {
2443 char *src = NULL;
2444 char *dst = NULL;
2445 plist_get_string_val(srcpath, &src);
2446 plist_get_string_val(dstpath, &dst);
2447 if (src && dst) {
2448 char *oldpath = string_build_path(backup_directory, src, NULL);
2449 char *newpath = string_build_path(backup_directory, dst, NULL);
2450
2451 PRINT_VERBOSE(1, "Copying '%s' to '%s'\n", src, dst);
2452
2453 /* check that src exists */
2454 if ((stat(oldpath, &st) == 0) && S_ISDIR(st.st_mode)) {
2455 mb2_copy_directory_by_path(oldpath, newpath);
2456 } else if ((stat(oldpath, &st) == 0) && S_ISREG(st.st_mode)) {
2457 mb2_copy_file_by_path(oldpath, newpath);
2458 }
2459
2460 free(newpath);
2461 free(oldpath);
2462 }
2463 free(src);
2464 free(dst);
2465 }
2466 plist_t empty_dict = plist_new_dict();
2467 err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
2468 plist_free(empty_dict);
2469 if (err != MOBILEBACKUP2_E_SUCCESS) {
2470 printf("Could not send status response, error %d\n", err);
2471 }
2472 } else if (!strcmp(dlmsg, "DLMessageDisconnect")) {
2473 break;
2474 } else if (!strcmp(dlmsg, "DLMessageProcessMessage")) {
2475 node_tmp = plist_array_get_item(message, 1);
2476 if (plist_get_node_type(node_tmp) != PLIST_DICT) {
2477 printf("Unknown message received!\n");
2478 }
2479 plist_t nn;
2480 int error_code = -1;
2481 nn = plist_dict_get_item(node_tmp, "ErrorCode");
2482 if (nn && (plist_get_node_type(nn) == PLIST_UINT)) {
2483 uint64_t ec = 0;
2484 plist_get_uint_val(nn, &ec);
2485 error_code = (uint32_t)ec;
2486 if (error_code == 0) {
2487 operation_ok = 1;
2488 result_code = 0;
2489 } else {
2490 result_code = -error_code;
2491 }
2492 }
2493 nn = plist_dict_get_item(node_tmp, "ErrorDescription");
2494 char *str = NULL;
2495 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2496 plist_get_string_val(nn, &str);
2497 }
2498 if (error_code != 0) {
2499 if (str) {
2500 printf("ErrorCode %d: %s\n", error_code, str);
2501 } else {
2502 printf("ErrorCode %d: (Unknown)\n", error_code);
2503 }
2504 }
2505 if (str) {
2506 free(str);
2507 }
2508 nn = plist_dict_get_item(node_tmp, "Content");
2509 if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
2510 str = NULL;
2511 plist_get_string_val(nn, &str);
2512 PRINT_VERBOSE(1, "Content:\n");
2513 printf("%s", str);
2514 free(str);
2515 }
2516 break;
2517 }
2518
2519 /* print status */
2520 if ((overall_progress > 0) && !progress_finished) {
2521 if (overall_progress >= 100.0F) {
2522 progress_finished = 1;
2523 }
2524 print_progress_real(overall_progress, 0);
2525 PRINT_VERBOSE(1, " Finished\n");
2526 }
2527
2528files_out:
2529 plist_free(message);
2530 message = NULL;
2531 free(dlmsg);
2532 dlmsg = NULL;
2533
2534 if (quit_flag > 0) {
2535 /* need to cancel the backup here */
2536 //mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
2537
2538 /* remove any atomic Manifest.plist.tmp */
2539
2540 /*manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
2541 if (stat(manifest_path, &st) == 0)
2542 remove(manifest_path);*/
2543 break;
2544 }
2545 } while (1);
2546
2547 plist_free(message);
2548 free(dlmsg);
2549
2550 /* report operation status to user */
2551 switch (cmd) {
2552 case CMD_CLOUD:
2553 if (cmd_flags & CMD_FLAG_CLOUD_ENABLE) {
2554 if (operation_ok) {
2555 PRINT_VERBOSE(1, "Cloud backup has been enabled successfully.\n");
2556 } else {
2557 PRINT_VERBOSE(1, "Could not enable cloud backup.\n");
2558 }
2559 } else if (cmd_flags & CMD_FLAG_CLOUD_DISABLE) {
2560 if (operation_ok) {
2561 PRINT_VERBOSE(1, "Cloud backup has been disabled successfully.\n");
2562 } else {
2563 PRINT_VERBOSE(1, "Could not disable cloud backup.\n");
2564 }
2565 }
2566 break;
2567 case CMD_BACKUP:
2568 PRINT_VERBOSE(1, "Received %d files from device.\n", file_count);
2569 if (operation_ok && mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
2570 PRINT_VERBOSE(1, "Backup Successful.\n");
2571 } else {
2572 if (quit_flag) {
2573 PRINT_VERBOSE(1, "Backup Aborted.\n");
2574 } else {
2575 PRINT_VERBOSE(1, "Backup Failed (Error Code %d).\n", -result_code);
2576 }
2577 }
2578 break;
2579 case CMD_UNBACK:
2580 if (quit_flag) {
2581 PRINT_VERBOSE(1, "Unback Aborted.\n");
2582 } else {
2583 PRINT_VERBOSE(1, "The files can now be found in the \"_unback_\" directory.\n");
2584 PRINT_VERBOSE(1, "Unback Successful.\n");
2585 }
2586 break;
2587 case CMD_CHANGEPW:
2588 if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
2589 if (operation_ok) {
2590 PRINT_VERBOSE(1, "Backup encryption has been enabled successfully.\n");
2591 } else {
2592 PRINT_VERBOSE(1, "Could not enable backup encryption.\n");
2593 }
2594 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
2595 if (operation_ok) {
2596 PRINT_VERBOSE(1, "Backup encryption has been disabled successfully.\n");
2597 } else {
2598 PRINT_VERBOSE(1, "Could not disable backup encryption.\n");
2599 }
2600 } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
2601 if (operation_ok) {
2602 PRINT_VERBOSE(1, "Backup encryption password has been changed successfully.\n");
2603 } else {
2604 PRINT_VERBOSE(1, "Could not change backup encryption password.\n");
2605 }
2606 }
2607 break;
2608 case CMD_RESTORE:
2609 if (operation_ok) {
2610 if ((cmd_flags & CMD_FLAG_RESTORE_NO_REBOOT) == 0)
2611 PRINT_VERBOSE(1, "The device should reboot now.\n");
2612 PRINT_VERBOSE(1, "Restore Successful.\n");
2613 } else {
2614 afc_remove_path(afc, "/iTunesRestore/RestoreApplications.plist");
2615 afc_remove_path(afc, "/iTunesRestore");
2616 if (quit_flag) {
2617 PRINT_VERBOSE(1, "Restore Aborted.\n");
2618 } else {
2619 PRINT_VERBOSE(1, "Restore Failed (Error Code %d).\n", -result_code);
2620 }
2621 }
2622 break;
2623 case CMD_INFO:
2624 case CMD_LIST:
2625 case CMD_LEAVE:
2626 default:
2627 if (quit_flag) {
2628 PRINT_VERBOSE(1, "Operation Aborted.\n");
2629 } else if (cmd == CMD_LEAVE) {
2630 PRINT_VERBOSE(1, "Operation Failed.\n");
2631 } else {
2632 PRINT_VERBOSE(1, "Operation Successful.\n");
2633 }
2634 break;
2635 }
2636 }
2637 if (lockfile) {
2638 afc_file_lock(afc, lockfile, AFC_LOCK_UN);
2639 afc_file_close(afc, lockfile);
2640 lockfile = 0;
2641 if (cmd == CMD_BACKUP || cmd == CMD_RESTORE)
2642 do_post_notification(device, NP_SYNC_DID_FINISH);
2643 }
2644 } else {
2645 printf("ERROR: Could not start service %s: %s\n", MOBILEBACKUP2_SERVICE_NAME, lockdownd_strerror(ldret));
2646 lockdownd_client_free(lockdown);
2647 lockdown = NULL;
2648 }
2649
2650 if (lockdown) {
2651 lockdownd_client_free(lockdown);
2652 lockdown = NULL;
2653 }
2654
2655 if (mobilebackup2) {
2656 mobilebackup2_client_free(mobilebackup2);
2657 mobilebackup2 = NULL;
2658 }
2659
2660 if (afc) {
2661 afc_client_free(afc);
2662 afc = NULL;
2663 }
2664
2665 if (np) {
2666 np_client_free(np);
2667 np = NULL;
2668 }
2669
2670 idevice_free(device);
2671 device = NULL;
2672
2673 if (backup_password) {
2674 free(backup_password);
2675 }
2676
2677 if (udid) {
2678 free(udid);
2679 udid = NULL;
2680 }
2681 if (source_udid) {
2682 free(source_udid);
2683 source_udid = NULL;
2684 }
2685
2686 return result_code;
2687}
2688
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}