text: #include "backup2.h"
  
  #ifdef HAVE_CONFIG_H
  #include <config.h>
  #endif
  
  #define TOOL_NAME "idevicebackup2"
  
  #include <stdio.h>
  #include <string.h>
  #include <errno.h>
  #include <stdlib.h>
  #include <signal.h>
  #include <math.h>
  #include <float.h>
  
  #ifdef WIN32
      #define STDIN_FILENO 0
      #include "common/dirent.h"
  #else
      #include <unistd.h>
      #include <dirent.h>
      #include <libgen.h>
  #endif 
      
  #include <ctype.h>
  #include <time.h>
  
  #include <libimobiledevice/libimobiledevice.h>
  #include <libimobiledevice/lockdown.h>
  #include <libimobiledevice/mobilebackup2.h>
  #include <libimobiledevice/notification_proxy.h>
  #include <libimobiledevice/afc.h>
  #include <libimobiledevice/installation_proxy.h>
  #include <libimobiledevice/sbservices.h>
  #include <libimobiledevice/diagnostics_relay.h>
  #include "common/utils.h"
  
  #include "common/endianness.h"
  
  #define LOCK_ATTEMPTS 50
  #define LOCK_WAIT 200000
  #define MAX_RETRIES 8
  
  #ifdef WIN32
      #include <windows.h>
      #include <conio.h>
      #define sleep(x) Sleep(x*1000)
      #ifndef ELOOP
      #define ELOOP 114
      #endif
  #else
      #include <termios.h>
      #include <sys/statvfs.h>
  #endif
  
  #include <sys/stat.h>
  
  #define CODE_SUCCESS 0x00
  #define CODE_ERROR_LOCAL 0x06
  #define CODE_ERROR_REMOTE 0x0b
  #define CODE_FILE_DATA 0x0c
  
  static int verbose = 1;
  static int quit_flag = 0;
  
  #define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { fprintf(__VA_ARGS__); };
  #define PRINT_VERBOSE_DEBUG(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
  
  enum cmd_mode {
      CMD_BACKUP,
      CMD_RESTORE,
      CMD_INFO,
      CMD_LIST,
      CMD_UNBACK,
      CMD_CHANGEPW,
      CMD_LEAVE,
      CMD_CLOUD
  };
  
  enum cmd_flags {
      CMD_FLAG_RESTORE_SYSTEM_FILES       = (1 << 1),
      CMD_FLAG_RESTORE_REBOOT             = (1 << 2),
      CMD_FLAG_RESTORE_COPY_BACKUP        = (1 << 3),
      CMD_FLAG_RESTORE_SETTINGS           = (1 << 4),
      CMD_FLAG_RESTORE_REMOVE_ITEMS       = (1 << 5),
      CMD_FLAG_ENCRYPTION_ENABLE          = (1 << 6),
      CMD_FLAG_ENCRYPTION_DISABLE         = (1 << 7),
      CMD_FLAG_ENCRYPTION_CHANGEPW        = (1 << 8),
      CMD_FLAG_FORCE_FULL_BACKUP          = (1 << 9),
      CMD_FLAG_CLOUD_ENABLE               = (1 << 10),
      CMD_FLAG_CLOUD_DISABLE              = (1 << 11),
      CMD_FLAG_RESTORE_SKIP_APPS          = (1 << 12)
  };
  
  const struct idevice_backup2_options default_idevice_backup2_options = {
      false,                  // debug
      NULL,                   // udid
      NULL,                   // source
      false,                  // network
      NULL,                   // command
      { false },              // backup
      {                       // restore
          false,                  // system
          true,                   // reboot
          false,                  // copy
          false,                  // settings
          true,                   // remove
          false,                  // skip_apps
          NULL                    // password
      },
      { false, NULL },        // encryption
      { NULL, NULL },         // changepw
      { false },              // cloud
      false,                  // interactive
      NULL                    // backup_directory
  };
  
  static uint64_t get_among_space_to_purge(plist_t message) {
      uint64_t space = 0;
      plist_t plist_tmp = NULL;
      plist_tmp = plist_array_get_item(message, 1);
      if (plist_tmp && (plist_get_node_type(plist_tmp) == PLIST_UINT)) {
          plist_get_uint_val(plist_tmp, &space);
      }
      plist_free(plist_tmp);
      return space;
  }
  
  static int backup_domain_changed = 0;
  
  void command_run_successfully(FILE *stream_out) {
      fprintf(stream_out, "SUCCESS");
  }
  
  static void notify_cb(const char *notification, void *userdata)
  {
      if (notification == NULL || strlen(notification) == 0) {
          return;
      }
      if (!strcmp(notification, NP_SYNC_CANCEL_REQUEST)) {
          printf("User has cancelled the backup process on the device.\n");
          quit_flag++;
      } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
          backup_domain_changed = 1;
      } else {
          printf("Unhandled notification '%s' (TODO: implement)\n", notification);
      }
  }
  
  static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *filename, char **data, uint64_t *size)
  {
      if (!afc || !data || !size) {
          return;
      }
      
      char **fileinfo = NULL;
      uint32_t fsize = 0;
      
      afc_get_file_info(afc, filename, &fileinfo);
      if (!fileinfo) {
          return;
      }
      int i;
      for (i = 0; fileinfo[i]; i+=2) {
          if (!strcmp(fileinfo[i], "st_size")) {
              fsize = atol(fileinfo[i+1]);
              break;
          }
      }
      afc_dictionary_free(fileinfo);
      
      if (fsize == 0) {
          return;
      }
      
      uint64_t f = 0;
      afc_file_open(afc, filename, AFC_FOPEN_RDONLY, &f);
      if (!f) {
          return;
      }
      char *buf = (char*)malloc((uint32_t)fsize);
      uint32_t done = 0;
      while (done < fsize) {
          uint32_t bread = 0;
          afc_file_read(afc, f, buf+done, 65536, &bread);
          if (bread > 0) {
              done += bread;
          } else {
              break;
          }
      }
      if (done == fsize) {
          *size = fsize;
          *data = buf;
      } else {
          free(buf);
      }
      afc_file_close(afc, f);
  }
  
  static int __mkdir(const char* path, int mode)
  {
  #ifdef WIN32
      return mkdir(path);
  #else
      return mkdir(path, mode);
  #endif
  }
  
  static int mkdir_with_parents(const char *dir, int mode)
  {
      if (!dir) return -1;
      if (__mkdir(dir, mode) == 0) {
          return 0;
      } else {
          if (errno == EEXIST) return 0;
      }
      int res;
      char *parent = strdup(dir);
      char *parentdir = dirname(parent);
      if (parentdir) {
          res = mkdir_with_parents(parentdir, mode);
      } else {
          res = -1;
      }
      free(parent);
      if (res == 0) {
          mkdir_with_parents(dir, mode);
      }
      return res;
  }
  
  #ifdef WIN32
  static int win32err_to_errno(int err_value)
  {
      switch (err_value) {
          case ERROR_FILE_NOT_FOUND:
              return ENOENT;
          case ERROR_ALREADY_EXISTS:
              return EEXIST;
          default:
              return EFAULT;
      }
  }
  #endif
  
  static int remove_file(const char* path)
  {
      int e = 0;
  #ifdef WIN32
      if (!DeleteFile(path)) {
          e = win32err_to_errno(GetLastError());
      }
  #else
      if (remove(path) < 0) {
          e = errno;
      }
  #endif
      return e;
  }
  
  static int remove_directory(const char* path)
  {
      int e = 0;
  #ifdef WIN32
      if (!RemoveDirectory(path)) {
          e = win32err_to_errno(GetLastError());
      }
  #else
      if (remove(path) < 0) {
          e = errno;
      }
  #endif
      return e;
  }
  
  static int rmdir_recursive(const char* path)
  {
      DIR* cur_dir = opendir(path);
      if (cur_dir) {
          struct dirent* ep;
          while ((ep = readdir(cur_dir))) {
              if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
                  continue;
              }
              char *fpath = string_build_path(path, ep->d_name, NULL);
              if (fpath) {
                  struct stat st;
                  if (stat(fpath, &st) == 0) {
                      int res = 0;
                      if (S_ISDIR(st.st_mode)) {
                          res = rmdir_recursive(fpath);
                      } else {
                          res = remove_file(fpath);
                      }
                      if (res != 0) {
                          free(fpath);
                          closedir(cur_dir);
                          return res;
                      }
                  } else {
                      free(fpath);
                      closedir(cur_dir);
                      return errno;
                  }
              }
              free(fpath);
          }
          closedir(cur_dir);
      }
      
      return remove_directory(path);
  }
  
  static char* get_uuid()
  {
      const char *chars = "ABCDEF0123456789";
      int i = 0;
      char *uuid = (char*)malloc(sizeof(char) * 33);
      
      srand(time(NULL));
      
      for (i = 0; i < 32; i++) {
          uuid[i] = chars[rand() % 16];
      }
      
      uuid[32] = '\0';
      
      return uuid;
  }
  
  static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t device, afc_client_t afc)
  {
  	/* gather data from lockdown */
  	plist_t value_node = NULL;
  	plist_t root_node = NULL;
  	plist_t itunes_settings = NULL;
  	plist_t min_itunes_version = NULL;
  	char *udid_uppercase = NULL;
  
  	lockdownd_client_t lockdown = NULL;
  	if (lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME) != LOCKDOWN_E_SUCCESS) {
  		return NULL;
  	}
  
  	plist_t ret = plist_new_dict();
  
  	/* get basic device information in one go */
  	lockdownd_get_value(lockdown, NULL, NULL, &root_node);
  
  	/* get iTunes settings */
  	lockdownd_get_value(lockdown, "com.apple.iTunes", NULL, &itunes_settings);
  
  	/* get minimum iTunes version */
  	lockdownd_get_value(lockdown, "com.apple.mobile.iTunes", "MinITunesVersion", &min_itunes_version);
  
  	lockdownd_client_free(lockdown);
  
  	/* get a list of installed user applications */
  	plist_t app_dict = plist_new_dict();
  	plist_t installed_apps = plist_new_array();
  	instproxy_client_t ip = NULL;
  	if (instproxy_client_start_service(device, &ip, TOOL_NAME) == INSTPROXY_E_SUCCESS) {
  		plist_t client_opts = instproxy_client_options_new();
  		instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
  		instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata", NULL);
  
  		plist_t apps = NULL;
  		instproxy_browse(ip, client_opts, &apps);
  
  		sbservices_client_t sbs = NULL;
  		if (sbservices_client_start_service(device, &sbs, TOOL_NAME) != SBSERVICES_E_SUCCESS) {
  			printf("Couldn't establish sbservices connection. Continuing anyway.\n");
  		}
  
  		if (apps && (plist_get_node_type(apps) == PLIST_ARRAY)) {
  			uint32_t app_count = plist_array_get_size(apps);
  			uint32_t i;
  			for (i = 0; i < app_count; i++) {
  				plist_t app_entry = plist_array_get_item(apps, i);
  				plist_t bundle_id = plist_dict_get_item(app_entry, "CFBundleIdentifier");
  				if (bundle_id) {
  					char *bundle_id_str = NULL;
  					plist_array_append_item(installed_apps, plist_copy(bundle_id));
  
  					plist_get_string_val(bundle_id, &bundle_id_str);
  					plist_t sinf = plist_dict_get_item(app_entry, "ApplicationSINF");
  					plist_t meta = plist_dict_get_item(app_entry, "iTunesMetadata");
  					if (sinf && meta) {
  						plist_t adict = plist_new_dict();
  						plist_dict_set_item(adict, "ApplicationSINF", plist_copy(sinf));
  						if (sbs) {
  							char *pngdata = NULL;
  							uint64_t pngsize = 0;
  							sbservices_get_icon_pngdata(sbs, bundle_id_str, &pngdata, &pngsize);
  							if (pngdata) {
  								plist_dict_set_item(adict, "PlaceholderIcon", plist_new_data(pngdata, pngsize));
  								free(pngdata);
  							}
  						}
  						plist_dict_set_item(adict, "iTunesMetadata", plist_copy(meta));
  						plist_dict_set_item(app_dict, bundle_id_str, adict);
  					}
  					free(bundle_id_str);
  				}
  			}
  		}
  		plist_free(apps);
  
  		if (sbs) {
  			sbservices_client_free(sbs);
  		}
  
  		instproxy_client_options_free(client_opts);
  
  		instproxy_client_free(ip);
  	}
  
  	/* Applications */
  	plist_dict_set_item(ret, "Applications", app_dict);
  
  	/* set fields we understand */
  	value_node = plist_dict_get_item(root_node, "BuildVersion");
  	plist_dict_set_item(ret, "Build Version", plist_copy(value_node));
  
  	value_node = plist_dict_get_item(root_node, "DeviceName");
  	plist_dict_set_item(ret, "Device Name", plist_copy(value_node));
  	plist_dict_set_item(ret, "Display Name", plist_copy(value_node));
  
  	char *uuid = get_uuid();
  	plist_dict_set_item(ret, "GUID", plist_new_string(uuid));
  	free(uuid);
  
  	value_node = plist_dict_get_item(root_node, "IntegratedCircuitCardIdentity");
  	if (value_node)
  		plist_dict_set_item(ret, "ICCID", plist_copy(value_node));
  
  	value_node = plist_dict_get_item(root_node, "InternationalMobileEquipmentIdentity");
  	if (value_node)
  		plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
  
  	/* Installed Applications */
  	plist_dict_set_item(ret, "Installed Applications", installed_apps);
  
  	plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
  
  	value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
  	if (value_node)
  		plist_dict_set_item(ret, "MEID", plist_copy(value_node));
  
  	value_node = plist_dict_get_item(root_node, "PhoneNumber");
  	if (value_node && (plist_get_node_type(value_node) == PLIST_STRING)) {
  		plist_dict_set_item(ret, "Phone Number", plist_copy(value_node));
  	}
  
  	/* FIXME Product Name */
  
  	value_node = plist_dict_get_item(root_node, "ProductType");
  	plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
  
  	value_node = plist_dict_get_item(root_node, "ProductVersion");
  	plist_dict_set_item(ret, "Product Version", plist_copy(value_node));
  
  	value_node = plist_dict_get_item(root_node, "SerialNumber");
  	plist_dict_set_item(ret, "Serial Number", plist_copy(value_node));
  
  	/* FIXME Sync Settings? */
  
  	value_node = plist_dict_get_item(root_node, "UniqueDeviceID");
  	plist_dict_set_item(ret, "Target Identifier", plist_new_string(udid));
  
  	plist_dict_set_item(ret, "Target Type", plist_new_string("Device"));
  
  	/* uppercase */
  	udid_uppercase = string_toupper((char*)udid);
  	plist_dict_set_item(ret, "Unique Identifier", plist_new_string(udid_uppercase));
  	free(udid_uppercase);
  
  	char *data_buf = NULL;
  	uint64_t data_size = 0;
  	mobilebackup_afc_get_file_contents(afc, "/Books/iBooksData2.plist", &data_buf, &data_size);
  	if (data_buf) {
  		plist_dict_set_item(ret, "iBooks Data 2", plist_new_data(data_buf, data_size));
  		free(data_buf);
  	}
  
  	plist_t files = plist_new_dict();
  	const char *itunesfiles[] = {
  		"ApertureAlbumPrefs",
  		"IC-Info.sidb",
  		"IC-Info.sidv",
  		"PhotosFolderAlbums",
  		"PhotosFolderName",
  		"PhotosFolderPrefs",
  		"VoiceMemos.plist",
  		"iPhotoAlbumPrefs",
  		"iTunesApplicationIDs",
  		"iTunesPrefs",
  		"iTunesPrefs.plist",
  		NULL
  	};
  	int i = 0;
  	for (i = 0; itunesfiles[i]; i++) {
  		data_buf = NULL;
  		data_size = 0;
  		char *fname = (char*)malloc(strlen("/iTunes_Control/iTunes/") + strlen(itunesfiles[i]) + 1);
  		strcpy(fname, "/iTunes_Control/iTunes/");
  		strcat(fname, itunesfiles[i]);
  		mobilebackup_afc_get_file_contents(afc, fname, &data_buf, &data_size);
  		free(fname);
  		if (data_buf) {
  			plist_dict_set_item(files, itunesfiles[i], plist_new_data(data_buf, data_size));
  			free(data_buf);
  		}
  	}
  	plist_dict_set_item(ret, "iTunes Files", files);
  
  	plist_dict_set_item(ret, "iTunes Settings", itunes_settings ? plist_copy(itunes_settings) : plist_new_dict());
  
  	/* since we usually don't have iTunes, let's get the minimum required iTunes version from the device */
  	if (min_itunes_version) {
  		plist_dict_set_item(ret, "iTunes Version", plist_copy(min_itunes_version));
  	} else {
  		plist_dict_set_item(ret, "iTunes Version", plist_new_string("10.0.1"));
  	}
  
  	plist_free(itunes_settings);
  	plist_free(min_itunes_version);
  	plist_free(root_node);
  
  	return ret;
  }
  
  static int write_restore_applications(plist_t info_plist, afc_client_t afc)
  {
  	int res = -1;
  	uint64_t restore_applications_file = 0;
  	char * applications_plist_xml = NULL;
  	uint32_t applications_plist_xml_length = 0;
  
  	plist_t applications_plist = plist_dict_get_item(info_plist, "Applications");
  	if (!applications_plist) {
  		printf("No Applications in Info.plist, skipping creation of RestoreApplications.plist\n");
  		return 0;
  	}
  	plist_to_xml(applications_plist, &applications_plist_xml, &applications_plist_xml_length);
  	if (!applications_plist_xml) {
  		printf("Error preparing RestoreApplications.plist\n");
  		goto leave;
  	}
  
  	afc_error_t afc_err = 0;
  	afc_err = afc_make_directory(afc, "/iTunesRestore");
  	if (afc_err != AFC_E_SUCCESS) {
  		printf("Error creating directory /iTunesRestore, error code %d\n", afc_err);
  		goto leave;
  	}
  
  	afc_err = afc_file_open(afc, "/iTunesRestore/RestoreApplications.plist", AFC_FOPEN_WR, &restore_applications_file);
  	if (afc_err != AFC_E_SUCCESS  || !restore_applications_file) {
  		printf("Error creating /iTunesRestore/RestoreApplications.plist, error code %d\n", afc_err);
  		goto leave;
  	}
  
  	uint32_t bytes_written = 0;
  	afc_err = afc_file_write(afc, restore_applications_file, applications_plist_xml, applications_plist_xml_length, &bytes_written);
  	if (afc_err != AFC_E_SUCCESS  || bytes_written != applications_plist_xml_length) {
  		printf("Error writing /iTunesRestore/RestoreApplications.plist, error code %d, wrote %u of %u bytes\n", afc_err, bytes_written, applications_plist_xml_length);
  		goto leave;
  	}
  
  	afc_err = afc_file_close(afc, restore_applications_file);
  	restore_applications_file = 0;
  	if (afc_err != AFC_E_SUCCESS) {
  		goto leave;
  	}
  	/* success */
  	res = 0;
  
  leave:
  	free(applications_plist_xml);
  
  	if (restore_applications_file) {
  		afc_file_close(afc, restore_applications_file);
  		restore_applications_file = 0;
  	}
  
  	return res;
  }
  
  static int mb2_status_check_snapshot_state(const char *path, const char *udid, const char *matches)
  {
      int ret = 0;
      plist_t status_plist = NULL;
      char *file_path = string_build_path(path, udid, "Status.plist", NULL);
      
      plist_read_from_filename(&status_plist, file_path);
      free(file_path);
      if (!status_plist) {
          printf("Could not read Status.plist!\n");
          return ret;
      }
      plist_t node = plist_dict_get_item(status_plist, "SnapshotState");
      if (node && (plist_get_node_type(node) == PLIST_STRING)) {
          char* sval = NULL;
          plist_get_string_val(node, &sval);
          if (sval) {
              ret = (strcmp(sval, matches) == 0) ? 1 : 0;
              free(sval);
          }
      } else {
          printf("%s: ERROR could not get SnapshotState key from Status.plist!\n", __func__);
      }
      plist_free(status_plist);
      return ret;
  }
  
  static void do_post_notification(idevice_t device, const char *notification)
  {
      lockdownd_service_descriptor_t service = NULL;
      np_client_t np;
      
      lockdownd_client_t lockdown = NULL;
      lockdownd_error_t lockdown_error;
      
      if ((lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2")) != LOCKDOWN_E_SUCCESS) {
          PRINT_VERBOSE_DEBUG(1, "Could not lockdownd_client_new_with_handshake with lockdown error %d", lockdown_error);
          return;
      }
  
      if ((lockdown_error = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service)) != LOCKDOWN_E_SUCCESS) {
          PRINT_VERBOSE_DEBUG(1, "Could not start %s service with lockdown error %d", NP_SERVICE_NAME, lockdown_error);
      }
  
      if (service && service->port) {
          np_client_new(device, service, &np);
          if (np) {
              np_post_notification(np, notification);
              np_client_free(np);
          }
      } else {
          printf("Could not start %s\n", NP_SERVICE_NAME);
      }
  
      if (service) {
          lockdownd_service_descriptor_free(service);
          service = NULL;
      }
  
      lockdownd_client_free(lockdown);
  }
  
  static void print_progress_real(FILE *stream_progress, double progress, int flush)
  {
      int i = 0;
      fprintf(stream_progress, "\r[");
      for(i = 0; i < 50; i++) {
          if(i < progress / 2) {
              fprintf(stream_progress, "=");
          } else {
              fprintf(stream_progress, " ");
          }
      }
      fprintf(stream_progress, "] %3.0f%%", progress);
      
      if (flush > 0) {
          fflush(stdout);
          if (progress == 100)
              fprintf(stream_progress, "\n");
      }
  }
  
  static void print_progress(node_progress_callback callback, uint64_t current, uint64_t total)
  {
      FILE *stream_progress = tmpfile();
      char *format_size = NULL;
      double progress = ((double)current/(double)total)*100;
      if (progress < 0)
          return;
      
      if (progress > 100)
          progress = 100;
      
      print_progress_real(stream_progress, (double)progress, 0);
      
      format_size = string_format_size(current);
      fprintf(stream_progress, " (%s", format_size);
      free(format_size);
      format_size = string_format_size(total);
      fprintf(stream_progress, "/%s)     ", format_size);
      free(format_size);
      
      fflush(stdout);
      if (progress == 100)
          fprintf(stream_progress, "\n");
      
      callback(stream_progress);
  }
  
  static double overall_progress = 0;
  static double previous_overall_progress = 0;
  
  static void mb2_set_overall_progress(double progress)
  {
      if (progress > 0.0)
          overall_progress = progress;
  }
  
  static void mb2_set_overall_progress_from_message(plist_t message, char* identifier)
  {
      plist_t node = NULL;
      double progress = 0.0;
      
      if (!strcmp(identifier, "DLMessageDownloadFiles")) {
          node = plist_array_get_item(message, 3);
      } else if (!strcmp(identifier, "DLMessageUploadFiles")) {
          node = plist_array_get_item(message, 2);
      } else if (!strcmp(identifier, "DLMessageMoveFiles") || !strcmp(identifier, "DLMessageMoveItems")) {
          node = plist_array_get_item(message, 3);
      } else if (!strcmp(identifier, "DLMessageRemoveFiles") || !strcmp(identifier, "DLMessageRemoveItems")) {
          node = plist_array_get_item(message, 3);
      }
      
      if (node != NULL) {
          plist_get_real_val(node, &progress);
          mb2_set_overall_progress(progress);
      }
  }
  
  static void mb2_multi_status_add_file_error(plist_t status_dict, const char *path, int error_code, const char *error_message)
  {
      if (!status_dict) return;
      plist_t filedict = plist_new_dict();
      plist_dict_set_item(filedict, "DLFileErrorString", plist_new_string(error_message));
      plist_dict_set_item(filedict, "DLFileErrorCode", plist_new_uint(error_code));
      plist_dict_set_item(status_dict, path, filedict);
  }
  
  static int errno_to_device_error(int errno_value)
  {
  	switch (errno_value) {
  		case ENOENT:
  			return -6;
  		case EEXIST:
  			return -7;
  		case ENOTDIR:
  			return -8;
  		case EISDIR:
  			return -9;
  		case ELOOP:
  			return -10;
  		case EIO:
  			return -11;
  		case ENOSPC:
  			return -15;
  		default:
  			return -1;
  	}
  }
  
  static int mb2_handle_send_file(FILE *stream_err, FILE *stream_out, mobilebackup2_client_t mobilebackup2, const char *backup_dir, const char *path, plist_t *errplist)
  {
      uint32_t nlen = 0;
      uint32_t pathlen = strlen(path);
      uint32_t bytes = 0;
      char *localfile = string_build_path(backup_dir, path, NULL);
      char buf[32768];
  #ifdef WIN32
      struct _stati64 fst;
  #else
      struct stat fst;
  #endif
      
      FILE *f = NULL;
      uint32_t slen = 0;
      int errcode = -1;
      int result = -1;
      uint32_t length;
  #ifdef WIN32
      uint64_t total;
      uint64_t sent;
  #else
      off_t total;
      off_t sent;
  #endif
      
      mobilebackup2_error_t err;
      
      /* send path length */
      nlen = htobe32(pathlen);
      err = mobilebackup2_send_raw(mobilebackup2, (const char*)&nlen, sizeof(nlen), &bytes);
      if (err != MOBILEBACKUP2_E_SUCCESS) {
          goto leave_proto_err;
      }
      if (bytes != (uint32_t)sizeof(nlen)) {
          err = MOBILEBACKUP2_E_MUX_ERROR;
          goto leave_proto_err;
      }
      
      /* send path */
      err = mobilebackup2_send_raw(mobilebackup2, path, pathlen, &bytes);
      if (err != MOBILEBACKUP2_E_SUCCESS) {
          goto leave_proto_err;
      }
      if (bytes != pathlen) {
          err = MOBILEBACKUP2_E_MUX_ERROR;
          goto leave_proto_err;
      }
      
  #ifdef WIN32
      if (_stati64(localfile, &fst) < 0)
  #else
          if (stat(localfile, &fst) < 0)
  #endif
          {
              if (errno != ENOENT)
                  fprintf(stream_err, "%s: stat failed on '%s': %d\n", __func__, localfile, errno);
              errcode = errno;
              goto leave;
          }
      
      total = fst.st_size;
      
      char *format_size = string_format_size(total);
      PRINT_VERBOSE(1, stream_out, "Sending '%s' (%s)\n", path, format_size);
      PRINT_VERBOSE_DEBUG(1, "Sending '%s' (%s)\n", path, format_size);
      free(format_size);
      
      if (total == 0) {
          errcode = 0;
          goto leave;
      }
      
      f = fopen(localfile, "rb");
      if (!f) {
          fprintf(stream_err, "%s: Error opening local file '%s': %d\n", __func__, localfile, errno);
          errcode = errno;
          goto leave;
      }
      
      sent = 0;
      do {
          length = ((total-sent) < (long long)sizeof(buf)) ? (uint32_t)total-sent : (uint32_t)sizeof(buf);
          /* send data size (file size + 1) */
          nlen = htobe32(length+1);
          memcpy(buf, &nlen, sizeof(nlen));
          buf[4] = CODE_FILE_DATA;
          err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, 5, &bytes);
          if (err != MOBILEBACKUP2_E_SUCCESS) {
              goto leave_proto_err;
          }
          if (bytes != 5) {
              goto leave_proto_err;
          }
          
          /* send file contents */
          size_t r = fread(buf, 1, sizeof(buf), f);
          if (r <= 0) {
              printf("%s: read error\n", __func__);
              errcode = errno;
              goto leave;
          }
          err = mobilebackup2_send_raw(mobilebackup2, buf, r, &bytes);
          if (err != MOBILEBACKUP2_E_SUCCESS) {
              goto leave_proto_err;
          }
          if (bytes != (uint32_t)r) {
              printf("Error: sent only %d of %d bytes\n", bytes, (int)r);
              goto leave_proto_err;
          }
          sent += r;
      } while (sent < total);
      fclose(f);
      f = NULL;
      errcode = 0;
      
  leave:
      if (errcode == 0) {
          result = 0;
          nlen = 1;
          nlen = htobe32(nlen);
          memcpy(buf, &nlen, 4);
          buf[4] = CODE_SUCCESS;
          mobilebackup2_send_raw(mobilebackup2, buf, 5, &bytes);
      } else {
          if (!*errplist) {
              *errplist = plist_new_dict();
          }
          char *errdesc = strerror(errcode);
          mb2_multi_status_add_file_error(*errplist, path, errno_to_device_error(errcode), errdesc);
          
          length = strlen(errdesc);
          nlen = htobe32(length+1);
          memcpy(buf, &nlen, 4);
          buf[4] = CODE_ERROR_LOCAL;
          slen = 5;
          memcpy(buf+slen, errdesc, length);
          slen += length;
          err = mobilebackup2_send_raw(mobilebackup2, (const char*)buf, slen, &bytes);
          if (err != MOBILEBACKUP2_E_SUCCESS) {
              printf("could not send message\n");
          }
          if (bytes != slen) {
              printf("could only send %d from %d\n", bytes, slen);
          }
      }
      
  leave_proto_err:
      if (f)
          fclose(f);
      free(localfile);
      return result;
  }
  
  static void mb2_handle_send_files(FILE *stream_err, FILE *stream_out, mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  {
      uint32_t cnt;
      uint32_t i = 0;
      uint32_t sent;
      plist_t errplist = NULL;
      
      if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || (plist_array_get_size(message) < 2) || !backup_dir) return;
      
      plist_t files = plist_array_get_item(message, 1);
      cnt = plist_array_get_size(files);
      
      for (i = 0; i < cnt; i++) {
          plist_t val = plist_array_get_item(files, i);
          if (plist_get_node_type(val) != PLIST_STRING) {
              continue;
          }
          char *str = NULL;
          plist_get_string_val(val, &str);
          if (!str)
              continue;
          
          if (mb2_handle_send_file(stream_err, stream_out, mobilebackup2, backup_dir, str, &errplist) < 0) {
              free(str);
              //printf("Error when sending file '%s' to device\n", str);
              // TODO: perhaps we can continue, we've got a multi status response?!
              break;
          }
          free(str);
      }
      
      /* send terminating 0 dword */
      uint32_t zero = 0;
      mobilebackup2_send_raw(mobilebackup2, (char*)&zero, 4, &sent);
      
      if (!errplist) {
          plist_t emptydict = plist_new_dict();
          mobilebackup2_send_status_response(mobilebackup2, 0, NULL, emptydict);
          plist_free(emptydict);
      } else {
          mobilebackup2_send_status_response(mobilebackup2, -13, "Multi status", errplist);
          plist_free(errplist);
      }
  }
  
  static int mb2_receive_filename(FILE *stream_error, mobilebackup2_client_t mobilebackup2, char** filename)
  {
      uint32_t nlen = 0;
      uint32_t rlen = 0;
      
      do {
          nlen = 0;
          rlen = 0;
          mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &rlen);
          nlen = be32toh(nlen);
          
          if ((nlen == 0) && (rlen == 4)) {
              // a zero length means no more files to receive
              return 0;
          } else if(rlen == 0) {
              // device needs more time, waiting...
              continue;
          } else if (nlen > 4096) {
              // filename length is too large
              fprintf(stream_error, "ERROR: %s: too large filename length (%d)!\n", __func__, nlen);
              return 0;
          }
          
          if (*filename != NULL) {
              free(*filename);
              *filename = NULL;
          }
          
          *filename = (char*)malloc(nlen+1);
          
          rlen = 0;
          mobilebackup2_receive_raw(mobilebackup2, *filename, nlen, &rlen);
          if (rlen != nlen) {
              fprintf(stream_error, "ERROR: %s: could not read filename\n", __func__);
              return 0;
          }
          
          char* p = *filename;
          p[rlen] = 0;
          
          break;
      } while(1 && !quit_flag);
      
      return nlen;
  }
  
  static int mb2_handle_receive_files(FILE *stream_error, FILE *stream_out, node_progress_callback progress_callback, mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  {
      uint64_t backup_real_size = 0;
      uint64_t backup_total_size = 0;
      uint32_t blocksize;
      uint32_t bdone;
      uint32_t rlen;
      uint32_t nlen = 0;
      uint32_t r;
      char buf[32768];
      char *fname = NULL;
      char *dname = NULL;
      char *bname = NULL;
      char code = 0;
      char last_code = 0;
      plist_t node = NULL;
      FILE *f = NULL;
      unsigned int file_count = 0;
      
      if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 4 || !backup_dir) return 0;
      
      node = plist_array_get_item(message, 3);
      if (plist_get_node_type(node) == PLIST_UINT) {
          plist_get_uint_val(node, &backup_total_size);
      }
      if (backup_total_size > 0) {
          PRINT_VERBOSE(1, stream_out, "Receiving files\n");
          PRINT_VERBOSE_DEBUG(1, "Receiving files\n");
      }
      
      do {
          if (quit_flag)
              break;
          
          nlen = mb2_receive_filename(stream_error, mobilebackup2, &dname);
          if (nlen == 0) {
              break;
          }
          
          nlen = mb2_receive_filename(stream_error, mobilebackup2, &fname);
          if (!nlen) {
              break;
          }
          
          if (bname != NULL) {
              free(bname);
              bname = NULL;
          }
          
          bname = string_build_path(backup_dir, fname, NULL);
          
          if (fname != NULL) {
              free(fname);
              fname = NULL;
          }
          
          r = 0;
          nlen = 0;
          mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
          if (r != 4) {
              fprintf(stream_error, "ERROR: %s: could not receive code length!\n", __func__);
              break;
          }
          nlen = be32toh(nlen);
          
          last_code = code;
          code = 0;
          
          mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
          if (r != 1) {
              fprintf(stream_error, "ERROR: %s: could not receive code!\n", __func__);
              break;
          }
          
          /* TODO remove this */
          if ((code != CODE_SUCCESS) && (code != CODE_FILE_DATA) && (code != CODE_ERROR_REMOTE)) {
              PRINT_VERBOSE(1, stream_out, "Found new flag %02x\n", code);
              PRINT_VERBOSE_DEBUG(1, "Found new flag %02x\n", code);
          }
          
          remove_file(bname);
          f = fopen(bname, "wb");
          while (f && (code == CODE_FILE_DATA)) {
              blocksize = nlen-1;
              bdone = 0;
              rlen = 0;
              while (bdone < blocksize) {
                  if ((blocksize - bdone) < sizeof(buf)) {
                      rlen = blocksize - bdone;
                  } else {
                      rlen = sizeof(buf);
                  }
                  mobilebackup2_receive_raw(mobilebackup2, buf, rlen, &r);
                  if ((int)r <= 0) {
                      break;
                  }
                  fwrite(buf, 1, r, f);
                  bdone += r;
              }
              if (bdone == blocksize) {
                  backup_real_size += blocksize;
              }
              // if (backup_total_size > 0) {
              //     print_progress(progress_callback, backup_real_size, backup_total_size);
              // }
              if (quit_flag)
                  break;
              nlen = 0;
              mobilebackup2_receive_raw(mobilebackup2, (char*)&nlen, 4, &r);
              nlen = be32toh(nlen);
              if (nlen > 0) {
                  last_code = code;
                  mobilebackup2_receive_raw(mobilebackup2, &code, 1, &r);
              } else {
                  break;
              }
          }
          if (f) {
              fclose(f);
              file_count++;
          } else {
              fprintf(stream_error, "Error opening '%s' for writing: %s\n", bname, strerror(errno));
          }
          if (nlen == 0) {
              break;
          }
          
          /* check if an error message was received */
          if (code == CODE_ERROR_REMOTE) {
              /* error message */
              char *msg = (char*)malloc(nlen);
              mobilebackup2_receive_raw(mobilebackup2, msg, nlen-1, &r);
              msg[r] = 0;
              /* If sent using CODE_FILE_DATA, end marker will be CODE_ERROR_REMOTE which is not an error! */
              if (last_code != CODE_FILE_DATA) {
                  fprintf(stream_error, "\nReceived an error message from device: %s\n", msg);
              }
              free(msg);
          }
      } while (1);
      
      if (fname != NULL)
          free(fname);
      
      /* if there are leftovers to read, finish up cleanly */
      if ((int)nlen-1 > 0) {
          PRINT_VERBOSE(1, stream_out, "\nDiscarding current data hunk.\n");
          PRINT_VERBOSE_DEBUG(1, "\nDiscarding current data hunk.\n");
          fname = (char*)malloc(nlen-1);
          mobilebackup2_receive_raw(mobilebackup2, fname, nlen-1, &r);
          free(fname);
          remove_file(bname);
      }
      
      /* clean up */
      if (bname != NULL)
          free(bname);
      
      if (dname != NULL)
          free(dname);
      
      // TODO error handling?!
      plist_t empty_plist = plist_new_dict();
      mobilebackup2_send_status_response(mobilebackup2, 0, NULL, empty_plist);
      plist_free(empty_plist);
      
      return file_count;
  }
  
  static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  {
      if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
      
      plist_t node = plist_array_get_item(message, 1);
      char *str = NULL;
      if (plist_get_node_type(node) == PLIST_STRING) {
          plist_get_string_val(node, &str);
      }
      if (!str) {
          printf("ERROR: Malformed DLContentsOfDirectory message\n");
          // TODO error handling
          return;
      }
      
      char *path = string_build_path(backup_dir, str, NULL);
      free(str);
      
      plist_t dirlist = plist_new_dict();
      
      DIR* cur_dir = opendir(path);
      if (cur_dir) {
          struct dirent* ep;
          while ((ep = readdir(cur_dir))) {
              if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
                  continue;
              }
              char *fpath = string_build_path(path, ep->d_name, NULL);
              if (fpath) {
                  plist_t fdict = plist_new_dict();
                  struct stat st;
                  stat(fpath, &st);
                  const char *ftype = "DLFileTypeUnknown";
                  if (S_ISDIR(st.st_mode)) {
                      ftype = "DLFileTypeDirectory";
                  } else if (S_ISREG(st.st_mode)) {
                      ftype = "DLFileTypeRegular";
                  }
                  plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
                  plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
                  plist_dict_set_item(fdict, "DLFileModificationDate",
                                      plist_new_date(st.st_mtime - MAC_EPOCH, 0));
                  
                  plist_dict_set_item(dirlist, ep->d_name, fdict);
                  free(fpath);
              }
          }
          closedir(cur_dir);
      }
      free(path);
      
      /* TODO error handling */
      mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, 0, NULL, dirlist);
      plist_free(dirlist);
      if (err != MOBILEBACKUP2_E_SUCCESS) {
          printf("Could not send status response, error %d\n", err);
      }
  }
  
  static void mb2_handle_make_directory(mobilebackup2_client_t mobilebackup2, plist_t message, const char *backup_dir)
  {
      if (!message || (plist_get_node_type(message) != PLIST_ARRAY) || plist_array_get_size(message) < 2 || !backup_dir) return;
      
      plist_t dir = plist_array_get_item(message, 1);
      char *str = NULL;
      int errcode = 0;
      char *errdesc = NULL;
      plist_get_string_val(dir, &str);
      
      char *newpath = string_build_path(backup_dir, str, NULL);
      free(str);
      
      if (mkdir_with_parents(newpath, 0755) < 0) {
          errdesc = strerror(errno);
          if (errno != EEXIST) {
              printf("mkdir: %s (%d)\n", errdesc, errno);
          }
          errcode = errno_to_device_error(errno);
      }
      free(newpath);
      mobilebackup2_error_t err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, NULL);
      if (err != MOBILEBACKUP2_E_SUCCESS) {
          printf("Could not send status response, error %d\n", err);
      }
  }
  
  static void mb2_copy_file_by_path(const char *src, const char *dst)
  {
      FILE *from, *to;
      char buf[BUFSIZ];
      size_t length;
      
      /* open source file */
      if ((from = fopen(src, "rb")) == NULL) {
          printf("Cannot open source path '%s'.\n", src);
          return;
      }
      
      /* open destination file */
      if ((to = fopen(dst, "wb")) == NULL) {
          printf("Cannot open destination file '%s'.\n", dst);
          return;
      }
      
      /* copy the file */
      while ((length = fread(buf, 1, BUFSIZ, from)) != 0) {
          fwrite(buf, 1, length, to);
      }
      
      if(fclose(from) == EOF) {
          printf("Error closing source file.\n");
      }
      
      if(fclose(to) == EOF) {
          printf("Error closing destination file.\n");
      }
  }
  
  static void mb2_copy_directory_by_path(const char *src, const char *dst)
  {
      if (!src || !dst) {
          return;
      }
      
      struct stat st;
      
      /* if src does not exist */
      if ((stat(src, &st) < 0) || !S_ISDIR(st.st_mode)) {
          printf("ERROR: Source directory does not exist '%s': %s (%d)\n", src, strerror(errno), errno);
          return;
      }
      
      /* if dst directory does not exist */
      if ((stat(dst, &st) < 0) || !S_ISDIR(st.st_mode)) {
          /* create it */
          if (mkdir_with_parents(dst, 0755) < 0) {
              printf("ERROR: Unable to create destination directory '%s': %s (%d)\n", dst, strerror(errno), errno);
              return;
          }
      }
      
      /* loop over src directory contents */
      DIR *cur_dir = opendir(src);
      if (cur_dir) {
          struct dirent* ep;
          while ((ep = readdir(cur_dir))) {
              if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) {
                  continue;
              }
              char *srcpath = string_build_path(src, ep->d_name, NULL);
              char *dstpath = string_build_path(dst, ep->d_name, NULL);
              if (srcpath && dstpath) {
                  /* copy file */
                  mb2_copy_file_by_path(srcpath, dstpath);
                  
                  free(srcpath);
                  free(dstpath);
              }
          }
          closedir(cur_dir);
      }
  }
  
  #ifdef WIN32
  #define BS_CC '\b'
  #define my_getch getch
  #else
  #define BS_CC 0x7f
  static int my_getch(void)
  {
      struct termios oldt, newt;
      int ch;
      tcgetattr(STDIN_FILENO, &oldt);
      newt = oldt;
      newt.c_lflag &= ~(ICANON | ECHO);
      tcsetattr(STDIN_FILENO, TCSANOW, &newt);
      ch = getchar();
      tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
      return ch;
  }
  #endif
  
  static void get_hidden_input(char *buf, int maxlen)
  {
      int pwlen = 0;
      int c;
      
      while ((c = my_getch())) {
          if ((c == '\r') || (c == '\n')) {
              break;
          } else if (isprint(c)) {
              if (pwlen < maxlen-1)
                  buf[pwlen++] = c;
              fputc('*', stderr);
          } else if (c == BS_CC) {
              if (pwlen > 0) {
                  fputs("\b \b", stderr);
                  pwlen--;
              }
          }
      }
      buf[pwlen] = 0;
  }
  
  static char* ask_for_password(const char* msg, int type_again)
  {
      char pwbuf[256];
      
      fprintf(stderr, "%s: ", msg);
      fflush(stderr);
      get_hidden_input(pwbuf, 256);
      fputc('\n', stderr);
      
      if (type_again) {
          char pwrep[256];
          
          fprintf(stderr, "%s (repeat): ", msg);
          fflush(stderr);
          get_hidden_input(pwrep, 256);
          fputc('\n', stderr);
          
          if (strcmp(pwbuf, pwrep) != 0) {
              printf("ERROR: passwords don't match\n");
              return NULL;
          }
      }
      return strdup(pwbuf);
  }
  
  /**
   * signal handler function for cleaning up properly
   */
  static void clean_exit(int sig)
  {
      fprintf(stderr, "Exiting...\n");
      quit_flag++;
  }
  
  static double minimum_d (double a, double b) {
       if (a < b) {
            return a;
      } else {
           return b; 
      }
  }
  
  static bool nearly_equal(double a, double b, double epsilon) {
      double abs_a = fabs(a);
      double abs_b = fabs(b);
      double diff = fabs(a - b);
      if (a == b) { // shortcut, handles infinities
  		return true;
      } else if(a == 0 || b == 0 || diff < DBL_MIN){
          // a or b is zero or both are extremely close to it
          // relative error is less meaningful here
          return diff < (epsilon * DBL_MIN);
      } else {
          // compute relative 0
          return diff / minimum_d(abs_a + abs_b, DBL_MAX) < epsilon;
      }
  }
  
  #define DEVICE_VERSION(maj, min, patch) (((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF))
  
  void put_device_passcode(node_progress_callback progress_callback, bool must_set_it) {
      FILE *stream_progress = tmpfile();
      fprintf(stream_progress, "%s", must_set_it ? "true" : "false");
      progress_callback(stream_progress);
  }
  
  void idevice_backup2(struct idevice_backup2_options options, FILE *stream_err, FILE *stream_out, node_progress_callback progress_callback)
  {
      idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
      lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
      int i;
      char* udid = NULL;
      char* source_udid = NULL;
      int use_network = 0;
      lockdownd_service_descriptor_t service = NULL;
      char *command = NULL;
      int cmd = -1;
      int cmd_flags = 0;
      int is_full_backup = 0;
      int result_code = -1;
      char* backup_directory = NULL;
      int interactive_mode = 0;
      char* backup_password = NULL;
      char* newpw = NULL;
      struct stat st;
      plist_t node_tmp = NULL;
      plist_t info_plist = NULL;
      plist_t opts = NULL;
      mobilebackup2_error_t err;
      
      /* we need to exit cleanly on running backups and restores or we cause havok */
      signal(SIGINT, clean_exit);
      signal(SIGTERM, clean_exit);
  #ifndef WIN32
      signal(SIGQUIT, clean_exit);
      signal(SIGPIPE, SIG_IGN);
  #endif
      
      if (options.debug) { 
          idevice_set_debug_level(1);
      } else {
          verbose = 0;
      }
      if (options.udid != NULL) udid = strdup(options.udid);
      if (options.source != NULL) source_udid = strdup(options.source);
      if (options.network) use_network = 1;
      if (options.command) command = options.command;
      if (options.interactive) interactive_mode = 1;
      if (options.backup_directory != NULL) {
          if(backup_directory) {
              free(backup_directory);
              backup_directory = NULL;
          }
          backup_directory = strdup(options.backup_directory);
      };
  
      if (!strcmp(command, "backup")) {
          cmd = CMD_BACKUP;
          if (options.backup.full) cmd_flags |= CMD_FLAG_FORCE_FULL_BACKUP;
      } else if (!strcmp(command, "restore")) {
          cmd = CMD_RESTORE;
          if (options.restore.system) cmd_flags |= CMD_FLAG_RESTORE_SYSTEM_FILES;
          if (options.restore.reboot) cmd_flags |= CMD_FLAG_RESTORE_REBOOT;
          if (options.restore.copy) cmd_flags |= CMD_FLAG_RESTORE_COPY_BACKUP;
          if (options.restore.settings) cmd_flags |= CMD_FLAG_RESTORE_SETTINGS;
          if (options.restore.remove) cmd_flags |= CMD_FLAG_RESTORE_REMOVE_ITEMS;
          if (options.restore.skip_apps) cmd_flags |= CMD_FLAG_RESTORE_SKIP_APPS;
          if (options.restore.password != NULL) {
              if (backup_password) free(backup_password);
              backup_password = strdup(options.restore.password);
          }
      } else if (!strcmp(command, "info")) {
          cmd = CMD_INFO;
          verbose = 0;
      } else if (!strcmp(command, "list")) {
          cmd = CMD_LIST;
          verbose = 0;
      } else if (!strcmp(command, "unback")) {
          cmd = CMD_UNBACK;
      } else if (!strcmp(command, "encryption")) {
          cmd = CMD_CHANGEPW;
          if (options.encryption.enable) {
              cmd_flags |= CMD_FLAG_ENCRYPTION_ENABLE;
          } else {
              cmd_flags |= CMD_FLAG_ENCRYPTION_DISABLE;
          }
          
          if (newpw) {
              free(newpw);
              newpw = NULL;
          }
          if (backup_password) {
              free(backup_password);
              backup_password = NULL;
          }
          
          if (options.encryption.password != NULL) {
              if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
                  newpw = strdup(options.encryption.password);
              } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
                  backup_password = strdup(options.encryption.password);
              }
          }
      } else if (!strcmp(command, "changepw")) {
          cmd = CMD_CHANGEPW;
          cmd_flags |= CMD_FLAG_ENCRYPTION_CHANGEPW;
          if (newpw) {
              free(newpw);
              newpw = NULL;
          }
          if (backup_password) {
              free(backup_password);
              backup_password = NULL;
          }
          
          if(options.changepw.backup_password != NULL && options.changepw.newpw != NULL) {
              backup_password = strdup(options.changepw.backup_password);
              newpw = strdup(options.changepw.newpw);
          } else {
              fprintf(stream_err, "Old and new passwords have to be passed as arguments for the changepw command\n");
              return;
          }
      } else if (!strcmp(command, "cloud")) {
          cmd = CMD_CLOUD;
          if (options.cloud.enable) {
              cmd_flags |= CMD_FLAG_CLOUD_ENABLE;
          } else {
              cmd_flags |= CMD_FLAG_CLOUD_DISABLE;
          }
      } else {
          fprintf(stream_err, "Invalid command '%s' provided", command);
          return;
      }
      
      /* verify options */
      if (cmd == -1) {
          fprintf(stream_err, "No command specified.\n");
          return;
      }
      
      if (cmd == CMD_CHANGEPW || cmd == CMD_CLOUD) {
          backup_directory = (char*)".this_folder_is_not_present_on_purpose";
      } else {
          if (backup_directory == NULL) {
              PRINT_VERBOSE_DEBUG(1, "No backup directory specified, using default.\n");
              fprintf(stream_err, "No target backup directory specified.\n");
              return;
          }
          
          /* verify if passed backup directory exists */
          if (stat(backup_directory, &st) != 0) {
              fprintf(stream_err, "ERROR: Backup directory \"%s\" does not exist!\n", backup_directory);
              return;
          }
      }
      
      idevice_t device = NULL;
      ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
      if (ret != IDEVICE_E_SUCCESS) {
  		if (udid) {
  			if(use_network) { 
                  fprintf(stream_err, "ERROR: No device found in network with udid %s.\n", udid); 
              } else {
                  fprintf(stream_err, "ERROR: No device found with udid %s.\n, is it plugged in?", udid); 
              }
  		} else {
  			fprintf(stream_err, "ERROR: No device found.\n");
  		}
  		return;
  	}
  
  	if (!udid) {
  		idevice_get_udid(device, &udid);
  	}
      
      if (!source_udid) {
          source_udid = strdup(udid);
      }
      
      uint8_t is_encrypted = 0;
      char *info_path = NULL;
      if (cmd == CMD_CHANGEPW) {
          if (!interactive_mode && !backup_password && !newpw) {
              idevice_free(device);
              fprintf(stream_err, "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");
              return;
          }
      } else if (cmd != CMD_CLOUD) {
          /* backup directory must contain an Info.plist */
          info_path = string_build_path(backup_directory, source_udid, "Info.plist", NULL);
          if (cmd == CMD_RESTORE || cmd == CMD_UNBACK) {
              if (stat(info_path, &st) != 0) {
                  idevice_free(device);
                  free(info_path);
                  fprintf(stream_err, "ERROR: Backup directory \"%s\" is invalid. No Info.plist found for UDID %s.\n", backup_directory, source_udid);
                  return;
              }
              char* manifest_path = string_build_path(backup_directory, source_udid, "Manifest.plist", NULL);
              if (stat(manifest_path, &st) != 0) {
                  free(info_path);
              }
              plist_t manifest_plist = NULL;
              plist_read_from_filename(&manifest_plist, manifest_path);
              if (!manifest_plist) {
                  idevice_free(device);
                  free(info_path);
                  free(manifest_path);
                  fprintf(stream_err, "ERROR: Backup directory \"%s\" is invalid. No Manifest.plist found for UDID %s.\n", backup_directory, source_udid);
                  return;
              }
              node_tmp = plist_dict_get_item(manifest_plist, "IsEncrypted");
              if (node_tmp && (plist_get_node_type(node_tmp) == PLIST_BOOLEAN)) {
                  plist_get_bool_val(node_tmp, &is_encrypted);
              }
              plist_free(manifest_plist);
              free(manifest_path);
          }
          PRINT_VERBOSE(1, stream_out, "Backup directory is \"%s\"\n", backup_directory);
          PRINT_VERBOSE_DEBUG(1, "Backup directory is \"%s\"\n", backup_directory);
      }
      
      if (cmd != CMD_CLOUD && is_encrypted) {
          PRINT_VERBOSE(1, stream_out, "This is an encrypted backup.\n");
          PRINT_VERBOSE_DEBUG(1, "This is an encrypted backup.\n");
          if (backup_password == NULL) {
              if (interactive_mode) {
                  backup_password = ask_for_password("Enter backup password", 0);
              }
              if (!backup_password || (strlen(backup_password) == 0)) {
                  if (backup_password) {
                      free(backup_password);
                  }
                  idevice_free(device);
                  if (cmd == CMD_RESTORE) {
                      fprintf(stream_err, "ERROR: a backup password is required to restore an encrypted backup. Cannot continue.\n");
                  } else if (cmd == CMD_UNBACK) {
                      fprintf(stream_err, "ERROR: a backup password is required to unback an encrypted backup. Cannot continue.\n");
                  }
                  return;
              }
          }
      }
  
  
      
      lockdownd_client_t lockdown = NULL;
      if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lockdown, "idevicebackup2"))) {
          PRINT_VERBOSE_DEBUG(1, "ERROR: Could not connect to lockdownd., error code %d\n", ldret);
          fprintf(stream_err, "ERROR: Could not connect to lockdownd, error code %d\n", ldret);
          idevice_free(device);
          return;
      }
  
  
  
      uint8_t willEncrypt = 0;
      node_tmp = NULL;
      lockdownd_get_value(lockdown, "com.apple.mobile.backup", "WillEncrypt", &node_tmp);
      if(node_tmp) {
          if(plist_get_node_type(node_tmp) == PLIST_BOOLEAN) {
              plist_get_bool_val(node_tmp, &willEncrypt);
          }
          plist_free(node_tmp);
  		node_tmp = NULL;
      }
  
  
  
  	/* get ProductVersion */
  	char *product_version = NULL;
  	int device_version = 0;
  	node_tmp = NULL;
  	lockdownd_get_value(lockdown, NULL, "ProductVersion", &node_tmp);
  	if (node_tmp) {
  		if (plist_get_node_type(node_tmp) == PLIST_STRING) {
  			plist_get_string_val(node_tmp, &product_version);
  		}
  		plist_free(node_tmp);
  		node_tmp = NULL;
  	}
  	if (product_version) {
  		int vers[3] = { 0, 0, 0 };
  		if (sscanf(product_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
  			device_version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
  		}
  	}
  
  
      
      /* start notification_proxy */
      np_client_t np = NULL;
      ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
  
      if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
          np_error_t np_error = np_client_new(device, service, &np);
          if(np_error != NP_E_SUCCESS) {
              PRINT_VERBOSE_DEBUG(1, "ERROR: Could not create client for service %s, error code %d\n", NP_SERVICE_NAME, np_error);
              PRINT_VERBOSE(1, stream_out, "ERROR: Could not create client for service %s, error code %d\n", NP_SERVICE_NAME, np_error);
          }
  
          np_set_notify_callback(np, notify_cb, NULL);
  
          const char *noties[5] = {
              NP_SYNC_CANCEL_REQUEST,
              NP_SYNC_SUSPEND_REQUEST,
              NP_SYNC_RESUME_REQUEST,
              NP_BACKUP_DOMAIN_CHANGED,
              NULL
          };
          np_observe_notifications(np, noties);
      } else {
          PRINT_VERBOSE_DEBUG(1, "ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
          fprintf(stream_err, "ERROR: Could not start service %s.\n", NP_SERVICE_NAME);
      }
  
  
      
      afc_client_t afc = NULL;
      if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
          /* start AFC, we need this for the lock file */
          service->port = 0;
          service->ssl_enabled = 0;
          ldret = lockdownd_start_service(lockdown, AFC_SERVICE_NAME, &service);
  
          if ((ldret == LOCKDOWN_E_SUCCESS) && service->port) {
              afc_client_new(device, service, &afc);
          }
  
  
          if(ldret != LOCKDOWN_E_SUCCESS) {
              PRINT_VERBOSE_DEBUG(1, "ERROR: Could not start service %s.\n due to %d", AFC_SERVICE_NAME, ldret);
              //fprintf(stream_err, "ERROR: Could not start service %s.\n", AFC_SERVICE_NAME);
          }
  
      }
  
  
      
      if (service) {
          lockdownd_service_descriptor_free(service);
          service = NULL;
      }
      
  
      /* start mobilebackup service and retrieve port */
      mobilebackup2_client_t mobilebackup2 = NULL;
      ldret = lockdownd_start_service_with_escrow_bag(lockdown, MOBILEBACKUP2_SERVICE_NAME, &service);
      lockdownd_client_free(lockdown);
  	lockdown = NULL;
  
      if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
          PRINT_VERBOSE(1, stream_out, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
          PRINT_VERBOSE_DEBUG(1, "Started \"%s\" service on port %d.\n", MOBILEBACKUP2_SERVICE_NAME, service->port);
          err = mobilebackup2_client_new(device, service, &mobilebackup2);
  
          if (err != MOBILEBACKUP2_E_SUCCESS) {
              PRINT_VERBOSE(1, stream_out, "Could not create a new client, error code %d\n", err);
              PRINT_VERBOSE_DEBUG(1, "Could not create a new client, error code %d\n", err);
              cmd = CMD_LEAVE;
              goto checkpoint;
          }
          
          if (service) {
              lockdownd_service_descriptor_free(service);
              service = NULL;
          }
          
          /* send Hello message */
          double local_versions[2] = {2.0, 2.1};
          double remote_version = 0.0;
          err = mobilebackup2_version_exchange(mobilebackup2, local_versions, 2, &remote_version);
          if (err != MOBILEBACKUP2_E_SUCCESS) {
              printf("Could not perform backup protocol version exchange, error code %d\n", err);
              fprintf(stream_err, "Could not perform backup protocol version exchange, error code %d\n", err);
              cmd = CMD_LEAVE;
              goto checkpoint;
          }
          
          PRINT_VERBOSE(1, stream_out, "Negotiated Protocol Version %.1f\n", remote_version);
          PRINT_VERBOSE_DEBUG(1, "Negotiated Protocol Version %.1f\n", remote_version);
          
          /* check abort conditions */
          if (quit_flag > 0) {
              PRINT_VERBOSE(1, stream_out, "Aborting as requested by user...\n");
              PRINT_VERBOSE_DEBUG(1, "Aborting as requested by user...\n");
              cmd = CMD_LEAVE;
              goto checkpoint;
          }
          
          /* verify existing Info.plist */
          if (info_path && (stat(info_path, &st) == 0) && cmd != CMD_CLOUD) {
              PRINT_VERBOSE(1, stream_out, "Reading Info.plist from backup.\n");
              PRINT_VERBOSE_DEBUG(1, "Reading Info.plist from backup.\n");
              plist_read_from_filename(&info_plist, info_path);
              
              if (!info_plist) {
                  printf("Could not read Info.plist\n");
                  is_full_backup = 1;
              }
          } else {
              if (cmd == CMD_RESTORE) {
                  printf("Aborting restore. Info.plist is missing.\n");
                  cmd = CMD_LEAVE;
              } else {
                  is_full_backup = 1;
              }
          }
  
          uint64_t lockfile = 0;
          if (cmd == CMD_BACKUP || cmd == CMD_RESTORE) {
              do_post_notification(device, NP_SYNC_WILL_START);
              afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
          }
  
          if (lockfile) {
              afc_error_t aerr;
              do_post_notification(device, NP_SYNC_LOCK_REQUEST);
              for (i = 0; i < LOCK_ATTEMPTS; i++) {
                  aerr = afc_file_lock(afc, lockfile, AFC_LOCK_EX);
                  if (aerr == AFC_E_SUCCESS) {
                      do_post_notification(device, NP_SYNC_DID_START);
                      break;
                  } else if (aerr == AFC_E_OP_WOULD_BLOCK) {
                      usleep(LOCK_WAIT);
                      continue;
                  } else {
                      PRINT_VERBOSE_DEBUG(1,"ERROR: could not lock file! error code: %d\n", aerr);
                      fprintf(stderr, "ERROR: could not lock file! error code: %d\n", aerr);
                      afc_file_close(afc, lockfile);
                      lockfile = 0;
                      cmd = CMD_LEAVE;
                  }
              }
              if (i == LOCK_ATTEMPTS) {
                  PRINT_VERBOSE_DEBUG(1, "ERROR: timeout while locking for sync\n");
                  fprintf(stderr, "ERROR: timeout while locking for sync\n");
                  afc_file_close(afc, lockfile);
                  lockfile = 0;
                  cmd = CMD_LEAVE;
              }
          }
  
          
      checkpoint:
          
          switch(cmd) {
              case CMD_CLOUD:
                  opts = plist_new_dict();
                  plist_dict_set_item(opts, "CloudBackupState", plist_new_bool(cmd_flags & CMD_FLAG_CLOUD_ENABLE ? 1: 0));
                  err = mobilebackup2_send_request(mobilebackup2, "EnableCloudBackup", udid, source_udid, opts);
                  plist_free(opts);
                  opts = NULL;
                  if (err != MOBILEBACKUP2_E_SUCCESS) {
                      fprintf(stream_err, "Error setting cloud backup state on device, error code %d\n", err);
                      cmd = CMD_LEAVE;
                  }
                  break;
              case CMD_BACKUP:
                  PRINT_VERBOSE(1, stream_out, "Starting backup...\n");
                  PRINT_VERBOSE_DEBUG(1, "Starting backup...\n");
                  
                  /* make sure backup device sub-directory exists */
                  char* devbackupdir = string_build_path(backup_directory, source_udid, NULL);
                  __mkdir(devbackupdir, 0755);
                  free(devbackupdir);
                  
                  if (strcmp(source_udid, udid) != 0) {
                      /* handle different source backup directory */
                      // make sure target backup device sub-directory exists
                      devbackupdir = string_build_path(backup_directory, udid, NULL);
                      __mkdir(devbackupdir, 0755);
                      free(devbackupdir);
                      
                      // use Info.plist path in target backup folder */
                      free(info_path);
                      info_path = string_build_path(backup_directory, udid, "Info.plist", NULL);
                  }
                  
                  /* TODO: check domain com.apple.mobile.backup key RequiresEncrypt and WillEncrypt with lockdown */
                  /* TODO: verify battery on AC enough battery remaining */
                  
                  /* re-create Info.plist (Device infos, IC-Info.sidb, photos, app_ids, iTunesPrefs) */
                  if (info_plist) {
                      plist_free(info_plist);
                      info_plist = NULL;
                  }
                  info_plist = mobilebackup_factory_info_plist_new(udid, device, afc);
                  if (!info_plist) {
                      fprintf(stream_err, "Failed to generate Info.plist - aborting\n");
                      cmd = CMD_LEAVE;
                  }
                  remove_file(info_path);
                  plist_write_to_filename(info_plist, info_path, PLIST_FORMAT_XML);
                  free(info_path);
                  
                  plist_free(info_plist);
                  info_plist = NULL;
                  
                  if (cmd_flags & CMD_FLAG_FORCE_FULL_BACKUP) {
                      PRINT_VERBOSE(1, stream_out, "Enforcing full backup from device.\n");
                      PRINT_VERBOSE_DEBUG(1, "Enforcing full backup from device.\n");
                      opts = plist_new_dict();
                      plist_dict_set_item(opts, "ForceFullBackup", plist_new_bool(1));
                  }
                  /* request backup from device with manifest from last backup */
                  if (willEncrypt) {
                      PRINT_VERBOSE(1, stream_out, "Backup will be encrypted.\n");
                      PRINT_VERBOSE_DEBUG(1, "Backup will be encrypted.\n");
                  } else {
                      PRINT_VERBOSE(1, stream_out, "Backup will be unencrypted.\n");
                      PRINT_VERBOSE_DEBUG(1, "Backup will be unencrypted.\n");
                  }
                  PRINT_VERBOSE(1, stream_out, "Requesting backup from device...\n");
                  PRINT_VERBOSE_DEBUG(1, "Requesting backup from device...\n");
                  err = mobilebackup2_send_request(mobilebackup2, "Backup", udid, source_udid, opts);
                  if (opts)
                      plist_free(opts);
                  if (err == MOBILEBACKUP2_E_SUCCESS) {
                      if (is_full_backup) {
                          PRINT_VERBOSE(1, stream_out, "Full backup mode.\n");
                          PRINT_VERBOSE_DEBUG(1, "Full backup mode.\n");
                      }	else {
                          PRINT_VERBOSE(1, stream_out, "Incremental backup mode.\n");
                          PRINT_VERBOSE_DEBUG(1, "Incremental backup mode.\n");
                      }
                  } else {
                      if (err == MOBILEBACKUP2_E_BAD_VERSION) {
                          fprintf(stream_err, "ERROR: Could not start backup process: backup protocol version mismatch!\n");
                      } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
                          fprintf(stream_err, "ERROR: Could not start backup process: device refused to start the backup process.\n");
                      } else {
                          fprintf(stream_err, "ERROR: Could not start backup process: unspecified error occured\n");
                      }
                      cmd = CMD_LEAVE;
                  }
                  break;
              case CMD_RESTORE:
                  /* TODO: verify battery on AC enough battery remaining */
                  
                  /* verify if Status.plist says we read from an successful backup */
                  if (!mb2_status_check_snapshot_state(backup_directory, source_udid, "finished")) {
                      fprintf(stream_err, "ERROR: Cannot ensure we restore from a successful backup. Aborting.\n");
                      cmd = CMD_LEAVE;
                      break;
                  }
                  
                  PRINT_VERBOSE(1, stream_out, "Starting Restore...\n");
                  PRINT_VERBOSE_DEBUG(1, "Starting Restore...\n");
                  
                  opts = plist_new_dict();
                  plist_dict_set_item(opts, "RestoreSystemFiles", plist_new_bool(cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES));
                  PRINT_VERBOSE(1, stream_out, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "YES" : "NO"));
                  PRINT_VERBOSE_DEBUG(1, "Restoring system files: %s\n", (cmd_flags & CMD_FLAG_RESTORE_SYSTEM_FILES ? "Yes":"No"));
                  if ((cmd_flags & CMD_FLAG_RESTORE_REBOOT) == 0)
                      plist_dict_set_item(opts, "RestoreShouldReboot", plist_new_bool(0));
                  PRINT_VERBOSE(1, stream_out, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_REBOOT ? "YES" : "NO"));
                  PRINT_VERBOSE_DEBUG(1, "Rebooting after restore: %s\n", (cmd_flags & CMD_FLAG_RESTORE_REBOOT ? "Yes":"No"));
                  if ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0)
                      plist_dict_set_item(opts, "RestoreDontCopyBackup", plist_new_bool(1));
                  PRINT_VERBOSE(1, stream_out, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "YES" : "NO"));
                  PRINT_VERBOSE_DEBUG(1, "Don't copy backup: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_COPY_BACKUP) == 0 ? "Yes":"No"));
                  plist_dict_set_item(opts, "RestorePreserveSettings", plist_new_bool((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0));
                  PRINT_VERBOSE(1, stream_out, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "YES" : "NO"));
                  PRINT_VERBOSE_DEBUG(1, "Preserve settings of device: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_SETTINGS) == 0 ? "Yes":"No"));
                  if (cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS)
                      plist_dict_set_item(opts, "RemoveItemsNotRestored", plist_new_bool(1));
                  PRINT_VERBOSE(1, stream_out, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
                  PRINT_VERBOSE_DEBUG(1, "Remove items that are not restored: %s\n", ((cmd_flags & CMD_FLAG_RESTORE_REMOVE_ITEMS) ? "Yes":"No"));
                  if (backup_password != NULL) {
                      plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
                  }
                  PRINT_VERBOSE(1, stream_out, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
                  PRINT_VERBOSE_DEBUG(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
  
                  if (cmd_flags & CMD_FLAG_RESTORE_SKIP_APPS) {
                      PRINT_VERBOSE(1, stream_out, "Not writing RestoreApplications.plist - apps will not be re-installed after restore\n");
                      PRINT_VERBOSE_DEBUG(1, "Not writing RestoreApplications.plist - apps will not be re-installed after restore\n");
                  } else {
                      /* Write /iTunesRestore/RestoreApplications.plist so that the device will start
                      * restoring applications once the rest of the restore process is finished */
                      if (write_restore_applications(info_plist, afc) < 0) {
                          cmd = CMD_LEAVE;
                          break;
                      } else {
                          PRINT_VERBOSE(1, stream_out, "Wrote RestoreApplications.plist\n");
                          PRINT_VERBOSE_DEBUG(1, "Wrote RestoreApplications.plist\n");
                      }
                  }
                  
                  err = mobilebackup2_send_request(mobilebackup2, "Restore", udid, source_udid, opts);
                  plist_free(opts);
                  if (err != MOBILEBACKUP2_E_SUCCESS) {
                      if (err == MOBILEBACKUP2_E_BAD_VERSION) {
                          fprintf(stream_err, "ERROR: Could not start restore process: backup protocol version mismatch!\n");
                      } else if (err == MOBILEBACKUP2_E_REPLY_NOT_OK) {
                          fprintf(stream_err, "ERROR: Could not start restore process: device refused to start the restore process.\n");
                      } else {
                          fprintf(stream_err, "ERROR: Could not start restore process: unspecified error occured\n");
                      }
                      cmd = CMD_LEAVE;
                  }
                  break;
              case CMD_INFO:
                  PRINT_VERBOSE(1, stream_out, "Requesting backup info from device...\n");
                  PRINT_VERBOSE_DEBUG(1, "Requesting backup info from device...\n");
                  err = mobilebackup2_send_request(mobilebackup2, "Info", udid, source_udid, NULL);
                  if (err != MOBILEBACKUP2_E_SUCCESS) {
                      fprintf(stream_err, "Error requesting backup info from device, error code %d\n", err);
                      cmd = CMD_LEAVE;
                  }
                  break;
              case CMD_LIST:
                  PRINT_VERBOSE(1, stream_out, "Requesting backup list from device...\n");
                  PRINT_VERBOSE_DEBUG(1, "Requesting backup list from device...\n");
                  err = mobilebackup2_send_request(mobilebackup2, "List", udid, source_udid, NULL);
                  if (err != MOBILEBACKUP2_E_SUCCESS) {
                      fprintf(stream_err, "Error requesting backup list from device, error code %d\n", err);
                      cmd = CMD_LEAVE;
                  }
                  break;
              case CMD_UNBACK:
                  PRINT_VERBOSE(1, stream_out, "Starting to unpack backup...\n");
                  PRINT_VERBOSE_DEBUG(1, "Starting to unpack backup...\n");
                  if (backup_password != NULL) {
                      opts = plist_new_dict();
                      plist_dict_set_item(opts, "Password", plist_new_string(backup_password));
                  }
                  PRINT_VERBOSE(1, stream_out, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
                  PRINT_VERBOSE_DEBUG(1, "Backup password: %s\n", (backup_password == NULL ? "No":"Yes"));
                  err = mobilebackup2_send_request(mobilebackup2, "Unback", udid, source_udid, opts);
                  if (backup_password !=NULL) {
                      plist_free(opts);
                  }
                  if (err != MOBILEBACKUP2_E_SUCCESS) {
                      fprintf(stream_err, "Error requesting unback operation from device, error code %d\n", err);
                      cmd = CMD_LEAVE;
                  }
                  break;
              case CMD_CHANGEPW:
                  opts = plist_new_dict();
                  plist_dict_set_item(opts, "TargetIdentifier", plist_new_string(udid));
                  if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
                      if (!willEncrypt) {
                          if (!newpw) {
                              newpw = ask_for_password("Enter new backup password", 1);
                          }
                          if (!newpw) {
                              fprintf(stream_err, "No backup password given. Aborting.\n");
                          }
                      } else {
                          fprintf(stream_err, "ERROR: Backup encryption is already enabled. Aborting.\n");
                          cmd = CMD_LEAVE;
                          if (newpw) {
                              free(newpw);
                              newpw = NULL;
                          }
                      }
                  } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
                      if (willEncrypt) {
                          if (!backup_password) {
                              backup_password = ask_for_password("Enter current backup password", 0);
                          }
                      } else {
                          fprintf(stream_err, "ERROR: Backup encryption is not enabled. Aborting.\n");
                          cmd = CMD_LEAVE;
                          if (backup_password) {
                              free(backup_password);
                              backup_password = NULL;
                          }
                      }
                  } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
                      if (willEncrypt) {
                          if (!backup_password) {
                              backup_password = ask_for_password("Enter old backup password", 0);
                              newpw = ask_for_password("Enter new backup password", 1);
                          }
                      } else {
                          fprintf(stream_err, "ERROR: Backup encryption is not enabled so can't change password. Aborting.\n");
                          cmd = CMD_LEAVE;
                          if (newpw) {
                              free(newpw);
                              newpw = NULL;
                          }
                          if (backup_password) {
                              free(backup_password);
                              backup_password = NULL;
                          }
                      }
                  }
                  if (newpw) {
                      plist_dict_set_item(opts, "NewPassword", plist_new_string(newpw));
                  }
                  if (backup_password) {
                      plist_dict_set_item(opts, "OldPassword", plist_new_string(backup_password));
                  }
                  if (newpw || backup_password) {
                      mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
                      uint8_t passcode_hint = 0;
                      if (device_version >= DEVICE_VERSION(13,0,0)) {
                          diagnostics_relay_client_t diag = NULL;
                          if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
                              plist_t dict = NULL;
                              plist_t keys = plist_new_array();
                              plist_array_append_item(keys, plist_new_string("PasswordConfigured"));
                              if (diagnostics_relay_query_mobilegestalt(diag, keys, &dict) == DIAGNOSTICS_RELAY_E_SUCCESS) {
                                  plist_t node = plist_access_path(dict, 2, "MobileGestalt", "PasswordConfigured");
                                  plist_get_bool_val(node, &passcode_hint);
                              }
                              plist_free(keys);
                              plist_free(dict);
                              diagnostics_relay_goodbye(diag);
                              diagnostics_relay_client_free(diag);
                          }
                      }
                      if (passcode_hint) {
                          if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
                              PRINT_VERBOSE(1, stream_out,"Please confirm changing the backup password by entering the passcode on the device.\n");
                              PRINT_VERBOSE_DEBUG(1, "Please confirm changing the backup password by entering the passcode on the device.\n");
                              put_device_passcode(progress_callback, true);
                          } else if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
                              PRINT_VERBOSE(1, stream_out,"Please confirm enabling the backup encryption by entering the passcode on the device.\n");
                              PRINT_VERBOSE_DEBUG(1, "Please confirm enabling the backup encryption by entering the passcode on the device.\n");
                              put_device_passcode(progress_callback, true);
                          } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
                              PRINT_VERBOSE(1, stream_out,"Please confirm disabling the backup encryption by entering the passcode on the device.\n");
                              PRINT_VERBOSE_DEBUG(1, "Please confirm disabling the backup encryption by entering the passcode on the device.\n");
                              put_device_passcode(progress_callback, true);
                          }
  				    }
                  } else {
                      cmd = CMD_LEAVE;
                  }
                  plist_free(opts);
                  break;
              default:
                  break;
          }
          
          /* close down the lockdown connection as it is no longer needed */
          if (lockdown) {
              lockdownd_client_free(lockdown);
              lockdown = NULL;
          }
          
          if (cmd != CMD_LEAVE) {
              /* reset operation success status */
              int operation_ok = 0;
              plist_t message = NULL;
              
              char *dlmsg = NULL;
              int file_count = 0;
              int errcode = 0;
              const char *errdesc = NULL;
              int progress_finished = 0;
              int retry_count = 0;
              
              /* process series of DLMessage* operations */
              do {
                  free(dlmsg);
                  dlmsg = NULL;
                  mobilebackup2_receive_message(mobilebackup2, &message, &dlmsg);
  
                  if (!message || !dlmsg) {
                      PRINT_VERBOSE(1, stream_out, "Device is not ready yet. Going to try again in 2 seconds...\n");
                      PRINT_VERBOSE_DEBUG(1, "Device is not ready yet. Going to try again in 2 seconds...\n");
                      sleep(2);
                      if (retry_count++ > MAX_RETRIES - 1) quit_flag++;
                      goto files_out;
                  }
                  
                  if (!strcmp(dlmsg, "DLMessageDownloadFiles")) {
                      /* device wants to download files from the computer */
                      mb2_set_overall_progress_from_message(message, dlmsg);
                      mb2_handle_send_files(stream_err, stream_out, mobilebackup2, message, backup_directory);
                  } else if (!strcmp(dlmsg, "DLMessageUploadFiles")) {
                      /* device wants to send files to the computer */
                      mb2_set_overall_progress_from_message(message, dlmsg);
                      file_count += mb2_handle_receive_files(stream_err, stream_out, progress_callback, mobilebackup2, message, backup_directory);
                  } else if (!strcmp(dlmsg, "DLMessageGetFreeDiskSpace")) {
                      /* device wants to know how much disk space is available on the computer */
                      PRINT_VERBOSE_DEBUG(1, "Device wants to know how much disk space is available on the computer\n");
                      uint64_t freespace = 0;
                      int res = -1;
  #ifdef WIN32
                      if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
                          res = 0;
                      }
  #else
                      struct statvfs fs;
                      memset(&fs, '\0', sizeof(fs));
                      res = statvfs(backup_directory, &fs);
                      if (res == 0) {
                          freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize;
                      }
  #endif
                      PRINT_VERBOSE_DEBUG(1, "Available free space %llu\n", freespace);
                      plist_t freespace_item = plist_new_uint(freespace);
                      mobilebackup2_send_status_response(mobilebackup2, res, NULL, freespace_item);
                      plist_free(freespace_item);
                  } else if (!strcmp(dlmsg, "DLMessagePurgeDiskSpace")) {
                      fprintf(stream_err, "ERROR: DLMessagePurgeDiskSpace, space to purge: %llu bytes\n", get_among_space_to_purge(message));
                      cmd = CMD_LEAVE;
                      break;
                  } else if (!strcmp(dlmsg, "DLContentsOfDirectory")) {
                      /* list directory contents */
                      mb2_handle_list_directory(mobilebackup2, message, backup_directory);
                  } else if (!strcmp(dlmsg, "DLMessageCreateDirectory")) {
                      /* make a directory */
                      mb2_handle_make_directory(mobilebackup2, message, backup_directory);
                  } else if (!strcmp(dlmsg, "DLMessageMoveFiles") || !strcmp(dlmsg, "DLMessageMoveItems")) {
                      /* perform a series of rename operations */
                      mb2_set_overall_progress_from_message(message, dlmsg);
                      plist_t moves = plist_array_get_item(message, 1);
                      uint32_t cnt = plist_dict_get_size(moves);
                      PRINT_VERBOSE(1, stream_out, "Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
                      PRINT_VERBOSE_DEBUG(1, "Moving %d file%s\n", cnt, (cnt == 1) ? "" : "s");
                      plist_dict_iter iter = NULL;
                      plist_dict_new_iter(moves, &iter);
                      errcode = 0;
                      errdesc = NULL;
                      if (iter) {
                          char *key = NULL;
                          plist_t val = NULL;
                          do {
                              plist_dict_next_item(moves, iter, &key, &val);
                              if (key && (plist_get_node_type(val) == PLIST_STRING)) {
                                  char *str = NULL;
                                  plist_get_string_val(val, &str);
                                  if (str) {
                                      char *newpath = string_build_path(backup_directory, str, NULL);
                                      free(str);
                                      char *oldpath = string_build_path(backup_directory, key, NULL);
                                      
                                      if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode))
                                          rmdir_recursive(newpath);
                                      else
                                          remove_file(newpath);
                                      if (rename(oldpath, newpath) < 0) {
                                          fprintf(stream_err, "Renameing '%s' to '%s' failed: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
                                          errcode = errno_to_device_error(errno);
                                          errdesc = strerror(errno);
                                          break;
                                      }
                                      free(oldpath);
                                      free(newpath);
                                  }
                                  free(key);
                                  key = NULL;
                              }
                          } while (val);
                          free(iter);
                      } else {
                          errcode = -1;
                          errdesc = "Could not create dict iterator";
                          fprintf(stream_err, "Could not create dict iterator\n");
                      }
                      plist_t empty_dict = plist_new_dict();
                      err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
                      plist_free(empty_dict);
                      if (err != MOBILEBACKUP2_E_SUCCESS) {
                          fprintf(stream_err, "Could not send status response, error %d\n", err);
                      }
                  } else if (!strcmp(dlmsg, "DLMessageRemoveFiles") || !strcmp(dlmsg, "DLMessageRemoveItems")) {
                      mb2_set_overall_progress_from_message(message, dlmsg);
                      plist_t removes = plist_array_get_item(message, 1);
                      uint32_t cnt = plist_array_get_size(removes);
                      PRINT_VERBOSE(1, stream_out, "Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
                      PRINT_VERBOSE_DEBUG(1, "Removing %d file%s\n", cnt, (cnt == 1) ? "" : "s");
                      uint32_t ii = 0;
                      errcode = 0;
                      errdesc = NULL;
                      for (ii = 0; ii < cnt; ii++) {
                          plist_t val = plist_array_get_item(removes, ii);
                          if (plist_get_node_type(val) == PLIST_STRING) {
                              char *str = NULL;
                              plist_get_string_val(val, &str);
                              if (str) {
                                  const char *checkfile = strchr(str, '/');
                                  int suppress_warning = 0;
                                  if (checkfile) {
                                      if (strcmp(checkfile+1, "Manifest.mbdx") == 0) {
                                          suppress_warning = 1;
                                      }
                                  }
                                  char *newpath = string_build_path(backup_directory, str, NULL);
                                  free(str);
                                  int res = 0;
                                  if ((stat(newpath, &st) == 0) && S_ISDIR(st.st_mode)) {
                                      res = rmdir_recursive(newpath);
                                  } else {
                                      res = remove_file(newpath);
                                  }
                                  if (res != 0 && res != ENOENT) {
                                      if (!suppress_warning)
                                          fprintf(stream_err, "Could not remove '%s': %s (%d)\n", newpath, strerror(res), res);
                                      errcode = errno_to_device_error(res);
                                      errdesc = strerror(res);
                                  }
                                  free(newpath);
                              }
                          }
                      }
                      plist_t empty_dict = plist_new_dict();
                      err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
                      plist_free(empty_dict);
                      if (err != MOBILEBACKUP2_E_SUCCESS) {
                          fprintf(stream_err, "Could not send status response, error %d\n", err);
                      }
                  } else if (!strcmp(dlmsg, "DLMessageCopyItem")) {
                      plist_t srcpath = plist_array_get_item(message, 1);
                      plist_t dstpath = plist_array_get_item(message, 2);
                      errcode = 0;
                      errdesc = NULL;
                      if ((plist_get_node_type(srcpath) == PLIST_STRING) && (plist_get_node_type(dstpath) == PLIST_STRING)) {
                          char *src = NULL;
                          char *dst = NULL;
                          plist_get_string_val(srcpath, &src);
                          plist_get_string_val(dstpath, &dst);
                          if (src && dst) {
                              char *oldpath = string_build_path(backup_directory, src, NULL);
                              char *newpath = string_build_path(backup_directory, dst, NULL);
                              
                              PRINT_VERBOSE(1, stream_out, "Copying '%s' to '%s'\n", src, dst);
                              PRINT_VERBOSE_DEBUG(1, "Copying '%s' to '%s'\n", src, dst);
                              
                              /* check that src exists */
                              if ((stat(oldpath, &st) == 0) && S_ISDIR(st.st_mode)) {
                                  mb2_copy_directory_by_path(oldpath, newpath);
                              } else if ((stat(oldpath, &st) == 0) && S_ISREG(st.st_mode)) {
                                  mb2_copy_file_by_path(oldpath, newpath);
                              }
                              
                              free(newpath);
                              free(oldpath);
                          }
                          free(src);
                          free(dst);
                      }
                      plist_t empty_dict = plist_new_dict();
                      err = mobilebackup2_send_status_response(mobilebackup2, errcode, errdesc, empty_dict);
                      plist_free(empty_dict);
                      if (err != MOBILEBACKUP2_E_SUCCESS) {
                          fprintf(stream_err, "Could not send status response, error %d\n", err);
                      }
                  } else if (!strcmp(dlmsg, "DLMessageDisconnect")) {
                      break;
                  } else if (!strcmp(dlmsg, "DLMessageProcessMessage")) {
                      node_tmp = plist_array_get_item(message, 1);
                      if (plist_get_node_type(node_tmp) != PLIST_DICT) {
                          fprintf(stream_err, "Unknown message received!\n");
                      }
                      plist_t nn;
                      int error_code = -1;
                      nn = plist_dict_get_item(node_tmp, "ErrorCode");
                      if (nn && (plist_get_node_type(nn) == PLIST_UINT)) {
                          uint64_t ec = 0;
                          plist_get_uint_val(nn, &ec);
                          error_code = (uint32_t)ec;
                          if (error_code == 0) {
                              operation_ok = 1;
                              result_code = 0;
                          } else {
                              result_code = -error_code;
                          }
                      }
                      nn = plist_dict_get_item(node_tmp, "ErrorDescription");
                      char *str = NULL;
                      if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
                          plist_get_string_val(nn, &str);
                      }
                      if (error_code != 0) {
                          if (str) {
                              fprintf(stream_err, "ErrorCode %d: %s\n", error_code, str);
                          } else {
                              fprintf(stream_err, "ErrorCode %d: (Unknown)\n", error_code);
                          }
                      }
                      if (str) {
                          free(str);
                      }
                      nn = plist_dict_get_item(node_tmp, "Content");
                      if (nn && (plist_get_node_type(nn) == PLIST_STRING)) {
                          str = NULL;
                          plist_get_string_val(nn, &str);
                          PRINT_VERBOSE(1, stream_out, "Content:\n");
                          PRINT_VERBOSE_DEBUG(1, "Content:\n");
                          printf("%s", str);
                          free(str);
                      }
                      break;
                  }
                  
                  /* print status */
                  if ((overall_progress > 0) && !progress_finished && !nearly_equal(previous_overall_progress, overall_progress, 0.0001f)) {
                      if (overall_progress >= 100.0f) {
                          progress_finished = 1;
                      }
                      FILE *stream_progress = tmpfile();
                      fprintf(stream_progress, "%.2f", overall_progress);
                      previous_overall_progress = overall_progress;
                      progress_callback(stream_progress);
                  }
                  
              files_out:
                  plist_free(message);
                  message = NULL;
                  free(dlmsg);
                  dlmsg = NULL;
                  
                  if (quit_flag > 0) {
                      /* need to cancel the backup here */
                      //mobilebackup_send_error(mobilebackup, "Cancelling DLSendFile");
                      
                      /* remove any atomic Manifest.plist.tmp */
                      
                      /*manifest_path = mobilebackup_build_path(backup_directory, "Manifest", ".plist.tmp");
                       if (stat(manifest_path, &st) == 0)
                       remove(manifest_path);*/
                      break;
                  }
              } while (1);
              
              plist_free(message);
              free(dlmsg);
              
              /* report operation status to user */
              switch (cmd) {
                  case CMD_CLOUD:
                      if (cmd_flags & CMD_FLAG_CLOUD_ENABLE) {
                          if (operation_ok) {
                              PRINT_VERBOSE(1, stream_out, "Cloud backup has been enabled successfully.\n");
                              PRINT_VERBOSE_DEBUG(1, "Cloud backup has been enabled successfully.\n");
                          } else {
                              PRINT_VERBOSE(1, stream_out, "Could not enable cloud backup.\n");
                              PRINT_VERBOSE_DEBUG(1, "Could not enable cloud backup.\n");
                          }
                      } else if (cmd_flags & CMD_FLAG_CLOUD_DISABLE) {
                          if (operation_ok) {
                              PRINT_VERBOSE(1, stream_out, "Cloud backup has been disabled successfully.\n");
                              PRINT_VERBOSE_DEBUG(1, "Cloud backup has been disabled successfully.\n");
                          } else {
                              PRINT_VERBOSE(1, stream_out, "Could not disable cloud backup.\n");
                              PRINT_VERBOSE_DEBUG(1, "Could not disable cloud backup.\n");
                          }
                      }
                      break;
                  case CMD_BACKUP:
                      PRINT_VERBOSE(1, stream_out, "Received %d files from device.\n", file_count);
                      PRINT_VERBOSE_DEBUG(1, "Received %d files from device.\n", file_count);
                      if (operation_ok && mb2_status_check_snapshot_state(backup_directory, udid, "finished")) {
                          fprintf(stream_out, "Backup Successful.\n");
                          PRINT_VERBOSE_DEBUG(1, "Backup Successful.\n");
                          command_run_successfully(stream_out);
                      } else {
                          if (quit_flag) {
                              fprintf(stream_out, "Backup Aborted.\n");
                              PRINT_VERBOSE_DEBUG(1, "Backup Aborted.\n");
                          } else {
                              fprintf(stream_err, "Backup Failed (Error Code %d).\n", -result_code);
                              PRINT_VERBOSE_DEBUG(1, "Backup Failed (Error Code %d).\n", -result_code);
                          }
                      }
                      break;
                  case CMD_UNBACK:
                      if (quit_flag) {
                          PRINT_VERBOSE(1, stream_out, "Unback Aborted.\n");
                          PRINT_VERBOSE_DEBUG(1, "Unback Aborted.\n");
                      } else {
                          PRINT_VERBOSE(1, stream_out, "The files can now be found in the \"_unback_\" directory.\n");
                          PRINT_VERBOSE_DEBUG(1, "The files can now be found in the \"_unback_\" directory.\n");
                          PRINT_VERBOSE(1, stream_out, "Unback Successful.\n");
                          PRINT_VERBOSE_DEBUG(1, "Unback Successful.\n");
                          command_run_successfully(stream_out);
                      }
                      break;
                  case CMD_CHANGEPW:
                      if (cmd_flags & CMD_FLAG_ENCRYPTION_ENABLE) {
                          if (operation_ok) {
                              PRINT_VERBOSE(1, stream_out, "Backup encryption has been enabled successfully.\n");
                              PRINT_VERBOSE_DEBUG(1, "Backup encryption has been enabled successfully.\n");
                              command_run_successfully(stream_out);
                          } else {
                              PRINT_VERBOSE(1, stream_out, "Could not enable backup encryption.\n");
                              PRINT_VERBOSE_DEBUG(1, "Could not enable backup encryption.\n");
                          }
                      } else if (cmd_flags & CMD_FLAG_ENCRYPTION_DISABLE) {
                          if (operation_ok) {
                              PRINT_VERBOSE(1, stream_out, "Backup encryption has been disabled successfully.\n");
                              PRINT_VERBOSE_DEBUG(1, "Backup encryption has been disabled successfully.\n");
                              command_run_successfully(stream_out);
                          } else {
                              PRINT_VERBOSE(1, stream_out, "Could not disable backup encryption.\n");
                              PRINT_VERBOSE_DEBUG(1, "Could not disable backup encryption.\n");
                          }
                      } else if (cmd_flags & CMD_FLAG_ENCRYPTION_CHANGEPW) {
                          if (operation_ok) {
                              PRINT_VERBOSE(1, stream_out, "Backup encryption password has been changed successfully.\n");
                              PRINT_VERBOSE_DEBUG(1, "Backup encryption password has been changed successfully.\n");
                              command_run_successfully(stream_out);
                          } else {
                              PRINT_VERBOSE(1, stream_out, "Could not change backup encryption password.\n");
                              PRINT_VERBOSE_DEBUG(1, "Could not change backup encryption password.\n");
                          }
                      }
                      break;
                  case CMD_RESTORE:
                      if (cmd_flags & CMD_FLAG_RESTORE_REBOOT)
                          PRINT_VERBOSE(1, stream_out, "The device should reboot now.\n");
                          PRINT_VERBOSE_DEBUG(1, "The device should reboot now.\n");
                      if (operation_ok) {
                          PRINT_VERBOSE(1, stream_out, "Restore Successful.\n");
                          PRINT_VERBOSE_DEBUG(1, "Restore Successful.\n");
                          command_run_successfully(stream_out);
                      } else {
                          PRINT_VERBOSE(1, stream_out, "Restore Failed (Error Code %d).\n", -result_code);
                          PRINT_VERBOSE_DEBUG(1, "Restore Failed (Error Code %d).\n", -result_code);
                      }
                      break;
                  case CMD_INFO:
                  case CMD_LIST:
                  case CMD_LEAVE:
                  default:
                      if (quit_flag) {
                          PRINT_VERBOSE(1, stream_out, "Operation Aborted.\n");
                          PRINT_VERBOSE_DEBUG(1, "Operation Aborted.\n");
                      } else if (cmd == CMD_LEAVE) {
                          fprintf(stream_err, "Operation Failed.\n");
                          PRINT_VERBOSE_DEBUG(1, "Operation Failed.\n");
                      } else {
                          PRINT_VERBOSE(1, stream_out, "Operation Successful.\n");
                          PRINT_VERBOSE_DEBUG(1, "Operation Successful.\n");
                          command_run_successfully(stream_out);
                      }
                      break;
              }
          }
          if (lockfile) {
              afc_file_lock(afc, lockfile, AFC_LOCK_UN);
              afc_file_close(afc, lockfile);
              lockfile = 0;
              if (cmd == CMD_BACKUP)
                  do_post_notification(device, NP_SYNC_DID_FINISH);
          }
      } else {
          fprintf(stream_err, "ERROR: Could not start service %s due to %d.\n", MOBILEBACKUP2_SERVICE_NAME, ldret);
          lockdownd_client_free(lockdown);
          lockdown = NULL;
      }
      
      if (lockdown) {
          lockdownd_client_free(lockdown);
          lockdown = NULL;
      }
      
      if (mobilebackup2) {
          mobilebackup2_client_free(mobilebackup2);
          mobilebackup2 = NULL;
      }
      
      if (afc) {
          afc_client_free(afc);
          afc = NULL;
      }
      
      if (np) {
          np_client_free(np);
          np = NULL;
      }
      
      idevice_free(device);
      device = NULL;
      
      if (backup_password) {
          free(backup_password);
          backup_password = NULL;
      }
      
      if (udid) {
          free(udid);
          udid = NULL;
      }
  
      if (source_udid) {
          free(source_udid);
          source_udid = NULL;
      }
  }
  
  