/libimobiledevice-tools/idevicebackup2/idevicebackup2.cpp

https://bitbucket.org/hjwhang/libimobiledevice-win32 · C++ · 2303 lines · 1956 code · 243 blank · 104 comment · 648 complexity · 73371ec58a9f1749a9458a8aa5d986cb MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * idevicebackup2.c
  3. * Command line interface to use the device's backup and restore service
  4. *
  5. * Copyright (c) 2009-2010 Martin Szulecki All Rights Reserved.
  6. * Copyright (c) 2010 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. #include <stdio.h>
  23. #include <string.h>
  24. #include <errno.h>
  25. #include <stdlib.h>
  26. #include <signal.h>
  27. #include <unistd.h>
  28. #include <dirent.h>
  29. #include <libgen.h>
  30. #include <ctype.h>
  31. #include <time.h>
  32. #include <libimobiledevice/libimobiledevice.h>
  33. #include <libimobiledevice/lockdown.h>
  34. #include <libimobiledevice/mobilebackup2.h>
  35. #include <libimobiledevice/notification_proxy.h>
  36. #include <libimobiledevice/afc.h>
  37. #include <endianness.h>
  38. #define MOBILEBACKUP2_SERVICE_NAME "com.apple.mobilebackup2"
  39. #define NP_SERVICE_NAME "com.apple.mobile.notification_proxy"
  40. #define LOCK_ATTEMPTS 50
  41. #define LOCK_WAIT 200000
  42. #ifdef WIN32
  43. #include <windows.h>
  44. #include <conio.h>
  45. #include <direct.h>
  46. #define sleep(x) Sleep(x*1000)
  47. #else
  48. #include <termios.h>
  49. #include <sys/statvfs.h>
  50. #endif
  51. #ifndef __func__
  52. # define __func__ __FUNCTION__
  53. #endif
  54. #define CODE_SUCCESS 0x00
  55. #define CODE_ERROR_LOCAL 0x06
  56. #define CODE_ERROR_REMOTE 0x0b
  57. #define CODE_FILE_DATA 0x0c
  58. static int verbose = 1;
  59. static int quit_flag = 0;
  60. #define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
  61. enum cmd_mode {
  62. CMD_BACKUP,
  63. CMD_RESTORE,
  64. CMD_INFO,
  65. CMD_LIST,
  66. CMD_UNBACK,
  67. CMD_CHANGEPW,
  68. CMD_LEAVE
  69. };
  70. enum plist_format_t {
  71. PLIST_FORMAT_XML,
  72. PLIST_FORMAT_BINARY
  73. };
  74. enum cmd_flags {
  75. CMD_FLAG_RESTORE_SYSTEM_FILES = (1 << 1),
  76. CMD_FLAG_RESTORE_REBOOT = (1 << 2),
  77. CMD_FLAG_RESTORE_COPY_BACKUP = (1 << 3),
  78. CMD_FLAG_RESTORE_SETTINGS = (1 << 4),
  79. CMD_FLAG_RESTORE_REMOVE_ITEMS = (1 << 5),
  80. CMD_FLAG_ENCRYPTION_ENABLE = (1 << 6),
  81. CMD_FLAG_ENCRYPTION_DISABLE = (1 << 7),
  82. CMD_FLAG_ENCRYPTION_CHANGEPW = (1 << 8)
  83. };
  84. static int backup_domain_changed = 0;
  85. static void notify_cb(const char *notification, void *userdata)
  86. {
  87. if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
  88. PRINT_VERBOSE(1, "User has cancelled the backup process on the device.\n");
  89. quit_flag++;
  90. } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
  91. backup_domain_changed = 1;
  92. } else {
  93. PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
  94. }
  95. }
  96. static void free_dictionary(char **dictionary)
  97. {
  98. int i = 0;
  99. if (!dictionary)
  100. return;
  101. for (i = 0; dictionary[i]; i++) {
  102. free(dictionary[i]);
  103. }
  104. free(dictionary);
  105. }
  106. static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *filename, char **data, uint64_t *size)
  107. {
  108. if (!afc || !data || !size) {
  109. return;
  110. }
  111. char **fileinfo = NULL;
  112. uint32_t fsize = 0;
  113. afc_get_file_info(afc, filename, &fileinfo);
  114. if (!fileinfo) {
  115. return;
  116. }
  117. int i;
  118. for (i = 0; fileinfo[i]; i+=2) {
  119. if (!strcmp(fileinfo[i], "st_size")) {
  120. fsize = atol(fileinfo[i+1]);
  121. break;
  122. }
  123. }
  124. free_dictionary(fileinfo);
  125. if (fsize == 0) {
  126. return;
  127. }
  128. uint64_t f = 0;
  129. afc_file_open(afc, filename, AFC_FOPEN_RDONLY, &f);
  130. if (!f) {
  131. return;
  132. }
  133. char *buf = (char*)malloc((uint32_t)fsize);
  134. uint32_t done = 0;
  135. while (done < fsize) {
  136. uint32_t bread = 0;
  137. afc_file_read(afc, f, buf+done, 65536, &bread);
  138. if (bread > 0) {
  139. } else {
  140. break;
  141. }
  142. done += bread;
  143. }
  144. if (done == fsize) {
  145. *size = fsize;
  146. *data = buf;
  147. } else {
  148. free(buf);
  149. }
  150. afc_file_close(afc, f);
  151. }
  152. static char *str_toupper(char* str)
  153. {
  154. char *res = strdup(str);
  155. unsigned int i;
  156. for (i = 0; i < strlen(res); i++) {
  157. res[i] = toupper(res[i]);
  158. }
  159. return res;
  160. }
  161. static int __mkdir(const char* path, int mode)
  162. {
  163. #ifdef WIN32
  164. return _mkdir(path);
  165. #else
  166. return mkdir(path, mode);
  167. #endif
  168. }
  169. static int mkdir_with_parents(const char *dir, int mode)
  170. {
  171. if (!dir) return -1;
  172. if (__mkdir(dir, mode) == 0) {
  173. return 0;
  174. } else {
  175. if (errno == EEXIST) return 0;
  176. }
  177. int res;
  178. char *parent = strdup(dir);
  179. char *parentdir = dirname(parent);
  180. if (parentdir) {
  181. res = mkdir_with_parents(parentdir, mode);
  182. } else {
  183. res = -1;
  184. }
  185. free(parent);
  186. if (res == 0) {
  187. mkdir_with_parents(dir, mode);
  188. }
  189. return res;
  190. }
  191. static char* build_path(const char* elem, ...)
  192. {
  193. if (!elem) return NULL;
  194. va_list args;
  195. int len = strlen(elem)+1;
  196. va_start(args, elem);
  197. char *arg = va_arg(args, char*);
  198. while (arg) {
  199. len += strlen(arg)+1;
  200. arg = va_arg(args, char*);
  201. }
  202. va_end(args);
  203. char* out = (char*)malloc(len);
  204. strcpy(out, elem);
  205. va_start(args, elem);
  206. arg = va_arg(args, char*);
  207. while (arg) {
  208. strcat(out, "/");
  209. strcat(out, arg);
  210. arg = va_arg(args, char*);
  211. }
  212. va_end(args);
  213. return out;
  214. }
  215. static char* format_size_for_display(uint64_t size)
  216. {
  217. char buf[32];
  218. double sz;
  219. if (size >= 1000000000LL) {
  220. sz = ((double)size / 1000000000.0f);
  221. sprintf(buf, "%0.1f GB", sz);
  222. } else if (size >= 1000000LL) {
  223. sz = ((double)size / 1000000.0f);
  224. sprintf(buf, "%0.1f MB", sz);
  225. } else if (size >= 1000LL) {
  226. sz = ((double)size / 1000.0f);
  227. sprintf(buf, "%0.1f kB", sz);
  228. } else {
  229. sprintf(buf, "%d Bytes", (int)size);
  230. }
  231. return strdup(buf);
  232. }
  233. static plist_t mobilebackup_factory_info_plist_new(const char* udid, lockdownd_client_t lockdown, afc_client_t afc)
  234. {
  235. /* gather data from lockdown */
  236. plist_t value_node = NULL;
  237. plist_t root_node = NULL;
  238. char *udid_uppercase = NULL;
  239. plist_t ret = plist_new_dict();
  240. /* get basic device information in one go */
  241. lockdownd_get_value(lockdown, NULL, NULL, &root_node);
  242. /* set fields we understand */
  243. value_node = plist_dict_get_item(root_node, "BuildVersion");
  244. plist_dict_insert_item(ret, "Build Version", plist_copy(value_node));
  245. value_node = plist_dict_get_item(root_node, "DeviceName");
  246. plist_dict_insert_item(ret, "Device Name", plist_copy(value_node));
  247. plist_dict_insert_item(ret, "Display Name", plist_copy(value_node));
  248. /* FIXME: How is the GUID generated? */
  249. plist_dict_insert_item(ret, "GUID", plist_new_string("---"));
  250. value_node = plist_dict_get_item(root_node, "IntegratedCircuitCardIdentity");
  251. if (value_node)
  252. plist_dict_insert_item(ret, "ICCID", plist_copy(value_node));
  253. value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
  254. if (value_node)
  255. plist_dict_insert_item(ret, "IMEI", plist_copy(value_node));
  256. plist_dict_insert_item(ret, "Last Backup Date", plist_new_date(time(NULL), 0));
  257. value_node = plist_dict_get_item(root_node, "PhoneNumber");
  258. if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
  259. plist_dict_insert_item(ret, "Phone Number", plist_copy(value_node));
  260. }
  261. value_node = plist_dict_get_item(root_node, "ProductType");
  262. plist_dict_insert_item(ret, "Product Type", plist_copy(value_node));
  263. value_node = plist_dict_get_item(root_node, "ProductVersion");
  264. plist_dict_insert_item(ret, "Product Version", plist_copy(value_node));
  265. value_node = plist_dict_get_item(root_node, "SerialNumber");
  266. plist_dict_insert_item(ret, "Serial Number", plist_copy(value_node));
  267. /* FIXME Sync Settings? */
  268. value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
  269. plist_dict_insert_item(ret, "Target Identifier", plist_new_string(udid));
  270. plist_dict_insert_item(ret, "Target Type", plist_new_string("Device"));
  271. /* uppercase */
  272. udid_uppercase = str_toupper((char*)udid);
  273. plist_dict_insert_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
  274. free(udid_uppercase);
  275. char *data_buf = NULL;
  276. uint64_t data_size = 0;
  277. mobilebackup_afc_get_file_contents(afc, "/Books/iBooksData2.plist", &data_buf, &data_size);
  278. if (data_buf) {
  279. plist_dict_insert_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
  280. free(data_buf);
  281. }
  282. plist_t files = plist_new_dict();
  283. const char *itunesfiles[] = {
  284. "ApertureAlbumPrefs",
  285. "IC-Info.sidb",
  286. "IC-Info.sidv",
  287. "PhotosFolderAlbums",
  288. "PhotosFolderName",
  289. "PhotosFolderPrefs",
  290. "iPhotoAlbumPrefs",
  291. "iTunesApplicationIDs",
  292. "iTunesPrefs",
  293. "iTunesPrefs.plist",
  294. NULL
  295. };
  296. int i = 0;
  297. for (i = 0; itunesfiles[i]; i++) {
  298. data_buf = NULL;
  299. data_size = 0;
  300. char *fname = (char*)malloc(strlen("/iTunes_Control/iTunes/") + strlen(itunesfiles[i]) + 1);
  301. strcpy(fname, "/iTunes_Control/iTunes/");
  302. strcat(fname, itunesfiles[i]);
  303. mobilebackup_afc_get_file_contents(afc, fname, &data_buf, &data_size);
  304. free(fname);
  305. if (data_buf) {
  306. plist_dict_insert_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
  307. free(data_buf);
  308. }
  309. }
  310. plist_dict_insert_item(ret, "iTunes Files", files);
  311. plist_t itunes_settings = NULL;
  312. lockdownd_get_value(lockdown, "com.apple.iTunes", NULL, &itunes_settings);
  313. plist_dict_insert_item(ret, "iTunes Settings", itunes_settings ? itunes_settings : plist_new_dict());
  314. plist_dict_insert_item(ret, "iTunes Version", plist_new_string("10.0.1"));
  315. plist_free(root_node);
  316. return ret;
  317. }
  318. static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
  319. {
  320. FILE *f;
  321. uint64_t size;
  322. *length = 0;
  323. f = fopen(filename, "rb");
  324. if (!f) {
  325. return;
  326. }
  327. fseek(f, 0, SEEK_END);
  328. size = ftell(f);
  329. rewind(f);
  330. if (size == 0) {
  331. return;
  332. }
  333. *buffer = (char*)malloc(sizeof(char)*size);
  334. fread(*buffer, sizeof(char), size, f);
  335. fclose(f);
  336. *length = size;
  337. }
  338. static void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length)
  339. {
  340. FILE *f;
  341. f = fopen(filename, "ab");
  342. if (!f)
  343. f = fopen(filename, "wb");
  344. if (f) {
  345. fwrite(buffer, sizeof(char), length, f);
  346. fclose(f);
  347. }
  348. }
  349. static int plist_read_from_filename(plist_t *plist, const char *filename)
  350. {
  351. char *buffer = NULL;
  352. uint64_t length;
  353. if (!filename)
  354. return 0;
  355. buffer_read_from_filename(filename, &buffer, &length);
  356. if (!buffer) {
  357. return 0;
  358. }
  359. if ((length > 8) && (memcmp(buffer, "bplist00", 8) == 0)) {
  360. plist_from_bin(buffer, length, plist);
  361. } else {
  362. plist_from_xml(buffer, length, plist);
  363. }
  364. free(buffer);
  365. return 1;
  366. }
  367. static int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format)
  368. {
  369. char *buffer = NULL;
  370. uint32_t length;
  371. if (!plist || !filename)
  372. return 0;
  373. if (format == PLIST_FORMAT_XML)
  374. plist_to_xml(plist, &buffer, &length);
  375. else if (format == PLIST_FORMAT_BINARY)
  376. plist_to_bin(plist, &buffer, &length);
  377. else
  378. return 0;
  379. buffer_write_to_filename(filename, buffer, length);
  380. free(buffer);
  381. return 1;
  382. }
  383. static int mb2_status_check_snapshot_state(const char *path, const char *udid, const char *matches)
  384. {
  385. int ret = -1;
  386. plist_t status_plist = NULL;
  387. char *file_path = build_path(path, udid, "Status.plist", NULL);
  388. plist_read_from_filename(&status_plist, file_path);
  389. free(file_path);
  390. if (!status_plist) {
  391. printf("Could not read Status.plist!\n");
  392. return ret;
  393. }
  394. plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
  395. if (node && (plist_get_node_type(node) == PLIST_STRING)) {
  396. char* sval = NULL;
  397. plist_get_string_val(node, &sval);
  398. if (sval) {
  399. ret = (strcmp(sval, matches) == 0) ? 1 : 0;
  400. free(sval);
  401. }
  402. } else {
  403. printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
  404. }
  405. plist_free(status_plist);
  406. return ret;
  407. }
  408. static void do_post_notification(idevice_t device, const char *notification)
  409. {
  410. lockdownd_service_descriptor_t service = NULL;
  411. np_client_t np;
  412. lockdownd_client_t lockdown = NULL;
  413. if (lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup") != LOCKDOWN_E_SUCCESS) {
  414. return;
  415. }
  416. lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
  417. if (service && service->port) {
  418. np_client_new(device, service, &np);
  419. if (np) {
  420. np_post_notification(np, notification);
  421. np_client_free(np);
  422. }
  423. } else {
  424. printf("Could not start %s\n", NP_SERVICE_NAME);
  425. }
  426. if (service) {
  427. lockdownd_service_descriptor_free(service);
  428. service = NULL;
  429. }
  430. lockdownd_client_free(lockdown);
  431. }
  432. static void print_progress_real(double progress, int flush)
  433. {
  434. int i = 0;
  435. PRINT_VERBOSE(1, "\r[");
  436. for(i = 0; i < 50; i++) {
  437. if(i < progress / 2) {
  438. PRINT_VERBOSE(1, "=");
  439. } else {
  440. PRINT_VERBOSE(1, " ");
  441. }
  442. }
  443. PRINT_VERBOSE(1, "] %3.0f%%", progress);
  444. if (flush > 0) {
  445. fflush(stdout);
  446. if (progress == 100)
  447. PRINT_VERBOSE(1, "\n");
  448. }
  449. }
  450. static void print_progress(uint64_t current, uint64_t total)
  451. {
  452. char *format_size = NULL;
  453. double progress = ((double)current/(double)total)*100;
  454. if (progress < 0)
  455. return;
  456. if (progress > 100)
  457. progress = 100;
  458. print_progress_real((double)progress, 0);
  459. format_size = format_size_for_display(current);
  460. PRINT_VERBOSE(1, " (%s", format_size);
  461. free(format_size);
  462. format_size = format_size_for_display(total);
  463. PRINT_VERBOSE(1, "/%s) ", format_size);
  464. free(format_size);
  465. fflush(stdout);
  466. if (progress == 100)
  467. PRINT_VERBOSE(1, "\n");
  468. }
  469. static double overall_progress = 0;
  470. static void mb2_set_overall_progress(double progress)
  471. {
  472. if (progress > 0.0)
  473. overall_progress = progress;
  474. }
  475. static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
  476. {
  477. plist_t node = NULL;
  478. double progress = 0.0;
  479. if (!strcmp(identifier, "DLMessageDownloadFiles")) {
  480. node = plist_array_get_item(message, 3);
  481. } else if (!strcmp(identifier, "DLMessageUploadFiles")) {
  482. node = plist_array_get_item(message, 2);
  483. } else if (!strcmp(identifier, "DLMessageMoveFiles") || !strcmp(identifier, "DLMessageMoveItems")) {
  484. node = plist_array_get_item(message, 3);
  485. } else if (!strcmp(identifier, "DLMessageRemoveFiles") || !strcmp(identifier, "DLMessageRemoveItems")) {
  486. node = plist_array_get_item(message, 3);
  487. }
  488. if (node != NULL) {
  489. plist_get_real_val(node, &progress);
  490. mb2_set_overall_progress(progress);
  491. }
  492. }
  493. static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
  494. {
  495. if (!status_dict) return;
  496. plist_t filedict = plist_new_dict();
  497. plist_dict_insert_item(filedict, "DLFileErrorString", plist_new_string(error_message));
  498. plist_dict_insert_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
  499. plist_dict_insert_item(status_dict, path, filedict);
  500. }
  501. static int errno_to_device_error(int errno_value)
  502. {
  503. switch (errno_value) {
  504. case ENOENT:
  505. return -6;
  506. case EEXIST:
  507. return -7;
  508. default:
  509. return -errno_value;
  510. }
  511. }
  512. #ifdef WIN32
  513. static int win32err_to_errno(int err_value)
  514. {
  515. switch (err_value) {
  516. case ERROR_FILE_NOT_FOUND:
  517. return ENOENT;
  518. case ERROR_ALREADY_EXISTS:
  519. return EEXIST;
  520. default:
  521. return EFAULT;
  522. }
  523. }
  524. #endif
  525. static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char *backup_dir, const char *path, plist_t *errplist)
  526. {
  527. uint32_t nlen = 0;
  528. uint32_t pathlen = strlen(path);
  529. uint32_t bytes = 0;
  530. char *localfile = build_path(backup_dir, path, NULL);
  531. char buf[32768];
  532. struct stat fst;
  533. FILE *f = NULL;
  534. uint32_t slen = 0;
  535. int errcode = -1;
  536. int result = -1;
  537. uint32_t length;
  538. off_t total;
  539. off_t sent;
  540. mobilebackup2_error_t err;
  541. /* send path length */
  542. nlen = htobe32(pathlen);
  543. err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), &bytes);
  544. if (err != MOBILEBACKUP2_E_SUCCESS) {
  545. goto leave_proto_err;
  546. }
  547. if (bytes != (uint32_t)sizeof(nlen)) {
  548. err = MOBILEBACKUP2_E_MUX_ERROR;
  549. goto leave_proto_err;
  550. }
  551. /* send path */
  552. err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, &bytes);
  553. if (err != MOBILEBACKUP2_E_SUCCESS) {
  554. goto leave_proto_err;
  555. }
  556. if (bytes != pathlen) {
  557. err = MOBILEBACKUP2_E_MUX_ERROR;
  558. goto leave_proto_err;
  559. }
  560. if (stat(localfile, &fst) < 0) {
  561. if (errno != ENOENT)
  562. printf("%s: stat failed on '%s': %d\n", __func__, localfile, errno);
  563. errcode = errno;
  564. goto leave;
  565. }
  566. total = fst.st_size;
  567. char *format_size = format_size_for_display(total);
  568. PRINT_VERBOSE(1, "Sending '%s' (%s)\n", path, format_size);
  569. free(format_size);
  570. if (total == 0) {
  571. errcode = 0;
  572. goto leave;
  573. }
  574. f = fopen(localfile, "rb");
  575. if (!f) {
  576. printf("%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
  577. errcode = errno;
  578. goto leave;
  579. }
  580. sent = 0;
  581. do {
  582. length = ((total-sent) < (off_t)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
  583. /* send data size (file size + 1) */
  584. nlen = htobe32(length+1);
  585. memcpy(buf, &nlen, sizeof(nlen));
  586. buf[4] = CODE_FILE_DATA;
  587. err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, &bytes);
  588. if (err != MOBILEBACKUP2_E_SUCCESS) {
  589. goto leave_proto_err;
  590. }
  591. if (bytes != 5) {
  592. goto leave_proto_err;
  593. }
  594. /* send file contents */
  595. size_t r = fread(buf, 1, sizeof(buf), f);
  596. if (r <= 0) {
  597. printf("%s: read error\n", __func__);
  598. errcode = errno;
  599. goto leave;
  600. }
  601. err = mobilebackup2_send_raw(mobilebackup2, buf, r, &bytes);
  602. if (err != MOBILEBACKUP2_E_SUCCESS) {
  603. goto leave_proto_err;
  604. }
  605. if (bytes != (uint32_t)r) {
  606. printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
  607. goto leave_proto_err;
  608. }
  609. sent += r;
  610. } while (sent < total);
  611. fclose(f);
  612. f = NULL;
  613. errcode = 0;
  614. leave:
  615. if (errcode == 0) {
  616. result = 0;
  617. nlen = 1;
  618. nlen = htobe32(nlen);
  619. memcpy(buf, &nlen, 4);
  620. buf[4] = CODE_SUCCESS;
  621. mobilebackup2_send_raw(mobilebackup2, buf, 5, &bytes);
  622. } else {
  623. if (!*errplist) {
  624. *errplist = plist_new_dict();
  625. }
  626. char *errdesc = strerror(errcode);
  627. mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
  628. length = strlen(errdesc);
  629. nlen = htobe32(length+1);
  630. memcpy(buf, &nlen, 4);
  631. buf[4] = CODE_ERROR_LOCAL;
  632. slen = 5;
  633. memcpy(buf+slen, errdesc, length);
  634. slen += length;
  635. err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
  636. if (err != MOBILEBACKUP2_E_SUCCESS) {
  637. printf("could not send message\n");
  638. }
  639. if (bytes != slen) {
  640. printf("could only send %d from %d\n", bytes, slen);
  641. }
  642. }
  643. leave_proto_err:
  644. if (f)
  645. fclose(f);
  646. free(localfile);
  647. return result;
  648. }
  649. static void mb2_handle_send_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  650. {
  651. uint32_t cnt;
  652. uint32_t i = 0;
  653. uint32_t sent;
  654. plist_t errplist = NULL;
  655. if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
  656. plist_t files = plist_array_get_item(message, 1);
  657. cnt = plist_array_get_size(files);
  658. if (cnt == 0) return;
  659. for (i = 0; i < cnt; i++) {
  660. plist_t val = plist_array_get_item(files, i);
  661. if (plist_get_node_type(val) != PLIST_STRING) {
  662. continue;
  663. }
  664. char *str = NULL;
  665. plist_get_string_val(val, &str);
  666. if (!str)
  667. continue;
  668. if (mb2_handle_send_file(mobilebackup2, backup_dir, str, &errplist) < 0) {
  669. free(str);
  670. //printf("Error when sending file '%s' to device\n", str);
  671. // TODO: perhaps we can continue, we've got a multi status response?!
  672. break;
  673. }
  674. free(str);
  675. }
  676. /* send terminating 0 dword */
  677. uint32_t zero = 0;
  678. mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
  679. if (!errplist) {
  680. plist_t emptydict = plist_new_dict();
  681. mobilebackup2_send_status_response(mobilebackup2, 0, NULL, emptydict);
  682. plist_free(emptydict);
  683. } else {
  684. mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
  685. plist_free(errplist);
  686. }
  687. }
  688. static int mb2_receive_filename(mobilebackup2_client_t mobilebackup2, char** filename)
  689. {
  690. uint32_t nlen = 0;
  691. uint32_t rlen = 0;
  692. do {
  693. nlen = 0;
  694. rlen = 0;
  695. mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &rlen);
  696. nlen = be32toh(nlen);
  697. if ((nlen == 0) && (rlen == 4)) {
  698. // a zero length means no more files to receive
  699. return 0;
  700. } else if(rlen == 0) {
  701. // device needs more time, waiting...
  702. continue;
  703. } else if (nlen > 4096) {
  704. // filename length is too large
  705. printf("ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
  706. return 0;
  707. }
  708. if (*filename != NULL) {
  709. free(*filename);
  710. *filename = NULL;
  711. }
  712. *filename = (char*)malloc(nlen+1);
  713. rlen = 0;
  714. mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
  715. if (rlen != nlen) {
  716. printf("ERROR: %s: could not read filename\n", __func__);
  717. return 0;
  718. }
  719. char* p = *filename;
  720. p[rlen] = 0;
  721. break;
  722. } while(1 && !quit_flag);
  723. return nlen;
  724. }
  725. static int mb2_handle_receive_files(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  726. {
  727. uint64_t backup_real_size = 0;
  728. uint64_t backup_total_size = 0;
  729. uint32_t blocksize;
  730. uint32_t bdone;
  731. uint32_t rlen;
  732. uint32_t nlen = 0;
  733. uint32_t r;
  734. char buf[32768];
  735. char *fname = NULL;
  736. char *dname = NULL;
  737. char *bname = NULL;
  738. char code = 0;
  739. char last_code = 0;
  740. plist_t node = NULL;
  741. FILE *f = NULL;
  742. unsigned int file_count = 0;
  743. if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
  744. node = plist_array_get_item(message, 3);
  745. if (plist_get_node_type(node) == PLIST_UINT) {
  746. plist_get_uint_val(node, &backup_total_size);
  747. }
  748. if (backup_total_size > 0) {
  749. PRINT_VERBOSE(1, "Receiving files\n");
  750. }
  751. do {
  752. if (quit_flag)
  753. break;
  754. nlen = mb2_receive_filename(mobilebackup2, &dname);
  755. if (nlen == 0) {
  756. break;
  757. }
  758. nlen = mb2_receive_filename(mobilebackup2, &fname);
  759. if (!nlen) {
  760. break;
  761. }
  762. if (bname != NULL) {
  763. free(bname);
  764. bname = NULL;
  765. }
  766. bname = build_path(backup_dir, fname, NULL);
  767. if (fname != NULL) {
  768. free(fname);
  769. fname = NULL;
  770. }
  771. r = 0;
  772. nlen = 0;
  773. mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
  774. if (r != 4) {
  775. printf("ERROR: %s: could not receive code length!\n", __func__);
  776. break;
  777. }
  778. nlen = be32toh(nlen);
  779. last_code = code;
  780. code = 0;
  781. mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
  782. if (r != 1) {
  783. printf("ERROR: %s: could not receive code!\n", __func__);
  784. break;
  785. }
  786. /* TODO remove this */
  787. if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
  788. PRINT_VERBOSE(1, "Found new flag %02x\n", code);
  789. }
  790. remove(bname);
  791. f = fopen(bname, "wb");
  792. while (f && (code == CODE_FILE_DATA)) {
  793. blocksize = nlen-1;
  794. bdone = 0;
  795. rlen = 0;
  796. while (bdone < blocksize) {
  797. if ((blocksize - bdone) < sizeof(buf)) {
  798. rlen = blocksize - bdone;
  799. } else {
  800. rlen = sizeof(buf);
  801. }
  802. mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
  803. if ((int)r <= 0) {
  804. break;
  805. }
  806. fwrite(buf, 1, r, f);
  807. bdone += r;
  808. }
  809. if (bdone == blocksize) {
  810. backup_real_size += blocksize;
  811. }
  812. if (backup_total_size > 0) {
  813. print_progress(backup_real_size, backup_total_size);
  814. }
  815. if (quit_flag)
  816. break;
  817. nlen = 0;
  818. mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
  819. nlen = be32toh(nlen);
  820. if (nlen > 0) {
  821. last_code = code;
  822. mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
  823. } else {
  824. break;
  825. }
  826. }
  827. if (f) {
  828. fclose(f);
  829. file_count++;
  830. } else {
  831. printf("Error opening '%s' for writing: %s\n", bname, strerror(errno));
  832. }
  833. if (nlen == 0) {
  834. break;
  835. }
  836. /* check if an error message was received */
  837. if (code == CODE_ERROR_REMOTE) {
  838. /* error message */
  839. char *msg = (char*)malloc(nlen);
  840. mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
  841. msg[r] = 0;
  842. /* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
  843. if (last_code != CODE_FILE_DATA) {
  844. fprintf(stdout, "\nReceived an error message from device: %s\n", msg);
  845. }
  846. free(msg);
  847. }
  848. } while (1);
  849. if (fname != NULL)
  850. free(fname);
  851. /* if there are leftovers to read, finish up cleanly */
  852. if ((int)nlen-1 > 0) {
  853. PRINT_VERBOSE(1, "\nDiscarding current data hunk.\n");
  854. fname = (char*)malloc(nlen-1);
  855. mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
  856. free(fname);
  857. remove(bname);
  858. }
  859. /* clean up */
  860. if (bname != NULL)
  861. free(bname);
  862. if (dname != NULL)
  863. free(dname);
  864. // TODO error handling?!
  865. plist_t empty_plist = plist_new_dict();
  866. mobilebackup2_send_status_response(mobilebackup2, 0, NULL, empty_plist);
  867. plist_free(empty_plist);
  868. return file_count;
  869. }
  870. static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  871. {
  872. if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
  873. plist_t node = plist_array_get_item(message, 1);
  874. char *str = NULL;
  875. if (plist_get_node_type(node) == PLIST_STRING) {
  876. plist_get_string_val(node, &str);
  877. }
  878. if (!str) {
  879. printf("ERROR: Malformed DLContentsOfDirectory message\n");
  880. // TODO error handling
  881. return;
  882. }
  883. char *path = build_path(backup_dir, str, NULL);
  884. free(str);
  885. plist_t dirlist = plist_new_dict();
  886. DIR* cur_dir = opendir(path);
  887. if (cur_dir) {
  888. struct dirent* ep;
  889. while ((ep = readdir(cur_dir))) {
  890. if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
  891. continue;
  892. }
  893. char *fpath = build_path(path, ep->d_name, NULL);
  894. if (fpath) {
  895. plist_t fdict = plist_new_dict();
  896. struct stat st;
  897. stat(fpath, &st);
  898. const char *ftype = "DLFileTypeUnknown";
  899. if (S_ISDIR(st.st_mode)) {
  900. ftype = "DLFileTypeDirectory";
  901. } else if (S_ISREG(st.st_mode)) {
  902. ftype = "DLFileTypeRegular";
  903. }
  904. plist_dict_insert_item(fdict, "DLFileType", plist_new_string(ftype));
  905. plist_dict_insert_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
  906. plist_dict_insert_item(fdict, "DLFileModificationDate", plist_new_date(st.st_mtime, 0));
  907. plist_dict_insert_item(dirlist, ep->d_name, fdict);
  908. free(fpath);
  909. }
  910. }
  911. closedir(cur_dir);
  912. }
  913. free(path);
  914. /* TODO error handling */
  915. mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, dirlist);
  916. plist_free(dirlist);
  917. if (err != MOBILEBACKUP2_E_SUCCESS) {
  918. printf("Could not send status response, error %d\n", err);
  919. }
  920. }
  921. static void mb2_handle_make_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  922. {
  923. if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
  924. plist_t dir = plist_array_get_item(message, 1);
  925. char *str = NULL;
  926. int errcode = 0;
  927. char *errdesc = NULL;
  928. plist_get_string_val(dir, &str);
  929. char *newpath = build_path(backup_dir, str, NULL);
  930. free(str);
  931. if (mkdir_with_parents(newpath, 0755) < 0) {
  932. errdesc = strerror(errno);
  933. if (errno != EEXIST) {
  934. printf("mkdir: %s (%d)\n", errdesc, errno);
  935. }
  936. errcode = errno_to_device_error(errno);
  937. }
  938. free(newpath);
  939. mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
  940. if (err != MOBILEBACKUP2_E_SUCCESS) {
  941. printf("Could not send status response, error %d\n", err);
  942. }
  943. }
  944. static void mb2_copy_file_by_path(const char *src, const char *dst)
  945. {
  946. FILE *from, *to;
  947. char buf[BUFSIZ];
  948. size_t length;
  949. /* open source file */
  950. if ((from = fopen(src, "rb")) == NULL) {
  951. printf("Cannot open source path '%s'.\n", src);
  952. return;
  953. }
  954. /* open destination file */
  955. if ((to = fopen(dst, "wb")) == NULL) {
  956. printf("Cannot open destination file '%s'.\n", dst);
  957. return;
  958. }
  959. /* copy the file */
  960. while ((length = fread(buf, 1, BUFSIZ, from)) != 0) {
  961. fwrite(buf, 1, length, to);
  962. }
  963. if(fclose(from) == EOF) {
  964. printf("Error closing source file.\n");
  965. }
  966. if(fclose(to) == EOF) {
  967. printf("Error closing destination file.\n");
  968. }
  969. }
  970. static void mb2_copy_directory_by_path(const char *src, const char *dst)
  971. {
  972. if (!src || !dst) {
  973. return;
  974. }
  975. struct stat st;
  976. /* if src does not exist */
  977. if ((stat(src, &st) < 0) || !S_ISDIR(st.st_mode)) {
  978. printf("ERROR: Source directory does not exist '%s': %s (%d)\n", src, strerror(errno), errno);
  979. return;
  980. }
  981. /* if dst directory does not exist */
  982. if ((stat(dst, &st) < 0) || !S_ISDIR(st.st_mode)) {
  983. /* create it */
  984. if (mkdir_with_parents(dst, 0755) < 0) {
  985. printf("ERROR: Unable to create destination directory '%s': %s (%d)\n", dst, strerror(errno), errno);
  986. return;
  987. }
  988. }
  989. /* loop over src directory contents */
  990. DIR *cur_dir = opendir(src);
  991. if (cur_dir) {
  992. struct dirent* ep;
  993. while ((ep = readdir(cur_dir))) {
  994. if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
  995. continue;
  996. }
  997. char *srcpath = build_path(src, ep->d_name, NULL);
  998. char *dstpath = build_path(dst, ep->d_name, NULL);
  999. if (srcpath && dstpath) {
  1000. /* copy file */
  1001. mb2_copy_file_by_path(srcpath, dstpath);
  1002. free(srcpath);
  1003. free(dstpath);
  1004. }
  1005. }
  1006. closedir(cur_dir);
  1007. }
  1008. }
  1009. #ifdef WIN32
  1010. #define BS_CC '\b'
  1011. #define my_getch getch
  1012. #else
  1013. #define BS_CC 0x7f
  1014. static int my_getch()
  1015. {
  1016. struct termios oldt, newt;
  1017. int ch;
  1018. tcgetattr(STDIN_FILENO, &oldt);
  1019. newt = oldt;
  1020. newt.c_lflag &= ~(ICANON | ECHO);
  1021. tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  1022. ch = getchar();
  1023. tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  1024. return ch;
  1025. }
  1026. #endif
  1027. static void get_hidden_input(char *buf, int maxlen)
  1028. {
  1029. int pwlen = 0;
  1030. int c;
  1031. while ((c = my_getch())) {
  1032. if ((c == '\r') || (c == '\n')) {
  1033. break;
  1034. } else if (isprint(c)) {
  1035. if (pwlen < maxlen-1)
  1036. buf[pwlen++] = c;
  1037. fputc('*', stderr);
  1038. } else if (c == BS_CC) {
  1039. if (pwlen > 0) {
  1040. fputs("\b \b", stderr);
  1041. pwlen--;
  1042. }
  1043. }
  1044. }
  1045. buf[pwlen] = 0;
  1046. }
  1047. static char* ask_for_password(const char* msg, int type_again)
  1048. {
  1049. char pwbuf[256];
  1050. fprintf(stderr, "%s: ", msg);
  1051. fflush(stderr);
  1052. get_hidden_input(pwbuf, 256);
  1053. fputc('\n', stderr);
  1054. if (type_again) {
  1055. char pwrep[256];
  1056. fprintf(stderr, "%s (repeat): ", msg);
  1057. fflush(stderr);
  1058. get_hidden_input(pwrep, 256);
  1059. fputc('\n', stderr);
  1060. if (strcmp(pwbuf, pwrep) != 0) {
  1061. printf("ERROR: passwords don't match\n");
  1062. return NULL;
  1063. }
  1064. }
  1065. return strdup(pwbuf);
  1066. }
  1067. /**
  1068. * signal handler function for cleaning up properly
  1069. */
  1070. static void clean_exit(int sig)
  1071. {
  1072. fprintf(stderr, "Exiting...\n");
  1073. quit_flag++;
  1074. }
  1075. static void print_usage(int argc, char **argv)
  1076. {
  1077. char *name = NULL;
  1078. name = strrchr(argv[0], '/');
  1079. printf("Usage: %s [OPTIONS] CMD [CMDOPTIONS] DIRECTORY\n", (name ? name + 1: argv[0]));
  1080. printf("Create or restore backup from the current or specified directory.\n\n");
  1081. printf("commands:\n");
  1082. printf(" backup\tcreate backup for the device\n");
  1083. printf(" restore\trestore last backup to the device\n");
  1084. printf(" --system\t\trestore system files, too.\n");
  1085. printf(" --reboot\t\treboot the system when done.\n");
  1086. printf(" --copy\t\tcreate a copy of backup folder before restoring.\n");
  1087. printf(" --settings\t\trestore device settings from the backup.\n");
  1088. printf(" --remove\t\tremove items which are not being restored\n");
  1089. printf(" --password PWD\tsupply the password of the source backup\n");
  1090. printf(" info\t\tshow details about last completed backup of device\n");
  1091. printf(" list\t\tlist files of last completed backup in CSV format\n");
  1092. printf(" unback\tunpack a completed backup in DIRECTORY/_unback_/\n");
  1093. printf(" encryption on|off [PWD]\tenable or disable backup encryption\n");
  1094. printf(" NOTE: password will be requested in interactive mode if omitted\n");
  1095. printf(" changepw [OLD NEW] change backup password on target device\n");
  1096. printf(" NOTE: passwords will be requested in interactive mode if omitted\n");
  1097. printf("\n");
  1098. printf("options:\n");
  1099. printf(" -d, --debug\t\tenable communication debugging\n");
  1100. printf(" -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n");
  1101. printf(" -s, --source UDID\tuse backup data from device specified by UDID\n");
  1102. printf(" -i, --interactive\trequest passwords interactively\n");
  1103. printf(" -h, --help\t\tprints usage information\n");
  1104. printf("\n");
  1105. }
  1106. int main(int argc, char *argv[])
  1107. {
  1108. idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  1109. int i;
  1110. char* udid = NULL;
  1111. char* source_udid = NULL;
  1112. lockdownd_service_descriptor_t service = NULL;
  1113. int cmd = -1;
  1114. int cmd_flags = 0;
  1115. int is_full_backup = 0;
  1116. int result_code = -1;
  1117. char* backup_directory = NULL;
  1118. int interactive_mode = 0;
  1119. char* backup_password = NULL;
  1120. char* newpw = NULL;
  1121. struct stat st;
  1122. plist_t node_tmp = NULL;
  1123. plist_t info_plist = NULL;
  1124. plist_t opts = NULL;
  1125. mobilebackup2_error_t err;
  1126. /* we need to exit cleanly on running backups and restores or we cause havok */
  1127. signal(SIGINT, clean_exit);
  1128. signal(SIGTERM, clean_exit);
  1129. #ifndef WIN32
  1130. signal(SIGQUIT, clean_exit);
  1131. signal(SIGPIPE, SIG_IGN);
  1132. #endif
  1133. /* parse cmdline args */
  1134. for (i = 1; i < argc; i++) {
  1135. if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
  1136. idevice_set_debug_level(1);
  1137. continue;
  1138. }
  1139. else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
  1140. i++;
  1141. if (!argv[i] || (strlen(argv[i]) != 40)) {
  1142. print_usage(argc, argv);
  1143. return -1;
  1144. }
  1145. udid = strdup(argv[i]);
  1146. continue;
  1147. }
  1148. else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--source")) {
  1149. i++;
  1150. if (!argv[i] || (strlen(argv[i]) != 40)) {
  1151. print_usage(argc, argv);
  1152. return -1;
  1153. }
  1154. source_udid = strdup(argv[i]);
  1155. continue;
  1156. }
  1157. else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--interactive")) {
  1158. interactive_mode = 1;
  1159. continue;
  1160. }
  1161. else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
  1162. print_usage(argc, argv);
  1163. return 0;
  1164. }
  1165. else if (!strcmp(argv[i], "backup")) {
  1166. cmd = CMD_BACKUP;
  1167. }
  1168. else if (!strcmp(argv[i], "restore")) {
  1169. cmd = CMD_RESTORE;
  1170. }
  1171. else if (!strcmp(argv[i], "--system")) {
  1172. cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
  1173. }
  1174. else if (!strcmp(argv[i], "--reboot")) {
  1175. cmd_flags |= CMD_FLAG_RESTORE_REBOOT;
  1176. }
  1177. else if (!strcmp(argv[i], "--copy")) {
  1178. cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
  1179. }
  1180. else if (!strcmp(argv[i], "--settings")) {
  1181. cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
  1182. }
  1183. else if (!strcmp(argv[i], "--remove")) {
  1184. cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
  1185. }
  1186. else if (!strcmp(argv[i], "--password")) {
  1187. i++;
  1188. if (!argv[i]) {
  1189. print_usage(argc, argv);
  1190. return -1;
  1191. }
  1192. if (backup_password)
  1193. free(backup_password);
  1194. backup_password = strdup(argv[i]);
  1195. continue;
  1196. }
  1197. else if (!strcmp(argv[i], "info")) {
  1198. cmd = CMD_INFO;
  1199. verbose = 0;
  1200. }
  1201. else if (!strcmp(argv[i], "list")) {
  1202. cmd = CMD_LIST;
  1203. verbose = 0;
  1204. }
  1205. else if (!strcmp(argv[i], "unback")) {
  1206. cmd = CMD_UNBACK;
  1207. }
  1208. else if (!strcmp(argv[i], "encryption")) {
  1209. cmd = CMD_CHANGEPW;
  1210. i++;
  1211. if (!argv[i]) {
  1212. printf("No argument given for encryption command; requires either 'on' or 'off'.\n");
  1213. print_usage(argc, argv);
  1214. return -1;
  1215. }
  1216. if (!strcmp(argv[i], "on")) {
  1217. cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
  1218. } else if (!strcmp(argv[i], "off")) {
  1219. cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
  1220. } else {
  1221. printf("Invalid argument '%s' for encryption command; must be either 'on' or 'off'.\n", argv[i]);
  1222. }
  1223. // check if a password was given on the command line
  1224. if (newpw) {
  1225. free(newpw);
  1226. newpw = NULL;
  1227. }
  1228. if (backup_password) {
  1229. free(backup_password);
  1230. backup_password = NULL;
  1231. }
  1232. i++;
  1233. if (argv[i]) {
  1234. if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
  1235. newpw = strdup(argv[i]);
  1236. } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
  1237. backup_password = strdup(argv[i]);
  1238. }
  1239. }
  1240. continue;
  1241. }
  1242. else if (!strcmp(argv[i], "changepw")) {
  1243. cmd = CMD_CHANGEPW;
  1244. cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
  1245. // check if passwords were given on command line
  1246. if (newpw) {
  1247. free(newpw);
  1248. newpw = NULL;
  1249. }
  1250. if (backup_password) {
  1251. free(backup_password);
  1252. backup_password = NULL;
  1253. }
  1254. i++;
  1255. if (argv[i]) {
  1256. backup_password = strdup(argv[i]);
  1257. i++;
  1258. if (!argv[i]) {
  1259. printf("Old and new passwords have to be passed as arguments for the changepw command\n");
  1260. print_usage(argc, argv);
  1261. return -1;
  1262. }
  1263. newpw = strdup(argv[i]);
  1264. }
  1265. continue;
  1266. }
  1267. else if (backup_directory == NULL) {
  1268. backup_directory = argv[i];
  1269. }
  1270. else {
  1271. print_usage(argc, argv);
  1272. return -1;
  1273. }
  1274. }
  1275. /* verify options */
  1276. if (cmd == -1) {
  1277. printf("No command specified.\n");
  1278. print_usage(argc, argv);
  1279. return -1;
  1280. }
  1281. if (cmd == CMD_CHANGEPW) {
  1282. backup_directory = strdup(".this_folder_is_not_present_on_purpose");
  1283. } else {
  1284. if (backup_directory == NULL) {
  1285. printf("No target backup directory specified.\n");
  1286. print_usage(argc, argv);
  1287. return -1;
  1288. }
  1289. /* verify if passed backup directory exists */
  1290. if (stat(backup_directory, &st) != 0) {
  1291. printf("ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
  1292. return -1;
  1293. }
  1294. }
  1295. idevice_t device = NULL;
  1296. if (udid) {
  1297. ret = idevice_new(&device, udid);
  1298. if (ret != IDEVICE_E_SUCCESS) {
  1299. printf("No device found with udid %s, is it plugged in?\n", udid);
  1300. return -1;
  1301. }
  1302. }
  1303. else
  1304. {
  1305. ret = idevice_new(&device, NULL);
  1306. if (ret != IDEVICE_E_SUCCESS) {
  1307. printf("No device found, is it plugged in?\n");
  1308. return -1;
  1309. }
  1310. idevice_get_udid(device, &udid);
  1311. }
  1312. if (!source_udid) {
  1313. source_udid = strdup(udid);
  1314. }
  1315. uint8_t is_encrypted = 0;
  1316. char *info_path = NULL;
  1317. if (cmd == CMD_CHANGEPW) {
  1318. if (!interactive_mode && (!backup_password || !newpw)) {
  1319. 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");
  1320. return -1;
  1321. }
  1322. } else {
  1323. /* backup directory must contain an Info.plist */
  1324. info_path = build_path(backup_directory, source_udid, "Info.plist", NULL);
  1325. if (cmd == CMD_RESTORE) {
  1326. if (stat(info_path, &st) != 0) {
  1327. free(info_path);
  1328. printf("ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, source_udid);
  1329. return -1;
  1330. }
  1331. char* manifest_path = build_path(backup_directory, source_udid, "Manifest.plist", NULL);
  1332. if (stat(manifest_path, &st) != 0) {
  1333. free(info_path);
  1334. }
  1335. plist_t manifest_plist = NULL;
  1336. plist_read_from_filename(&manifest_plist, manifest_path);
  1337. if (!manifest_plist) {
  1338. free(info_path);
  1339. free(manifest_path);
  1340. printf("ERROR: Backup directory \"%s\" is invalid. No Manifest.plist found for UDID %s.\n", backup_directory, source_udid);
  1341. return -1;
  1342. }
  1343. node_tmp = plist_dict_get_item(manifest_plist, "IsEncrypted");
  1344. if (node_tmp && (plist_get_node_type(node_tmp) == PLIST_BOOLEAN)) {
  1345. plist_get_bool_val(node_tmp, &is_encrypted);
  1346. }
  1347. plist_free(manifest_plist);
  1348. free(manifest_path);
  1349. }
  1350. PRINT_VERBOSE(1, "Backup directory is \"%s\"\n", backup_directory);
  1351. }
  1352. if (is_encrypted) {
  1353. PRINT_VERBOSE(1, "This is an encrypted backup.\n");
  1354. if (backup_password == NULL) {
  1355. if (interactive_mode) {
  1356. backup_password = ask_for_password("Enter backup password", 0);
  1357. }
  1358. if (!backup_password || (strlen(backup_password) == 0)) {
  1359. if (backup_password) {
  1360. free(backup_password);
  1361. }
  1362. idevice_free(device);
  1363. printf("ERROR: a backup password is required to restore an encrypted backup. Cannot continue.\n");
  1364. return -1;
  1365. }
  1366. }
  1367. }
  1368. lockdownd_client_t lockdown = NULL;
  1369. if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup")) {
  1370. idevice_free(device);
  1371. return -1;
  1372. }
  1373. /* start notification_proxy */
  1374. np_client_t np = NULL;
  1375. ret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
  1376. if ((ret == LOCKDOWN_E_SUCCESS) && service && service->port) {
  1377. np_client_new(device, service, &np);
  1378. np_set_notify_callback(np, notify_cb, NULL);
  1379. const char *noties[5] = {
  1380. NP_SYNC_CANCEL_REQUEST,
  1381. NP_SYNC_SUSPEND_REQUEST,
  1382. NP_SYNC_RESUME_REQUEST,
  1383. NP_BACKUP_DOMAIN_CHANGED,
  1384. NULL
  1385. };
  1386. np_observe_notifications(np, noties);
  1387. } else {
  1388. printf("ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
  1389. }
  1390. afc_client_t afc = NULL;
  1391. if (cmd == CMD_BACKUP) {
  1392. /* start AFC, we need this for the lock file */
  1393. service->port = 0;
  1394. service->ssl_enabled = 0;
  1395. ret = lockdownd_start_service(lockdown, "com.apple.afc", &service);
  1396. if ((ret == LOCKDOWN_E_SUCCESS) && service->port) {
  1397. afc_client_new(device, service, &afc);
  1398. }
  1399. }
  1400. if (service) {
  1401. lockdownd_service_descriptor_free(service);
  1402. service = NULL;
  1403. }
  1404. /* start mobilebackup service and retrieve port */
  1405. mobilebackup2_client_t mobilebackup2 = NULL;
  1406. ret = lockdownd_start_service(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
  1407. if ((ret == LOCKDOWN_E_SUCCESS) && service && service->port) {
  1408. PRINT_VERBOSE(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
  1409. mobilebackup2_client_new(device, service, &mobilebackup2);
  1410. if (service) {
  1411. lockdownd_service_descriptor_free(service);
  1412. service = NULL;
  1413. }
  1414. /* send Hello message */
  1415. double local_versions[2] = {2.0, 2.1};
  1416. double remote_version = 0.0;
  1417. err = mobilebackup2_version_exchange(mobilebackup2, local_versions, 2, &remote_version);
  1418. if (err != MOBILEBACKUP2_E_SUCCESS) {
  1419. printf("Could not perform backup protocol version exchange, error code %d\n", err);
  1420. cmd = CMD_LEAVE;
  1421. goto checkpoint;
  1422. }
  1423. PRINT_VERBOSE(1, "Negotiated Protocol Version %.1f\n", remote_version);
  1424. /* check abort conditions */
  1425. if (quit_flag > 0) {
  1426. PRINT_VERBOSE(1, "Aborting as requested by user...\n");
  1427. cmd = CMD_LEAVE;
  1428. goto checkpoint;
  1429. }
  1430. /* verify existing Info.plist */
  1431. if (info_path && (stat(info_path, &st) == 0)) {
  1432. PRINT_VERBOSE(1, "Reading Info.plist from backup.\n");
  1433. plist_read_from_filename(&info_plist, info_path);
  1434. if (!info_plist) {
  1435. printf("Could not read Info.plist\n");
  1436. is_full_backup = 1;
  1437. }
  1438. } else {
  1439. if (cmd == CMD_RESTORE) {
  1440. printf("Aborting restore. Info.plist is missing.\n");
  1441. cmd = CMD_LEAVE;
  1442. } else {
  1443. is_full_backup = 1;
  1444. }
  1445. }
  1446. uint64_t lockfile = 0;
  1447. if (cmd == CMD_BACKUP) {
  1448. do_post_notification(device, NP_SYNC_WILL_START);
  1449. afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
  1450. }
  1451. if (lockfile) {
  1452. afc_error_t aerr;
  1453. do_post_notification(device, NP_SYNC_LOCK_REQUEST);
  1454. for (i = 0; i < LOCK_ATTEMPTS; i++) {
  1455. aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
  1456. if (aerr == AFC_E_SUCCESS) {
  1457. do_post_notification(device, NP_SYNC_DID_START);
  1458. break;
  1459. } else if (aerr == AFC_E_OP_WOULD_BLOCK) {
  1460. sleep(LOCK_WAIT);
  1461. continue;
  1462. } else {
  1463. fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
  1464. afc_file_close(afc, lockfile);
  1465. lockfile = 0;
  1466. cmd = CMD_LEAVE;
  1467. }
  1468. }
  1469. if (i == LOCK_ATTEMPTS) {
  1470. fprintf(stderr, "ERROR: timeout while locking for sync\n");
  1471. afc_file_close(afc, lockfile);
  1472. lockfile = 0;
  1473. cmd = CMD_LEAVE;
  1474. }
  1475. }
  1476. uint8_t willEncrypt = 0;
  1477. node_tmp = NULL;
  1478. lockdownd_get_value(lockdown, "com.apple.mobile.backup", "WillEncrypt", &node_tmp);
  1479. if (node_tmp) {
  1480. if (plist_get_node_type(node_tmp) == PLIST_BOOLEAN) {
  1481. plist_get_bool_val(node_tmp, &willEncrypt);
  1482. }
  1483. plist_free(node_tmp);
  1484. node_tmp = NULL;
  1485. }
  1486. checkpoint:
  1487. switch(cmd) {
  1488. case CMD_BACKUP:
  1489. {
  1490. PRINT_VERBOSE(1, "Starting backup...\n");
  1491. /* make sure backup device sub-directory exists */
  1492. char* devbackupdir = build_path(backup_directory, source_udid, NULL);
  1493. __mkdir(devbackupdir, 0755);
  1494. free(devbackupdir);
  1495. if (strcmp(source_udid, udid) != 0) {
  1496. /* handle different source backup directory */
  1497. // make sure target backup device sub-directory exists
  1498. devbackupdir = build_path(backup_directory, udid, NULL);
  1499. __mkdir(devbackupdir, 0755);
  1500. free(devbackupdir);
  1501. // use Info.plist path in target backup folder */
  1502. free(info_path);
  1503. info_path = build_path(backup_directory, udid, "Info.plist", NULL);
  1504. }
  1505. /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
  1506. /* TODO: verify battery on AC enough battery remaining */
  1507. /* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
  1508. if (info_plist) {
  1509. plist_free(info_plist);
  1510. info_plist = NULL;
  1511. }
  1512. info_plist = mobilebackup_factory_info_plist_new(udid, lockdown, afc);
  1513. remove(info_path);
  1514. plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
  1515. free(info_path);
  1516. plist_free(info_plist);
  1517. info_plist = NULL;
  1518. /* request backup from device with manifest from last backup */
  1519. if (willEncrypt) {
  1520. PRINT_VERBOSE(1, "Backup will be encrypted.\n");
  1521. } else {
  1522. PRINT_VERBOSE(1, "Backup will be unencrypted.\n");
  1523. }
  1524. PRINT_VERBOSE(1, "Requesting backup from device...\n");
  1525. err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, source_udid, NULL);
  1526. if (err == MOBILEBACKUP2_E_SUCCESS) {
  1527. if (is_full_backup) {
  1528. PRINT_VERBOSE(1, "Full backup mode.\n");
  1529. } else {
  1530. PRINT_VERBOSE(1, "Incremental backup mode.\n");
  1531. }
  1532. } else {
  1533. if (err == MOBILEBACKUP2_E_BAD_VERSION) {
  1534. printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
  1535. } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
  1536. printf("ERROR: Could not start backup process: device refused to start the backup process.\n");
  1537. } else {
  1538. printf("ERROR: Could not start backup process: unspecified error occured\n");
  1539. }
  1540. cmd = CMD_LEAVE;
  1541. }
  1542. }
  1543. break;
  1544. case CMD_RESTORE:
  1545. {
  1546. /* TODO: verify battery on AC enough battery remaining */
  1547. /* verify if Status.plist says we read from an successful backup */
  1548. if (!mb2_status_check_snapshot_state(backup_directory, source_udid, "finished")) {
  1549. printf("ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
  1550. cmd = CMD_LEAVE;
  1551. break;
  1552. }
  1553. PRINT_VERBOSE(1, "Starting Restore...\n");
  1554. opts = plist_new_dict();
  1555. plist_dict_insert_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
  1556. PRINT_VERBOSE(1, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "Yes":"No"));
  1557. if ((cmd_flags & CMD_FLAG_RESTORE_REBOOT) == 0)
  1558. plist_dict_insert_item(opts, "RestoreShouldReboot", plist_new_bool(0));
  1559. PRINT_VERBOSE(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_REBOOT ? "Yes":"No"));
  1560. if ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0)
  1561. plist_dict_insert_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
  1562. PRINT_VERBOSE(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
  1563. plist_dict_insert_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
  1564. PRINT_VERBOSE(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
  1565. if (cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS)
  1566. plist_dict_insert_item(opts, "RemoveItemsNotRestored", plist_new_bool(1));
  1567. PRINT_VERBOSE(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
  1568. if (backup_password != NULL) {
  1569. plist_dict_insert_item(opts, "Password", plist_new_string(backup_password));
  1570. }
  1571. PRINT_VERBOSE(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
  1572. err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, source_udid, opts);
  1573. plist_free(opts);
  1574. if (err != MOBILEBACKUP2_E_SUCCESS) {
  1575. if (err == MOBILEBACKUP2_E_BAD_VERSION) {
  1576. printf("ERROR: Could not start restore process: backup protocol version mismatch!\n");
  1577. } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
  1578. printf("ERROR: Could not start restore process: device refused to start the restore process.\n");
  1579. } else {
  1580. printf("ERROR: Could not start restore process: unspecified error occured\n");
  1581. }
  1582. cmd = CMD_LEAVE;
  1583. }
  1584. }
  1585. break;
  1586. case CMD_INFO:
  1587. {
  1588. PRINT_VERBOSE(1, "Requesting backup info from device...\n");
  1589. err = mobilebackup2_send_request(mobilebackup2, "Info", udid, source_udid, NULL);
  1590. if (err != MOBILEBACKUP2_E_SUCCESS) {
  1591. printf("Error requesting backup info from device, error code %d\n", err);
  1592. cmd = CMD_LEAVE;
  1593. }
  1594. }
  1595. break;
  1596. case CMD_LIST:
  1597. {
  1598. PRINT_VERBOSE(1, "Requesting backup list from device...\n");
  1599. err = mobilebackup2_send_request(mobilebackup2, "List", udid, source_udid, NULL);
  1600. if (err != MOBILEBACKUP2_E_SUCCESS) {
  1601. prin