summaryrefslogtreecommitdiffstats
path: root/tools/idevicescreenshot.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/idevicescreenshot.c')
-rw-r--r--tools/idevicescreenshot.c259
1 files changed, 181 insertions, 78 deletions
diff --git a/tools/idevicescreenshot.c b/tools/idevicescreenshot.c
index 8567f77..0e694c7 100644
--- a/tools/idevicescreenshot.c
+++ b/tools/idevicescreenshot.c
@@ -1,113 +1,227 @@
1/** 1/*
2 * idevicescreenshot -- Gets a screenshot from a connected iPhone/iPod Touch 2 * idevicescreenshot.c
3 * Gets a screenshot from a device
3 * 4 *
4 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li> 5 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
5 * 6 *
6 * Licensed under the GNU General Public License Version 2 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
7 * 11 *
8 * This program is free software; you can redistribute it and/or modify 12 * This library is distributed in the hope that it will be useful,
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * GNU General Public License for more profile. 15 * Lesser General Public License for more details.
17 * 16 *
18 * You should have received a copy of the GNU General Public License 17 * You should have received a copy of the GNU Lesser General Public
19 * along with this program; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * USA
22 */ 20 */
23 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define TOOL_NAME "idevicescreenshot"
27
24#include <stdio.h> 28#include <stdio.h>
25#include <string.h> 29#include <string.h>
26#include <stdlib.h> 30#include <stdlib.h>
31#include <getopt.h>
27#include <errno.h> 32#include <errno.h>
28#include <time.h> 33#include <time.h>
34#include <unistd.h>
35#ifndef WIN32
36#include <signal.h>
37#endif
29 38
30#include <libimobiledevice/libimobiledevice.h> 39#include <libimobiledevice/libimobiledevice.h>
31#include <libimobiledevice/lockdown.h> 40#include <libimobiledevice/lockdown.h>
32#include <libimobiledevice/screenshotr.h> 41#include <libimobiledevice/screenshotr.h>
33 42
34void print_usage(int argc, char **argv); 43static void get_image_filename(char *imgdata, char **filename)
44{
45 // If the provided filename already has an extension, use it as is.
46 if (*filename) {
47 char *last_dot = strrchr(*filename, '.');
48 if (last_dot && !strchr(last_dot, '/')) {
49 return;
50 }
51 }
52
53 // Find the appropriate file extension for the filename.
54 const char *fileext = NULL;
55 if (memcmp(imgdata, "\x89PNG", 4) == 0) {
56 fileext = ".png";
57 } else if (memcmp(imgdata, "MM\x00*", 4) == 0) {
58 fileext = ".tiff";
59 } else {
60 printf("WARNING: screenshot data has unexpected image format.\n");
61 fileext = ".dat";
62 }
63
64 // If a filename without an extension is provided, append the extension.
65 // Otherwise, generate a filename based on the current time.
66 char *basename = NULL;
67 if (*filename) {
68 basename = (char*)malloc(strlen(*filename) + 1);
69 strcpy(basename, *filename);
70 free(*filename);
71 *filename = NULL;
72 } else {
73 time_t now = time(NULL);
74 basename = (char*)malloc(32);
75 strftime(basename, 31, "screenshot-%Y-%m-%d-%H-%M-%S", gmtime(&now));
76 }
77
78 // Ensure the filename is unique on disk.
79 char *unique_filename = (char*)malloc(strlen(basename) + strlen(fileext) + 7);
80 sprintf(unique_filename, "%s%s", basename, fileext);
81 int i;
82 for (i = 2; i < (1 << 16); i++) {
83 if (access(unique_filename, F_OK) == -1) {
84 *filename = unique_filename;
85 break;
86 }
87 sprintf(unique_filename, "%s-%d%s", basename, i, fileext);
88 }
89 if (!*filename) {
90 free(unique_filename);
91 }
92 free(basename);
93}
94
95static void print_usage(int argc, char **argv, int is_error)
96{
97 char *name = strrchr(argv[0], '/');
98 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] [FILE]\n", (name ? name + 1: argv[0]));
99 fprintf(is_error ? stderr : stdout,
100 "\n"
101 "Gets a screenshot from a connected device.\n"
102 "\n"
103 "The image is in PNG format for iOS 9+ and otherwise in TIFF format.\n"
104 "The screenshot is saved as an image with the given FILE name.\n"
105 "If FILE has no extension, FILE will be a prefix of the saved filename.\n"
106 "If FILE is not specified, \"screenshot-DATE\", will be used as a prefix\n"
107 "of the filename, e.g.:\n"
108 " ./screenshot-2013-12-31-23-59-59.tiff\n"
109 "\n"
110 "NOTE: A mounted developer disk image is required on the device, otherwise\n"
111 "the screenshotr service is not available.\n"
112 "\n"
113 " -u, --udid UDID target specific device by UDID\n"
114 " -n, --network connect to network device\n"
115 " -d, --debug enable communication debugging\n"
116 " -h, --help prints usage information\n"
117 " -v, --version prints version information\n"
118 "\n"
119 "Homepage: <" PACKAGE_URL ">\n"
120 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
121 );
122}
35 123
36int main(int argc, char **argv) 124int main(int argc, char **argv)
37{ 125{
38 idevice_t device = NULL; 126 idevice_t device = NULL;
39 lockdownd_client_t lckd = NULL; 127 lockdownd_client_t lckd = NULL;
128 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
40 screenshotr_client_t shotr = NULL; 129 screenshotr_client_t shotr = NULL;
41 uint16_t port = 0; 130 lockdownd_service_descriptor_t service = NULL;
42 int result = -1; 131 int result = -1;
43 int i; 132 const char *udid = NULL;
44 char *uuid = NULL; 133 int use_network = 0;
134 char *filename = NULL;
135 int c = 0;
136 const struct option longopts[] = {
137 { "debug", no_argument, NULL, 'd' },
138 { "help", no_argument, NULL, 'h' },
139 { "udid", required_argument, NULL, 'u' },
140 { "network", no_argument, NULL, 'n' },
141 { "version", no_argument, NULL, 'v' },
142 { NULL, 0, NULL, 0}
143 };
45 144
145#ifndef WIN32
146 signal(SIGPIPE, SIG_IGN);
147#endif
46 /* parse cmdline args */ 148 /* parse cmdline args */
47 for (i = 1; i < argc; i++) { 149
48 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 150 /* parse cmdline arguments */
151 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
152 switch (c) {
153 case 'd':
49 idevice_set_debug_level(1); 154 idevice_set_debug_level(1);
50 continue; 155 break;
51 } 156 case 'u':
52 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uuid")) { 157 if (!*optarg) {
53 i++; 158 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
54 if (!argv[i] || (strlen(argv[i]) != 40)) { 159 print_usage(argc, argv, 1);
55 print_usage(argc, argv); 160 return 2;
56 return 0;
57 } 161 }
58 uuid = strdup(argv[i]); 162 udid = optarg;
59 continue; 163 break;
60 } 164 case 'n':
61 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 165 use_network = 1;
62 print_usage(argc, argv); 166 break;
167 case 'h':
168 print_usage(argc, argv, 0);
63 return 0; 169 return 0;
64 } 170 case 'v':
65 else { 171 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
66 print_usage(argc, argv);
67 return 0; 172 return 0;
173 default:
174 print_usage(argc, argv, 1);
175 return 2;
68 } 176 }
69 } 177 }
178 argc -= optind;
179 argv += optind;
70 180
71 if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) { 181 if (argv[0]) {
72 printf("No device found, is it plugged in?\n"); 182 filename = strdup(argv[0]);
73 if (uuid) { 183 }
74 free(uuid); 184
185 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
186 if (udid) {
187 printf("No device found with udid %s.\n", udid);
188 } else {
189 printf("No device found.\n");
75 } 190 }
76 return -1; 191 return -1;
77 } 192 }
78 if (uuid) {
79 free(uuid);
80 }
81 193
82 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, NULL)) { 194 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
83 idevice_free(device); 195 idevice_free(device);
84 printf("Exiting.\n"); 196 printf("ERROR: Could not connect to lockdownd, error code %d\n", ldret);
85 return -1; 197 return -1;
86 } 198 }
87 199
88 lockdownd_start_service(lckd, "com.apple.mobile.screenshotr", &port); 200 lockdownd_error_t lerr = lockdownd_start_service(lckd, SCREENSHOTR_SERVICE_NAME, &service);
89 lockdownd_client_free(lckd); 201 lockdownd_client_free(lckd);
90 if (port > 0) { 202 if (lerr == LOCKDOWN_E_SUCCESS) {
91 if (screenshotr_client_new(device, port, &shotr) != SCREENSHOTR_E_SUCCESS) { 203 if (screenshotr_client_new(device, service, &shotr) != SCREENSHOTR_E_SUCCESS) {
92 printf("Could not connect to screenshotr!\n"); 204 printf("Could not connect to screenshotr!\n");
93 } else { 205 } else {
94 char *imgdata = NULL; 206 char *imgdata = NULL;
95 char filename[36];
96 uint64_t imgsize = 0; 207 uint64_t imgsize = 0;
97 time_t now = time(NULL);
98 strftime(filename, 36, "screenshot-%Y-%m-%d-%H-%M-%S.tiff", gmtime(&now));
99 if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) { 208 if (screenshotr_take_screenshot(shotr, &imgdata, &imgsize) == SCREENSHOTR_E_SUCCESS) {
100 FILE *f = fopen(filename, "w"); 209 get_image_filename(imgdata, &filename);
101 if (f) { 210 if (!filename) {
102 if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) { 211 printf("FATAL: Could not find a unique filename!\n");
103 printf("Screenshot saved to %s\n", filename); 212 } else {
104 result = 0; 213 FILE *f = fopen(filename, "wb");
214 if (f) {
215 if (fwrite(imgdata, 1, (size_t)imgsize, f) == (size_t)imgsize) {
216 printf("Screenshot saved to %s\n", filename);
217 result = 0;
218 } else {
219 printf("Could not save screenshot to file %s!\n", filename);
220 }
221 fclose(f);
105 } else { 222 } else {
106 printf("Could not save screenshot to file %s!\n", filename); 223 printf("Could not open %s for writing: %s\n", filename, strerror(errno));
107 } 224 }
108 fclose(f);
109 } else {
110 printf("Could not open %s for writing: %s\n", filename, strerror(errno));
111 } 225 }
112 } else { 226 } else {
113 printf("Could not get screenshot!\n"); 227 printf("Could not get screenshot!\n");
@@ -115,25 +229,14 @@ int main(int argc, char **argv)
115 screenshotr_client_free(shotr); 229 screenshotr_client_free(shotr);
116 } 230 }
117 } else { 231 } else {
118 printf("Could not start screenshotr service! Remember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n"); 232 printf("Could not start screenshotr service: %s\nRemember that you have to mount the Developer disk image on your device if you want to use the screenshotr service.\n", lockdownd_strerror(lerr));
119 } 233 }
234
235 if (service)
236 lockdownd_service_descriptor_free(service);
237
120 idevice_free(device); 238 idevice_free(device);
121 239 free(filename);
122 return result;
123}
124 240
125void print_usage(int argc, char **argv) 241 return result;
126{
127 char *name = NULL;
128
129 name = strrchr(argv[0], '/');
130 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
131 printf("Gets a screenshot from the connected iPhone/iPod Touch.\n");
132 printf("The screenshot is saved as a TIFF image in the current directory.\n");
133 printf("NOTE: A mounted developer disk image is required on the device, otherwise\n");
134 printf("the screenshotr service is not available.\n\n");
135 printf(" -d, --debug\t\tenable communication debugging\n");
136 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n");
137 printf(" -h, --help\t\tprints usage information\n");
138 printf("\n");
139} 242}