diff options
62 files changed, 3064 insertions, 1077 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e995b30 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: nikias +patreon: nikias +custom: ["https://www.paypal.me/NikiasBassen"] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 76cd02a..fbbf10a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,7 @@ name: build on: push: + pull_request: schedule: - cron: '0 0 1 * *' @@ -17,26 +18,33 @@ jobs: run: | echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{env.target_triplet}} repo: libimobiledevice/libplist - name: fetch libusbmuxd - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_${{env.target_triplet}} repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_${{env.target_triplet}} repo: libimobiledevice/libimobiledevice-glue + - name: fetch libtatsu + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libtatsu-latest_${{env.target_triplet}} + repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract @@ -77,26 +85,33 @@ jobs: pip3 install --break-system-packages cython shell: bash - name: fetch libplist - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_macOS repo: libimobiledevice/libplist - name: fetch libusbmuxd - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_macOS repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_macOS repo: libimobiledevice/libimobiledevice-glue + - name: fetch libtatsu + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libtatsu-latest_macOS + repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract @@ -140,6 +155,7 @@ jobs: PYTHON_EXEC_PREFIX=`$PYTHON3_BIN -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('exec_prefix'))"` PYTHON_LIBS_PATH=$PYTHON_EXEC_PREFIX/lib PYTHON_FRAMEWORK_PATH=$PYTHON_EXEC_PREFIX/Python3 + export PYTHON_CPPFLAGS="-I$PYTHON_EXEC_PREFIX/Headers" export PYTHON_LIBS="-L$PYTHON_LIBS_PATH -lpython$PYTHON_VER" export PYTHON_EXTRA_LDFLAGS="-Wl,-stack_size,1000000 -framework CoreFoundation $PYTHON_FRAMEWORK_PATH" fi @@ -161,7 +177,7 @@ jobs: name: libimobiledevice-latest_macOS path: libimobiledevice.tar build-windows: - runs-on: windows-2019 + runs-on: windows-latest defaults: run: shell: msys2 {0} @@ -182,6 +198,8 @@ jobs: base-devel git mingw-w64-${{ matrix.arch }}-gcc + mingw-w64-${{ matrix.arch }}-pkg-config + mingw-w64-${{ matrix.arch }}-openssl make libtool autoconf @@ -193,26 +211,33 @@ jobs: echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV git config --global core.autocrlf false - name: fetch libplist - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libplist - name: fetch libusbmuxd - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libimobiledevice-glue + - name: fetch libtatsu + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libtatsu-latest_${{ matrix.arch }}-${{ env.dest }} + repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract diff --git a/3rd_party/ed25519/Makefile.am b/3rd_party/ed25519/Makefile.am index d8e4e04..438cb53 100644 --- a/3rd_party/ed25519/Makefile.am +++ b/3rd_party/ed25519/Makefile.am @@ -15,12 +15,19 @@ libed25519_la_LIBADD = libed25519_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined libed25519_la_SOURCES = \ add_scalar.c \ + ed25519.h \ fe.c \ + fe.h \ + fixedint.h \ ge.c \ + ge.h \ keypair.c \ key_exchange.c \ + precomp_data.h \ sc.c \ + sc.h \ seed.c \ sign.c \ sha512.c \ + sha512.h \ verify.c diff --git a/3rd_party/libsrp6a-sha512/Makefile.am b/3rd_party/libsrp6a-sha512/Makefile.am index 2acd582..bdacb5f 100644 --- a/3rd_party/libsrp6a-sha512/Makefile.am +++ b/3rd_party/libsrp6a-sha512/Makefile.am @@ -25,7 +25,8 @@ libsrp6a_sha512_la_SOURCES = \ t_truerand.c cstr.c \ srp.c srp6a_sha512_client.c \ srp.h srp_aux.h cstr.h \ - t_sha.c + t_defines.h t_pwd.h \ + t_sha.c t_sha.h #if !HAVE_OPENSSL #libsrp6a_sha512_la_SOURCES += t_sha.c #endif diff --git a/3rd_party/libsrp6a-sha512/cstr.c b/3rd_party/libsrp6a-sha512/cstr.c index 9856f46..58a5638 100644 --- a/3rd_party/libsrp6a-sha512/cstr.c +++ b/3rd_party/libsrp6a-sha512/cstr.c @@ -8,72 +8,31 @@ #define MINSIZE 4 /* Absolute minimum - one word */ static char cstr_empty_string[] = { '\0' }; -static cstr_allocator * default_alloc = NULL; - -/* - * It is assumed, for efficiency, that it is okay to pass more arguments - * to a function than are called for, as long as the required arguments - * are in proper form. If extra arguments to malloc() and free() cause - * problems, define PEDANTIC_ARGS below. - */ -#ifdef PEDANTIC_ARGS -static void * Cmalloc(int n, void * heap) { return malloc(n); } -static void Cfree(void * p, void * heap) { free(p); } -static cstr_allocator malloc_allocator = { Cmalloc, Cfree, NULL }; -#else -static cstr_allocator malloc_allocator = { malloc, free, NULL }; -#endif - -_TYPE( void ) -cstr_set_allocator(cstr_allocator * alloc) -{ - default_alloc = alloc; -} _TYPE( cstr * ) -cstr_new_alloc(cstr_allocator * alloc) +cstr_new() { cstr * str; - if(alloc == NULL) { - if(default_alloc == NULL) { - default_alloc = &malloc_allocator; - } - alloc = default_alloc; - } - - str = (cstr *) (*alloc->alloc)(sizeof(cstr), alloc->heap); + str = (cstr *) malloc(sizeof(cstr)); if(str) { str->data = cstr_empty_string; str->length = str->cap = 0; str->ref = 1; - str->allocator = alloc; } return str; } _TYPE( cstr * ) -cstr_new() -{ - return cstr_new_alloc(NULL); -} - -_TYPE( cstr * ) -cstr_dup_alloc(const cstr * str, cstr_allocator * alloc) +cstr_dup(const cstr * str) { - cstr * nstr = cstr_new_alloc(alloc); + cstr * nstr = cstr_new(); if(nstr) cstr_setn(nstr, str->data, str->length); return nstr; } _TYPE( cstr * ) -cstr_dup(const cstr * str) -{ - return cstr_dup_alloc(str, NULL); -} - -_TYPE( cstr * ) cstr_create(const char * s) { return cstr_createn(s, strlen(s)); @@ -101,9 +60,9 @@ cstr_clear_free(cstr * str) if(--str->ref == 0) { if(str->cap > 0) { memset(str->data, 0, str->cap); - (*str->allocator->free)(str->data, str->allocator->heap); + free(str->data); } - (*str->allocator->free)(str, str->allocator->heap); + free(str); } } @@ -112,8 +71,8 @@ cstr_free(cstr * str) { if(--str->ref == 0) { if(str->cap > 0) - (*str->allocator->free)(str->data, str->allocator->heap); - (*str->allocator->free)(str, str->allocator->heap); + free(str->data); + free(str); } } @@ -121,7 +80,7 @@ _TYPE( void ) cstr_empty(cstr * str) { if(str->cap > 0) - (*str->allocator->free)(str->data, str->allocator->heap); + free(str->data); str->data = cstr_empty_string; str->length = str->cap = 0; } @@ -137,8 +96,7 @@ cstr_alloc(cstr * str, int len) if(len < MINSIZE) len = MINSIZE; - t = (char *) (*str->allocator->alloc)(len * sizeof(char), - str->allocator->heap); + t = (char *) malloc(len * sizeof(char)); if(t) { if(str->data) { t[str->length] = 0; diff --git a/3rd_party/libsrp6a-sha512/cstr.h b/3rd_party/libsrp6a-sha512/cstr.h index 7cc019a..29afea7 100644 --- a/3rd_party/libsrp6a-sha512/cstr.h +++ b/3rd_party/libsrp6a-sha512/cstr.h @@ -38,7 +38,7 @@ #define _MSVC15DEXPORT #define _MSVC20EXPORT #define _DLLAPI -#if defined(WINDOWS) || defined(WIN32) +#if defined(WINDOWS) || defined(_WIN32) #define _CDECL _cdecl #else #define _CDECL @@ -51,27 +51,15 @@ extern "C" { #endif /* __cplusplus */ -/* Arguments to allocator methods ordered this way for compatibility */ -typedef struct cstr_alloc_st { - void * (_CDECL * alloc)(size_t n, void * heap); - void (_CDECL * free)(void * p, void * heap); - void * heap; -} cstr_allocator; - typedef struct cstr_st { char * data; /* Okay to access data and length fields directly */ int length; int cap; int ref; /* Simple reference counter */ - cstr_allocator * allocator; } cstr; -_TYPE( void ) cstr_set_allocator P((cstr_allocator * alloc)); - _TYPE( cstr * ) cstr_new P((void)); -_TYPE( cstr * ) cstr_new_alloc P((cstr_allocator * alloc)); _TYPE( cstr * ) cstr_dup P((const cstr * str)); -_TYPE( cstr * ) cstr_dup_alloc P((const cstr * str, cstr_allocator * alloc)); _TYPE( cstr * ) cstr_create P((const char * s)); _TYPE( cstr * ) cstr_createn P((const char * s, int len)); diff --git a/3rd_party/libsrp6a-sha512/t_defines.h b/3rd_party/libsrp6a-sha512/t_defines.h index 447263f..a2a5fe5 100644 --- a/3rd_party/libsrp6a-sha512/t_defines.h +++ b/3rd_party/libsrp6a-sha512/t_defines.h @@ -65,7 +65,7 @@ #define _MSVC15DEXPORT #define _MSVC20EXPORT #define _DLLAPI -#if defined(WINDOWS) || defined(WIN32) +#if defined(WINDOWS) || defined(_WIN32) #define _CDECL _cdecl #else #define _CDECL @@ -122,7 +122,7 @@ char *strchr(), *strrchr(), *strtok(); #define USE_SGTTY #endif -#ifdef WIN32 +#ifdef _WIN32 #define USE_FTIME 1 #define USE_RENAME 1 #define NO_FCHMOD 1 diff --git a/3rd_party/libsrp6a-sha512/t_misc.c b/3rd_party/libsrp6a-sha512/t_misc.c index 3a2cda1..abd8e55 100644 --- a/3rd_party/libsrp6a-sha512/t_misc.c +++ b/3rd_party/libsrp6a-sha512/t_misc.c @@ -38,7 +38,7 @@ #include <sys/stat.h> #include <fcntl.h> -#ifdef WIN32 +#ifdef _WIN32 #include <process.h> #include <io.h> #endif @@ -207,7 +207,7 @@ t_initrand() #if defined(OPENSSL) /* OpenSSL has nifty win32 entropy-gathering code */ #if OPENSSL_VERSION_NUMBER >= 0x00905100 r = RAND_status(); -#if defined(WINDOWS) || defined(WIN32) +#if defined(WINDOWS) || defined(_WIN32) if(r) /* Don't do the Unix-y stuff on Windows if possible */ return; #else @@ -220,7 +220,7 @@ t_initrand() if(r > 0) { yarrow_add_entropy(entropy, r, &g_rng); memset(entropy, 0, sizeof(entropy)); -# if defined(WINDOWS) || defined(WIN32) +# if defined(WINDOWS) || defined(_WIN32) /* Don't do the Unix-y stuff on Windows if possible */ yarrow_ready(&g_rng); return; @@ -228,13 +228,13 @@ t_initrand() } #endif -#if !defined(WINDOWS) && !defined(WIN32) +#if !defined(WINDOWS) && !defined(_WIN32) i = open("/dev/urandom", O_RDONLY); if(i > 0) { r += read(i, preseed.devrand, sizeof(preseed.devrand)); close(i); } -#endif /* !WINDOWS && !WIN32 */ +#endif /* !WINDOWS && !_WIN32 */ /* Resort to truerand only if desperate for some Real entropy */ if(r == 0) @@ -250,7 +250,7 @@ t_initrand() preseed.subsec = t.tv_usec; #endif preseed.pid = getpid(); -#ifndef WIN32 +#ifndef _WIN32 preseed.ppid = getppid(); #endif t_envhash(preseed.envh); diff --git a/3rd_party/libsrp6a-sha512/t_truerand.c b/3rd_party/libsrp6a-sha512/t_truerand.c index f995ed7..cd27d0d 100644 --- a/3rd_party/libsrp6a-sha512/t_truerand.c +++ b/3rd_party/libsrp6a-sha512/t_truerand.c @@ -54,7 +54,7 @@ #include "t_defines.h" -#ifdef WIN32 +#ifdef _WIN32 # ifdef CRYPTOLIB @@ -69,7 +69,7 @@ raw_truerand() return truerand(); } -# else /* !CRYPTOLIB && WIN32 */ +# else /* !CRYPTOLIB && _WIN32 */ #include <windows.h> #include <wtypes.h> @@ -128,7 +128,7 @@ raw_truerand() { # endif /* CRYPTOLIB */ -#else /* !WIN32 */ +#else /* !_WIN32 */ #include <signal.h> #include <setjmp.h> @@ -238,4 +238,4 @@ raw_n_truerand(int n) return v % n; } -#endif /* !CRYPTOLIB || !WIN32 */ +#endif /* !CRYPTOLIB || !_WIN32 */ diff --git a/Makefile.am b/Makefile.am index 352b28f..f429f2f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,6 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = 3rd_party common src include $(CYTHON_SUB) tools docs EXTRA_DIST = \ - docs \ README.md \ git-version-gen @@ -1,3 +1,34 @@ +Version 1.4.0 +~~~~~~~~~~~~~ + +* Development release + - From now on, releases will be made more frequently +* Changes: + - Add support for MbedTLS + - Add Reverse Proxy implementation + - Add support for wireless pairing (AppleTV) + - Embed 3rd party libraries for ed25519 and SRP6a + - Fixes in idevicedebug + - idevicecrashreport: Allow filtering crash reports by filename + - Add idevicedevmodectl tool + - Fixes for idevicebackup2 + - Add property_list_client_get_service_client() and service_get_connection() functions + - Add idevicebtlogger + - Add new idevice_events_subscribe/unsubscribe API + - Move LIBIMOBILEDEVICE_API to public headers + - Add afc_strerror function + - Add libimobiledevice_version() function + - Use libimobiledevice-glue's SHA1 implementation + - Add support for iOS 17+ Personalized Developer Disk image mounting + - Fix compilation on MSVC + - Add idevice_strerror() to interface + - Add new idevice_get_device_version() to interface + - Add os_trace_relay service implementation + - Fixes for idevicesyslog + - afc: Add afc_get_file_info_plist and afc_get_device_info_plist functions + ... and several other internal changes + + Version 1.3.0 ~~~~~~~~~~~~~ @@ -179,8 +179,8 @@ We are still working on the guidelines so bear with us! ## Links * Homepage: https://libimobiledevice.org/ -* Repository: https://git.libimobiledevice.org/libimobiledevice.git -* Repository (Mirror): https://github.com/libimobiledevice/libimobiledevice.git +* Repository: https://github.com/libimobiledevice/libimobiledevice.git +* Repository (Mirror): https://git.libimobiledevice.org/libimobiledevice.git * Issue Tracker: https://github.com/libimobiledevice/libimobiledevice/issues * Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel * Twitter: https://twitter.com/libimobiledev @@ -198,4 +198,4 @@ iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. This project is an independent software and has not been authorized, sponsored, or otherwise approved by Apple Inc. -README Updated on: 2024-06-27 +README Updated on: 2024-10-22 diff --git a/common/debug.c b/common/debug.c index cf1bc2f..7a593fc 100644 --- a/common/debug.c +++ b/common/debug.c @@ -30,6 +30,9 @@ #include <stdint.h> #include <stdlib.h> #include <time.h> +#ifndef _WIN32 +#include <sys/time.h> +#endif #include "src/idevice.h" #include "debug.h" @@ -51,27 +54,31 @@ void internal_set_debug_level(int level) #ifndef STRIP_DEBUG_CODE static void debug_print_line(const char *func, const char *file, int line, const char *buffer) { - char *str_time = NULL; - char *header = NULL; + char str_time[24]; +#ifdef _WIN32 + SYSTEMTIME lt; + GetLocalTime(<); + snprintf(str_time, 24, "%02d:%02d:%02d.%03d", lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds); +#else +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + struct tm *tp; + gettimeofday(&tv, NULL); +#ifdef HAVE_LOCALTIME_R + struct tm tp_; + tp = localtime_r(&tv.tv_sec, &tp_); +#else + tp = localtime(&tv.tv_sec); +#endif + strftime(str_time, 9, "%H:%M:%S", tp); + snprintf(str_time+8, 10, ".%03d", (int)tv.tv_usec/1000); +#else time_t the_time; - time(&the_time); - str_time = (char*)malloc(255); - strftime(str_time, 254, "%H:%M:%S", localtime (&the_time)); - - /* generate header text */ - if(asprintf(&header, "%s %s:%d %s()", str_time, file, line, func)<0){} - free (str_time); - - /* trim ending newlines */ - - /* print header */ - fprintf(stderr, "%s: ", header); - - /* print actual debug content */ - fprintf(stderr, "%s\n", buffer); - - free (header); + strftime(str_time, 15, "%H:%M:%S", localtime (&the_time)); +#endif +#endif + fprintf(stderr, "%s %s:%d %s(): %s\n", str_time, file, line, func, buffer); } #endif diff --git a/common/userpref.c b/common/userpref.c index 48bcfcb..76945e1 100644 --- a/common/userpref.c +++ b/common/userpref.c @@ -29,13 +29,18 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <errno.h> + #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif -#ifndef WIN32 +#include <dirent.h> +#ifndef _WIN32 #include <pwd.h> -#endif #include <unistd.h> +#include <libgen.h> +#include <sys/stat.h> +#endif #include <usbmuxd.h> #if defined(HAVE_OPENSSL) #include <openssl/bn.h> @@ -64,12 +69,7 @@ #error No supported TLS/SSL library enabled #endif -#include <dirent.h> -#include <libgen.h> -#include <sys/stat.h> -#include <errno.h> - -#ifdef WIN32 +#ifdef _WIN32 #include <shlobj.h> #endif @@ -77,6 +77,7 @@ #define ETIMEDOUT 138 #endif +#include <libimobiledevice/libimobiledevice.h> #include <libimobiledevice-glue/utils.h> #include "userpref.h" @@ -93,7 +94,7 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { }; #endif -#ifdef WIN32 +#ifdef _WIN32 #define DIR_SEP '\\' #define DIR_SEP_S "\\" #else @@ -103,7 +104,7 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { #define USERPREF_CONFIG_EXTENSION ".plist" -#ifdef WIN32 +#ifdef _WIN32 #define USERPREF_CONFIG_DIR "Apple"DIR_SEP_S"Lockdown" #else #define USERPREF_CONFIG_DIR "lockdown" @@ -113,7 +114,7 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { static char *__config_dir = NULL; -#ifdef WIN32 +#ifdef _WIN32 static char *userpref_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written) { if (!unistr || (len <= 0)) return NULL; @@ -155,7 +156,7 @@ const char *userpref_get_config_dir() if (__config_dir) return __config_dir; -#ifdef WIN32 +#ifdef _WIN32 wchar_t path[MAX_PATH+1]; HRESULT hr; LPITEMIDLIST pidl = NULL; @@ -419,7 +420,7 @@ static int _mbedtls_x509write_crt_set_basic_constraints_critical(mbedtls_x509wri * * @return 1 if keys were successfully generated, 0 otherwise */ -userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key) +userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key, unsigned int device_version) { userpref_error_t ret = USERPREF_E_SSL_ERROR; @@ -484,7 +485,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da X509_set_pubkey(root_cert, root_pkey); /* sign root cert with root private key */ - X509_sign(root_cert, root_pkey, EVP_sha1()); + X509_sign(root_cert, root_pkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? EVP_sha1() : EVP_sha256()); } /* create host certificate */ @@ -517,7 +518,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da X509_set_pubkey(host_cert, host_pkey); /* sign host cert with root private key */ - X509_sign(host_cert, root_pkey, EVP_sha1()); + X509_sign(host_cert, root_pkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? EVP_sha1() : EVP_sha256()); } if (root_cert && root_pkey && host_cert && host_pkey) { @@ -609,7 +610,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da X509_add_ext_helper(dev_cert, NID_key_usage, (char*)"critical,digitalSignature,keyEncipherment"); /* sign device certificate with root private key */ - if (X509_sign(dev_cert, root_pkey, EVP_sha1())) { + if (X509_sign(dev_cert, root_pkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? EVP_sha1() : EVP_sha256())) { /* if signing succeeded, export in PEM format */ BIO* membp = BIO_new(BIO_s_mem()); if (PEM_write_bio_X509(membp, dev_cert) > 0) { @@ -661,7 +662,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da gnutls_x509_crt_set_ca_status(root_cert, 1); gnutls_x509_crt_set_activation_time(root_cert, time(NULL)); gnutls_x509_crt_set_expiration_time(root_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); - gnutls_x509_crt_sign2(root_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0); + gnutls_x509_crt_sign2(root_cert, root_cert, root_privkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, 0); gnutls_x509_crt_set_key(host_cert, host_privkey); gnutls_x509_crt_set_serial(host_cert, "\x01", 1); @@ -670,7 +671,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da gnutls_x509_crt_set_key_usage(host_cert, GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_DIGITAL_SIGNATURE); gnutls_x509_crt_set_activation_time(host_cert, time(NULL)); gnutls_x509_crt_set_expiration_time(host_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); - gnutls_x509_crt_sign2(host_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0); + gnutls_x509_crt_sign2(host_cert, root_cert, root_privkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, 0); /* export to PEM format */ size_t root_key_export_size = 0; @@ -768,17 +769,17 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); /* use custom hash generation for compatibility with the "Apple ecosystem" */ - const gnutls_digest_algorithm_t dig_sha1 = GNUTLS_DIG_SHA1; - size_t hash_size = gnutls_hash_get_len(dig_sha1); + const gnutls_digest_algorithm_t dig_sha = (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256; + size_t hash_size = gnutls_hash_get_len(dig_sha); unsigned char hash[hash_size]; - if (gnutls_hash_fast(dig_sha1, der_pub_key.data, der_pub_key.size, (unsigned char*)&hash) < 0) { - debug_info("ERROR: Failed to generate SHA1 for public key"); + if (gnutls_hash_fast(dig_sha, der_pub_key.data, der_pub_key.size, (unsigned char*)&hash) < 0) { + debug_info("ERROR: Failed to generate SHA for public key"); } else { gnutls_x509_crt_set_subject_key_id(dev_cert, hash, hash_size); } gnutls_x509_crt_set_key_usage(dev_cert, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT); - gnutls_error = gnutls_x509_crt_sign2(dev_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0); + gnutls_error = gnutls_x509_crt_sign2(dev_cert, root_cert, root_privkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, 0); if (GNUTLS_E_SUCCESS == gnutls_error) { /* if everything went well, export in PEM format */ size_t export_size = 0; @@ -872,7 +873,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da /* sign root cert with root private key */ mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey); - mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1); + mbedtls_x509write_crt_set_md_alg(&cert, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? MBEDTLS_MD_SHA1 : MBEDTLS_MD_SHA256); unsigned char outbuf[16384]; @@ -931,7 +932,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da /* sign host cert with root private key */ mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey); - mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1); + mbedtls_x509write_crt_set_md_alg(&cert, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? MBEDTLS_MD_SHA1 : MBEDTLS_MD_SHA256); /* write host private key */ mbedtls_pk_write_key_pem(&host_pkey, outbuf, sizeof(outbuf)); @@ -991,7 +992,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da /* sign device certificate with root private key */ mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey); - mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1); + mbedtls_x509write_crt_set_md_alg(&cert, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? MBEDTLS_MD_SHA1 : MBEDTLS_MD_SHA256); /* write device certificate */ mbedtls_x509write_crt_pem(&cert, outbuf, sizeof(outbuf), mbedtls_ctr_drbg_random, &ctr_drbg); diff --git a/common/userpref.h b/common/userpref.h index 75bb8b7..9a1832c 100644 --- a/common/userpref.h +++ b/common/userpref.h @@ -68,7 +68,7 @@ userpref_error_t userpref_read_pair_record(const char *udid, plist_t *pair_recor userpref_error_t userpref_save_pair_record(const char *udid, uint32_t device_id, plist_t pair_record); userpref_error_t userpref_delete_pair_record(const char *udid); -userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key); +userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key, unsigned int device_version); #if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS) userpref_error_t pair_record_import_key_with_name(plist_t pair_record, const char* name, key_data_t* key); userpref_error_t pair_record_import_crt_with_name(plist_t pair_record, const char* name, key_data_t* cert); diff --git a/configure.ac b/configure.ac index c67e896..055fe04 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ dnl libtool versioning # changes to the signature and the semantic) # ? :+1 : ? == just internal changes # CURRENT : REVISION : AGE -LIBIMOBILEDEVICE_SO_VERSION=6:0:0 +LIBIMOBILEDEVICE_SO_VERSION=7:0:1 AC_SUBST(LIBIMOBILEDEVICE_SO_VERSION) @@ -70,7 +70,7 @@ AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. -AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf getifaddrs]) +AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf getifaddrs gettimeofday localtime_r]) AC_CHECK_HEADER(endian.h, [ac_cv_have_endian_h="yes"], [ac_cv_have_endian_h="no"]) if test "x$ac_cv_have_endian_h" = "xno"; then @@ -84,8 +84,24 @@ if test "x$ac_cv_have_endian_h" = "xno"; then fi fi +CACHED_CFLAGS="$CFLAGS" +CFLAGS+=" $libplist_CFLAGS -Werror" + AC_CHECK_DECL([plist_from_json], [AC_DEFINE([HAVE_PLIST_JSON], [1], [Define if libplist has JSON support])], [], [[#include <plist/plist.h>]]) +# check if libplist has plist_new_unix_date() +AC_CACHE_CHECK(for plist_new_unix_date, ac_cv_plist_unix_date, + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <plist/plist.h> + ]], [[ + return plist_new_unix_date(0) ? 0 : 1 + ]])],[ac_cv_plist_unix_date=yes],[ac_cv_plist_unix_date=no])) +if test "$ac_cv_plist_unix_date" = "yes"; then + AC_DEFINE(HAVE_PLIST_UNIX_DATE, 1, [Define if libplist has new unix date API (>= 2.7.0)]) +fi + +CFLAGS="$CACHED_CFLAGS" + # Check for operating system AC_MSG_CHECKING([for platform-specific build settings]) case ${host_os} in @@ -93,6 +109,7 @@ case ${host_os} in AC_MSG_RESULT([${host_os}]) win32=true AC_DEFINE(WINVER, 0x0501, [minimum Windows version]) + deplibs_check_method='pass_all' ;; darwin*) AC_MSG_RESULT([${host_os}]) @@ -107,24 +124,6 @@ esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) AM_CONDITIONAL(DARWIN, test x$darwin = xtrue) -# Check if the C compiler supports __attribute__((constructor)) -AC_CACHE_CHECK([wether the C compiler supports constructor/destructor attributes], - ac_cv_attribute_constructor, [ - ac_cv_attribute_constructor=no - AC_COMPILE_IFELSE([AC_LANG_PROGRAM( - [[ - static void __attribute__((constructor)) test_constructor(void) { - } - static void __attribute__((destructor)) test_destructor(void) { - } - ]], [])], - [ac_cv_attribute_constructor=yes] - )] -) -if test "$ac_cv_attribute_constructor" = "yes"; then - AC_DEFINE(HAVE_ATTRIBUTE_CONSTRUCTOR, 1, [Define if the C compiler supports constructor/destructor attributes]) -fi - AC_CHECK_MEMBER(struct dirent.d_type, AC_DEFINE(HAVE_DIRENT_D_TYPE, 1, [define if struct dirent has member d_type]),, [#include <dirent.h>]) # Cython Python Bindings diff --git a/cython/debugserver.pxi b/cython/debugserver.pxi index a3b7d1e..fb96320 100644 --- a/cython/debugserver.pxi +++ b/cython/debugserver.pxi @@ -43,13 +43,7 @@ cdef class DebugServerError(BaseError): BaseError.__init__(self, *args, **kwargs) -# from http://stackoverflow.com/a/17511714 -# https://github.com/libimobiledevice/libimobiledevice/pull/198 -from cpython cimport PY_MAJOR_VERSION -if PY_MAJOR_VERSION <= 2: - from cpython.string cimport PyString_AsString -else: - from cpython.bytes cimport PyBytes_AsString as PyString_AsString +from cpython.bytes cimport PyBytes_AsString as PyString_AsString cdef char ** to_cstring_array(list_str): if not list_str: return NULL diff --git a/docs/Makefile.am b/docs/Makefile.am index 8156d4f..c9b742f 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -21,6 +21,7 @@ man_MANS = \ idevicesetlocation.1 \ afcclient.1 -EXTRA_DIST = $(man_MANS) +EXTRA_DIST = $(man_MANS) \ + doxygen DISTCLEANFILES = html/* html diff --git a/docs/idevicecrashreport.1 b/docs/idevicecrashreport.1 index 6acd6e9..f117c87 100644 --- a/docs/idevicecrashreport.1 +++ b/docs/idevicecrashreport.1 @@ -16,28 +16,31 @@ the device to the target DIRECTORY. .SH OPTIONS .TP .B \-u, \-\-udid UDID -target specific device by UDID. +Target specific device by UDID. .TP .B \-n, \-\-network -connect to network device. +Connect to network device. .TP .B \-e, \-\-extract -extract raw crash report into separate '.crash' files. +Extract raw crash report into separate '.crash' files. .TP .B \-k, \-\-keep -copy but do not remove crash reports from device. +Copy but do not remove crash reports from device. .TP .B \-d, \-\-debug -enable communication debugging. +Enable communication debugging. .TP .B \-f, \-\-filter NAME -filter crash reports by NAME (case sensitive) +Filter crash reports by NAME (case sensitive) +.TP +.B \-\-remove\-all +Remove all crash log files without copying. Can be used with \f[B]-f\f[] to only remove matching files. .TP .B \-h, \-\-help -prints usage information. +Prints usage information. .TP .B \-v, \-\-version -prints version information. +Prints version information. .SH AUTHOR Martin Szulecki diff --git a/docs/idevicenotificationproxy.1 b/docs/idevicenotificationproxy.1 index 56dd0b0..627ee5d 100644 --- a/docs/idevicenotificationproxy.1 +++ b/docs/idevicenotificationproxy.1 @@ -12,19 +12,22 @@ Post or observe notifications on an iOS device from the command line. .SH OPTIONS .TP .B \-u, \-\-udid UDID -target specific device by UDID. +Target specific device by UDID. +.TP +.B \-i, \-\-insecure +Connect to insecure notification proxy (for non-paired devices). .TP .B \-n, \-\-network -connect to network device. +Connect to network device. .TP .B \-d, \-\-debug -enable communication debugging. +Enable communication debugging. .TP .B \-h, \-\-help -prints usage information. +Prints usage information. .TP .B \-v, \-\-version -prints version information. +Prints version information. .SH COMMANDS .TP @@ -38,6 +41,8 @@ observe notification IDs in the foreground until CTRL+C or signal is received. Martin Szulecki +Nikias Bassen + .SH ON THE WEB https://libimobiledevice.org diff --git a/docs/idevicesyslog.1 b/docs/idevicesyslog.1 index 66ae2e4..5a677b6 100644 --- a/docs/idevicesyslog.1 +++ b/docs/idevicesyslog.1 @@ -38,13 +38,45 @@ If FILE already exists, it will be overwritten without warning. .TP .B \-\-colors Force writing colored output, e.g. when using \f[B]\-\-output\f[]. +.TP +.B \-\-syslog\-relay +Use old syslog_relay service instead of os_trace_relay (iOS 9+). + +.SH COMMANDS +.TP +.B pidlist +Print a list with PID and name of all processes currently running on the device. +.TP +.B archive PATH +Request a logarchive from the device. It will be written in tar format to PATH. To pipe to another process use \- as PATH. +Below are some options to restrict the log message data. + +In order to view the logarchive in a compatible log viewer, you can pipe the output data to \f[B]tar\f[] and have it extract into a new directory: + +\f[B]mkdir test.logarchive && tools/idevicesyslog archive - |tar -C test.logarchive -xv\f[] + +This will also print the filenames while they are extracted. +.TP +Further options for \f[B]archive\f[]: +.TP +.B \-\-start\-time VALUE +Start time of the log data as UNIX timestamp. Earlier messages will be dropped. +.TP +.B \-\-age\-limit VALUE +Maximum age of the log data, supposedly number of days. +.TP +.B \-\-size\-limit VALUE +Limit the size of the archive. The unit is currently unknown, so feel free to experiment. +.TP +Keep in mind that the device usually only has a backlog of a few minutes so the options might not have the desired effect. This is not a bug. .SH FILTER OPTIONS .TP .B \-m, \-\-match STRING only print messages that contain STRING - -This option will set a filter to only printed log messages that contain the given string. +.TP +.B \-M, \-\-unmatch STRING +print messages that do not contain STRING .TP .B \-t, \-\-trigger STRING start logging when matching STRING diff --git a/doxygen.cfg.in b/doxygen.cfg.in index 4cbbb2d..7bc1160 100644 --- a/doxygen.cfg.in +++ b/doxygen.cfg.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.16 +# Doxyfile 1.11.0 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -53,6 +63,12 @@ PROJECT_BRIEF = "API Documentation" PROJECT_LOGO = docs/doxygen/logo-vector-clean.svg +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If @@ -60,16 +76,28 @@ PROJECT_LOGO = docs/doxygen/logo-vector-clean.svg OUTPUT_DIRECTORY = docs -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,14 +109,14 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English @@ -209,6 +237,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -232,16 +268,16 @@ TAB_SIZE = 8 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = @@ -285,19 +321,22 @@ OPTIMIZE_OUTPUT_SLICE = YES # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is -# Fortran), use: inc=Fortran f=C. +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = @@ -315,11 +354,22 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. +# Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -332,8 +382,8 @@ AUTOLINK_SUPPORT = YES # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO @@ -345,9 +395,9 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO @@ -431,6 +481,27 @@ TYPEDEF_HIDES_STRUCT = YES LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = YES + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -494,6 +565,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -505,14 +583,15 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -531,12 +610,20 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) ands Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO @@ -554,6 +641,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -711,7 +804,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -757,24 +851,50 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO @@ -785,13 +905,27 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -805,19 +939,29 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = \ - include/@PACKAGE@ \ - README.md +INPUT = include/@PACKAGE@ \ + README.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -826,11 +970,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = @@ -869,10 +1017,7 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = LIBIMOBILEDEVICE_API @@ -917,6 +1062,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -958,6 +1108,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = README.md +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -972,7 +1131,8 @@ USE_MDFILE_AS_MAINPAGE = README.md SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO @@ -1055,10 +1215,11 @@ VERBATIM_HEADERS = NO ALPHABETICAL_INDEX = NO -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1137,11 +1298,15 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = \ - docs/doxygen/custom.css +HTML_EXTRA_STYLESHEET = docs/doxygen/custom.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1151,15 +1316,27 @@ HTML_EXTRA_STYLESHEET = \ # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = \ - docs/doxygen/favicon.ico \ - docs/doxygen/folder.png \ - docs/doxygen/folder-open.png \ - docs/doxygen/text-x-generic.png +HTML_EXTRA_FILES = docs/doxygen/favicon.ico \ + docs/doxygen/folder.png \ + docs/doxygen/folder-open.png \ + docs/doxygen/text-x-generic.png + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1169,7 +1346,7 @@ HTML_EXTRA_FILES = \ HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1187,20 +1364,11 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will +# are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, +# page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1215,6 +1383,33 @@ HTML_DYNAMIC_MENUS = NO HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1230,10 +1425,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1250,6 +1446,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1275,8 +1478,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1306,7 +1513,7 @@ CHM_FILE = HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1333,6 +1540,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1351,7 +1568,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1359,8 +1577,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1368,16 +1586,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1389,9 +1607,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1434,16 +1652,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NONE +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1468,6 +1698,24 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1477,19 +1725,14 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. -FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1499,11 +1742,29 @@ FORMULA_TRANSPARENT = YES USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1516,22 +1777,29 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1559,7 +1827,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1578,7 +1846,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1591,8 +1860,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1701,29 +1971,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1756,18 +2028,26 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = NO -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. USE_PDFLATEX = NO -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if <return> is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1788,14 +2068,6 @@ LATEX_HIDE_INDICES = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -1860,6 +2132,14 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTRA_FILES = + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -1961,7 +2241,7 @@ DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. @@ -1969,6 +2249,32 @@ DOCBOOK_OUTPUT = docbook GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + +#--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2042,7 +2348,8 @@ SEARCH_INCLUDES = NO # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2109,15 +2416,15 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2131,25 +2438,9 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2158,7 +2449,7 @@ HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. @@ -2175,49 +2466,77 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node, +# Edge and Graph Attributes specification</a> You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a +# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about +# arrows shapes.</a> +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' <a +# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a> +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2240,10 +2559,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2255,7 +2596,9 @@ TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2264,7 +2607,10 @@ INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2304,16 +2650,26 @@ GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2350,11 +2706,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2363,10 +2720,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2383,7 +2740,7 @@ PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. @@ -2404,18 +2761,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2428,14 +2773,34 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# <outfile_format> -o <outputfile> <inputfile>. The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/include/Makefile.am b/include/Makefile.am index 2abaf49..a41f53d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -23,9 +23,11 @@ nobase_include_HEADERS = \ libimobiledevice/diagnostics_relay.h \ libimobiledevice/debugserver.h \ libimobiledevice/syslog_relay.h \ + libimobiledevice/ostrace.h \ libimobiledevice/mobileactivation.h \ libimobiledevice/preboard.h \ libimobiledevice/companion_proxy.h \ libimobiledevice/reverse_proxy.h \ + libimobiledevice/bt_packet_logger.h \ libimobiledevice/property_list_service.h \ libimobiledevice/service.h diff --git a/include/libimobiledevice/afc.h b/include/libimobiledevice/afc.h index 4ad3dbd..3dcb5da 100644 --- a/include/libimobiledevice/afc.h +++ b/include/libimobiledevice/afc.h @@ -145,6 +145,20 @@ LIBIMOBILEDEVICE_API afc_error_t afc_client_free(afc_client_t client); LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info(afc_client_t client, char ***device_information); /** + * Get device information for a connected client. The device information + * returned is the device model as well as the free space, the total capacity + * and blocksize on the accessed disk partition. + * + * @param client The client to get device info for. + * @param device_information A pointer to a plist_t that will be populated + * with key-value pairs (dictionary) representing the device’s + * storage and model information. Free with plist_free(). + * + * @return AFC_E_SUCCESS on success or an AFC_E_* error value. + */ +LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information); + +/** * Gets a directory listing of the directory requested. * * @param client The client to get a directory listing from. @@ -163,7 +177,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c * @param client The client to use to get the information of the file. * @param path The fully-qualified path to the file. * @param file_information Pointer to a buffer that will be filled with a - * NULL-terminated list of strings with the file information. Set to NULL + * NULL-terminated list of strings with the file attributes. Set to NULL * before calling this function. Free with afc_dictionary_free(). * * @return AFC_E_SUCCESS on success or an AFC_E_* error value. @@ -171,6 +185,19 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information); /** + * Gets information about a specific file. + * + * @param client The client to use to get the information of the file. + * @param path The fully-qualified path to the file. + * @param file_information A pointer to a plist_t that will be populated + * with key-value pairs (dictionary) representing the file attributes. + * Free with plist_free(). + * + * @return AFC_E_SUCCESS on success or an AFC_E_* error value. + */ +LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information); + +/** * Opens a file on the device. * * @param client The client to use to open the file. diff --git a/include/libimobiledevice/libimobiledevice.h b/include/libimobiledevice/libimobiledevice.h index a9d270b..bc57778 100644 --- a/include/libimobiledevice/libimobiledevice.h +++ b/include/libimobiledevice/libimobiledevice.h @@ -31,10 +31,13 @@ extern "C" { #endif #include <stdint.h> -#include <sys/types.h> -#include <sys/stat.h> #include <plist/plist.h> +#if defined(_MSC_VER) +#include <basetsd.h> +typedef SSIZE_T ssize_t; +#endif + #ifndef LIBIMOBILEDEVICE_API #ifdef LIBIMOBILEDEVICE_STATIC #define LIBIMOBILEDEVICE_API @@ -401,12 +404,38 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_handle(idevice_t device, uint32 LIBIMOBILEDEVICE_API idevice_error_t idevice_get_udid(idevice_t device, char **udid); /** + * Returns the device ProductVersion in numerical form, where "X.Y.Z" + * will be returned as (X << 16) | (Y << 8) | Z . + * Use IDEVICE_DEVICE_VERSION macro for easy version comparison. + * @see IDEVICE_DEVICE_VERSION + * + * @param client Initialized device client + * + * @return A numerical representation of the X.Y.Z ProductVersion string + * or 0 if the version cannot be retrieved. + */ +LIBIMOBILEDEVICE_API unsigned int idevice_get_device_version(idevice_t device); + +/** + * Gets a readable error string for a given idevice error code. + * + * @param err An idevice error code + * + * @return A readable error string + */ +LIBIMOBILEDEVICE_API const char* idevice_strerror(idevice_error_t err); + +/** * Returns a static string of the libimobiledevice version. * * @return The libimobiledevice version as static ascii string */ LIBIMOBILEDEVICE_API const char* libimobiledevice_version(); +/* macros */ +/** Helper macro to get a numerical representation of a product version tuple */ +#define IDEVICE_DEVICE_VERSION(maj, min, patch) ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF)) + #ifdef __cplusplus } #endif diff --git a/include/libimobiledevice/ostrace.h b/include/libimobiledevice/ostrace.h new file mode 100644 index 0000000..f083ba7 --- /dev/null +++ b/include/libimobiledevice/ostrace.h @@ -0,0 +1,198 @@ +/** + * @file libimobiledevice/ostrace.h + * @brief System log and tracing capabilities. + * \internal + * + * Copyright (c) 2020-2025 Nikias Bassen, 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 + */ + +#ifndef OSTRACE_H +#define OSTRACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libimobiledevice/libimobiledevice.h> +#include <libimobiledevice/lockdown.h> + +/** Service identifier passed to lockdownd_start_service() to start the os trace relay service */ +#define OSTRACE_SERVICE_NAME "com.apple.os_trace_relay" + +/** Error Codes */ +typedef enum { + OSTRACE_E_SUCCESS = 0, + OSTRACE_E_INVALID_ARG = -1, + OSTRACE_E_MUX_ERROR = -2, + OSTRACE_E_SSL_ERROR = -3, + OSTRACE_E_NOT_ENOUGH_DATA = -4, + OSTRACE_E_TIMEOUT = -5, + OSTRACE_E_PLIST_ERROR = -6, + OSTRACE_E_REQUEST_FAILED = -7, + OSTRACE_E_UNKNOWN_ERROR = -256 +} ostrace_error_t; + +typedef struct ostrace_client_private ostrace_client_private; /**< \private */ +typedef ostrace_client_private *ostrace_client_t; /**< The client handle. */ + +#pragma pack(push,1) +struct ostrace_packet_header_t { + uint8_t marker; + uint32_t type; + uint32_t header_size; // 0x81 + uint32_t pid; + uint64_t procid; // == pid + unsigned char procuuid[16]; // procuuid + uint16_t procpath_len; // path to process + uint64_t aid; // activity id, usually 0 + uint64_t paid; // (parent?) activity id, usually 0 + uint64_t time_sec; // tv.tv_sec 64 bit + uint32_t time_usec; // tv.usec 32 bit + uint8_t unk06; + uint8_t level; // Notice=0, Info=0x01, Debug=0x02, Error=0x10, Fault=0x11 + uint8_t unk07; + uint8_t unk08; + uint8_t unk09; + uint8_t unk10; + uint8_t unk11; + uint8_t unk12; + uint64_t timestamp; // ? + uint32_t thread_id; + uint32_t unk13; // 0 + unsigned char imageuuid[16]; // framework/dylib uuid + uint16_t imagepath_len; // framework/dylib + uint32_t message_len; // actual log message + uint32_t offset; // offset for like timestamp or sth + uint16_t subsystem_len; // "subsystem" + uint16_t unk14; + uint16_t category_len; // "category" + uint16_t unk15; + uint32_t unk16; // 0 +}; +#pragma pack(pop) + +/** Receives unparsed ostrace data from the ostrace service */ +typedef void (*ostrace_activity_cb_t)(const void* buf, size_t len, void *user_data); + +/** Receives archive data from the ostrace service */ +typedef int (*ostrace_archive_write_cb_t)(const void* buf, size_t len, void *user_data); + +/* Interface */ + +/** + * Connects to the os_trace_relay service on the specified device. + * + * @param device The device to connect to. + * @param service The service descriptor returned by lockdownd_start_service. + * @param client Pointer that will point to a newly allocated + * ostrace_client_t upon successful return. Must be freed using + * ostrace_client_free() after use. + * + * @return OSTRACE_E_SUCCESS on success, OSTRACE_E_INVALID_ARG when + * client is NULL, or an OSTRACE_E_* error code otherwise. + */ +LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client); + +/** + * Starts a new os_trace_relay service on the specified device and connects to it. + * + * @param device The device to connect to. + * @param client Pointer that will point to a newly allocated + * ostrace_client_t upon successful return. Must be freed using + * ostrace_client_free() after use. + * @param label The label to use for communication. Usually the program name. + * Pass NULL to disable sending the label in requests to lockdownd. + * + * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise. + */ +LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label); + +/** + * Disconnects a ostrace client from the device and frees up the + * ostrace client data. + * + * @param client The ostrace client to disconnect and free. + * + * @return OSTRACE_E_SUCCESS on success, OSTRACE_E_INVALID_ARG when + * client is NULL, or an OSTRACE_E_* error code otherwise. + */ +LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_free(ostrace_client_t client); + +/** + * Starts capturing OS trace activity data of the device using a callback. + * + * Use ostrace_stop_activity() to stop receiving the ostrace. + * + * @param client The ostrace client to use + * @param options Options dictionary to pass to StartActivity request. + * Valid options are MessageFilter (PLIST_INT, default 65535), + * Pid (PLIST_INT, default -1), and StreamFlags (PLIST_INT, default 60) + * @param callback Callback to receive data from ostrace. + * @param user_data Custom pointer passed to the callback function. + * @param user_data_free_func Function pointer that will be called when the + * activity is stopped to release user_data. Can be NULL for none. + * + * @return OSTRACE_E_SUCCESS on success, + * OSTRACE_E_INVALID_ARG when one or more parameters are + * invalid or OSTRACE_E_UNKNOWN_ERROR when an unspecified + * error occurs or an ostrace activity has already been started. + */ +LIBIMOBILEDEVICE_API ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data); + +/** + * Stops the ostrace activity. + * + * Use ostrace_start_activity() to start receiving OS trace data. + * + * @param client The ostrace client to use + * + * @return OSTRACE_E_SUCCESS on success, + * OSTRACE_E_INVALID_ARG when one or more parameters are + * invalid or OSTRACE_E_UNKNOWN_ERROR when an unspecified + * error occurs or an ostrace activity has already been started. + */ +LIBIMOBILEDEVICE_API ostrace_error_t ostrace_stop_activity(ostrace_client_t client); + +/** + * Returns a dictionary with all currently running processes on the device. + * + * @param client The ostrace client to use + * @param list Pointer that will receive an allocated PLIST_DICT structure with the process data + * + * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise + */ +LIBIMOBILEDEVICE_API ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list); + +/** + * Creates a syslog archive. + * + * @note The device will close the connection once the transfer is complete. The client + * is not usable after that anymore and must be disposed with ostrace_client_free. + * + * @param client The ostrace client to use + * @param options A dictionary with options for the request. + * Valid parameters are StartTime (PLIST_UINT), SizeLimit (PLIST_UINT), and AgeLimit (PLIST_UINT). + * + * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise + */ +LIBIMOBILEDEVICE_API ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4 index 44dbd83..935056c 100644 --- a/m4/ax_python_devel.m4 +++ b/m4/ax_python_devel.m4 @@ -4,7 +4,7 @@ # # SYNOPSIS # -# AX_PYTHON_DEVEL([version]) +# AX_PYTHON_DEVEL([version[,optional]]) # # DESCRIPTION # @@ -23,6 +23,11 @@ # version number. Don't use "PYTHON_VERSION" for this: that environment # variable is declared as precious and thus reserved for the end-user. # +# By default this will fail if it does not detect a development version of +# python. If you want it to continue, set optional to true, like +# AX_PYTHON_DEVEL([], [true]). The ax_python_devel_found variable will be +# "no" if it fails. +# # This macro should work for all versions of Python >= 2.1.0. As an end # user, you can disable the check for the python version by setting the # PYTHON_NOVERSIONCHECK environment variable to something else than the @@ -67,10 +72,18 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 21 +#serial 37 AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) AC_DEFUN([AX_PYTHON_DEVEL],[ + # Get whether it's optional + if test -z "$2"; then + ax_python_devel_optional=false + else + ax_python_devel_optional=$2 + fi + ax_python_devel_found=yes + # # Allow the use of a (user set) custom python version # @@ -81,21 +94,26 @@ AC_DEFUN([AX_PYTHON_DEVEL],[ AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) if test -z "$PYTHON"; then - AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) + AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up, python development not available]) + fi + ax_python_devel_found=no PYTHON_VERSION="" fi - # - # Check for a version of Python >= 2.1.0 - # - AC_MSG_CHECKING([for a version of Python >= '2.1.0']) - ac_supports_python_ver=`$PYTHON -c "import sys; \ + if test $ax_python_devel_found = yes; then + # + # Check for a version of Python >= 2.1.0 + # + AC_MSG_CHECKING([for a version of Python >= '2.1.0']) + ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[[0]]; \ print (ver >= '2.1.0')"` - if test "$ac_supports_python_ver" != "True"; then + if test "$ac_supports_python_ver" != "True"; then if test -z "$PYTHON_NOVERSIONCHECK"; then AC_MSG_RESULT([no]) - AC_MSG_FAILURE([ + AC_MSG_WARN([ This version of the AC@&t@_PYTHON_DEVEL macro doesn't work properly with versions of Python before 2.1.0. You may need to re-run configure, setting the @@ -104,58 +122,119 @@ PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. Moreover, to disable this check, set PYTHON_NOVERSIONCHECK to something else than an empty string. ]) + if ! $ax_python_devel_optional; then + AC_MSG_FAILURE([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" else AC_MSG_RESULT([skip at user request]) fi - else + else AC_MSG_RESULT([yes]) + fi fi - # - # if the macro parameter ``version'' is set, honour it - # - if test -n "$1"; then + if test $ax_python_devel_found = yes; then + # + # If the macro parameter ``version'' is set, honour it. + # A Python shim class, VPy, is used to implement correct version comparisons via + # string expressions, since e.g. a naive textual ">= 2.7.3" won't work for + # Python 2.7.10 (the ".1" being evaluated as less than ".3"). + # + if test -n "$1"; then AC_MSG_CHECKING([for a version of Python $1]) - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[[0]]; \ + cat << EOF > ax_python_devel_vpy.py +class VPy: + def vtup(self, s): + return tuple(map(int, s.strip().replace("rc", ".").split("."))) + def __init__(self): + import sys + self.vpy = tuple(sys.version_info)[[:3]] + def __eq__(self, s): + return self.vpy == self.vtup(s) + def __ne__(self, s): + return self.vpy != self.vtup(s) + def __lt__(self, s): + return self.vpy < self.vtup(s) + def __gt__(self, s): + return self.vpy > self.vtup(s) + def __le__(self, s): + return self.vpy <= self.vtup(s) + def __ge__(self, s): + return self.vpy >= self.vtup(s) +EOF + ac_supports_python_ver=`$PYTHON -c "import ax_python_devel_vpy; \ + ver = ax_python_devel_vpy.VPy(); \ print (ver $1)"` + rm -rf ax_python_devel_vpy*.py* __pycache__/ax_python_devel_vpy*.py* if test "$ac_supports_python_ver" = "True"; then - AC_MSG_RESULT([yes]) + AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) - AC_MSG_ERROR([this package requires Python $1. + AC_MSG_WARN([this package requires Python $1. If you have it installed, but it isn't the default Python interpreter in your system path, please pass the PYTHON_VERSION variable to configure. See ``configure --help'' for reference. ]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no PYTHON_VERSION="" fi + fi fi - # - # Check if you have distutils, else fail - # - AC_MSG_CHECKING([for the distutils Python package]) - ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` - if test $? -eq 0; then + if test $ax_python_devel_found = yes; then + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the sysconfig Python package]) + ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1` + if test $? -eq 0; then AC_MSG_RESULT([yes]) - else + IMPORT_SYSCONFIG="import sysconfig" + else AC_MSG_RESULT([no]) - AC_MSG_ERROR([cannot import Python module "distutils". + + AC_MSG_CHECKING([for the distutils Python package]) + ac_sysconfig_result=`$PYTHON -c "from distutils import sysconfig" 2>&1` + if test $? -eq 0; then + AC_MSG_RESULT([yes]) + IMPORT_SYSCONFIG="from distutils import sysconfig" + else + AC_MSG_WARN([cannot import Python module "distutils". Please check your Python installation. The error was: -$ac_distutils_result]) - PYTHON_VERSION="" +$ac_sysconfig_result]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" + fi + fi fi - # - # Check for Python include path - # - AC_MSG_CHECKING([for Python include path]) - if test -z "$PYTHON_CPPFLAGS"; then - python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc ());"` - plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc (plat_specific=1));"` + if test $ax_python_devel_found = yes; then + # + # Check for Python include path + # + AC_MSG_CHECKING([for Python include path]) + if test -z "$PYTHON_CPPFLAGS"; then + if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then + # sysconfig module has different functions + python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_path ('include'));"` + plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_path ('platinclude'));"` + else + # old distutils way + python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_inc ());"` + plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_inc (plat_specific=1));"` + fi if test -n "${python_path}"; then if test "${plat_python_path}" != "${python_path}"; then python_path="-I$python_path -I$plat_python_path" @@ -164,22 +243,22 @@ $ac_distutils_result]) fi fi PYTHON_CPPFLAGS=$python_path - fi - AC_MSG_RESULT([$PYTHON_CPPFLAGS]) - AC_SUBST([PYTHON_CPPFLAGS]) + fi + AC_MSG_RESULT([$PYTHON_CPPFLAGS]) + AC_SUBST([PYTHON_CPPFLAGS]) - # - # Check for Python library path - # - AC_MSG_CHECKING([for Python library path]) - if test -z "$PYTHON_LIBS"; then + # + # Check for Python library path + # + AC_MSG_CHECKING([for Python library path]) + if test -z "$PYTHON_LIBS"; then # (makes two attempts to ensure we've got a version number # from the interpreter) ac_python_version=`cat<<EOD | $PYTHON - # join all versioning strings, on some systems # major/minor numbers could be in different list elements -from distutils.sysconfig import * +from sysconfig import * e = get_config_var('VERSION') if e is not None: print(e) @@ -190,7 +269,7 @@ EOD` ac_python_version=$PYTHON_VERSION else ac_python_version=`$PYTHON -c "import sys; \ - print (sys.version[[:3]])"` + print ("%d.%d" % sys.version_info[[:2]])"` fi fi @@ -202,8 +281,8 @@ EOD` ac_python_libdir=`cat<<EOD | $PYTHON - # There should be only one -import distutils.sysconfig -e = distutils.sysconfig.get_config_var('LIBDIR') +$IMPORT_SYSCONFIG +e = sysconfig.get_config_var('LIBDIR') if e is not None: print (e) EOD` @@ -211,8 +290,8 @@ EOD` # Now, for the library: ac_python_library=`cat<<EOD | $PYTHON - -import distutils.sysconfig -c = distutils.sysconfig.get_config_vars() +$IMPORT_SYSCONFIG +c = sysconfig.get_config_vars() if 'LDVERSION' in c: print ('python'+c[['LDVERSION']]) else: @@ -231,83 +310,140 @@ EOD` else # old way: use libpython from python_configdir ac_python_libdir=`$PYTHON -c \ - "from distutils.sysconfig import get_python_lib as f; \ + "from sysconfig import get_python_lib as f; \ import os; \ print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"` PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version" fi - if test -z "PYTHON_LIBS"; then - AC_MSG_ERROR([ + if test -z "$PYTHON_LIBS"; then + AC_MSG_WARN([ Cannot determine location of your Python DSO. Please check it was installed with dynamic libraries enabled, or try setting PYTHON_LIBS by hand. ]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" fi + fi fi - AC_MSG_RESULT([$PYTHON_LIBS]) - AC_SUBST([PYTHON_LIBS]) - # - # Check for site packages - # - AC_MSG_CHECKING([for Python site-packages path]) - if test -z "$PYTHON_SITE_PKG"; then - PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_lib(0,0));"` - fi - AC_MSG_RESULT([$PYTHON_SITE_PKG]) - AC_SUBST([PYTHON_SITE_PKG]) + if test $ax_python_devel_found = yes; then + AC_MSG_RESULT([$PYTHON_LIBS]) + AC_SUBST([PYTHON_LIBS]) - # - # libraries which must be linked in when embedding - # - AC_MSG_CHECKING(python extra libraries) - if test -z "$PYTHON_EXTRA_LIBS"; then - PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \ - conf = distutils.sysconfig.get_config_var; \ + # + # Check for site packages + # + AC_MSG_CHECKING([for Python site-packages path]) + if test -z "$PYTHON_SITE_PKG"; then + if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then + PYTHON_SITE_PKG=`$PYTHON -c " +$IMPORT_SYSCONFIG; +if hasattr(sysconfig, 'get_default_scheme'): + scheme = sysconfig.get_default_scheme() +else: + scheme = sysconfig._get_default_scheme() +if scheme == 'posix_local': + # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ + scheme = 'posix_prefix' +prefix = '$prefix' +if prefix == 'NONE': + prefix = '$ac_default_prefix' +sitedir = sysconfig.get_path('purelib', scheme, vars={'base': prefix}) +print(sitedir)"` + else + # distutils.sysconfig way + PYTHON_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_lib(0,0));"` + fi + fi + AC_MSG_RESULT([$PYTHON_SITE_PKG]) + AC_SUBST([PYTHON_SITE_PKG]) + + # + # Check for platform-specific site packages + # + AC_MSG_CHECKING([for Python platform specific site-packages path]) + if test -z "$PYTHON_PLATFORM_SITE_PKG"; then + if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then + PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c " +$IMPORT_SYSCONFIG; +if hasattr(sysconfig, 'get_default_scheme'): + scheme = sysconfig.get_default_scheme() +else: + scheme = sysconfig._get_default_scheme() +if scheme == 'posix_local': + # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ + scheme = 'posix_prefix' +prefix = '$prefix' +if prefix == 'NONE': + prefix = '$ac_default_prefix' +sitedir = sysconfig.get_path('platlib', scheme, vars={'platbase': prefix}) +print(sitedir)"` + else + # distutils.sysconfig way + PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_lib(1,0));"` + fi + fi + AC_MSG_RESULT([$PYTHON_PLATFORM_SITE_PKG]) + AC_SUBST([PYTHON_PLATFORM_SITE_PKG]) + + # + # libraries which must be linked in when embedding + # + AC_MSG_CHECKING(python extra libraries) + if test -z "$PYTHON_EXTRA_LIBS"; then + PYTHON_EXTRA_LIBS=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + conf = sysconfig.get_config_var; \ print (conf('LIBS') + ' ' + conf('SYSLIBS'))"` - fi - AC_MSG_RESULT([$PYTHON_EXTRA_LIBS]) - AC_SUBST(PYTHON_EXTRA_LIBS) + fi + AC_MSG_RESULT([$PYTHON_EXTRA_LIBS]) + AC_SUBST(PYTHON_EXTRA_LIBS) - # - # linking flags needed when embedding - # - AC_MSG_CHECKING(python extra linking flags) - if test -z "$PYTHON_EXTRA_LDFLAGS"; then - PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \ - conf = distutils.sysconfig.get_config_var; \ + # + # linking flags needed when embedding + # + AC_MSG_CHECKING(python extra linking flags) + if test -z "$PYTHON_EXTRA_LDFLAGS"; then + PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + conf = sysconfig.get_config_var; \ print (conf('LINKFORSHARED'))"` - fi - AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS]) - AC_SUBST(PYTHON_EXTRA_LDFLAGS) + # Hack for macos, it sticks this in here. + PYTHON_EXTRA_LDFLAGS=`echo $PYTHON_EXTRA_LDFLAGS | sed 's/CoreFoundation.*$/CoreFoundation/'` + fi + AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS]) + AC_SUBST(PYTHON_EXTRA_LDFLAGS) - # - # final check to see if everything compiles alright - # - AC_MSG_CHECKING([consistency of all components of python development environment]) - # save current global flags - ac_save_LIBS="$LIBS" - ac_save_LDFLAGS="$LDFLAGS" - ac_save_CPPFLAGS="$CPPFLAGS" - LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS $PYTHON_EXTRA_LIBS" - LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS" - CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" - AC_LANG_PUSH([C]) - AC_LINK_IFELSE([ + # + # final check to see if everything compiles alright + # + AC_MSG_CHECKING([consistency of all components of python development environment]) + # save current global flags + ac_save_LIBS="$LIBS" + ac_save_LDFLAGS="$LDFLAGS" + ac_save_CPPFLAGS="$CPPFLAGS" + LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS" + LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS" + CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" + AC_LANG_PUSH([C]) + AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include <Python.h>]], [[Py_Initialize();]]) ],[pythonexists=yes],[pythonexists=no]) - AC_LANG_POP([C]) - # turn back to default flags - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - LDFLAGS="$ac_save_LDFLAGS" + AC_LANG_POP([C]) + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + LDFLAGS="$ac_save_LDFLAGS" - AC_MSG_RESULT([$pythonexists]) + AC_MSG_RESULT([$pythonexists]) - if test ! "x$pythonexists" = "xyes"; then - AC_MSG_FAILURE([ + if test ! "x$pythonexists" = "xyes"; then + AC_MSG_WARN([ Could not link test program to Python. Maybe the main Python library has been installed in some non-standard library path. If so, pass it to configure, via the LIBS environment variable. @@ -317,8 +453,13 @@ EOD` You probably have to install the development version of the Python package for your distribution. The exact name of this package varies among them. ============================================================================ - ]) - PYTHON_VERSION="" + ]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" + fi fi # diff --git a/src/Makefile.am b/src/Makefile.am index 58cf07c..1ee9be8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -58,6 +58,7 @@ libimobiledevice_1_0_la_SOURCES = \ companion_proxy.c companion_proxy.h \ reverse_proxy.c reverse_proxy.h \ syslog_relay.c syslog_relay.h \ + ostrace.c ostrace.h \ bt_packet_logger.c bt_packet_logger.h if WIN32 @@ -26,9 +26,12 @@ #endif #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> +#ifndef _MSC_VER +#include <unistd.h> +#endif + #include "idevice.h" #include "afc.h" #include "common/debug.h" @@ -115,7 +118,7 @@ afc_error_t afc_client_new(idevice_t device, lockdownd_service_descriptor_t serv afc_error_t afc_client_start_service(idevice_t device, afc_client_t * client, const char* label) { - afc_error_t err = AFC_E_UNKNOWN_ERROR; + int32_t err = AFC_E_UNKNOWN_ERROR; service_client_factory_start_service(device, AFC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(afc_client_new), &err); return err; } @@ -338,7 +341,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t free(buf); debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); -#ifndef WIN32 +#ifndef _WIN32 fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1); #endif @@ -399,6 +402,50 @@ static char **make_strings_list(char *tokens, uint32_t length) return list; } +static plist_t *make_dictionary(char *tokens, size_t length) +{ + size_t j = 0; + plist_t dict = NULL; + + if (!tokens || !length) + return NULL; + + dict = plist_new_dict(); + + while (j < length) { + size_t key_len = strnlen(tokens + j, length - j); + if (j + key_len >= length) { + plist_free(dict); + return NULL; + } + char* key = tokens + j; + j += key_len + 1; + + if (j >= length) { + plist_free(dict); + return NULL; + } + + size_t val_len = strnlen(tokens + j, length - j); + if (j + val_len >= length) { + plist_free(dict); + return NULL; + } + char* val = tokens + j; + j += val_len + 1; + + char* endp = NULL; + unsigned long long u64val = strtoull(val, &endp, 10); + if (endp && *endp == '\0') { + plist_dict_set_item(dict, key, plist_new_uint(u64val)); + } else { + plist_dict_set_item(dict, key, plist_new_string(val)); + } + } + + return dict; +} + static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len) { if (data_len > client->packet_extra) { @@ -495,6 +542,40 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***device_information) return ret; } +afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information) +{ + uint32_t bytes = 0; + char *data = NULL; + afc_error_t ret = AFC_E_UNKNOWN_ERROR; + + if (!client || !device_information) + return AFC_E_INVALID_ARG; + + afc_lock(client); + + /* Send the command */ + ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes); + if (ret != AFC_E_SUCCESS) { + afc_unlock(client); + return AFC_E_NOT_ENOUGH_DATA; + } + /* Receive the data */ + ret = afc_receive_data(client, &data, &bytes); + if (ret != AFC_E_SUCCESS) { + if (data) + free(data); + afc_unlock(client); + return ret; + } + /* Parse the data */ + *device_information = make_dictionary(data, bytes); + free(data); + + afc_unlock(client); + + return ret; +} + afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) { afc_error_t ret = AFC_E_INTERNAL_ERROR; @@ -644,8 +725,6 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***fil return AFC_E_NO_MEM; } - debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR); - /* Send command */ memcpy(AFC_PACKET_DATA_PTR, path, data_len); ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); @@ -666,6 +745,44 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***fil return ret; } +afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information) +{ + char *received = NULL; + uint32_t bytes = 0; + afc_error_t ret = AFC_E_UNKNOWN_ERROR; + + if (!client || !path || !file_information) + return AFC_E_INVALID_ARG; + + afc_lock(client); + + uint32_t data_len = (uint32_t)strlen(path)+1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + + /* Send command */ + memcpy(AFC_PACKET_DATA_PTR, path, data_len); + ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); + if (ret != AFC_E_SUCCESS) { + afc_unlock(client); + return AFC_E_NOT_ENOUGH_DATA; + } + + /* Receive data */ + ret = afc_receive_data(client, &received, &bytes); + if (received) { + *file_information = make_dictionary(received, bytes); + free(received); + } + + afc_unlock(client); + + return ret; +} + afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) { if (!client || !client->parent || !client->afc_packet) diff --git a/src/house_arrest.c b/src/house_arrest.c index caad731..06068c6 100644 --- a/src/house_arrest.c +++ b/src/house_arrest.c @@ -24,7 +24,11 @@ #endif #include <string.h> #include <stdlib.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> #include "house_arrest.h" diff --git a/src/idevice.c b/src/idevice.c index b9bbb1f..0af27fd 100644 --- a/src/idevice.c +++ b/src/idevice.c @@ -30,7 +30,7 @@ #include <errno.h> #include <time.h> -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> @@ -124,32 +124,32 @@ static void id_function(CRYPTO_THREADID *thread) #endif #endif /* HAVE_OPENSSL */ -static void internal_idevice_init(void) -{ -#if defined(HAVE_OPENSSL) -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - int i; - SSL_library_init(); - - mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); - if (!mutex_buf) - return; - for (i = 0; i < CRYPTO_num_locks(); i++) - mutex_init(&mutex_buf[i]); - -#if OPENSSL_VERSION_NUMBER < 0x10000000L - CRYPTO_set_id_callback(id_function); +// Reference: https://stackoverflow.com/a/2390626/1806760 +// Initializer/finalizer sample for MSVC and GCC/Clang. +// 2010-2016 Joe Lowe. Released into the public domain. + +#ifdef __cplusplus + #define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) + #pragma section(".CRT$XCU",read) + #define INITIALIZER2_(f,p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) + #ifdef _WIN64 + #define INITIALIZER(f) INITIALIZER2_(f,"") + #else + #define INITIALIZER(f) INITIALIZER2_(f,"_") + #endif #else - CRYPTO_THREADID_set_callback(id_function); -#endif - CRYPTO_set_locking_callback(locking_function); -#endif -#elif defined(HAVE_GNUTLS) - gnutls_global_init(); -#elif defined(HAVE_MBEDTLS) - // NO-OP + #define INITIALIZER(f) \ + static void f(void) __attribute__((__constructor__)); \ + static void f(void) #endif -} static void internal_idevice_deinit(void) { @@ -181,43 +181,33 @@ static void internal_idevice_deinit(void) #endif } -static thread_once_t init_once = THREAD_ONCE_INIT; -static thread_once_t deinit_once = THREAD_ONCE_INIT; - -#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR - #if defined(__llvm__) || defined(__GNUC__) - #define HAVE_ATTRIBUTE_CONSTRUCTOR - #endif -#endif - -#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR -static void __attribute__((constructor)) libimobiledevice_initialize(void) +INITIALIZER(internal_idevice_init) { - thread_once(&init_once, internal_idevice_init); -} +#if defined(HAVE_OPENSSL) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + int i; + SSL_library_init(); -static void __attribute__((destructor)) libimobiledevice_deinitialize(void) -{ - thread_once(&deinit_once, internal_idevice_deinit); -} -#elif defined(WIN32) -BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) -{ - switch (dwReason) { - case DLL_PROCESS_ATTACH: - thread_once(&init_once, internal_idevice_init); - break; - case DLL_PROCESS_DETACH: - thread_once(&deinit_once, internal_idevice_deinit); - break; - default: - break; - } - return 1; -} + mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); + if (!mutex_buf) + return; + for (i = 0; i < CRYPTO_num_locks(); i++) + mutex_init(&mutex_buf[i]); + +#if OPENSSL_VERSION_NUMBER < 0x10000000L + CRYPTO_set_id_callback(id_function); #else -#warning No compiler support for constructor/destructor attributes, some features might not be available. + CRYPTO_THREADID_set_callback(id_function); +#endif + CRYPTO_set_locking_callback(locking_function); +#endif +#elif defined(HAVE_GNUTLS) + gnutls_global_init(); +#elif defined(HAVE_MBEDTLS) + // NO-OP #endif + atexit(internal_idevice_deinit); +} const char* libimobiledevice_version() { @@ -548,7 +538,7 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect } idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); new_connection->type = CONNECTION_USBMUXD; - new_connection->data = (void*)(long)sfd; + new_connection->data = (void*)(uintptr_t)sfd; new_connection->ssl_data = NULL; new_connection->device = device; new_connection->ssl_recv_timeout = (unsigned int)-1; @@ -593,7 +583,7 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); new_connection->type = CONNECTION_NETWORK; - new_connection->data = (void*)(long)sfd; + new_connection->data = (void*)(uintptr_t)sfd; new_connection->ssl_data = NULL; new_connection->device = device; new_connection->ssl_recv_timeout = (unsigned int)-1; @@ -618,11 +608,11 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection) } idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; if (connection->type == CONNECTION_USBMUXD) { - usbmuxd_disconnect((int)(long)connection->data); + usbmuxd_disconnect((int)(uintptr_t)connection->data); connection->data = NULL; result = IDEVICE_E_SUCCESS; } else if (connection->type == CONNECTION_NETWORK) { - socket_close((int)(long)connection->data); + socket_close((int)(uintptr_t)connection->data); connection->data = NULL; result = IDEVICE_E_SUCCESS; } else { @@ -647,7 +637,7 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection, if (connection->type == CONNECTION_USBMUXD) { int res; do { - res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); + res = usbmuxd_send((int)(uintptr_t)connection->data, data, len, sent_bytes); } while (res == -EAGAIN); if (res < 0) { debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); @@ -656,7 +646,7 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection, return IDEVICE_E_SUCCESS; } if (connection->type == CONNECTION_NETWORK) { - int s = socket_send((int)(long)connection->data, (void*)data, len); + int s = socket_send((int)(uintptr_t)connection->data, (void*)data, len); if (s < 0) { *sent_bytes = 0; return IDEVICE_E_UNKNOWN_ERROR; @@ -763,7 +753,7 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t } if (connection->type == CONNECTION_USBMUXD) { - int conn_error = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); + int conn_error = usbmuxd_recv_timeout((int)(uintptr_t)connection->data, data, len, recv_bytes, timeout); idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes); if (error == IDEVICE_E_UNKNOWN_ERROR) { debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error)); @@ -771,7 +761,7 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t return error; } if (connection->type == CONNECTION_NETWORK) { - int res = socket_receive_timeout((int)(long)connection->data, data, len, 0, timeout); + int res = socket_receive_timeout((int)(uintptr_t)connection->data, data, len, 0, timeout); idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0); if (error == IDEVICE_E_SUCCESS) { *recv_bytes = (uint32_t)res; @@ -863,7 +853,7 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti } if (connection->type == CONNECTION_USBMUXD) { - int res = usbmuxd_recv((int)(long)connection->data, data, len, recv_bytes); + int res = usbmuxd_recv((int)(uintptr_t)connection->data, data, len, recv_bytes); if (res < 0) { debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); return IDEVICE_E_UNKNOWN_ERROR; @@ -871,7 +861,7 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti return IDEVICE_E_SUCCESS; } if (connection->type == CONNECTION_NETWORK) { - int res = socket_receive((int)(long)connection->data, data, len); + int res = socket_receive((int)(uintptr_t)connection->data, data, len); if (res < 0) { debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res)); return IDEVICE_E_UNKNOWN_ERROR; @@ -924,11 +914,11 @@ idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int * } if (connection->type == CONNECTION_USBMUXD) { - *fd = (int)(long)connection->data; + *fd = (int)(uintptr_t)connection->data; return IDEVICE_E_SUCCESS; } if (connection->type == CONNECTION_NETWORK) { - *fd = (int)(long)connection->data; + *fd = (int)(uintptr_t)connection->data; return IDEVICE_E_SUCCESS; } @@ -956,6 +946,20 @@ idevice_error_t idevice_get_udid(idevice_t device, char **udid) return IDEVICE_E_SUCCESS; } +unsigned int idevice_get_device_version(idevice_t device) +{ + if (!device) { + return 0; + } + if (!device->version) { + lockdownd_client_t lockdown = NULL; + lockdownd_client_new(device, &lockdown, NULL); + // we don't handle any errors here. We should have the product version cached now. + lockdownd_client_free(lockdown); + } + return device->version; +} + #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) typedef ssize_t ssl_cb_ret_type_t; #elif defined(HAVE_MBEDTLS) @@ -1075,13 +1079,14 @@ static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int arg idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b); #if OPENSSL_VERSION_NUMBER < 0x30000000L size_t len = (size_t)argi; - size_t *processed = (size_t*)&bytes; #endif switch (oper) { case (BIO_CB_READ|BIO_CB_RETURN): if (argp) { bytes = internal_ssl_read(conn, (char *)argp, len); - *processed = bytes; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + *processed = (size_t)(bytes < 0) ? 0 : bytes; +#endif return (long)bytes; } return 0; @@ -1090,7 +1095,9 @@ static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int arg // fallthrough case (BIO_CB_WRITE|BIO_CB_RETURN): bytes = internal_ssl_write(conn, argp, len); - *processed = bytes; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + *processed = (size_t)(bytes < 0) ? 0 : bytes; +#endif return (long)bytes; default: return retvalue; @@ -1239,7 +1246,7 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) #if OPENSSL_VERSION_NUMBER < 0x10100002L || \ (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL)) /* force use of TLSv1 for older devices */ - if (connection->device->version < DEVICE_VERSION(10,0,0)) { + if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) { #ifdef SSL_OP_NO_TLSv1_1 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); #endif @@ -1252,7 +1259,7 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) } #else SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); - if (connection->device->version < DEVICE_VERSION(10,0,0)) { + if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) { SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION); if (connection->device->version == 0) { /* @@ -1338,7 +1345,7 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) { break; } -#ifdef WIN32 +#ifdef _WIN32 Sleep(100); #else struct timespec ts = { 0, 100000000 }; @@ -1544,3 +1551,28 @@ idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t conne return IDEVICE_E_SUCCESS; } + +const char* idevice_strerror(idevice_error_t err) +{ + switch (err) { + case IDEVICE_E_SUCCESS: + return "Success"; + case IDEVICE_E_INVALID_ARG: + return "Invalid argument"; + case IDEVICE_E_UNKNOWN_ERROR: + return "Unknown Error"; + case IDEVICE_E_NO_DEVICE: + return "No device"; + case IDEVICE_E_NOT_ENOUGH_DATA: + return "Not enough data"; + case IDEVICE_E_CONNREFUSED: + return "Connection refused"; + case IDEVICE_E_SSL_ERROR: + return "SSL error"; + case IDEVICE_E_TIMEOUT: + return "Timeout"; + default: + break; + } + return "Unknown Error"; +} diff --git a/src/idevice.h b/src/idevice.h index dd72f9d..e05338e 100644 --- a/src/idevice.h +++ b/src/idevice.h @@ -52,8 +52,6 @@ #include "common/userpref.h" #include "libimobiledevice/libimobiledevice.h" -#define DEVICE_VERSION(maj, min, patch) (((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF)) - #define DEVICE_CLASS_IPHONE 1 #define DEVICE_CLASS_IPAD 2 #define DEVICE_CLASS_IPOD 3 diff --git a/src/installation_proxy.c b/src/installation_proxy.c index ec19da0..bb6ef01 100644 --- a/src/installation_proxy.c +++ b/src/installation_proxy.c @@ -26,7 +26,11 @@ #include <string.h> #include <stdlib.h> #include <inttypes.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> #include "installation_proxy.h" @@ -251,7 +255,7 @@ instproxy_error_t instproxy_client_new(idevice_t device, lockdownd_service_descr instproxy_error_t instproxy_client_start_service(idevice_t device, instproxy_client_t * client, const char* label) { - instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR; + int32_t err = INSTPROXY_E_UNKNOWN_ERROR; service_client_factory_start_service(device, INSTPROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(instproxy_client_new), &err); return err; } diff --git a/src/lockdown-cu.c b/src/lockdown-cu.c index 1afc2c5..c457cb2 100644 --- a/src/lockdown-cu.c +++ b/src/lockdown-cu.c @@ -29,7 +29,11 @@ #define __USE_GNU 1 #include <stdio.h> #include <ctype.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> #include "idevice.h" @@ -505,7 +509,7 @@ lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdow char *s_version = NULL; plist_get_string_val(p_version, &s_version); if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { - client->device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); + client->device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]); } free(s_version); } @@ -653,7 +657,7 @@ lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdow CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8); CFRelease(cname); #else -#ifdef WIN32 +#ifdef _WIN32 DWORD hostname_len = sizeof(hostname); GetComputerName(hostname, &hostname_len); #else @@ -957,12 +961,12 @@ lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t cli hkdf_md(MD_ALGO_SHA512, (unsigned char*)READ_KEY_SALT_MDLD, sizeof(READ_KEY_SALT_MDLD)-1, (unsigned char*)READ_KEY_INFO_MDLD, sizeof(READ_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_read_key, &cu_read_key_len); // Starting with iOS/tvOS 11.2 and WatchOS 4.2, this nonce is random and sent along with the request. Before, the request doesn't have a nonce and it uses hardcoded nonce "sendone01234". - unsigned char cu_nonce[12] = "sendone01234"; // guaranteed to be random by fair dice troll - if (client->device->version >= DEVICE_VERSION(11,2,0)) { + unsigned char cu_nonce[] = "sendone01234"; // guaranteed to be random by fair dice troll + if (client->device->version >= IDEVICE_DEVICE_VERSION(11,2,0)) { #if defined(HAVE_OPENSSL) - RAND_bytes(cu_nonce, sizeof(cu_nonce)); + RAND_bytes(cu_nonce, sizeof(cu_nonce)-1); #elif defined(HAVE_GCRYPT) - gcry_create_nonce(cu_nonce, sizeof(cu_nonce)); + gcry_create_nonce(cu_nonce, sizeof(cu_nonce)-1); #endif } @@ -1128,7 +1132,7 @@ lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client) plist_free(pubkey); plist_t pair_record_plist = plist_new_dict(); - pair_record_generate_keys_and_certs(pair_record_plist, public_key); + pair_record_generate_keys_and_certs(pair_record_plist, public_key, client->device->version); char* host_id = NULL; char* system_buid = NULL; diff --git a/src/lockdown.c b/src/lockdown.c index 256bff0..32389c9 100644 --- a/src/lockdown.c +++ b/src/lockdown.c @@ -32,7 +32,11 @@ #define __USE_GNU 1 #include <stdio.h> #include <ctype.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> #include <libimobiledevice-glue/utils.h> @@ -43,7 +47,7 @@ #include "common/userpref.h" #include "asprintf.h" -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define sleep(x) Sleep(x*1000) #endif @@ -617,6 +621,7 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli .port = 0xf27e, .ssl_enabled = 0 }; + char *type = NULL; property_list_service_client_t plistclient = NULL; if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { @@ -638,51 +643,32 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli client_loc->label = label ? strdup(label) : NULL; - *client = client_loc; - - return LOCKDOWN_E_SUCCESS; -} - -lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) -{ - if (!client) - return LOCKDOWN_E_INVALID_ARG; - - lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; - lockdownd_client_t client_loc = NULL; - plist_t pair_record = NULL; - char *host_id = NULL; - char *type = NULL; - - ret = lockdownd_client_new(device, &client_loc, label); - if (LOCKDOWN_E_SUCCESS != ret) { - debug_info("failed to create lockdownd client."); - return ret; - } - - /* perform handshake */ - ret = lockdownd_query_type(client_loc, &type); - if (LOCKDOWN_E_SUCCESS != ret) { + int is_lockdownd = 0; + if (lockdownd_query_type(client_loc, &type) != LOCKDOWN_E_SUCCESS) { debug_info("QueryType failed in the lockdownd client."); - } else if (strcmp("com.apple.mobile.lockdown", type) != 0) { - debug_info("Warning QueryType request returned \"%s\".", type); + } else if (!strcmp("com.apple.mobile.lockdown", type)) { + is_lockdownd = 1; + } else { + debug_info("QueryType request returned \"%s\"", type); } free(type); - if (device->version == 0) { + *client = client_loc; + + if (is_lockdownd && device->version == 0) { plist_t p_version = NULL; if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { int vers[3] = {0, 0, 0}; char *s_version = NULL; plist_get_string_val(p_version, &s_version); if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { - device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); + device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]); } free(s_version); } plist_free(p_version); } - if (device->device_class == 0) { + if (is_lockdownd && device->device_class == 0) { plist_t p_device_class = NULL; if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) { char* s_device_class = NULL; @@ -707,6 +693,26 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown plist_free(p_device_class); } + return LOCKDOWN_E_SUCCESS; +} + +lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) +{ + if (!client) + return LOCKDOWN_E_INVALID_ARG; + + lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; + lockdownd_client_t client_loc = NULL; + plist_t pair_record = NULL; + char *host_id = NULL; + + ret = lockdownd_client_new(device, &client_loc, label); + if (LOCKDOWN_E_SUCCESS != ret) { + debug_info("failed to create lockdownd client."); + return ret; + } + + /* perform handshake */ userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); if (uerr == USERPREF_E_READ_ERROR) { debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); @@ -730,7 +736,7 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown plist_free(pair_record); pair_record = NULL; - if (device->version < DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) { + if (device->version < IDEVICE_DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) { /* for older devices, we need to validate pairing to receive trusted host status */ ret = lockdownd_validate_pair(client_loc, NULL); @@ -836,7 +842,7 @@ static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t /* generate keys and certificates into pair record */ userpref_error_t uret = USERPREF_E_SUCCESS; - uret = pair_record_generate_keys_and_certs(*pair_record, public_key); + uret = pair_record_generate_keys_and_certs(*pair_record, public_key, client->device->version); switch(uret) { case USERPREF_E_INVALID_ARG: ret = LOCKDOWN_E_INVALID_ARG; @@ -846,6 +852,7 @@ static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t break; case USERPREF_E_SSL_ERROR: ret = LOCKDOWN_E_SSL_ERROR; + break; default: break; } diff --git a/src/misagent.c b/src/misagent.c index e3da997..3fdca4d 100644 --- a/src/misagent.c +++ b/src/misagent.c @@ -24,9 +24,13 @@ #endif #include <string.h> #include <stdlib.h> +#include <stdio.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> -#include <stdio.h> #include "misagent.h" #include "property_list_service.h" diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c index 6df50c4..6677882 100644 --- a/src/mobile_image_mounter.c +++ b/src/mobile_image_mounter.c @@ -24,7 +24,11 @@ #endif #include <string.h> #include <stdlib.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> #include "mobile_image_mounter.h" diff --git a/src/notification_proxy.c b/src/notification_proxy.c index 60b2e03..c7e4660 100644 --- a/src/notification_proxy.c +++ b/src/notification_proxy.c @@ -24,14 +24,19 @@ #endif #include <string.h> #include <stdlib.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> #include "notification_proxy.h" #include "property_list_service.h" #include "common/debug.h" -#ifdef WIN32 +#ifdef _WIN32 +#include <windows.h> #define sleep(x) Sleep(x*1000) #endif @@ -109,7 +114,7 @@ np_error_t np_client_new(idevice_t device, lockdownd_service_descriptor_t servic np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label) { - np_error_t err = NP_E_UNKNOWN_ERROR; + int32_t err = NP_E_UNKNOWN_ERROR; service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err); return err; } diff --git a/src/ostrace.c b/src/ostrace.c new file mode 100644 index 0000000..68eb6bf --- /dev/null +++ b/src/ostrace.c @@ -0,0 +1,436 @@ +/* + * ostrace.c + * com.apple.os_trace_relay service implementation. + * + * Copyright (c) 2020-2025 Nikias Bassen, 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 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <string.h> +#include <stdlib.h> + +#include <plist/plist.h> + +#include "ostrace.h" +#include "lockdown.h" +#include "common/debug.h" +#include "endianness.h" + +struct ostrace_worker_thread { + ostrace_client_t client; + ostrace_activity_cb_t cbfunc; + void *user_data; +}; + +/** + * Convert a service_error_t value to a ostrace_error_t value. + * Used internally to get correct error codes. + * + * @param err An service_error_t error code + * + * @return A matching ostrace_error_t error code, + * OSTRACE_E_UNKNOWN_ERROR otherwise. + */ +static ostrace_error_t ostrace_error(service_error_t err) +{ + switch (err) { + case SERVICE_E_SUCCESS: + return OSTRACE_E_SUCCESS; + case SERVICE_E_INVALID_ARG: + return OSTRACE_E_INVALID_ARG; + case SERVICE_E_MUX_ERROR: + return OSTRACE_E_MUX_ERROR; + case SERVICE_E_SSL_ERROR: + return OSTRACE_E_SSL_ERROR; + case SERVICE_E_NOT_ENOUGH_DATA: + return OSTRACE_E_NOT_ENOUGH_DATA; + case SERVICE_E_TIMEOUT: + return OSTRACE_E_TIMEOUT; + default: + break; + } + return OSTRACE_E_UNKNOWN_ERROR; +} + +ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client) +{ + *client = NULL; + + if (!device || !service || service->port == 0 || !client || *client) { + debug_info("Incorrect parameter passed to ostrace_client_new."); + return OSTRACE_E_INVALID_ARG; + } + + debug_info("Creating ostrace_client, port = %d.", service->port); + + service_client_t parent = NULL; + ostrace_error_t ret = ostrace_error(service_client_new(device, service, &parent)); + if (ret != OSTRACE_E_SUCCESS) { + debug_info("Creating base service client failed. Error: %i", ret); + return ret; + } + + ostrace_client_t client_loc = (ostrace_client_t) malloc(sizeof(struct ostrace_client_private)); + client_loc->parent = parent; + client_loc->worker = THREAD_T_NULL; + + *client = client_loc; + + debug_info("ostrace_client successfully created."); + return 0; +} + +ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label) +{ + ostrace_error_t err = OSTRACE_E_UNKNOWN_ERROR; + service_client_factory_start_service(device, OSTRACE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(ostrace_client_new), &err); + return err; +} + +ostrace_error_t ostrace_client_free(ostrace_client_t client) +{ + if (!client) + return OSTRACE_E_INVALID_ARG; + ostrace_stop_activity(client); + ostrace_error_t err = ostrace_error(service_client_free(client->parent)); + free(client); + + return err; +} + +static ostrace_error_t ostrace_send_plist(ostrace_client_t client, plist_t plist) +{ + ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; + uint32_t blen = 0; + char* bin = NULL; + uint32_t sent = 0; + uint32_t swapped_len = 0; + + if (!client || !plist) { + return OSTRACE_E_INVALID_ARG; + } + + plist_to_bin(plist, &bin, &blen); + swapped_len = htobe32(blen); + + res = ostrace_error(service_send(client->parent, (char*)&swapped_len, 4, &sent)); + if (res == OSTRACE_E_SUCCESS) { + res = ostrace_error(service_send(client->parent, bin, blen, &sent)); + } + free(bin); + return res; +} + +static ostrace_error_t ostrace_receive_plist(ostrace_client_t client, plist_t *plist) +{ + ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; + uint8_t msgtype = 0; + uint32_t received = 0; + res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received)); + if (res != OSTRACE_E_SUCCESS) { + debug_info("Failed to read message type from service"); + return res; + } + uint32_t rlen = 0; + res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received)); + if (res != OSTRACE_E_SUCCESS) { + debug_info("Failed to read message size from service"); + return res; + } + + if (msgtype == 1) { + rlen = be32toh(rlen); + } else if (msgtype == 2) { + rlen = le32toh(rlen); + } else { + debug_info("Unexpected message type %d", msgtype); + return OSTRACE_E_UNKNOWN_ERROR; + } + debug_info("got length %d", rlen); + + char* buf = (char*)malloc(rlen); + res = ostrace_error(service_receive(client->parent, buf, rlen, &received)); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + plist_t reply = NULL; + plist_err_t perr = plist_from_memory(buf, received, &reply, NULL); + free(buf); + if (perr != PLIST_ERR_SUCCESS) { + return OSTRACE_E_UNKNOWN_ERROR; + } + *plist = reply; + return OSTRACE_E_SUCCESS; +} + +static ostrace_error_t _ostrace_check_result(plist_t reply) +{ + ostrace_error_t res = OSTRACE_E_REQUEST_FAILED; + if (!reply) { + return res; + } + plist_t p_status = plist_dict_get_item(reply, "Status"); + if (!p_status) { + return res; + } + const char* status = plist_get_string_ptr(p_status, NULL); + if (!status) { + return res; + } + if (!strcmp(status, "RequestSuccessful")) { + res = OSTRACE_E_SUCCESS; + } + return res; +} + +void *ostrace_worker(void *arg) +{ + ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; + struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)arg; + + if (!oswt) + return NULL; + + uint8_t msgtype = 0; + uint32_t received = 0; + + debug_info("Running"); + + while (oswt->client->parent) { + res = ostrace_error(service_receive_with_timeout(oswt->client->parent, (char*)&msgtype, 1, &received, 100)); + if (res == OSTRACE_E_TIMEOUT) { + continue; + } + if (res != OSTRACE_E_SUCCESS) { + debug_info("Failed to read message type from service"); + break; + } + uint32_t rlen = 0; + res = ostrace_error(service_receive(oswt->client->parent, (char*)&rlen, 4, &received)); + if (res != OSTRACE_E_SUCCESS) { + debug_info("Failed to read message size from service"); + break; + } + + if (msgtype == 1) { + rlen = be32toh(rlen); + } else if (msgtype == 2) { + rlen = le32toh(rlen); + } else { + debug_info("Unexpected message type %d", msgtype); + break; + } + + debug_info("got length %d", rlen); + + void* buf = malloc(rlen); + res = ostrace_error(service_receive(oswt->client->parent, (char*)buf, rlen, &received)); + if (res != OSTRACE_E_SUCCESS) { + debug_info("Failed to receive %d bytes, error %d", rlen, res); + break; + } + if (received < rlen) { + debug_info("Failed to receive all data, got %d/%d", received, rlen); + break; + } + oswt->cbfunc(buf, received, oswt->user_data); + } + + if (oswt) { + free(oswt); + } + + debug_info("Exiting"); + + return NULL; +} + +ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data) +{ + if (!client || !callback) + return OSTRACE_E_INVALID_ARG; + + ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; + + if (client->worker) { + debug_info("Another ostrace activity thread appears to be running already."); + return res; + } + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Pid", plist_new_uint(0x0FFFFFFFF)); + plist_dict_set_item(dict, "MessageFilter", plist_new_uint(0xFFFF)); + plist_dict_set_item(dict, "StreamFlags", plist_new_uint(0x3C)); + if (options) { + plist_dict_merge(&dict, options); + } + plist_dict_set_item(dict, "Request", plist_new_string("StartActivity")); + + res = ostrace_send_plist(client, dict); + plist_free(dict); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + dict = NULL; + res = ostrace_receive_plist(client, &dict); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + res = _ostrace_check_result(dict); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + /* start worker thread */ + struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)malloc(sizeof(struct ostrace_worker_thread)); + if (oswt) { + oswt->client = client; + oswt->cbfunc = callback; + oswt->user_data = user_data; + + if (thread_new(&client->worker, ostrace_worker, oswt) == 0) { + res = OSTRACE_E_SUCCESS; + } + } + + return res; +} + +ostrace_error_t ostrace_stop_activity(ostrace_client_t client) +{ + if (client->worker) { + /* notify thread to finish */ + service_client_t parent = client->parent; + client->parent = NULL; + /* join thread to make it exit */ + thread_join(client->worker); + thread_free(client->worker); + client->worker = THREAD_T_NULL; + client->parent = parent; + } + + return OSTRACE_E_SUCCESS; +} + +ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list) +{ + ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Request", plist_new_string("PidList")); + + if (!client || !list) { + return OSTRACE_E_INVALID_ARG; + } + + res = ostrace_send_plist(client, dict); + plist_free(dict); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + plist_t reply = NULL; + res = ostrace_receive_plist(client, &reply); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + res = _ostrace_check_result(reply); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + plist_t payload = plist_dict_get_item(reply, "Payload"); + if (!payload) { + return OSTRACE_E_REQUEST_FAILED; + } + *list = plist_copy(payload); + plist_free(reply); + + return OSTRACE_E_SUCCESS; +} + +ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data) +{ + ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; + if (!client || !callback) { + return OSTRACE_E_INVALID_ARG; + } + plist_t dict = plist_new_dict(); + if (options) { + plist_dict_merge(&dict, options); + } + plist_dict_set_item(dict, "Request", plist_new_string("CreateArchive")); + + res = ostrace_send_plist(client, dict); + plist_free(dict); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + plist_t reply = NULL; + res = ostrace_receive_plist(client, &reply); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + res = _ostrace_check_result(reply); + if (res != OSTRACE_E_SUCCESS) { + return res; + } + + debug_info("Receiving archive...\n"); + while (1) { + uint8_t msgtype = 0; + uint32_t received = 0; + res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received)); + if (res != OSTRACE_E_SUCCESS) { + debug_info("Could not read message type from service: %d", res); + break; + } + if (msgtype != 3) { + debug_info("Unexpected packet type %d", msgtype); + return OSTRACE_E_REQUEST_FAILED; + } + uint32_t rlen = 0; + res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received)); + if (res != OSTRACE_E_SUCCESS) { + debug_info("Failed to read message size from service"); + break; + } + + rlen = le32toh(rlen); + debug_info("got length %d", rlen); + + unsigned char* buf = (unsigned char*)malloc(rlen); + res = ostrace_error(service_receive(client->parent, (char*)buf, rlen, &received)); + if (res != OSTRACE_E_SUCCESS) { + debug_info("Could not read data from service: %d", res); + break; + } + if (callback(buf, received, user_data) < 0) { + debug_info("Aborted through callback"); + return OSTRACE_E_REQUEST_FAILED; + } + } + debug_info("Done.\n"); + + return OSTRACE_E_SUCCESS; +} + diff --git a/src/ostrace.h b/src/ostrace.h new file mode 100644 index 0000000..dcc3e8d --- /dev/null +++ b/src/ostrace.h @@ -0,0 +1,37 @@ +/* + * ostrace.h + * com.apple.os_trace_relay service header file. + * + * Copyright (c) 2020-2025 Nikias Bassen, 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 + */ + +#ifndef _OSTRACE_H +#define _OSTRACE_H + +#include "idevice.h" +#include "libimobiledevice/ostrace.h" +#include "service.h" +#include <libimobiledevice-glue/thread.h> + +struct ostrace_client_private { + service_client_t parent; + THREAD_T worker; +}; + +void *ostrace_worker(void *arg); + +#endif diff --git a/src/sbservices.c b/src/sbservices.c index 365e130..5df5122 100644 --- a/src/sbservices.c +++ b/src/sbservices.c @@ -24,7 +24,11 @@ #endif #include <string.h> #include <stdlib.h> + +#ifndef _MSC_VER #include <unistd.h> +#endif + #include <plist/plist.h> #include "sbservices.h" diff --git a/tools/Makefile.am b/tools/Makefile.am index 7c9060b..24cfc66 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -35,7 +35,7 @@ bin_PROGRAMS = \ afcclient idevicebtlogger_SOURCES = idevicebtlogger.c -iidevicebtlogger_CFLAGS = $(AM_CFLAGS) +idevicebtlogger_CFLAGS = $(AM_CFLAGS) idevicebtlogger_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS) idevicebtlogger_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la @@ -51,8 +51,8 @@ idevicename_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la idevicepair_SOURCES = idevicepair.c idevicepair_CFLAGS = $(AM_CFLAGS) -idevicepair_LDFLAGS = $(AM_LDFLAGS) $(libusbmuxd_LIBS) $(ssl_lib_LIBS) -idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS) +idevicepair_LDFLAGS = $(AM_LDFLAGS) $(libusbmuxd_LIBS) +idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS) $(ssl_lib_LIBS) idevicesyslog_SOURCES = idevicesyslog.c idevicesyslog_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) @@ -76,7 +76,7 @@ idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la ideviceimagemounter_SOURCES = ideviceimagemounter.c ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) $(libtatsu_CFLAGS) -ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(ssl_lib_LIBS) $(libtatsu_LIBS) +ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(libtatsu_LIBS) ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la idevicescreenshot_SOURCES = idevicescreenshot.c @@ -135,8 +135,8 @@ idevicesetlocation_LDFLAGS = $(AM_LDFLAGS) idevicesetlocation_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la afcclient_SOURCES = afcclient.c -afcclient_CFLAGS = $(AM_CFLAGS) -afcclient_LDFLAGS = $(AM_LDFLAGS) +afcclient_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) +afcclient_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) if HAVE_READLINE afcclient_CFLAGS += $(readline_CFLAGS) afcclient_LDFLAGS += $(readline_LIBS) diff --git a/tools/afcclient.c b/tools/afcclient.c index 8f49831..a958c23 100644 --- a/tools/afcclient.c +++ b/tools/afcclient.c @@ -38,8 +38,9 @@ #include <unistd.h> #include <dirent.h> #include <time.h> +#include <sys/stat.h> -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #include <sys/time.h> #include <conio.h> @@ -95,7 +96,7 @@ static size_t curdir_len = 0; static int file_exists(const char* path) { struct stat tst; -#ifdef WIN32 +#ifdef _WIN32 return (stat(path, &tst) == 0); #else return (lstat(path, &tst) == 0); @@ -105,7 +106,7 @@ static int file_exists(const char* path) static int is_directory(const char* path) { struct stat tst; -#ifdef WIN32 +#ifdef _WIN32 return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode); #else return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode); @@ -138,7 +139,7 @@ static void print_usage(int argc, char **argv, int is_error) } #ifndef HAVE_READLINE -#ifdef WIN32 +#ifdef _WIN32 #define BS_CC '\b' #else #define BS_CC 0x7f @@ -175,7 +176,7 @@ int stop_requested = 0; static void handle_signal(int sig) { stop_requested++; -#ifdef WIN32 +#ifdef _WIN32 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); #else kill(getpid(), SIGINT); @@ -364,62 +365,55 @@ static char* get_realpath(const char* path) static void handle_devinfo(afc_client_t afc, int argc, char** argv) { - char **info = NULL; - afc_error_t err = afc_get_device_info(afc, &info); + plist_t info = NULL; + afc_error_t err = afc_get_device_info_plist(afc, &info); if (err == AFC_E_SUCCESS && info) { - int i; - for (i = 0; info[i]; i += 2) { - printf("%s: %s\n", info[i], info[i+1]); + if (argc > 0 && !strcmp(argv[0], "--plain")) { + plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE); + } else { + plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE); } } else { printf("Error: Failed to get device info: %s (%d)\n", afc_strerror(err), err); } - afc_dictionary_free(info); + plist_free(info); } static int get_file_info_stat(afc_client_t afc, const char* path, struct afc_file_stat *stbuf) { - char **info = NULL; - afc_error_t ret = afc_get_file_info(afc, path, &info); + plist_t info = NULL; + afc_error_t ret = afc_get_file_info_plist(afc, path, &info); memset(stbuf, 0, sizeof(struct afc_file_stat)); if (ret != AFC_E_SUCCESS) { return -1; } else if (!info) { return -1; - } else { - // get file attributes from info list - int i; - for (i = 0; info[i]; i += 2) { - if (!strcmp(info[i], "st_size")) { - stbuf->st_size = atoll(info[i+1]); - } else if (!strcmp(info[i], "st_blocks")) { - stbuf->st_blocks = atoi(info[i+1]); - } else if (!strcmp(info[i], "st_ifmt")) { - if (!strcmp(info[i+1], "S_IFREG")) { - stbuf->st_mode = S_IFREG; - } else if (!strcmp(info[i+1], "S_IFDIR")) { - stbuf->st_mode = S_IFDIR; - } else if (!strcmp(info[i+1], "S_IFLNK")) { - stbuf->st_mode = S_IFLNK; - } else if (!strcmp(info[i+1], "S_IFBLK")) { - stbuf->st_mode = S_IFBLK; - } else if (!strcmp(info[i+1], "S_IFCHR")) { - stbuf->st_mode = S_IFCHR; - } else if (!strcmp(info[i+1], "S_IFIFO")) { - stbuf->st_mode = S_IFIFO; - } else if (!strcmp(info[i+1], "S_IFSOCK")) { - stbuf->st_mode = S_IFSOCK; - } - } else if (!strcmp(info[i], "st_nlink")) { - stbuf->st_nlink = atoi(info[i+1]); - } else if (!strcmp(info[i], "st_mtime")) { - stbuf->st_mtime = (time_t)(atoll(info[i+1]) / 1000000000); - } else if (!strcmp(info[i], "st_birthtime")) { /* available on iOS 7+ */ - stbuf->st_birthtime = (time_t)(atoll(info[i+1]) / 1000000000); - } - } - afc_dictionary_free(info); } + stbuf->st_size = plist_dict_get_uint(info, "st_size"); + stbuf->st_blocks = plist_dict_get_uint(info, "st_blocks"); + const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL); + if (s_ifmt) { + if (!strcmp(s_ifmt, "S_IFREG")) { + stbuf->st_mode = S_IFREG; + } else if (!strcmp(s_ifmt, "S_IFDIR")) { + stbuf->st_mode = S_IFDIR; + } else if (!strcmp(s_ifmt, "S_IFLNK")) { + stbuf->st_mode = S_IFLNK; + } else if (!strcmp(s_ifmt, "S_IFBLK")) { + stbuf->st_mode = S_IFBLK; + } else if (!strcmp(s_ifmt, "S_IFCHR")) { + stbuf->st_mode = S_IFCHR; + } else if (!strcmp(s_ifmt, "S_IFIFO")) { + stbuf->st_mode = S_IFIFO; + } else if (!strcmp(s_ifmt, "S_IFSOCK")) { + stbuf->st_mode = S_IFSOCK; + } + } + stbuf->st_nlink = plist_dict_get_uint(info, "st_nlink"); + stbuf->st_mtime = (time_t)(plist_dict_get_uint(info, "st_mtime") / 1000000000); + /* available on iOS 7+ */ + stbuf->st_birthtime = (time_t)(plist_dict_get_uint(info, "st_birthtime") / 1000000000); + plist_free(info); return 0; } @@ -430,22 +424,23 @@ static void handle_file_info(afc_client_t afc, int argc, char** argv) return; } - char **info = NULL; + plist_t info = NULL; char* abspath = get_absolute_path(argv[0]); if (!abspath) { printf("Error: Invalid argument\n"); return; } - afc_error_t err = afc_get_file_info(afc, abspath, &info); + afc_error_t err = afc_get_file_info_plist(afc, abspath, &info); if (err == AFC_E_SUCCESS && info) { - int i; - for (i = 0; info[i]; i += 2) { - printf("%s: %s\n", info[i], info[i+1]); + if (argc > 1 && !strcmp(argv[1], "--plain")) { + plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE); + } else { + plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE); } } else { printf("Error: Failed to get file info for %s: %s (%d)\n", argv[0], afc_strerror(err), err); } - afc_dictionary_free(info); + plist_free(info); free(abspath); } @@ -483,7 +478,7 @@ static void print_file_info(afc_client_t afc, const char* path, int list_verbose printf(" "); printf("%10lld", (long long)st.st_size); printf(" "); -#ifdef WIN32 +#ifdef _WIN32 strftime(timebuf, 64, "%d %b %Y %H:%M:%S", localtime(&t)); #else strftime(timebuf, 64, "%d %h %Y %H:%M:%S", localtime(&t)); @@ -774,7 +769,7 @@ static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char static int __mkdir(const char* path) { -#ifdef WIN32 +#ifdef _WIN32 return mkdir(path); #else return mkdir(path, 0755); @@ -783,28 +778,19 @@ static int __mkdir(const char* path) static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get) { - char **info = NULL; + plist_t info = NULL; uint64_t file_size = 0; - afc_error_t err = afc_get_file_info(afc, srcpath, &info); + afc_error_t err = afc_get_file_info_plist(afc, srcpath, &info); if (err == AFC_E_OBJECT_NOT_FOUND) { printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); return 0; } uint8_t is_dir = 0; if (info) { - char **p = info; - while (p && *p) { - if (!strcmp(*p, "st_size")) { - p++; - file_size = (uint64_t) strtoull(*p, NULL, 10); - } - if (!strcmp(*p, "st_ifmt")) { - p++; - is_dir = !strcmp(*p, "S_IFDIR"); - } - p++; - } - afc_dictionary_free(info); + file_size = plist_dict_get_uint(info, "st_size"); + const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL); + is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR")); + plist_free(info); } uint8_t succeed = 1; if (is_dir) { @@ -837,7 +823,7 @@ static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpa p++; continue; } - size_t len = srcpath_is_root ? strlen(*p) + 1 : srcpath_len + 1 + strlen(*p) + 1; + size_t len = srcpath_is_root ? (strlen(*p) + 2) : (srcpath_len + 1 + strlen(*p) + 1); char *testpath = (char *) malloc(len); if (srcpath_is_root) { snprintf(testpath, len, "/%s", *p); @@ -845,7 +831,7 @@ static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpa snprintf(testpath, len, "%s/%s", srcpath, *p); } uint8_t dst_is_root = strcmp(srcpath, "/") == 0; - size_t dst_len = dst_is_root ? strlen(*p) + 1 : strlen(dstpath) + 1 + strlen(*p) + 1; + size_t dst_len = dst_is_root ? (strlen(*p) + 2) : (strlen(dstpath) + 1 + strlen(*p) + 1); char *newdst = (char *) malloc(dst_len); if (dst_is_root) { snprintf(newdst, dst_len, "/%s", *p); @@ -921,9 +907,9 @@ static void handle_get(afc_client_t afc, int argc, char **argv) // target is a directory, put file under this target if (is_directory(dstpath)) { - const char *basen = path_get_basename(argv[0]); + const char *basen = path_get_basename(srcpath); uint8_t dst_is_root = strcmp(dstpath, "/") == 0; - size_t len = dst_is_root ? (strlen(basen) + 1) : (strlen(dstpath) + 1 + strlen(basen) + 1); + size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1); char *newdst = (char *) malloc(len); if (dst_is_root) { snprintf(newdst, len, "/%s", basen); @@ -944,11 +930,11 @@ static void handle_get(afc_client_t afc, int argc, char **argv) static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite) { - char **info = NULL; - afc_error_t ret = afc_get_file_info(afc, dstpath, &info); + plist_t info = NULL; + afc_error_t ret = afc_get_file_info_plist(afc, dstpath, &info); // file exists, only overwrite with '-f' option was set if (ret == AFC_E_SUCCESS && info) { - afc_dictionary_free(info); + plist_free(info); if (!force_overwrite) { printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath); return 0; @@ -1029,10 +1015,11 @@ static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpa printf("Error: Failed to put directory without '-r' option: %s\n", srcpath); return 0; } - char **info = NULL; - afc_error_t err = afc_get_file_info(afc, dstpath, &info); + plist_t info = NULL; + afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info); //create if target directory does not exist - afc_dictionary_free(info); + plist_free(info); + info = NULL; if (err == AFC_E_OBJECT_NOT_FOUND) { err = afc_make_directory(afc, dstpath); if (err != AFC_E_SUCCESS) { @@ -1043,19 +1030,12 @@ static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpa printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath); return 0; } - afc_get_file_info(afc, dstpath, &info); + afc_get_file_info_plist(afc, dstpath, &info); uint8_t is_dir = 0; if (info) { - char **p = info; - while (p && *p) { - if (!strcmp(*p, "st_ifmt")) { - p++; - is_dir = !strcmp(*p, "S_IFDIR"); - break; - } - p++; - } - afc_dictionary_free(info); + const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL); + is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR")); + plist_free(info); } if (!is_dir) { printf("Error: Failed to create or access directory: '%s'\n", dstpath); @@ -1073,7 +1053,7 @@ static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpa char *fpath = string_build_path(srcpath, ep->d_name, NULL); if (fpath) { uint8_t dst_is_root = strcmp(dstpath, "/") == 0; - size_t len = dst_is_root ? strlen(ep->d_name) + 1 : strlen(dstpath) + 1 + strlen(ep->d_name) + 1; + size_t len = dst_is_root ? (strlen(ep->d_name) + 2) : (strlen(dstpath) + 1 + strlen(ep->d_name) + 1); char *newdst = (char *) malloc(len); if (dst_is_root) { snprintf(newdst, len, "/%s", ep->d_name); @@ -1147,8 +1127,8 @@ static void handle_put(afc_client_t afc, int argc, char **argv) printf("Error: Invalid number of arguments\n"); return; } - char **info = NULL; - afc_error_t err = afc_get_file_info(afc, dstpath, &info); + plist_t info = NULL; + afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info); // target does not exist, put directly if (err == AFC_E_OBJECT_NOT_FOUND) { put_file(afc, srcpath, dstpath, force_overwrite, recursive_put); @@ -1157,22 +1137,15 @@ static void handle_put(afc_client_t afc, int argc, char **argv) } else { uint8_t is_dir = 0; if (info) { - char **p = info; - while (p && *p) { - if (!strcmp(*p, "st_ifmt")) { - p++; - is_dir = !strcmp(*p, "S_IFDIR"); - break; - } - p++; - } - afc_dictionary_free(info); + const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL); + is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR")); + plist_free(info); } // target is a directory, try to put under this directory if (is_dir) { const char *basen = path_get_basename(srcpath); uint8_t dst_is_root = strcmp(dstpath, "/") == 0; - size_t len = dst_is_root ? strlen(basen) + 1 : strlen(dstpath) + 1 + strlen(basen) + 1; + size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1); char *newdst = (char *) malloc(len); if (dst_is_root) { snprintf(newdst, len, "/%s", basen); @@ -1227,19 +1200,12 @@ static void handle_cd(afc_client_t afc, int argc, char** argv) char* path = get_realpath(argv[0]); int is_dir = 0; - char **info = NULL; - afc_error_t err = afc_get_file_info(afc, path, &info); + plist_t info = NULL; + afc_error_t err = afc_get_file_info_plist(afc, path, &info); if (err == AFC_E_SUCCESS && info) { - int i; - for (i = 0; info[i]; i += 2) { - if (!strcmp(info[i], "st_ifmt")) { - if (!strcmp(info[i+1], "S_IFDIR")) { - is_dir = 1; - } - break; - } - } - afc_dictionary_free(info); + const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL); + is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR")); + plist_free(info); } else { printf("Error: Failed to get file info for %s: %s (%d)\n", path, afc_strerror(err), err); free(path); @@ -1483,7 +1449,7 @@ int main(int argc, char** argv) }; signal(SIGTERM, handle_signal); -#ifndef WIN32 +#ifndef _WIN32 signal(SIGQUIT, handle_signal); signal(SIGPIPE, SIG_IGN); #endif @@ -1561,7 +1527,7 @@ int main(int argc, char** argv) idevice_events_subscribe(&context, device_event_cb, NULL); while (!connected && !stop_requested) { -#ifdef WIN32 +#ifdef _WIN32 Sleep(100); #else usleep(100000); diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c index c0537b8..363abad 100644 --- a/tools/idevicebackup.c +++ b/tools/idevicebackup.c @@ -35,6 +35,7 @@ #include <unistd.h> #include <ctype.h> #include <time.h> +#include <sys/stat.h> #include <libimobiledevice/libimobiledevice.h> #include <libimobiledevice/lockdown.h> @@ -51,7 +52,7 @@ #define LOCK_ATTEMPTS 50 #define LOCK_WAIT 200000 -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define sleep(x) Sleep(x*1000) #endif @@ -175,7 +176,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid) if (value_node) plist_dict_set_item(ret, "IMEI", plist_copy(value_node)); - plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0)); + plist_dict_set_item(ret, "Last Backup Date", +#ifdef HAVE_PLIST_UNIX_DATE + plist_new_unix_date(time(NULL)) +#else + plist_new_date(time(NULL) - MAC_EPOCH, 0) +#endif + ); value_node = plist_dict_get_item(root_node, "ProductType"); plist_dict_set_item(ret, "Product Type", plist_copy(value_node)); @@ -212,7 +219,11 @@ static void mobilebackup_info_update_last_backup_date(plist_t info_plist) return; node = plist_dict_get_item(info_plist, "Last Backup Date"); +#ifdef HAVE_PLIST_UNIX_DATE + plist_set_unix_date_val(node, time(NULL)); +#else plist_set_date_val(node, time(NULL) - MAC_EPOCH, 0); +#endif node = NULL; } @@ -642,7 +653,7 @@ int main(int argc, char *argv[]) /* we need to exit cleanly on running backups and restores or we cause havok */ signal(SIGINT, clean_exit); signal(SIGTERM, clean_exit); -#ifndef WIN32 +#ifndef _WIN32 signal(SIGQUIT, clean_exit); signal(SIGPIPE, SIG_IGN); #endif @@ -1352,7 +1363,7 @@ files_out: file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata"); /* determine file size */ -#ifdef WIN32 +#ifdef _WIN32 struct _stati64 fst; if (_stati64(file_info_path, &fst) != 0) #else diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c index c73b269..12d6083 100644 --- a/tools/idevicebackup2.c +++ b/tools/idevicebackup2.c @@ -54,7 +54,7 @@ #define LOCK_ATTEMPTS 50 #define LOCK_WAIT 200000 -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #include <conio.h> #define sleep(x) Sleep(x*1000) @@ -74,6 +74,7 @@ static int verbose = 1; static int quit_flag = 0; +static int passcode_requested = 0; #define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); }; @@ -115,6 +116,10 @@ static void notify_cb(const char *notification, void *userdata) quit_flag++; } else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) { backup_domain_changed = 1; + } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.presented")) { + passcode_requested = 1; + } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.dismissed")) { + passcode_requested = 0; } else { PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification); } @@ -126,21 +131,15 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil return; } - char **fileinfo = NULL; + plist_t fileinfo = NULL; uint32_t fsize = 0; - afc_get_file_info(afc, filename, &fileinfo); + afc_get_file_info_plist(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); + fsize = plist_dict_get_uint(fileinfo, "st_size"); + plist_free(fileinfo); if (fsize == 0) { return; @@ -173,7 +172,7 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil static int __mkdir(const char* path, int mode) { -#ifdef WIN32 +#ifdef _WIN32 return mkdir(path); #else return mkdir(path, mode); @@ -202,7 +201,7 @@ static int mkdir_with_parents(const char *dir, int mode) return res; } -#ifdef WIN32 +#ifdef _WIN32 static int win32err_to_errno(int err_value) { switch (err_value) { @@ -219,7 +218,7 @@ static int win32err_to_errno(int err_value) static int remove_file(const char* path) { int e = 0; -#ifdef WIN32 +#ifdef _WIN32 if (!DeleteFile(path)) { e = win32err_to_errno(GetLastError()); } @@ -234,7 +233,7 @@ static int remove_file(const char* path) static int remove_directory(const char* path) { int e = 0; -#ifdef WIN32 +#ifdef _WIN32 if (!RemoveDirectory(path)) { e = win32err_to_errno(GetLastError()); } @@ -455,7 +454,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d /* 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)); + plist_dict_set_item(ret, "Last Backup Date", +#ifdef HAVE_PLIST_UNIX_DATE + plist_new_unix_date(time(NULL)) +#else + plist_new_date(time(NULL) - MAC_EPOCH, 0) +#endif + ); value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier"); if (value_node) @@ -768,7 +773,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char uint32_t bytes = 0; char *localfile = string_build_path(backup_dir, path, NULL); char buf[32768]; -#ifdef WIN32 +#ifdef _WIN32 struct _stati64 fst; #else struct stat fst; @@ -779,7 +784,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char int errcode = -1; int result = -1; uint32_t length; -#ifdef WIN32 +#ifdef _WIN32 uint64_t total; uint64_t sent; #else @@ -810,7 +815,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char goto leave_proto_err; } -#ifdef WIN32 +#ifdef _WIN32 if (_stati64(localfile, &fst) < 0) #else if (stat(localfile, &fst) < 0) @@ -1218,7 +1223,12 @@ static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plis 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)); +#ifdef HAVE_PLIST_UNIX_DATE + plist_new_unix_date(st.st_mtime) +#else + plist_new_date(st.st_mtime - MAC_EPOCH, 0) +#endif + ); plist_dict_set_item(dirlist, ep->d_name, fdict); free(fpath); @@ -1343,7 +1353,7 @@ static void mb2_copy_directory_by_path(const char *src, const char *dst) } } -#ifdef WIN32 +#ifdef _WIN32 #define BS_CC '\b' #define my_getch getch #else @@ -1463,8 +1473,6 @@ static void print_usage(int argc, char **argv, int is_error) ); } -#define DEVICE_VERSION(maj, min, patch) ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF)) - int main(int argc, char *argv[]) { idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; @@ -1530,7 +1538,7 @@ int main(int argc, char *argv[]) /* we need to exit cleanly on running backups and restores or we cause havok */ signal(SIGINT, clean_exit); signal(SIGTERM, clean_exit); -#ifndef WIN32 +#ifndef _WIN32 signal(SIGQUIT, clean_exit); signal(SIGPIPE, SIG_IGN); #endif @@ -1844,34 +1852,20 @@ int main(int argc, char *argv[]) } /* 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]); - } - } + int device_version = idevice_get_device_version(device); /* start notification_proxy */ ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service); if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) { np_client_new(device, service, &np); np_set_notify_callback(np, notify_cb, NULL); - const char *noties[5] = { + const char *noties[7] = { NP_SYNC_CANCEL_REQUEST, NP_SYNC_SUSPEND_REQUEST, NP_SYNC_RESUME_REQUEST, NP_BACKUP_DOMAIN_CHANGED, + "com.apple.LocalAuthentication.ui.presented", + "com.apple.LocalAuthentication.ui.dismissed", NULL }; np_observe_notifications(np, noties); @@ -2058,6 +2052,16 @@ checkpoint: } else { PRINT_VERBOSE(1, "Incremental backup mode.\n"); } + if (device_version >= IDEVICE_DEVICE_VERSION(16,1,0)) { + /* let's wait 2 second to see if the device passcode is requested */ + int retries = 20; + while (retries-- > 0 && !passcode_requested) { + usleep(100000); + } + if (passcode_requested) { + printf("*** Waiting for passcode to be entered on the device ***\n"); + } + } } else { if (err == MOBILEBACKUP2_E_BAD_VERSION) { printf("ERROR: Could not start backup process: backup protocol version mismatch!\n"); @@ -2229,7 +2233,7 @@ checkpoint: if (newpw || backup_password) { mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts); uint8_t passcode_hint = 0; - if (device_version >= DEVICE_VERSION(13,0,0)) { + if (device_version >= IDEVICE_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; @@ -2307,7 +2311,7 @@ checkpoint: /* device wants to know how much disk space is available on the computer */ uint64_t freespace = 0; int res = -1; -#ifdef WIN32 +#ifdef _WIN32 if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) { res = 0; } @@ -2316,7 +2320,7 @@ checkpoint: memset(&fs, '\0', sizeof(fs)); res = statvfs(backup_directory, &fs); if (res == 0) { - freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_bsize; + freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize; } #endif plist_t freespace_item = plist_new_uint(freespace); diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c index 8de6b22..ca68b59 100644 --- a/tools/idevicebtlogger.c +++ b/tools/idevicebtlogger.c @@ -36,7 +36,7 @@ #include <assert.h> #include <fcntl.h> -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define sleep(x) Sleep(x*1000) #else @@ -334,7 +334,7 @@ int main(int argc, char *argv[]) signal(SIGINT, clean_exit); signal(SIGTERM, clean_exit); -#ifndef WIN32 +#ifndef _WIN32 signal(SIGQUIT, clean_exit); signal(SIGPIPE, SIG_IGN); #endif diff --git a/tools/idevicecrashreport.c b/tools/idevicecrashreport.c index 09bd537..b9869ae 100644 --- a/tools/idevicecrashreport.c +++ b/tools/idevicecrashreport.c @@ -31,7 +31,8 @@ #include <string.h> #include <unistd.h> #include <getopt.h> -#ifndef WIN32 +#include <sys/stat.h> +#ifndef _WIN32 #include <signal.h> #endif #include <libimobiledevice-glue/utils.h> @@ -42,7 +43,7 @@ #include <libimobiledevice/afc.h> #include <plist/plist.h> -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define S_IFLNK S_IFREG #define S_IFSOCK S_IFREG @@ -54,11 +55,12 @@ const char* target_directory = NULL; static int extract_raw_crash_reports = 0; static int keep_crash_reports = 0; +static int remove_all = 0; static int file_exists(const char* path) { struct stat tst; -#ifdef WIN32 +#ifdef _WIN32 return (stat(path, &tst) == 0); #else return (lstat(path, &tst) == 0); @@ -144,7 +146,7 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char continue; } - char **fileinfo = NULL; + plist_t fileinfo = NULL; struct stat stbuf; memset(&stbuf, '\0', sizeof(struct stat)); @@ -152,7 +154,7 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char strcpy(((char*)source_filename) + device_directory_length, list[k]); /* assemble absolute target filename */ -#ifdef WIN32 +#ifdef _WIN32 /* replace every ':' with '-' since ':' is an illegal character for file names in windows */ char* current_pos = strchr(list[k], ':'); while (current_pos) { @@ -171,88 +173,93 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char } /* get file information */ - afc_get_file_info(afc, source_filename, &fileinfo); + afc_get_file_info_plist(afc, source_filename, &fileinfo); if (!fileinfo) { printf("Failed to read information for '%s'. Skipping...\n", source_filename); continue; } /* parse file information */ - int i; - for (i = 0; fileinfo[i]; i+=2) { - if (!strcmp(fileinfo[i], "st_size")) { - stbuf.st_size = atoll(fileinfo[i+1]); - } else if (!strcmp(fileinfo[i], "st_ifmt")) { - if (!strcmp(fileinfo[i+1], "S_IFREG")) { - stbuf.st_mode = S_IFREG; - } else if (!strcmp(fileinfo[i+1], "S_IFDIR")) { - stbuf.st_mode = S_IFDIR; - } else if (!strcmp(fileinfo[i+1], "S_IFLNK")) { - stbuf.st_mode = S_IFLNK; - } else if (!strcmp(fileinfo[i+1], "S_IFBLK")) { - stbuf.st_mode = S_IFBLK; - } else if (!strcmp(fileinfo[i+1], "S_IFCHR")) { - stbuf.st_mode = S_IFCHR; - } else if (!strcmp(fileinfo[i+1], "S_IFIFO")) { - stbuf.st_mode = S_IFIFO; - } else if (!strcmp(fileinfo[i+1], "S_IFSOCK")) { - stbuf.st_mode = S_IFSOCK; - } - } else if (!strcmp(fileinfo[i], "st_nlink")) { - stbuf.st_nlink = atoi(fileinfo[i+1]); - } else if (!strcmp(fileinfo[i], "st_mtime")) { - stbuf.st_mtime = (time_t)(atoll(fileinfo[i+1]) / 1000000000); - } else if (!strcmp(fileinfo[i], "LinkTarget")) { - /* report latest crash report filename */ - printf("Link: %s\n", (char*)target_filename + strlen(target_directory)); - - /* remove any previous symlink */ - if (file_exists(target_filename)) { - remove(target_filename); - } + stbuf.st_size = plist_dict_get_uint(fileinfo, "st_size"); + const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(fileinfo, "st_ifmt"), NULL); + if (s_ifmt) { + if (!strcmp(s_ifmt, "S_IFREG")) { + stbuf.st_mode = S_IFREG; + } else if (!strcmp(s_ifmt, "S_IFDIR")) { + stbuf.st_mode = S_IFDIR; + } else if (!strcmp(s_ifmt, "S_IFLNK")) { + stbuf.st_mode = S_IFLNK; + } else if (!strcmp(s_ifmt, "S_IFBLK")) { + stbuf.st_mode = S_IFBLK; + } else if (!strcmp(s_ifmt, "S_IFCHR")) { + stbuf.st_mode = S_IFCHR; + } else if (!strcmp(s_ifmt, "S_IFIFO")) { + stbuf.st_mode = S_IFIFO; + } else if (!strcmp(s_ifmt, "S_IFSOCK")) { + stbuf.st_mode = S_IFSOCK; + } + } + stbuf.st_nlink = plist_dict_get_uint(fileinfo, "st_nlink"); + stbuf.st_mtime = (time_t)(plist_dict_get_uint(fileinfo, "st_mtime") / 1000000000); + const char* linktarget = plist_get_string_ptr(plist_dict_get_item(fileinfo, "LinkTarget"), NULL); + if (linktarget && !remove_all) { + /* report latest crash report filename */ + printf("Link: %s\n", (char*)target_filename + strlen(target_directory)); + + /* remove any previous symlink */ + if (file_exists(target_filename)) { + remove(target_filename); + } -#ifndef WIN32 - /* use relative filename */ - char* b = strrchr(fileinfo[i+1], '/'); - if (b == NULL) { - b = fileinfo[i+1]; +#ifndef _WIN32 + /* use relative filename */ + const char* b = strrchr(linktarget, '/'); + if (b == NULL) { + b = linktarget; } else { - b++; - } + b++; + } - /* create a symlink pointing to latest log */ - if (symlink(b, target_filename) < 0) { - fprintf(stderr, "Can't create symlink to %s\n", b); - } + /* create a symlink pointing to latest log */ + if (symlink(b, target_filename) < 0) { + fprintf(stderr, "Can't create symlink to %s\n", b); + } #endif - if (!keep_crash_reports) - afc_remove_path(afc, source_filename); + if (!keep_crash_reports) + afc_remove_path(afc, source_filename); - res = 0; - } + res = 0; } /* free file information */ - afc_dictionary_free(fileinfo); + plist_free(fileinfo); /* recurse into child directories */ if (S_ISDIR(stbuf.st_mode)) { -#ifdef WIN32 - mkdir(target_filename); + if (!remove_all) { +#ifdef _WIN32 + mkdir(target_filename); #else - mkdir(target_filename, 0755); + mkdir(target_filename, 0755); #endif + } res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename, filename_filter); /* remove directory from device */ - if (!keep_crash_reports) + if (!remove_all && !keep_crash_reports) afc_remove_path(afc, source_filename); } else if (S_ISREG(stbuf.st_mode)) { if (filename_filter != NULL && strstr(source_filename, filename_filter) == NULL) { continue; } + if (remove_all) { + printf("Remove: %s\n", source_filename); + afc_remove_path(afc, source_filename); + continue; + } + /* copy file to host */ afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle); if(afc_error != AFC_E_SUCCESS) { @@ -331,6 +338,7 @@ static void print_usage(int argc, char **argv, int is_error) " -f, --filter NAME filter crash reports by NAME (case sensitive)\n" " -h, --help prints usage information\n" " -v, --version prints version information\n" + " --remove-all remove all crash logs found\n" "\n" "Homepage: <" PACKAGE_URL ">\n" "Bug Reports: <" PACKAGE_BUGREPORT ">\n" @@ -361,10 +369,11 @@ int main(int argc, char* argv[]) { "filter", required_argument, NULL, 'f' }, { "extract", no_argument, NULL, 'e' }, { "keep", no_argument, NULL, 'k' }, + { "remove-all", no_argument, NULL, 1 }, { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif @@ -405,6 +414,9 @@ int main(int argc, char* argv[]) case 'k': keep_crash_reports = 1; break; + case 1: + remove_all = 1; + break; default: print_usage(argc, argv, 1); return 2; @@ -414,12 +426,16 @@ int main(int argc, char* argv[]) argv += optind; /* ensure a target directory was supplied */ - if (!argv[0]) { - fprintf(stderr, "ERROR: missing target directory.\n"); - print_usage(argc+optind, argv-optind, 1); - return 2; + if (!remove_all) { + if (!argv[0]) { + fprintf(stderr, "ERROR: missing target directory.\n"); + print_usage(argc+optind, argv-optind, 1); + return 2; + } + target_directory = argv[0]; + } else { + target_directory = "."; } - target_directory = argv[0]; /* check if target directory exists */ if (!file_exists(target_directory)) { diff --git a/tools/idevicedate.c b/tools/idevicedate.c index d05f63e..31b0cf7 100644 --- a/tools/idevicedate.c +++ b/tools/idevicedate.c @@ -33,7 +33,7 @@ #if HAVE_LANGINFO_CODESET #include <langinfo.h> #endif -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif @@ -43,7 +43,7 @@ #ifdef _DATE_FMT #define DATE_FMT_LANGINFO nl_langinfo (_DATE_FMT) #else -#ifdef WIN32 +#ifdef _WIN32 #define DATE_FMT_LANGINFO "%a %b %#d %H:%M:%S %Z %Y" #else #define DATE_FMT_LANGINFO "%a %b %e %H:%M:%S %Z %Y" @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif /* parse cmdline args */ diff --git a/tools/idevicedebug.c b/tools/idevicedebug.c index 36c594e..3f2e289 100644 --- a/tools/idevicedebug.c +++ b/tools/idevicedebug.c @@ -34,7 +34,7 @@ #include <libgen.h> #include <getopt.h> -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define sleep(x) Sleep(x*1000) #endif @@ -239,7 +239,7 @@ int main(int argc, char *argv[]) /* map signals */ signal(SIGINT, on_signal); signal(SIGTERM, on_signal); -#ifndef WIN32 +#ifndef _WIN32 signal(SIGQUIT, on_signal); signal(SIGPIPE, SIG_IGN); #endif diff --git a/tools/idevicedebugserverproxy.c b/tools/idevicedebugserverproxy.c index 9fe7051..fb082b3 100644 --- a/tools/idevicedebugserverproxy.c +++ b/tools/idevicedebugserverproxy.c @@ -32,7 +32,7 @@ #include <getopt.h> #include <errno.h> #include <signal.h> -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #else @@ -219,7 +219,7 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 struct sigaction sa; struct sigaction si; memset(&sa, '\0', sizeof(struct sigaction)); diff --git a/tools/idevicedevmodectl.c b/tools/idevicedevmodectl.c index bd1de6a..6bf1a1c 100644 --- a/tools/idevicedevmodectl.c +++ b/tools/idevicedevmodectl.c @@ -32,11 +32,11 @@ #include <sys/stat.h> #include <unistd.h> #include <errno.h> -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define __usleep(x) Sleep(x/1000) #else @@ -259,7 +259,7 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif /* parse cmdline args */ diff --git a/tools/idevicediagnostics.c b/tools/idevicediagnostics.c index e699bc4..365c0a4 100644 --- a/tools/idevicediagnostics.c +++ b/tools/idevicediagnostics.c @@ -31,7 +31,7 @@ #include <getopt.h> #include <errno.h> #include <time.h> -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif @@ -113,7 +113,7 @@ int main(int argc, char **argv) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif /* parse cmdline args */ diff --git a/tools/ideviceenterrecovery.c b/tools/ideviceenterrecovery.c index 29cc5c9..65eb882 100644 --- a/tools/ideviceenterrecovery.c +++ b/tools/ideviceenterrecovery.c @@ -30,7 +30,7 @@ #include <stdlib.h> #include <getopt.h> #include <errno.h> -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif @@ -70,7 +70,7 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif /* parse cmdline args */ diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c index 52b0666..b319d05 100644 --- a/tools/ideviceimagemounter.c +++ b/tools/ideviceimagemounter.c @@ -36,7 +36,8 @@ #include <time.h> #include <sys/time.h> #include <inttypes.h> -#ifndef WIN32 +#include <sys/stat.h> +#ifndef _WIN32 #include <signal.h> #endif @@ -87,7 +88,7 @@ static void print_usage(int argc, char **argv, int is_error) " mount PATH Mount the developer disk image at PATH.\n" " For iOS 17+, PATH is a directory containing a .dmg image,\n" " a BuildManifest.plist, and a Firmware sub-directory;\n" - " for older versions PATH is a .dmg filename with a" + " for older versions PATH is a .dmg filename with a\n" " .dmg.signature in the same directory, or with another\n" " parameter pointing to a file elsewhere.\n" " list List mounted disk images.\n" @@ -183,7 +184,7 @@ int main(int argc, char **argv) size_t image_size = 0; char *image_sig_path = NULL; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif parse_opts(argc, argv); @@ -267,23 +268,14 @@ int main(int argc, char **argv) goto leave; } - plist_t pver = NULL; - char *product_version = NULL; - lockdownd_get_value(lckd, NULL, "ProductVersion", &pver); - if (pver && plist_get_node_type(pver) == PLIST_STRING) { - plist_get_string_val(pver, &product_version); - } + unsigned int device_version = idevice_get_device_version(device); + disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC; - int product_version_major = 0; - int product_version_minor = 0; - if (product_version) { - if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) { - if (product_version_major >= 7) - disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE; - } + if (device_version >= IDEVICE_DEVICE_VERSION(7,0,0)) { + disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE; } - if (product_version_major >= 16) { + if (device_version >= IDEVICE_DEVICE_VERSION(16,0,0)) { uint8_t dev_mode_status = 0; plist_t val = NULL; ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val); @@ -336,7 +328,7 @@ int main(int argc, char **argv) goto leave; } image_size = fst.st_size; - if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) { + if (device_version < IDEVICE_DEVICE_VERSION(17,0,0) && stat(image_sig_path, &fst) != 0) { fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno)); goto leave; } @@ -351,7 +343,7 @@ int main(int argc, char **argv) if (cmd == CMD_LIST) { /* list mounts mode */ if (!imagetype) { - if (product_version_major < 17) { + if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) { imagetype = "Developer"; } else { imagetype = "Personalized"; @@ -371,14 +363,17 @@ int main(int argc, char **argv) struct stat fst; plist_t mount_options = NULL; - if (product_version_major < 17) { + if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) { f = fopen(image_sig_path, "rb"); if (!f) { fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); goto leave; } - fstat(fileno(f), &fst); - sig = malloc(sig_length); + if (fstat(fileno(f), &fst) != 0) { + fprintf(stderr, "Error: fstat: %s\n", strerror(errno)); + goto leave; + } + sig = malloc(fst.st_size); sig_length = fread(sig, 1, fst.st_size, f); fclose(f); if (sig_length == 0) { @@ -603,20 +598,13 @@ int main(int argc, char **argv) case DISK_IMAGE_UPLOAD_TYPE_AFC: default: printf("Uploading %s --> afc:///%s\n", image_path, targetname); - char **strs = NULL; - if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) { + plist_t fileinfo = NULL; + if (afc_get_file_info_plist(afc, PKG_PATH, &fileinfo) != AFC_E_SUCCESS) { if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) { fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH); } } - if (strs) { - int i = 0; - while (strs[i]) { - free(strs[i]); - i++; - } - free(strs); - } + plist_free(fileinfo); uint64_t af = 0; if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) != diff --git a/tools/ideviceinfo.c b/tools/ideviceinfo.c index fd45763..20cc916 100644 --- a/tools/ideviceinfo.c +++ b/tools/ideviceinfo.c @@ -31,7 +31,7 @@ #include <errno.h> #include <stdlib.h> #include <getopt.h> -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif @@ -152,7 +152,7 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif diff --git a/tools/idevicename.c b/tools/idevicename.c index 69b76f6..248bda3 100644 --- a/tools/idevicename.c +++ b/tools/idevicename.c @@ -30,7 +30,7 @@ #include <unistd.h> #include <stdlib.h> #include <getopt.h> -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif @@ -72,7 +72,7 @@ int main(int argc, char** argv) const char* udid = NULL; int use_network = 0; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif diff --git a/tools/idevicenotificationproxy.c b/tools/idevicenotificationproxy.c index d1e25c1..192192a 100644 --- a/tools/idevicenotificationproxy.c +++ b/tools/idevicenotificationproxy.c @@ -2,7 +2,8 @@ * idevicenotificationproxy.c * Simple client for the notification_proxy service * - * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved. + * Copyright (c) 2018-2024 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2009-2015 Martin Szulecki, 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 @@ -32,7 +33,7 @@ #include <errno.h> #include <signal.h> -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define sleep(x) Sleep(x*1000) #else @@ -75,6 +76,7 @@ static void print_usage(int argc, char **argv, int is_error) "\n" "The following OPTIONS are accepted:\n" " -u, --udid UDID target specific device by UDID\n" + " -i, --insecure use insecure notification proxy (non-paired device)\n" " -n, --network connect to network device\n" " -d, --debug enable communication debugging\n" " -h, --help prints usage information\n" @@ -102,6 +104,7 @@ int main(int argc, char *argv[]) int i = 0; const char* udid = NULL; int use_network = 0; + int insecure = 0; int cmd = CMD_NONE; char* cmd_arg = NULL; @@ -114,6 +117,7 @@ int main(int argc, char *argv[]) { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "udid", required_argument, NULL, 'u' }, + { "insecure", no_argument, NULL, 'i' }, { "network", no_argument, NULL, 'n' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0} @@ -121,13 +125,13 @@ int main(int argc, char *argv[]) signal(SIGINT, clean_exit); signal(SIGTERM, clean_exit); -#ifndef WIN32 +#ifndef _WIN32 signal(SIGQUIT, clean_exit); signal(SIGPIPE, SIG_IGN); #endif /* parse cmdline args */ - while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "dhu:inv", longopts, NULL)) != -1) { switch (c) { case 'd': idevice_set_debug_level(1); @@ -143,6 +147,9 @@ int main(int argc, char *argv[]) case 'n': use_network = 1; break; + case 'i': + insecure = 1; + break; case 'h': print_usage(argc, argv, 0); return 0; @@ -214,12 +221,17 @@ int main(int argc, char *argv[]) goto cleanup; } - if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) { - fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ret); + if (insecure) { + ret = lockdownd_client_new(device, &client, TOOL_NAME); + } else { + ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME); + } + if (LOCKDOWN_E_SUCCESS != ret) { + fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ret), ret); goto cleanup; } - ret = lockdownd_start_service(client, NP_SERVICE_NAME, &service); + ret = lockdownd_start_service(client, (insecure) ? "com.apple.mobile.insecure_notification_proxy" : NP_SERVICE_NAME, &service); lockdownd_client_free(client); diff --git a/tools/idevicepair.c b/tools/idevicepair.c index 94d3f04..884c690 100644 --- a/tools/idevicepair.c +++ b/tools/idevicepair.c @@ -32,7 +32,7 @@ #include <getopt.h> #include <ctype.h> #include <unistd.h> -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #include <conio.h> #else @@ -50,7 +50,7 @@ static char *udid = NULL; #ifdef HAVE_WIRELESS_PAIRING -#ifdef WIN32 +#ifdef _WIN32 #define BS_CC '\b' #define my_getch getch #else @@ -293,7 +293,7 @@ int main(int argc, char **argv) } } -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif diff --git a/tools/ideviceprovision.c b/tools/ideviceprovision.c index 4080a28..94f4ec5 100644 --- a/tools/ideviceprovision.c +++ b/tools/ideviceprovision.c @@ -32,11 +32,12 @@ #include <getopt.h> #include <sys/stat.h> #include <errno.h> -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif -#ifdef WIN32 +#ifdef _WIN32 +#include <winsock2.h> #include <windows.h> #else #include <arpa/inet.h> @@ -314,7 +315,7 @@ int main(int argc, char *argv[]) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif /* parse cmdline args */ @@ -475,27 +476,7 @@ int main(int argc, char *argv[]) return -1; } - plist_t pver = NULL; - char *pver_s = NULL; - lockdownd_get_value(client, NULL, "ProductVersion", &pver); - if (pver && plist_get_node_type(pver) == PLIST_STRING) { - plist_get_string_val(pver, &pver_s); - } - plist_free(pver); - int product_version_major = 0; - int product_version_minor = 0; - int product_version_patch = 0; - if (pver_s) { - sscanf(pver_s, "%d.%d.%d", &product_version_major, &product_version_minor, &product_version_patch); - free(pver_s); - } - if (product_version_major == 0) { - fprintf(stderr, "ERROR: Could not determine the device's ProductVersion\n"); - lockdownd_client_free(client); - idevice_free(device); - return -1; - } - int product_version = ((product_version_major & 0xFF) << 16) | ((product_version_minor & 0xFF) << 8) | (product_version_patch & 0xFF); + unsigned int device_version = idevice_get_device_version(device); lockdownd_error_t lerr = lockdownd_start_service(client, MISAGENT_SERVICE_NAME, &service); if (lerr != LOCKDOWN_E_SUCCESS) { @@ -546,7 +527,7 @@ int main(int argc, char *argv[]) { plist_t profiles = NULL; misagent_error_t merr; - if (product_version < 0x090300) { + if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) { merr = misagent_copy(mis, &profiles); } else { merr = misagent_copy_all(mis, &profiles); @@ -631,7 +612,7 @@ int main(int argc, char *argv[]) /* remove all provisioning profiles */ plist_t profiles = NULL; misagent_error_t merr; - if (product_version < 0x090300) { + if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) { merr = misagent_copy(mis, &profiles); } else { merr = misagent_copy_all(mis, &profiles); diff --git a/tools/idevicescreenshot.c b/tools/idevicescreenshot.c index 0e694c7..bfaa059 100644 --- a/tools/idevicescreenshot.c +++ b/tools/idevicescreenshot.c @@ -32,7 +32,7 @@ #include <errno.h> #include <time.h> #include <unistd.h> -#ifndef WIN32 +#ifndef _WIN32 #include <signal.h> #endif @@ -142,7 +142,7 @@ int main(int argc, char **argv) { NULL, 0, NULL, 0} }; -#ifndef WIN32 +#ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif /* parse cmdline args */ diff --git a/tools/idevicesetlocation.c b/tools/idevicesetlocation.c index 69fbaf5..dca8830 100644 --- a/tools/idevicesetlocation.c +++ b/tools/idevicesetlocation.c @@ -113,7 +113,7 @@ int main(int argc, char **argv) if ((argc > 2) || (argc < 1)) { print_usage(argc+optind, argv-optind, 1); - return -1; + return 1; } if (argc == 2) { @@ -123,7 +123,7 @@ int main(int argc, char **argv) mode = RESET_LOCATION; } else { print_usage(argc+optind, argv-optind, 1); - return -1; + return 1; } } @@ -135,19 +135,30 @@ int main(int argc, char **argv) } else { printf("ERROR: No device found!\n"); } - return -1; + return 1; } - lockdownd_client_t lockdown; - lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME); + lockdownd_client_t lockdown = NULL; + lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME); + if (lerr != LOCKDOWN_E_SUCCESS) { + idevice_free(device); + printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(lerr), lerr); + return 1; + } lockdownd_service_descriptor_t svc = NULL; - lockdownd_error_t lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc); + lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc); if (lerr != LOCKDOWN_E_SUCCESS) { + unsigned int device_version = idevice_get_device_version(device); lockdownd_client_free(lockdown); idevice_free(device); - printf("ERROR: Could not start the simulatelocation service: %s\nMake sure a developer disk image is mounted!\n", lockdownd_strerror(lerr)); - return -1; + printf("ERROR: Could not start the simulatelocation service: %s\n", lockdownd_strerror(lerr)); + if (device_version >= IDEVICE_DEVICE_VERSION(17,0,0)) { + printf("Note: This tool is currently not supported on iOS 17+\n"); + } else { + printf("Make sure a developer disk image is mounted!\n"); + } + return 1; } lockdownd_client_free(lockdown); @@ -158,10 +169,9 @@ int main(int argc, char **argv) lockdownd_service_descriptor_free(svc); if (serr != SERVICE_E_SUCCESS) { - lockdownd_client_free(lockdown); idevice_free(device); printf("ERROR: Could not connect to simulatelocation service (%d)\n", serr); - return -1; + return 1; } uint32_t l; diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c index a0e641d..88af4c1 100644 --- a/tools/idevicesyslog.c +++ b/tools/idevicesyslog.c @@ -33,8 +33,9 @@ #include <stdlib.h> #include <unistd.h> #include <getopt.h> +#include <time.h> -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #define sleep(x) Sleep(x*1000) #endif @@ -42,10 +43,12 @@ #include <libimobiledevice/libimobiledevice.h> #include <libimobiledevice/syslog_relay.h> #include <libimobiledevice-glue/termcolors.h> +#include <libimobiledevice/ostrace.h> static int quit_flag = 0; static int exit_on_disconnect = 0; static int show_device_name = 0; +static int force_syslog_relay = 0; static char* udid = NULL; static char** proc_filters = NULL; @@ -58,6 +61,9 @@ static int num_pid_filters = 0; static char** msg_filters = NULL; static int num_msg_filters = 0; +static char** msg_reverse_filters = NULL; +static int num_msg_reverse_filters = 0; + static char** trigger_filters = NULL; static int num_trigger_filters = 0; static char** untrigger_filters = NULL; @@ -66,11 +72,16 @@ static int triggered = 0; static idevice_t device = NULL; static syslog_relay_client_t syslog = NULL; +static ostrace_client_t ostrace = NULL; static const char QUIET_FILTER[] = "CircleJoinRequested|CommCenter|HeuristicInterpreter|MobileMail|PowerUIAgent|ProtectedCloudKeySyncing|SpringBoard|UserEventAgent|WirelessRadioManagerd|accessoryd|accountsd|aggregated|analyticsd|appstored|apsd|assetsd|assistant_service|backboardd|biometrickitd|bluetoothd|calaccessd|callservicesd|cloudd|com.apple.Safari.SafeBrowsing.Service|contextstored|corecaptured|coreduetd|corespeechd|cdpd|dasd|dataaccessd|distnoted|dprivacyd|duetexpertd|findmydeviced|fmfd|fmflocatord|gpsd|healthd|homed|identityservicesd|imagent|itunescloudd|itunesstored|kernel|locationd|maild|mDNSResponder|mediaremoted|mediaserverd|mobileassetd|nanoregistryd|nanotimekitcompaniond|navd|nsurlsessiond|passd|pasted|photoanalysisd|powerd|powerlogHelperd|ptpd|rapportd|remindd|routined|runningboardd|searchd|sharingd|suggestd|symptomsd|timed|thermalmonitord|useractivityd|vmd|wifid|wirelessproxd"; static int use_network = 0; +static long long start_time = -1; +static long long size_limit = -1; +static long long age_limit = -1; + static char *line = NULL; static int line_buffer_size = 0; static int lp = 0; @@ -129,6 +140,70 @@ static int find_char(char c, char** p, const char* end) static void stop_logging(void); +static int message_filter_matching(const char* message) +{ + if (num_msg_filters > 0) { + int found = 0; + int i; + for (i = 0; i < num_msg_filters; i++) { + if (strstr(message, msg_filters[i])) { + found = 1; + break; + } + } + if (!found) { + return 0; + } + } + if (num_msg_reverse_filters > 0) { + int found = 0; + int i; + for (i = 0; i < num_msg_reverse_filters; i++) { + if (strstr(message, msg_reverse_filters[i])) { + found = 1; + break; + } + } + if (found) { + return 0; + } + } + return 1; +} + +static int process_filter_matching(int pid, const char* process_name, int process_name_length) +{ + int proc_matched = 0; + if (num_pid_filters > 0) { + int found = proc_filter_excluding; + int i = 0; + for (i = 0; i < num_pid_filters; i++) { + if (pid == pid_filters[i]) { + found = !proc_filter_excluding; + break; + } + } + if (found) { + proc_matched = 1; + } + } + if (num_proc_filters > 0 && !proc_matched) { + int found = proc_filter_excluding; + int i = 0; + for (i = 0; i < num_proc_filters; i++) { + if (!proc_filters[i]) continue; + if (strncmp(proc_filters[i], process_name, process_name_length) == 0) { + found = !proc_filter_excluding; + break; + } + } + if (found) { + proc_matched = 1; + } + } + return proc_matched; +} + static void syslog_callback(char c, void *user_data) { if (lp >= line_buffer_size-1) { @@ -202,20 +277,9 @@ static void syslog_callback(char c, void *user_data) } /* check message filters */ - if (num_msg_filters > 0) { - int found = 0; - int i; - for (i = 0; i < num_msg_filters; i++) { - if (strstr(device_name_end+1, msg_filters[i])) { - found = 1; - break; - } - } - if (!found) { - shall_print = 0; - break; - } - shall_print = 1; + shall_print = message_filter_matching(device_name_end+1); + if (!shall_print) { + break; } /* process name */ @@ -235,39 +299,10 @@ static void syslog_callback(char c, void *user_data) proc_name_end = p; p++; - int proc_matched = 0; - if (num_pid_filters > 0) { - char* endp = NULL; - int pid_value = (int)strtol(pid_start, &endp, 10); - if (endp && (*endp == ']')) { - int found = proc_filter_excluding; - int i = 0; - for (i = 0; i < num_pid_filters; i++) { - if (pid_value == pid_filters[i]) { - found = !proc_filter_excluding; - break; - } - } - if (found) { - proc_matched = 1; - } - } - } - if (num_proc_filters > 0 && !proc_matched) { - int found = proc_filter_excluding; - int i = 0; - for (i = 0; i < num_proc_filters; i++) { - if (!proc_filters[i]) continue; - if (strncmp(proc_filters[i], process_name_start, process_name_end-process_name_start) == 0) { - found = !proc_filter_excluding; - break; - } - } - if (found) { - proc_matched = 1; - } - } - if (proc_matched) { + /* match pid / process name */ + char* endp = NULL; + int pid_value = (int)strtol(pid_start, &endp, 10); + if (process_filter_matching(pid_value, process_name_start, process_name_end-process_name_start)) { shall_print = 1; } else { if (num_pid_filters > 0 || num_proc_filters > 0) { @@ -331,7 +366,7 @@ static void syslog_callback(char c, void *user_data) } } while (0); - if ((num_msg_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) { + if ((num_msg_filters == 0 && num_msg_reverse_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) { fwrite(linep, 1, lp, stdout); cprintf(COLOR_RESET); fflush(stdout); @@ -345,12 +380,231 @@ static void syslog_callback(char c, void *user_data) } } -static int start_logging(void) +static void ostrace_syslog_callback(const void* buf, size_t len, void* user_data) { - idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); - if (ret != IDEVICE_E_SUCCESS) { - fprintf(stderr, "Device with udid %s not found!?\n", udid); - return -1; + if (len < 0x81) { + fprintf(stderr, "Error: not enough data in callback function?!\n"); + return; + } + + struct ostrace_packet_header_t *trace_hdr = (struct ostrace_packet_header_t*)buf; + + if (trace_hdr->marker != 2 || (trace_hdr->type != 8 && trace_hdr->type != 2)) { + fprintf(stderr, "unexpected packet data %02x %08x\n", trace_hdr->marker, trace_hdr->type); + } + + const char* dataptr = (const char*)buf + trace_hdr->header_size; + const char* process_name = dataptr; + const char* image_name = (trace_hdr->imagepath_len > 0) ? dataptr + trace_hdr->procpath_len : NULL; + const char* message = (trace_hdr->message_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len : NULL; + //const char* subsystem = (trace_hdr->subsystem_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len : NULL; + //const char* category = (trace_hdr->category_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len + trace_hdr->subsystem_len : NULL; + + int shall_print = 1; + int trigger_off = 0; + const char* process_name_short = (process_name) ? strrchr(process_name, '/') : ""; + process_name_short = (process_name_short) ? process_name_short+1 : process_name; + const char* image_name_short = (image_name) ? strrchr(image_name, '/') : NULL; + image_name_short = (image_name_short) ? image_name_short+1 : process_name; + if (image_name_short && !strcmp(image_name_short, process_name_short)) { + image_name_short = NULL; + } + + do { + /* check if we have any triggers/untriggers */ + if (num_untrigger_filters > 0 && triggered) { + int found = 0; + int i; + for (i = 0; i < num_untrigger_filters; i++) { + if (strstr(message, untrigger_filters[i])) { + found = 1; + break; + } + } + if (!found) { + shall_print = 1; + } else { + shall_print = 1; + trigger_off = 1; + } + } else if (num_trigger_filters > 0 && !triggered) { + int found = 0; + int i; + for (i = 0; i < num_trigger_filters; i++) { + if (strstr(message, trigger_filters[i])) { + found = 1; + break; + } + } + if (!found) { + shall_print = 0; + break; + } + triggered = 1; + shall_print = 1; + } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) { + shall_print = 0; + quit_flag++; + break; + } + + /* check message filters */ + shall_print = message_filter_matching(message); + if (!shall_print) { + break; + } + + /* check process filters */ + if (process_filter_matching(trace_hdr->pid, process_name_short, strlen(process_name_short))) { + shall_print = 1; + } else { + if (num_pid_filters > 0 || num_proc_filters > 0) { + shall_print = 0; + } + } + if (!shall_print) { + break; + } + } while (0); + + if (!shall_print) { + return; + } + + const char* level_str = "Unknown"; + const char* level_color = FG_YELLOW; + switch (trace_hdr->level) { + case 0: + level_str = "Notice"; + level_color = FG_GREEN; + break; + case 0x01: + level_str = "Info"; + level_color = FG_WHITE; + break; + case 0x02: + level_str = "Debug"; + level_color = FG_MAGENTA; + break; + case 0x10: + level_str = "Error"; + level_color = FG_RED; + break; + case 0x11: + level_str = "Fault"; + level_color = FG_RED; + default: + break; + } + + char datebuf[24]; + struct tm *tp; + time_t time_sec = (time_t)trace_hdr->time_sec; +#ifdef HAVE_LOCALTIME_R + struct tm tp_ = {0, }; + tp = localtime_r(&time_sec, &tp_); +#else + tp = localtime(&time_sec); +#endif +#ifdef _WIN32 + strftime(datebuf, 16, "%b %#d %H:%M:%S", tp); +#else + strftime(datebuf, 16, "%b %e %H:%M:%S", tp); +#endif + snprintf(datebuf+15, 9, ".%06u", trace_hdr->time_usec); + + /* write date and time */ + cprintf(FG_LIGHT_GRAY "%s ", datebuf); + + if (show_device_name) { + /* write device name TODO do we need this? */ + //cprintf(FG_DARK_YELLOW "%s ", device_name); + } + + /* write process name */ + cprintf(FG_BRIGHT_CYAN "%s" FG_CYAN, process_name_short); + if (image_name_short) { + cprintf("(%s)", image_name_short); + } + cprintf("[%d]" COLOR_RESET " ", trace_hdr->pid); + + /* write log level */ + cprintf(level_color); + cprintf("<%s>:" COLOR_RESET " ", level_str); + + /* write message */ + cprintf(FG_WHITE); + cprintf("%s" COLOR_RESET "\n", message); + fflush(stdout); + + if (trigger_off) { + triggered = 0; + } +} + +static plist_t get_pid_list() +{ + plist_t list = NULL; + ostrace_client_t ostrace_tmp = NULL; + ostrace_client_start_service(device, &ostrace_tmp, TOOL_NAME); + if (ostrace_tmp) { + ostrace_get_pid_list(ostrace_tmp, &list); + ostrace_client_free(ostrace_tmp); + } + return list; +} + +static int pid_valid(int pid) +{ + plist_t list = get_pid_list(); + if (!list) return 0; + char valbuf[16]; + snprintf(valbuf, 16, "%d", pid); + return (plist_dict_get_item(list, valbuf)) ? 1 : 0; +} + +static int pid_for_proc(const char* procname) +{ + int result = -1; + plist_t list = get_pid_list(); + if (!list) { + return result; + } + plist_dict_iter iter = NULL; + plist_dict_new_iter(list, &iter); + if (iter) { + plist_t node = NULL; + do { + char* key = NULL; + node = NULL; + plist_dict_next_item(list, iter, &key, &node); + if (!key) { + break; + } + if (PLIST_IS_DICT(node)) { + plist_t pname = plist_dict_get_item(node, "ProcessName"); + if (PLIST_IS_STRING(pname)) { + if (!strcmp(plist_get_string_ptr(pname, NULL), procname)) { + result = (int)strtol(key, NULL, 10); + } + } + } + free(key); + } while (node); + plist_mem_free(iter); + } + plist_free(list); + return result; +} + +static int connect_service(int ostrace_required) +{ + if (!device) { + idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); + if (ret != IDEVICE_E_SUCCESS) { + fprintf(stderr, "Device with udid %s not found!?\n", udid); + return -1; + } } lockdownd_client_t lockdown = NULL; @@ -361,14 +615,28 @@ static int start_logging(void) device = NULL; return -1; } - - /* start syslog_relay service */ lockdownd_service_descriptor_t svc = NULL; - lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc); + + const char* service_name = OSTRACE_SERVICE_NAME; + int use_ostrace = 1; + if (idevice_get_device_version(device) < IDEVICE_DEVICE_VERSION(9,0,0) || force_syslog_relay) { + service_name = SYSLOG_RELAY_SERVICE_NAME; + use_ostrace = 0; + } + if (ostrace_required && !use_ostrace) { + fprintf(stderr, "ERROR: This operation requires iOS 9 or later.\n"); + lockdownd_client_free(lockdown); + idevice_free(device); + device = NULL; + return -1; + } + + /* start syslog_relay/os_trace_relay service */ + lerr = lockdownd_start_service(lockdown, service_name, &svc); if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { fprintf(stderr, "*** Device is passcode protected, enter passcode on the device to continue ***\n"); while (!quit_flag) { - lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc); + lerr = lockdownd_start_service(lockdown, service_name, &svc); if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) { break; } @@ -376,32 +644,84 @@ static int start_logging(void) } } if (lerr != LOCKDOWN_E_SUCCESS) { - fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr); + fprintf(stderr, "ERROR: Could not start %s service: %s (%d)\n", service_name, lockdownd_strerror(lerr), lerr); idevice_free(device); device = NULL; return -1; } lockdownd_client_free(lockdown); - /* connect to syslog_relay service */ - syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR; - serr = syslog_relay_client_new(device, svc, &syslog); - lockdownd_service_descriptor_free(svc); - if (serr != SYSLOG_RELAY_E_SUCCESS) { - fprintf(stderr, "ERROR: Could not start service com.apple.syslog_relay.\n"); - idevice_free(device); - device = NULL; + if (use_ostrace) { + /* connect to os_trace_relay service */ + ostrace_error_t serr = OSTRACE_E_UNKNOWN_ERROR; + serr = ostrace_client_new(device, svc, &ostrace); + lockdownd_service_descriptor_free(svc); + if (serr != OSTRACE_E_SUCCESS) { + fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr); + idevice_free(device); + device = NULL; + return -1; + } + } else { + /* connect to syslog_relay service */ + syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR; + serr = syslog_relay_client_new(device, svc, &syslog); + lockdownd_service_descriptor_free(svc); + if (serr != SYSLOG_RELAY_E_SUCCESS) { + fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr); + idevice_free(device); + device = NULL; + return -1; + } + } + return 0; +} + +static int start_logging(void) +{ + if (connect_service(0) < 0) { return -1; } /* start capturing syslog */ - serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL); - if (serr != SYSLOG_RELAY_E_SUCCESS) { - fprintf(stderr, "ERROR: Unable tot start capturing syslog.\n"); - syslog_relay_client_free(syslog); - syslog = NULL; - idevice_free(device); - device = NULL; + if (ostrace) { + plist_t options = plist_new_dict(); + if (num_proc_filters == 0 && num_pid_filters == 1 && !proc_filter_excluding) { + if (pid_filters[0] > 0) { + if (!pid_valid(pid_filters[0])) { + fprintf(stderr, "NOTE: A process with pid doesn't exists!\n"); + } + } + plist_dict_set_item(options, "Pid", plist_new_int(pid_filters[0])); + } else if (num_proc_filters == 1 && num_pid_filters == 0 && !proc_filter_excluding) { + int pid = pid_for_proc(proc_filters[0]); + if (!strcmp(proc_filters[0], "kernel")) { + pid = 0; + } + if (pid >= 0) { + plist_dict_set_item(options, "Pid", plist_new_int(pid)); + } + } + ostrace_error_t serr = ostrace_start_activity(ostrace, options, ostrace_syslog_callback, NULL); + if (serr != OSTRACE_E_SUCCESS) { + fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); + ostrace_client_free(ostrace); + ostrace = NULL; + idevice_free(device); + device = NULL; + return -1; + } + } else if (syslog) { + syslog_relay_error_t serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL); + if (serr != SYSLOG_RELAY_E_SUCCESS) { + fprintf(stderr, "ERROR: Unable to start capturing syslog.\n"); + syslog_relay_client_free(syslog); + syslog = NULL; + idevice_free(device); + device = NULL; + return -1; + } + } else { return -1; } @@ -419,6 +739,11 @@ static void stop_logging(void) syslog_relay_client_free(syslog); syslog = NULL; } + if (ostrace) { + ostrace_stop_activity(ostrace); + ostrace_client_free(ostrace); + ostrace = NULL; + } if (device) { idevice_free(device); @@ -426,6 +751,77 @@ static void stop_logging(void) } } +static int write_callback(const void* buf, size_t len, void *user_data) +{ + FILE* f = (FILE*)user_data; + ssize_t res = fwrite(buf, 1, len, f); + if (res < 0) { + return -1; + } + if (quit_flag > 0) { + return -1; + } + return 0; +} + +static void print_sorted_pidlist(plist_t list) +{ + struct listelem; + struct listelem { + int val; + struct listelem *next; + }; + struct listelem* sortedlist = NULL; + + plist_dict_iter iter = NULL; + plist_dict_new_iter(list, &iter); + if (iter) { + plist_t node = NULL; + do { + char* key = NULL; + node = NULL; + plist_dict_next_item(list, iter, &key, &node); + if (key) { + int pidval = (int)strtol(key, NULL, 10); + struct listelem* elem = (struct listelem*)malloc(sizeof(struct listelem)); + elem->val = pidval; + elem->next = NULL; + struct listelem* prev = NULL; + struct listelem* curr = sortedlist; + + while (curr && pidval > curr->val) { + prev = curr; + curr = curr->next; + } + + elem->next = curr; + if (prev == NULL) { + sortedlist = elem; + } else { + prev->next = elem; + } + free(key); + } + } while (node); + plist_mem_free(iter); + } + struct listelem *listp = sortedlist; + char pidstr[16]; + while (listp) { + snprintf(pidstr, 16, "%d", listp->val); + plist_t node = plist_dict_get_item(list, pidstr); + if (PLIST_IS_DICT(node)) { + plist_t pname = plist_dict_get_item(node, "ProcessName"); + if (PLIST_IS_STRING(pname)) { + printf("%d %s\n", listp->val, plist_get_string_ptr(pname, NULL)); + } + } + struct listelem *curr = listp; + listp = listp->next; + free(curr); + } +} + static void device_event_cb(const idevice_event_t* event, void* userdata) { if (use_network && event->conn_type != CONNECTION_NETWORK) { @@ -435,7 +831,7 @@ static void device_event_cb(const idevice_event_t* event, void* userdata) return; } if (event->event == IDEVICE_DEVICE_ADD) { - if (!syslog) { + if (!syslog && !ostrace) { if (!udid) { udid = strdup(event->udid); } @@ -446,7 +842,7 @@ static void device_event_cb(const idevice_event_t* event, void* userdata) } } } else if (event->event == IDEVICE_DEVICE_REMOVE) { - if (syslog && (strcmp(udid, event->udid) == 0)) { + if ((syslog || ostrace) && (strcmp(udid, event->udid) == 0)) { stop_logging(); fprintf(stdout, "[disconnected:%s]\n", udid); if (exit_on_disconnect) { @@ -484,9 +880,20 @@ static void print_usage(int argc, char **argv, int is_error) " -o, --output FILE write to FILE instead of stdout\n" " (existing FILE will be overwritten)\n" " --colors force writing colored output, e.g. for --output\n" + " --syslog-relay force use of syslog_relay service\n" + "\n" + "COMMANDS:\n" + " pidlist Print pid and name of all running processes.\n" + " archive PATH Request a logarchive and write it to PATH.\n" + " Output can be piped to another process using - as PATH.\n" + " The file data will be in .tar format.\n" + " --start-time VALUE start time of the log data as UNIX timestamp\n" + " --age-limit VALUE maximum age of the log data\n" + " --size-limit VALUE limit the size of the archive\n" "\n" "FILTER OPTIONS:\n" " -m, --match STRING only print messages that contain STRING\n" + " -M, --unmatch STRING print messages that not contain STRING\n" " -t, --trigger STRING start logging when matching STRING\n" " -T, --untrigger STRING stop logging when matching STRING\n" " -p, --process PROCESS only print messages from matching process(es)\n" @@ -530,6 +937,12 @@ int main(int argc, char *argv[]) { "quiet-list", no_argument, NULL, 1 }, { "no-colors", no_argument, NULL, 2 }, { "colors", no_argument, NULL, 3 }, + { "syslog_relay", no_argument, NULL, 4 }, + { "syslog-relay", no_argument, NULL, 4 }, + { "legacy", no_argument, NULL, 4 }, + { "start-time", required_argument, NULL, 5 }, + { "size-limit", required_argument, NULL, 6 }, + { "age-limit", required_argument, NULL, 7 }, { "output", required_argument, NULL, 'o' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0} @@ -537,12 +950,12 @@ int main(int argc, char *argv[]) signal(SIGINT, clean_exit); signal(SIGTERM, clean_exit); -#ifndef WIN32 +#ifndef _WIN32 signal(SIGQUIT, clean_exit); signal(SIGPIPE, SIG_IGN); #endif - while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:e:p:qkKo:v", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:M:e:p:qkKo:v", longopts, NULL)) != -1) { switch (c) { case 'd': idevice_set_debug_level(1); @@ -593,6 +1006,22 @@ int main(int argc, char *argv[]) num_msg_filters++; } break; + case 'M': + if (!*optarg) { + fprintf(stderr, "ERROR: reverse message filter string must not be empty!\n"); + print_usage(argc, argv, 1); + return 2; + } else { + char **new_msg_filters = realloc(msg_reverse_filters, sizeof(char*) * (num_msg_reverse_filters+1)); + if (!new_msg_filters) { + fprintf(stderr, "ERROR: realloc() failed\n"); + exit(EXIT_FAILURE); + } + msg_reverse_filters = new_msg_filters; + msg_reverse_filters[num_msg_reverse_filters] = strdup(optarg); + num_msg_reverse_filters++; + } + break; case 't': if (!*optarg) { fprintf(stderr, "ERROR: trigger filter string must not be empty!\n"); @@ -647,6 +1076,18 @@ int main(int argc, char *argv[]) case 3: force_colors = 1; break; + case 4: + force_syslog_relay = 1; + break; + case 5: + start_time = strtoll(optarg, NULL, 10); + break; + case 6: + size_limit = strtoll(optarg, NULL, 10); + break; + case 7: + age_limit = strtoll(optarg, NULL, 10); + break; case 'o': if (!*optarg) { fprintf(stderr, "ERROR: --output option requires an argument!\n"); @@ -719,14 +1160,92 @@ int main(int argc, char *argv[]) argc -= optind; argv += optind; + if (argc > 0) { + if (!strcmp(argv[0], "pidlist")) { + if (connect_service(1) < 0) { + return 1; + } + plist_t list = NULL; + ostrace_get_pid_list(ostrace, &list); + ostrace_client_free(ostrace); + ostrace = NULL; + idevice_free(device); + device = NULL; + if (!list) { + return 1; + } + print_sorted_pidlist(list); + plist_free(list); + return 0; + } else if (!strcmp(argv[0], "archive")) { + if (force_syslog_relay) { + force_syslog_relay = 0; + } + if (argc < 2) { + fprintf(stderr, "Please specify an output filename.\n"); + return 1; + } + FILE* outf = NULL; + if (!strcmp(argv[1], "-")) { + if (isatty(1)) { + fprintf(stderr, "Refusing to directly write to stdout. Pipe the output to another process.\n"); + return 1; + } + outf = stdout; + } else { + outf = fopen(argv[1], "w"); + } + if (!outf) { + fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno)); + return 1; + } + if (connect_service(1) < 0) { + if (outf != stdout) { + fclose(outf); + } + return 1; + } + plist_t options = plist_new_dict(); + if (start_time > 0) { + plist_dict_set_item(options, "StartTime", plist_new_int(start_time)); + } + if (size_limit > 0) { + plist_dict_set_item(options, "SizeLimit", plist_new_int(size_limit)); + } + if (age_limit > 0) { + plist_dict_set_item(options, "AgeLimit", plist_new_int(age_limit)); + } + ostrace_create_archive(ostrace, options, write_callback, outf); + ostrace_client_free(ostrace); + ostrace = NULL; + idevice_free(device); + device = NULL; + if (outf != stdout) { + fclose(outf); + } + return 0; + } else { + fprintf(stderr, "Unknown command '%s'. See --help for valid commands.\n", argv[0]); + return 1; + } + } + int num = 0; idevice_info_t *devices = NULL; idevice_get_device_list_extended(&devices, &num); + int count = 0; + for (int i = 0; i < num; i++) { + if (devices[i]->conn_type == CONNECTION_NETWORK && use_network) { + count++; + } else if (devices[i]->conn_type == CONNECTION_USBMUXD) { + count++; + } + } idevice_device_list_extended_free(devices); - if (num == 0) { + if (count == 0) { if (!udid) { - fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n"); - return -1; + fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to become available.\n"); + return 1; } fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid); @@ -761,6 +1280,13 @@ int main(int argc, char *argv[]) } free(msg_filters); } + if (num_msg_reverse_filters > 0) { + int i; + for (i = 0; i < num_msg_reverse_filters; i++) { + free(msg_reverse_filters[i]); + } + free(msg_reverse_filters); + } if (num_trigger_filters > 0) { int i; for (i = 0; i < num_trigger_filters; i++) { |