Spaces:
Configuration error
Configuration error
| /* | |
| * common.c | |
| * Misc functions used in idevicerestore | |
| * | |
| * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. | |
| * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. | |
| * Copyright (c) 2010 Joshua Hill. All Rights Reserved. | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| */ | |
| struct idevicerestore_mode_t idevicerestore_modes[] = { | |
| { 0, "WTF" }, | |
| { 1, "DFU" }, | |
| { 2, "Recovery" }, | |
| { 3, "Restore" }, | |
| { 4, "Normal" }, | |
| { -1, NULL } | |
| }; | |
| int idevicerestore_debug = 0; | |
| static char idevicerestore_err_buff[idevicerestore_err_buff_size] = {0, }; | |
| static FILE* info_stream = NULL; | |
| static FILE* error_stream = NULL; | |
| static FILE* debug_stream = NULL; | |
| static int info_disabled = 0; | |
| static int error_disabled = 0; | |
| static int debug_disabled = 0; | |
| void info(const char* format, ...) | |
| { | |
| if (info_disabled) return; | |
| va_list vargs; | |
| va_start(vargs, format); | |
| vfprintf((info_stream) ? info_stream : stdout, format, vargs); | |
| va_end(vargs); | |
| } | |
| void error(const char* format, ...) | |
| { | |
| va_list vargs, vargs2; | |
| va_start(vargs, format); | |
| va_copy(vargs2, vargs); | |
| vsnprintf(idevicerestore_err_buff, idevicerestore_err_buff_size, format, vargs); | |
| va_end(vargs); | |
| if (!error_disabled) { | |
| vfprintf((error_stream) ? error_stream : stderr, format, vargs2); | |
| } | |
| va_end(vargs2); | |
| } | |
| void debug(const char* format, ...) | |
| { | |
| if (debug_disabled) return; | |
| if (!idevicerestore_debug) { | |
| return; | |
| } | |
| va_list vargs; | |
| va_start(vargs, format); | |
| vfprintf((debug_stream) ? debug_stream : stderr, format, vargs); | |
| va_end(vargs); | |
| } | |
| void idevicerestore_set_info_stream(FILE* strm) | |
| { | |
| if (strm) { | |
| info_disabled = 0; | |
| info_stream = strm; | |
| } else { | |
| info_disabled = 1; | |
| } | |
| } | |
| void idevicerestore_set_error_stream(FILE* strm) | |
| { | |
| if (strm) { | |
| error_disabled = 0; | |
| error_stream = strm; | |
| } else { | |
| error_disabled = 1; | |
| } | |
| } | |
| void idevicerestore_set_debug_stream(FILE* strm) | |
| { | |
| if (strm) { | |
| debug_disabled = 0; | |
| debug_stream = strm; | |
| } else { | |
| debug_disabled = 1; | |
| } | |
| } | |
| const char* idevicerestore_get_error(void) | |
| { | |
| if (idevicerestore_err_buff[0] == 0) { | |
| return NULL; | |
| } else { | |
| char* p = NULL; | |
| while ((strlen(idevicerestore_err_buff) > 0) && (p = strrchr(idevicerestore_err_buff, '\n'))) { | |
| p[0] = '\0'; | |
| } | |
| return (const char*)idevicerestore_err_buff; | |
| } | |
| } | |
| int write_file(const char* filename, const void* data, size_t size) { | |
| size_t bytes = 0; | |
| FILE* file = NULL; | |
| debug("Writing data to %s\n", filename); | |
| file = fopen(filename, "wb"); | |
| if (file == NULL) { | |
| error("write_file: Unable to open file %s\n", filename); | |
| return -1; | |
| } | |
| bytes = fwrite(data, 1, size, file); | |
| fclose(file); | |
| if (bytes != size) { | |
| error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, (int)bytes, (int)size); | |
| return -1; | |
| } | |
| return size; | |
| } | |
| int read_file(const char* filename, void** data, size_t* size) { | |
| size_t bytes = 0; | |
| size_t length = 0; | |
| FILE* file = NULL; | |
| char* buffer = NULL; | |
| struct stat fst; | |
| debug("Reading data from %s\n", filename); | |
| *size = 0; | |
| *data = NULL; | |
| file = fopen(filename, "rb"); | |
| if (file == NULL) { | |
| error("read_file: cannot open %s: %s\n", filename, strerror(errno)); | |
| return -1; | |
| } | |
| if (fstat(fileno(file), &fst) < 0) { | |
| error("read_file: fstat: %s\n", strerror(errno)); | |
| return -1; | |
| } | |
| length = fst.st_size; | |
| buffer = (char*) malloc(length); | |
| if (buffer == NULL) { | |
| error("ERROR: Out of memory\n"); | |
| fclose(file); | |
| return -1; | |
| } | |
| bytes = fread(buffer, 1, length, file); | |
| fclose(file); | |
| if (bytes != length) { | |
| error("ERROR: Unable to read entire file\n"); | |
| free(buffer); | |
| return -1; | |
| } | |
| *size = length; | |
| *data = buffer; | |
| return 0; | |
| } | |
| void debug_plist(plist_t plist) { | |
| uint32_t size = 0; | |
| char* data = NULL; | |
| plist_to_xml(plist, &data, &size); | |
| if (size <= MAX_PRINT_LEN) | |
| info("%s:printing %i bytes plist:\n%s", __FILE__, size, data); | |
| else | |
| info("%s:supressed printing %i bytes plist...\n", __FILE__, size); | |
| free(data); | |
| } | |
| void print_progress_bar(double progress) { | |
| if (info_disabled) return; | |
| int i = 0; | |
| if(progress < 0) return; | |
| if(progress > 100) progress = 100; | |
| info("\r["); | |
| for(i = 0; i < 50; i++) { | |
| if(i < progress / 2) info("="); | |
| else info(" "); | |
| } | |
| info("] %5.1f%%", progress); | |
| if(progress >= 100) info("\n"); | |
| fflush((info_stream) ? info_stream : stdout); | |
| } | |
| char *generate_guid(void) | |
| { | |
| char *guid = (char *) malloc(sizeof(char) * 37); | |
| const char *chars = "ABCDEF0123456789"; | |
| srand(time(NULL)); | |
| int i = 0; | |
| for (i = 0; i < 36; i++) { | |
| if (i == 8 || i == 13 || i == 18 || i == 23) { | |
| guid[i] = '-'; | |
| continue; | |
| } else { | |
| guid[i] = chars[GET_RAND(0, 16)]; | |
| } | |
| } | |
| guid[36] = '\0'; | |
| return guid; | |
| } | |
| 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; | |
| } else if (errno == ENOENT) { | |
| // ignore | |
| } else { | |
| return -1; | |
| } | |
| } | |
| int res; | |
| char *parent = strdup(dir); | |
| char *parentdir = dirname(parent); | |
| if (parentdir && (strcmp(parentdir, ".") != 0) && (strcmp(parentdir, dir) != 0)) { | |
| res = mkdir_with_parents(parentdir, mode); | |
| } else { | |
| res = -1; | |
| } | |
| free(parent); | |
| if (res == 0) { | |
| mkdir_with_parents(dir, mode); | |
| } | |
| return res; | |
| } | |
| /* Based on libc's __gen_tempname() from sysdeps/posix/tempname.c | |
| Copyright (C) 1991-2018 Free Software Foundation, Inc. | |
| With changes from https://stackoverflow.com/a/6036308 and some | |
| additional changes. */ | |
| int mkstemp(char *tmpl) | |
| { | |
| static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; | |
| int len; | |
| char *XXXXXX; | |
| static unsigned long long value; | |
| unsigned long long random_time_bits; | |
| unsigned int count; | |
| int fd = -1; | |
| int save_errno = errno; | |
| /* A lower bound on the number of temporary files to attempt to | |
| generate. The maximum total number of temporary file names that | |
| can exist for a given template is 62**6. It should never be | |
| necessary to try all these combinations. Instead if a reasonable | |
| number of names is tried (we define reasonable as 62**3) fail to | |
| give the system administrator the chance to remove the problems. */ | |
| /* The number of times to attempt to generate a temporary file. To | |
| conform to POSIX, this must be no smaller than TMP_MAX. */ | |
| unsigned int attempts = TMP_MAX; | |
| unsigned int attempts = ATTEMPTS_MIN; | |
| len = strlen (tmpl); | |
| if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) | |
| { | |
| errno = EINVAL; | |
| return -1; | |
| } | |
| /* This is where the Xs start. */ | |
| XXXXXX = &tmpl[len - 6]; | |
| /* Get some more or less random data. */ | |
| { | |
| SYSTEMTIME stNow; | |
| FILETIME ftNow; | |
| // get system time | |
| GetSystemTime(&stNow); | |
| if (!SystemTimeToFileTime(&stNow, &ftNow)) | |
| { | |
| errno = -1; | |
| return -1; | |
| } | |
| random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32) | |
| | (unsigned long long)ftNow.dwLowDateTime); | |
| } | |
| value += random_time_bits ^ ((unsigned long long)GetCurrentProcessId() << 32 | (unsigned long long)GetCurrentThreadId()); | |
| { | |
| struct timeval tvNow = {0, 0}; | |
| gettimeofday(&tvNow, NULL); | |
| random_time_bits = (((unsigned long long)tvNow.tv_sec << 32) | |
| | (unsigned long long)tvNow.tv_usec); | |
| } | |
| value += random_time_bits ^ ((unsigned long long)getpid() << 32 | (unsigned long long)(uintptr_t)pthread_self()); | |
| for (count = 0; count < attempts; value += 7777, ++count) | |
| { | |
| unsigned long long v = value; | |
| /* Fill in the random bits. */ | |
| XXXXXX[0] = letters[v % 62]; | |
| v /= 62; | |
| XXXXXX[1] = letters[v % 62]; | |
| v /= 62; | |
| XXXXXX[2] = letters[v % 62]; | |
| v /= 62; | |
| XXXXXX[3] = letters[v % 62]; | |
| v /= 62; | |
| XXXXXX[4] = letters[v % 62]; | |
| v /= 62; | |
| XXXXXX[5] = letters[v % 62]; | |
| fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE); | |
| fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); | |
| if (fd >= 0) | |
| { | |
| errno = save_errno; | |
| return fd; | |
| } | |
| else if (errno != EEXIST) | |
| return -1; | |
| } | |
| /* We got out of the loop because we ran out of combinations to try. */ | |
| errno = EEXIST; | |
| return -1; | |
| } | |
| char *get_temp_filename(const char *prefix) | |
| { | |
| char *result = NULL; | |
| char *tmpdir; | |
| size_t lt; | |
| size_t lp; | |
| const char *TMPVARS[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", NULL }; | |
| int i = 0; | |
| int fd; | |
| /* check the prefix parameter */ | |
| if (!prefix) { | |
| prefix = "tmp_"; | |
| } | |
| if (strchr(prefix, '/') || strchr(prefix, '\\')) return NULL; | |
| if (strchr(prefix, '/')) return NULL; | |
| while (TMPVARS[i] && ((tmpdir = getenv(TMPVARS[i])) == NULL)) i++; | |
| if (!tmpdir || access(tmpdir, W_OK|X_OK) != 0) { | |
| tmpdir = "C:\\WINDOWS\\TEMP"; | |
| tmpdir = P_tmpdir; | |
| } | |
| if (!tmpdir || access(tmpdir, W_OK|X_OK) != 0) { | |
| return NULL; | |
| } | |
| lt = strlen(tmpdir); | |
| if (lt < 1) { | |
| return NULL; | |
| } | |
| lp = strlen(prefix); | |
| result = malloc(lt + lp + 8); | |
| memcpy(result, tmpdir, lt); | |
| if (tmpdir[lt-1] != '/' && tmpdir[lt-1] != '\\') result[lt++] = '\\'; | |
| if (tmpdir[lt-1] != '/') result[lt++] = '/'; | |
| strncpy(result + lt, prefix, lp); | |
| strcpy(result + lt + lp, "XXXXXX"); | |
| fd = mkstemp(result); | |
| if (fd < 0) { | |
| free(result); | |
| result = NULL; | |
| } | |
| close(fd); | |
| return result; | |
| } | |
| void idevicerestore_progress(struct idevicerestore_client_t* client, int step, double progress) | |
| { | |
| if(client && client->progress_cb) { | |
| client->progress_cb(step, progress, client->progress_cb_data); | |
| } else { | |
| // we don't want to be too verbose in regular idevicerestore. | |
| if ((step == RESTORE_STEP_UPLOAD_FS) || (step == RESTORE_STEP_VERIFY_FS) || (step == RESTORE_STEP_FLASH_FW)) { | |
| print_progress_bar(100.0 * progress); | |
| } | |
| } | |
| } | |
| char* strsep(char** strp, const char* delim) | |
| { | |
| char *p, *s; | |
| if (strp == NULL || *strp == NULL || **strp == '\0') return NULL; | |
| s = *strp; | |
| p = s + strcspn(s, delim); | |
| if (*p != '\0') *p++ = '\0'; | |
| *strp = p; | |
| return s; | |
| } | |
| char* realpath(const char *filename, char *resolved_name) | |
| { | |
| if (access(filename, F_OK) != 0) { | |
| return NULL; | |
| } | |
| if (GetFullPathName(filename, MAX_PATH, resolved_name, NULL) == 0) { | |
| return NULL; | |
| } | |
| return resolved_name; | |
| return NULL; | |
| } | |
| 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; | |
| } | |
| void get_user_input(char *buf, int maxlen, int secure) | |
| { | |
| int len = 0; | |
| int c; | |
| while ((c = my_getch()) > 0) { | |
| if ((c == '\r') || (c == '\n')) { | |
| break; | |
| } else if (isprint(c)) { | |
| if (len < maxlen-1) | |
| buf[len++] = c; | |
| fputc((secure) ? '*' : c, stdout); | |
| } else if (c == BS_CC) { | |
| if (len > 0) { | |
| fputs("\b \b", stdout); | |
| len--; | |
| } | |
| } | |
| else if (c == CTRL_C_CC || c == ESC_CC) { | |
| c = -1; | |
| break; | |
| } | |
| } | |
| if (c < 0) { | |
| len = 0; | |
| } | |
| fputs("\n", stdout); | |
| buf[len] = 0; | |
| } | |
| uint64_t _plist_dict_get_uint(plist_t dict, const char *key) | |
| { | |
| uint64_t uintval = 0; | |
| char *strval = NULL; | |
| uint64_t strsz = 0; | |
| plist_t node = plist_dict_get_item(dict, key); | |
| if (!node) { | |
| return (uint64_t)-1LL; | |
| } | |
| switch (plist_get_node_type(node)) { | |
| case PLIST_UINT: | |
| plist_get_uint_val(node, &uintval); | |
| break; | |
| case PLIST_STRING: | |
| plist_get_string_val(node, &strval); | |
| if (strval) { | |
| uintval = strtoull(strval, NULL, 0); | |
| free(strval); | |
| } | |
| break; | |
| case PLIST_DATA: | |
| plist_get_data_val(node, &strval, &strsz); | |
| if (strval) { | |
| if (strsz == 8) { | |
| uintval = le64toh(*(uint64_t*)strval); | |
| } else if (strsz == 4) { | |
| uintval = le32toh(*(uint32_t*)strval); | |
| } else if (strsz == 2) { | |
| uintval = le16toh(*(uint16_t*)strval); | |
| } else if (strsz == 1) { | |
| uintval = strval[0]; | |
| } else { | |
| error("%s: ERROR: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); | |
| } | |
| free(strval); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return uintval; | |
| } | |
| uint8_t _plist_dict_get_bool(plist_t dict, const char *key) | |
| { | |
| uint8_t bval = 0; | |
| uint64_t uintval = 0; | |
| char *strval = NULL; | |
| uint64_t strsz = 0; | |
| plist_t node = plist_dict_get_item(dict, key); | |
| if (!node) { | |
| return 0; | |
| } | |
| switch (plist_get_node_type(node)) { | |
| case PLIST_BOOLEAN: | |
| plist_get_bool_val(node, &bval); | |
| break; | |
| case PLIST_UINT: | |
| plist_get_uint_val(node, &uintval); | |
| bval = (uint8_t)uintval; | |
| break; | |
| case PLIST_STRING: | |
| plist_get_string_val(node, &strval); | |
| if (strval) { | |
| if (strcmp(strval, "true")) { | |
| bval = 1; | |
| } else if (strcmp(strval, "false")) { | |
| bval = 0; | |
| } | |
| free(strval); | |
| } | |
| break; | |
| case PLIST_DATA: | |
| plist_get_data_val(node, &strval, &strsz); | |
| if (strval) { | |
| if (strsz == 1) { | |
| bval = strval[0]; | |
| } else { | |
| error("%s: ERROR: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz); | |
| } | |
| free(strval); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return bval; | |
| } | |