diff options
29 files changed, 2049 insertions, 545 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31341af..1869190 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,9 @@ name: build -on: [push] +on: + push: + schedule: + - cron: '0 0 1 * *' jobs: build-linux-ubuntu: @@ -13,7 +16,7 @@ jobs: run: | echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -27,7 +30,7 @@ jobs: done sudo cp -r extract/* / sudo ldconfig - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: autogen run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig LDFLAGS="-Wl,-rpath=/usr/local/lib" - name: make @@ -40,7 +43,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf libimobiledevice-glue.tar usr - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: libimobiledevice-glue-latest_${{env.target_triplet}} path: libimobiledevice-glue.tar @@ -56,7 +59,7 @@ jobs: fi shell: bash - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -69,7 +72,7 @@ jobs: tar -C extract -xvf $I done sudo cp -r extract/* / - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: autogen run: | SDKDIR=`xcrun --sdk macosx --show-sdk-path` @@ -93,7 +96,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf libimobiledevice-glue.tar usr - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: libimobiledevice-glue-latest_macOS path: libimobiledevice-glue.tar @@ -129,7 +132,7 @@ jobs: echo "dest=$dest" >> $GITHUB_ENV echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -142,7 +145,7 @@ jobs: tar -C extract -xvf $I done cp -r extract/* / - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: autogen run: ./autogen.sh CC=gcc CXX=g++ - name: make @@ -155,7 +158,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf libimobiledevice-glue.tar ${{ env.dest }} - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }} path: libimobiledevice-glue.tar diff --git a/Makefile.am b/Makefile.am index 149678b..96d3c7b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,12 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src include EXTRA_DIST = \ - README.md + README.md \ + git-version-gen + +dist-hook: + @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi + echo $(VERSION) > $(distdir)/.tarball-version indent: indent -kr -ut -ts4 -l120 src/*.c src/*.h @@ -0,0 +1,26 @@ +Version 1.2.0 +~~~~~~~~~~~~~ + +- Changes: + * Add helper code to deal with NSKeyedArchiver plist data + +- Bugfixes: + * socket: Make sure errno is always set on error, and always return a meaningful error code + +Version 1.1.0 +~~~~~~~~~~~~~ + +- Changes: + * socket: Use poll() - when available - instead of select() + * socket: Allow NULL as address for socket_create() and socket_connect() + * win32: Remove windows.h from public headers + * Add version function to interface + +- Bugfixes: + * opack: Fixed 32bit buffer overflow + * opack: Fix parsing of 32 and 64 bit packed values + +Version 1.0.0 +~~~~~~~~~~~~~ + +First public release. @@ -5,6 +5,21 @@ Library with common code used by the libraries and tools around the ![](https://github.com/libimobiledevice/libimobiledevice-glue/workflows/build/badge.svg) +## Table of Contents +- [Features](#features) +- [Building](#building) + - [Prerequisites](#prerequisites) + - [Linux (Debian/Ubuntu based)](#linux-debianubuntu-based) + - [macOS](#macos) + - [Windows](#windows) + - [Configuring the source tree](#configuring-the-source-tree) + - [Building and installation](#building-and-installation) +- [Usage](#usage) +- [Contributing](#contributing) +- [Links](#links) +- [License](#license) +- [Credits](#credits) + ## Features The main functionality provided by this library are **socket** helper @@ -13,7 +28,7 @@ Besides that it comes with a number of string, file, and plist helper functions, as well as some other commonly used code that was originally duplicated in the dedicated projects. -Test on Linux, macOS, Windows. +Tested on Linux, macOS, and Windows. ## Projects using this library @@ -23,48 +38,167 @@ Test on Linux, macOS, Windows. - [libirecovery](https://github.com/libimobiledevice/libirecovery) - [idevicerestore](https://github.com/libimobiledevice/idevicerestore) -## Installation / Getting started +## Building + +### Prerequisites + +You need to have a working compiler (gcc/clang) and development environent +available. This project uses autotools for the build process, allowing to +have common build steps across different platforms. +Only the prerequisites differ and they are described in this section. + +libimobiledevice-glue requires [libplist](https://github.com/libimobiledevice/libplist). +Check the [Building](https://github.com/libimobiledevice/libplist?tab=readme-ov-file#building) +section of the README on how to build it. Note that some platforms might have it as a package. + +#### Linux (Debian/Ubuntu based) + +* Install all required dependencies and build tools: + ```shell + sudo apt-get install \ + build-essential \ + pkg-config \ + checkinstall \ + git \ + autoconf \ + automake \ + libtool-bin \ + libplist-dev + ``` + + In case libplist-dev is not available, you can manually build and install it. See note above. + +#### macOS + +* Make sure the Xcode command line tools are installed. Then, use either [MacPorts](https://www.macports.org/) + or [Homebrew](https://brew.sh/) to install `automake`, `autoconf`, `libtool`, etc. + + Using MacPorts: + ```shell + sudo port install libtool autoconf automake pkgconfig + ``` + + Using Homebrew: + ```shell + brew install libtool autoconf automake pkg-config + ``` -### Debian / Ubuntu Linux +#### Windows + +* Using [MSYS2](https://www.msys2.org/) is the official way of compiling this project on Windows. Download the MSYS2 installer + and follow the installation steps. + + It is recommended to use the _MSYS2 MinGW 64-bit_ shell. Run it and make sure the required dependencies are installed: + + ```shell + pacman -S base-devel \ + git \ + mingw-w64-x86_64-gcc \ + make \ + libtool \ + autoconf \ + automake-wrapper \ + pkg-config + ``` + NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly. + +### Configuring the source tree + +You can build the source code from a git checkout, or from a `.tar.bz2` release tarball from [Releases](https://github.com/libimobiledevice/libimobiledevice-glue/releases). +Before we can build it, the source tree has to be configured for building. The steps depend on where you got the source from. + +Since libimobiledevice-glue depends on other packages, you should set the pkg-config environment variable `PKG_CONFIG_PATH` +accordingly. Make sure to use a path with the same prefix as the dependencies. If they are installed in `/usr/local` you would do -First install all required dependencies and build tools: ```shell -sudo apt-get install \ - build-essential \ - pkg-config \ - checkinstall \ - git \ - autoconf \ - automake \ - libtool-bin \ - libplist-dev \ +export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ``` -Then clone the actual project repository: +* **From git** + + If you haven't done already, clone the actual project repository and change into the directory. + ```shell + git clone https://github.com/libimobiledevice/libimobiledevice-glue + cd libimobiledevice-glue + ``` + + Configure the source tree for building: + ```shell + ./autogen.sh + ``` + +* **From release tarball (.tar.bz2)** + + When using an official [release tarball](https://github.com/libimobiledevice/libimobiledevice-glue/releases) (`libimobiledevice-glue-x.y.z.tar.bz2`) + the procedure is slightly different. + + Extract the tarball: + ```shell + tar xjf libimobiledevice-glue-x.y.z.tar.bz2 + cd libimobiledevice-glue-x.y.z + ``` + + Configure the source tree for building: + ```shell + ./configure + ``` + +Both `./configure` and `./autogen.sh` (which generates and calls `configure`) accept a few options, for example `--prefix` to allow +building for a different target folder. You can simply pass them like this: + +```shell +./autogen.sh --prefix=/usr/local +``` +or ```shell -git clone https://github.com/libimobiledevice/libimobiledevice-glue.git -cd libimobiledevice-glue +./configure --prefix=/usr/local ``` -Now you can build and install it: +Once the command is successful, the last few lines of output will look like this: +``` +[...] +config.status: creating config.h +config.status: executing depfiles commands +config.status: executing libtool commands + +Configuration for libimobiledevice-glue 1.0.1: +------------------------------------------- + + Install prefix: .........: /usr/local + + Now type 'make' to build libimobiledevice-glue 1.0.1, + and then 'make install' for installation. +``` + +### Building and installation + +If you followed all the steps successfully, and `autogen.sh` or `configure` did not print any errors, +you are ready to build the project. This is simply done with + ```shell -./autogen.sh make -sudo make install ``` -If you require a custom prefix or other option being passed to `./configure` -you can pass them directly to `./autogen.sh` like this: -```bash -./autogen.sh --prefix=/opt/local -make +If no errors are emitted you are ready for installation. Depending on whether +the current user has permissions to write to the destination directory or not, +you would either run +```shell +make install +``` +_OR_ +```shell sudo make install ``` +If you are on Linux, you want to run `sudo ldconfig` after installation to +make sure the installed libraries are made available. + ## Usage This library is directly used by libusbmuxd, libimobiledevice, etc., so there -is no need to do anything in particular. +is no need to do anything in particular. Feel free to use it in your project, +check the header files for available functions. A documentation might be added +later. ## Contributing @@ -93,7 +227,7 @@ Please make sure your contribution adheres to: ## License -This library and utilities are licensed under the [GNU Lesser General Public License v2.1](https://www.gnu.org/licenses/lgpl-2.1.en.html), +This library is licensed under the [GNU Lesser General Public License v2.1](https://www.gnu.org/licenses/lgpl-2.1.en.html), also included in the repository in the `COPYING` file. ## Credits @@ -101,7 +235,8 @@ also included in the repository in the `COPYING` file. Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS, 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. +This project is an independent software library and has not been authorized, +sponsored, or otherwise approved by Apple Inc. + +README Updated on: 2024-02-21 -README Updated on: 2022-04-04 diff --git a/configure.ac b/configure.ac index 4e40db1..7aac157 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -AC_PREREQ(2.68) -AC_INIT([libimobiledevice-glue], [1.0.0], [https://github.com/libimobiledevice/libimobiledevice-glue/issues],, [https://libimobiledevice.org]) +AC_PREREQ([2.68]) +AC_INIT([libimobiledevice-glue], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/libimobiledevice-glue/issues], [], [https://libimobiledevice.org]) AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_SRCDIR([src/]) @@ -15,10 +15,15 @@ dnl libtool versioning # changes to the signature and the semantic) # ? :+1 : ? == just internal changes # CURRENT : REVISION : AGE -LIBIMOBILEDEVICE_GLUE_SO_VERSION=0:0:0 +LIBIMOBILEDEVICE_GLUE_SO_VERSION=2:0:2 + +# Check if we have a version defined +if test -z $PACKAGE_VERSION; then + AC_MSG_ERROR([PACKAGE_VERSION is not defined. Make sure to configure a source tree checked out from git or that .tarball-version is present.]) +fi dnl Minimum package versions -LIBPLIST_VERSION=2.2.0 +LIBPLIST_VERSION=2.3.0 AC_SUBST(LIBIMOBILEDEVICE_GLUE_SO_VERSION) AC_SUBST(LIBPLIST_VERSION) @@ -44,7 +49,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 poll]) 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 @@ -105,12 +110,12 @@ 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>]) AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fsigned-char -fvisibility=hidden") -AC_SUBST(GLOBAL_CFLAGS) -case "$GLOBAL_CFLAGS" in - *-fvisibility=hidden*) - AC_DEFINE([HAVE_FVISIBILITY], [1], [Define if compiled with -fvisibility=hidden]) -esac +if test "x$enable_static" = "xyes" -a "x$enable_shared" = "xno"; then + GLOBAL_CFLAGS+=" -DLIMD_GLUE_STATIC" +fi + +AC_SUBST(GLOBAL_CFLAGS) # check for large file support AC_SYS_LARGEFILE diff --git a/git-version-gen b/git-version-gen new file mode 100755 index 0000000..d868952 --- /dev/null +++ b/git-version-gen @@ -0,0 +1,20 @@ +#!/bin/sh +SRCDIR=`dirname $0` +if test -n "$1"; then + VER=$1 +else + if test -r "${SRCDIR}/.git" && test -x "`which git`" ; then + git update-index -q --refresh + if ! VER=`git describe --tags --dirty 2>/dev/null`; then + COMMIT=`git rev-parse --short HEAD` + DIRTY=`git diff --quiet HEAD || echo "-dirty"` + VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY} + fi + else + if test -f "${SRCDIR}/.tarball-version"; then + VER=`cat "${SRCDIR}/.tarball-version"` + fi + fi +fi +VER=`printf %s "$VER" | head -n1` +printf %s "$VER" diff --git a/include/Makefile.am b/include/Makefile.am index a071583..7aca2a0 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,9 +2,11 @@ EXTRA_DIST = \ endianness.h nobase_include_HEADERS = \ + libimobiledevice-glue/glue.h \ libimobiledevice-glue/socket.h \ libimobiledevice-glue/thread.h \ libimobiledevice-glue/utils.h \ + libimobiledevice-glue/nskeyedarchive.h \ libimobiledevice-glue/collection.h \ libimobiledevice-glue/termcolors.h \ libimobiledevice-glue/cbuf.h \ diff --git a/include/libimobiledevice-glue/cbuf.h b/include/libimobiledevice-glue/cbuf.h index 01e2f43..2463af7 100644 --- a/include/libimobiledevice-glue/cbuf.h +++ b/include/libimobiledevice-glue/cbuf.h @@ -22,14 +22,16 @@ #ifndef __CBUF_H #define __CBUF_H +#include <libimobiledevice-glue/glue.h> + struct char_buf { unsigned char* data; unsigned int length; unsigned int capacity; }; -struct char_buf* char_buf_new(); -void char_buf_free(struct char_buf* cbuf); -void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data); +LIMD_GLUE_API struct char_buf* char_buf_new(); +LIMD_GLUE_API void char_buf_free(struct char_buf* cbuf); +LIMD_GLUE_API void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data); #endif /* __CBUF_H */ diff --git a/include/libimobiledevice-glue/collection.h b/include/libimobiledevice-glue/collection.h index df1680c..46ef461 100644 --- a/include/libimobiledevice-glue/collection.h +++ b/include/libimobiledevice-glue/collection.h @@ -22,17 +22,19 @@ #ifndef COLLECTION_H #define COLLECTION_H +#include <libimobiledevice-glue/glue.h> + struct collection { void **list; int capacity; }; -void collection_init(struct collection *col); -void collection_add(struct collection *col, void *element); -int collection_remove(struct collection *col, void *element); -int collection_count(struct collection *col); -void collection_free(struct collection *col); -void collection_copy(struct collection *dest, struct collection *src); +LIMD_GLUE_API void collection_init(struct collection *col); +LIMD_GLUE_API void collection_add(struct collection *col, void *element); +LIMD_GLUE_API int collection_remove(struct collection *col, void *element); +LIMD_GLUE_API int collection_count(struct collection *col); +LIMD_GLUE_API void collection_free(struct collection *col); +LIMD_GLUE_API void collection_copy(struct collection *dest, struct collection *src); #define MERGE_(a,b) a ## _ ## b #define LABEL_(a,b) MERGE_(a, b) diff --git a/include/libimobiledevice-glue/glue.h b/include/libimobiledevice-glue/glue.h new file mode 100644 index 0000000..e1e9900 --- /dev/null +++ b/include/libimobiledevice-glue/glue.h @@ -0,0 +1,37 @@ +/* + * glue.h + * Common definitions + * + * Copyright (c) 2024 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 __GLUE_H +#define __GLUE_H + +#ifndef LIMD_GLUE_API + #ifdef LIMD_GLUE_STATIC + #define LIMD_GLUE_API + #elif defined(_WIN32) + #define LIMD_GLUE_API __declspec(dllimport) + #else + #define LIMD_GLUE_API + #endif +#endif + +LIMD_GLUE_API const char* libimobiledevice_glue_version(); + +#endif diff --git a/include/libimobiledevice-glue/nskeyedarchive.h b/include/libimobiledevice-glue/nskeyedarchive.h new file mode 100644 index 0000000..5aad4d6 --- /dev/null +++ b/include/libimobiledevice-glue/nskeyedarchive.h @@ -0,0 +1,90 @@ +/* + * nskeyedarchive.h + * Helper code to work with plist files containing NSKeyedArchiver data. + * + * Copyright (c) 2019 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 __NSKEYEDARCHIVE_H +#define __NSKEYEDARCHIVE_H + +#include <stdint.h> +#include <libimobiledevice-glue/glue.h> +#include <plist/plist.h> + +enum nskeyedarchive_class_type_t { + NSTYPE_INTEGER = 1, + NSTYPE_BOOLEAN, + NSTYPE_CHARS, + NSTYPE_STRING, + NSTYPE_REAL, + NSTYPE_ARRAY, + NSTYPE_DATA, + NSTYPE_INTREF, + NSTYPE_NSMUTABLESTRING, + NSTYPE_NSSTRING, + NSTYPE_NSMUTABLEARRAY, + NSTYPE_NSARRAY, + NSTYPE_NSMUTABLEDICTIONARY, + NSTYPE_NSDICTIONARY, + NSTYPE_NSDATE, + NSTYPE_NSURL, + NSTYPE_NSMUTABLEDATA, + NSTYPE_NSDATA, + NSTYPE_NSKEYEDARCHIVE, + NSTYPE_FROM_PLIST +}; + +typedef struct nskeyedarchive_st *nskeyedarchive_t; + +LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new(void); +LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new_from_plist(plist_t plist); +LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new_from_data(const void* data, uint32_t size); +LIMD_GLUE_API void nskeyedarchive_free(nskeyedarchive_t ka); + +LIMD_GLUE_API void nskeyedarchive_set_top_ref_key_name(nskeyedarchive_t ka, const char* keyname); + +LIMD_GLUE_API uint64_t nskeyedarchive_add_top_class(nskeyedarchive_t ka, const char* classname, ...) __attribute__ ((sentinel(0))); +LIMD_GLUE_API void nskeyedarchive_add_top_class_uid(nskeyedarchive_t ka, uint64_t uid); +LIMD_GLUE_API void nskeyedarchive_append_class(nskeyedarchive_t ka, const char* classname, ...) __attribute__ ((sentinel(0))); +LIMD_GLUE_API void nskeyedarchive_append_object(nskeyedarchive_t ka, plist_t object); + +LIMD_GLUE_API void nskeyedarchive_nsarray_append_item(nskeyedarchive_t ka, uint64_t uid, enum nskeyedarchive_class_type_t type, ...); +LIMD_GLUE_API void nskeyedarchive_nsdictionary_add_item(nskeyedarchive_t ka, uint64_t uid, const char* key, enum nskeyedarchive_class_type_t type, ...); + +LIMD_GLUE_API void nskeyedarchive_append_class_type_v(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, va_list* va); +LIMD_GLUE_API void nskeyedarchive_append_class_type(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, ...); + +LIMD_GLUE_API void nskeyedarchive_merge_object(nskeyedarchive_t ka, nskeyedarchive_t pka, plist_t object); + +LIMD_GLUE_API void nskeyedarchive_print(nskeyedarchive_t ka); +LIMD_GLUE_API plist_t nskeyedarchive_get_plist_ref(nskeyedarchive_t ka); +LIMD_GLUE_API plist_t nskeyedarchive_get_object_by_uid(nskeyedarchive_t ka, uint64_t uid); +LIMD_GLUE_API plist_t nskeyedarchive_get_class_by_uid(nskeyedarchive_t ka, uint64_t uid); +LIMD_GLUE_API plist_t nskeyedarchive_get_objects(nskeyedarchive_t ka); + +LIMD_GLUE_API uint64_t nskeyedarchive_get_class_uid(nskeyedarchive_t ka, const char* classref); +LIMD_GLUE_API const char* nskeyedarchive_get_classname(nskeyedarchive_t ka, uint64_t uid); + +LIMD_GLUE_API void nskeyedarchive_set_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, ...); +LIMD_GLUE_API int nskeyedarchive_get_class_uint64_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, uint64_t* value); +LIMD_GLUE_API int nskeyedarchive_get_class_int_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, int* value); +LIMD_GLUE_API int nskeyedarchive_get_class_string_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, char** value); +LIMD_GLUE_API int nskeyedarchive_get_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, plist_t* value); + +LIMD_GLUE_API plist_t nskeyedarchive_to_plist(nskeyedarchive_t ka); + +#endif diff --git a/include/libimobiledevice-glue/opack.h b/include/libimobiledevice-glue/opack.h index 43b1d1d..2d7a94a 100644 --- a/include/libimobiledevice-glue/opack.h +++ b/include/libimobiledevice-glue/opack.h @@ -21,9 +21,10 @@ #ifndef __OPACK_H #define __OPACK_H +#include <libimobiledevice-glue/glue.h> #include <plist/plist.h> -void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len); -int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out); +LIMD_GLUE_API void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len); +LIMD_GLUE_API int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out); #endif /* __OPACK_H */ diff --git a/include/libimobiledevice-glue/socket.h b/include/libimobiledevice-glue/socket.h index 53f58b8..39391c6 100644 --- a/include/libimobiledevice-glue/socket.h +++ b/include/libimobiledevice-glue/socket.h @@ -41,30 +41,32 @@ typedef enum fd_mode fd_mode; #include <sys/socket.h> #endif +#include <libimobiledevice-glue/glue.h> + #ifndef WIN32 -int socket_create_unix(const char *filename); -int socket_connect_unix(const char *filename); +LIMD_GLUE_API int socket_create_unix(const char *filename); +LIMD_GLUE_API int socket_connect_unix(const char *filename); #endif -int socket_create(const char *addr, uint16_t port); -int socket_connect_addr(struct sockaddr *addr, uint16_t port); -int socket_connect(const char *addr, uint16_t port); -int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout); -int socket_accept(int fd, uint16_t port); +LIMD_GLUE_API int socket_create(const char *addr, uint16_t port); +LIMD_GLUE_API int socket_connect_addr(struct sockaddr *addr, uint16_t port); +LIMD_GLUE_API int socket_connect(const char *addr, uint16_t port); +LIMD_GLUE_API int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout); +LIMD_GLUE_API int socket_accept(int fd, uint16_t port); -int socket_shutdown(int fd, int how); -int socket_close(int fd); +LIMD_GLUE_API int socket_shutdown(int fd, int how); +LIMD_GLUE_API int socket_close(int fd); -int socket_receive(int fd, void *data, size_t length); -int socket_peek(int fd, void *data, size_t length); -int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout); -int socket_send(int fd, void *data, size_t length); +LIMD_GLUE_API int socket_receive(int fd, void *data, size_t length); +LIMD_GLUE_API int socket_peek(int fd, void *data, size_t length); +LIMD_GLUE_API int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout); +LIMD_GLUE_API int socket_send(int fd, void *data, size_t length); -int socket_get_socket_port(int fd, uint16_t *port); +LIMD_GLUE_API int socket_get_socket_port(int fd, uint16_t *port); -void socket_set_verbose(int level); +LIMD_GLUE_API void socket_set_verbose(int level); -const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size); +LIMD_GLUE_API const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size); -int get_primary_mac_address(unsigned char mac_addr_buf[6]); +LIMD_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf[6]); #endif /* SOCKET_SOCKET_H */ diff --git a/include/libimobiledevice-glue/termcolors.h b/include/libimobiledevice-glue/termcolors.h index 3e03e97..d7799c3 100644 --- a/include/libimobiledevice-glue/termcolors.h +++ b/include/libimobiledevice-glue/termcolors.h @@ -26,6 +26,7 @@ #include <stdarg.h> #include <stdio.h> +#include <libimobiledevice-glue/glue.h> #define COLOR_RESET "\e[m" #define STYLE_NORMAL "\e[0m" @@ -74,14 +75,14 @@ #define BG_DEFAULT "\e[49m" /* automatically called by library constructor */ -void term_colors_init(); +LIMD_GLUE_API void term_colors_init(); /* enable / disable terminal colors */ -void term_colors_set_enabled(int en); +LIMD_GLUE_API void term_colors_set_enabled(int en); /* color-aware *printf variants */ -int cprintf(const char* fmt, ...); -int cfprintf(FILE* stream, const char* fmt, ...); -int cvfprintf(FILE* stream, const char* fmt, va_list vargs); +LIMD_GLUE_API int cprintf(const char* fmt, ...); +LIMD_GLUE_API int cfprintf(FILE* stream, const char* fmt, ...); +LIMD_GLUE_API int cvfprintf(FILE* stream, const char* fmt, va_list vargs); #endif diff --git a/include/libimobiledevice-glue/thread.h b/include/libimobiledevice-glue/thread.h index 2aadc6e..76646b8 100644 --- a/include/libimobiledevice-glue/thread.h +++ b/include/libimobiledevice-glue/thread.h @@ -23,16 +23,31 @@ #define __THREAD_H #include <stddef.h> +#include <libimobiledevice-glue/glue.h> #ifdef WIN32 -#include <windows.h> +typedef void* HANDLE; typedef HANDLE THREAD_T; -typedef CRITICAL_SECTION mutex_t; +#pragma pack(push, 8) +struct _CRITICAL_SECTION_ST { + void* DebugInfo; + long LockCount; + long RecursionCount; + HANDLE OwningThread; + HANDLE LockSemaphore; +#if defined(_WIN64) + unsigned __int64 SpinCount; +#else + unsigned long SpinCount; +#endif +}; +#pragma pack(pop) +typedef struct _CRITICAL_SECTION_ST mutex_t; typedef struct { HANDLE sem; } cond_t; typedef volatile struct { - LONG lock; + long lock; int state; } thread_once_t; #define THREAD_ONCE_INIT {0, 0} @@ -53,13 +68,13 @@ typedef pthread_once_t thread_once_t; typedef void* (*thread_func_t)(void* data); -int thread_new(THREAD_T* thread, thread_func_t thread_func, void* data); -void thread_detach(THREAD_T thread); -void thread_free(THREAD_T thread); -int thread_join(THREAD_T thread); -int thread_alive(THREAD_T thread); +LIMD_GLUE_API int thread_new(THREAD_T* thread, thread_func_t thread_func, void* data); +LIMD_GLUE_API void thread_detach(THREAD_T thread); +LIMD_GLUE_API void thread_free(THREAD_T thread); +LIMD_GLUE_API int thread_join(THREAD_T thread); +LIMD_GLUE_API int thread_alive(THREAD_T thread); -int thread_cancel(THREAD_T thread); +LIMD_GLUE_API int thread_cancel(THREAD_T thread); #ifdef WIN32 #undef HAVE_THREAD_CLEANUP @@ -71,17 +86,17 @@ int thread_cancel(THREAD_T thread); #endif #endif -void mutex_init(mutex_t* mutex); -void mutex_destroy(mutex_t* mutex); -void mutex_lock(mutex_t* mutex); -void mutex_unlock(mutex_t* mutex); +LIMD_GLUE_API void mutex_init(mutex_t* mutex); +LIMD_GLUE_API void mutex_destroy(mutex_t* mutex); +LIMD_GLUE_API void mutex_lock(mutex_t* mutex); +LIMD_GLUE_API void mutex_unlock(mutex_t* mutex); -void thread_once(thread_once_t *once_control, void (*init_routine)(void)); +LIMD_GLUE_API void thread_once(thread_once_t *once_control, void (*init_routine)(void)); -void cond_init(cond_t* cond); -void cond_destroy(cond_t* cond); -int cond_signal(cond_t* cond); -int cond_wait(cond_t* cond, mutex_t* mutex); -int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms); +LIMD_GLUE_API void cond_init(cond_t* cond); +LIMD_GLUE_API void cond_destroy(cond_t* cond); +LIMD_GLUE_API int cond_signal(cond_t* cond); +LIMD_GLUE_API int cond_wait(cond_t* cond, mutex_t* mutex); +LIMD_GLUE_API int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms); #endif diff --git a/include/libimobiledevice-glue/tlv.h b/include/libimobiledevice-glue/tlv.h index 895c883..42be4f6 100644 --- a/include/libimobiledevice-glue/tlv.h +++ b/include/libimobiledevice-glue/tlv.h @@ -22,6 +22,7 @@ #define __TLV_H #include <stdint.h> +#include <libimobiledevice-glue/glue.h> struct tlv_buf { unsigned char* data; @@ -30,13 +31,13 @@ struct tlv_buf { }; typedef struct tlv_buf* tlv_buf_t; -tlv_buf_t tlv_buf_new(); -void tlv_buf_free(tlv_buf_t tlv); +LIMD_GLUE_API tlv_buf_t tlv_buf_new(); +LIMD_GLUE_API void tlv_buf_free(tlv_buf_t tlv); -void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data); -unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length); -int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value); -int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value); -int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len); +LIMD_GLUE_API void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data); +LIMD_GLUE_API unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length); +LIMD_GLUE_API int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value); +LIMD_GLUE_API int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value); +LIMD_GLUE_API int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len); #endif /* __TLV_H */ diff --git a/include/libimobiledevice-glue/utils.h b/include/libimobiledevice-glue/utils.h index b8513c0..355f1da 100644 --- a/include/libimobiledevice-glue/utils.h +++ b/include/libimobiledevice-glue/utils.h @@ -25,38 +25,20 @@ #ifndef __UTILS_H #define __UTILS_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#ifdef WIN32 -#include <windows.h> -#endif - #include <stdio.h> -#include <plist/plist.h> +#include <stdint.h> +#include <libimobiledevice-glue/glue.h> #define MAC_EPOCH 978307200 -char *string_concat(const char *str, ...); -char *string_append(char *str, ...); -char *string_build_path(const char *elem, ...); -char *string_format_size(uint64_t size); -char *string_toupper(char *str); -char *generate_uuid(void); - -int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length); -int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length); - -enum plist_format_t { - PLIST_FORMAT_XML, - PLIST_FORMAT_BINARY -}; - -int plist_read_from_filename(plist_t *plist, const char *filename); -int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format); +LIMD_GLUE_API char *string_concat(const char *str, ...); +LIMD_GLUE_API char *string_append(char *str, ...); +LIMD_GLUE_API char *string_build_path(const char *elem, ...); +LIMD_GLUE_API char *string_format_size(uint64_t size); +LIMD_GLUE_API char *string_toupper(char *str); +LIMD_GLUE_API char *generate_uuid(void); -void plist_print_to_stream(plist_t plist, FILE* stream); -void plist_print_to_stream_with_indentation(plist_t plist, FILE* stream, unsigned int indentation); +LIMD_GLUE_API int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length); +LIMD_GLUE_API int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length); #endif diff --git a/src/Makefile.am b/src/Makefile.am index b709103..0db7ede 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,6 +12,7 @@ libimobiledevice_glue_1_0_la_SOURCES = \ socket.c \ thread.c \ utils.c \ + nskeyedarchive.c \ collection.c \ termcolors.c \ cbuf.c \ @@ -30,7 +30,7 @@ #include "common.h" #include "libimobiledevice-glue/cbuf.h" -LIBIMOBILEDEVICE_GLUE_API struct char_buf* char_buf_new() +struct char_buf* char_buf_new() { struct char_buf* cbuf = (struct char_buf*)malloc(sizeof(struct char_buf)); cbuf->capacity = 256; @@ -39,7 +39,7 @@ LIBIMOBILEDEVICE_GLUE_API struct char_buf* char_buf_new() return cbuf; } -LIBIMOBILEDEVICE_GLUE_API void char_buf_free(struct char_buf* cbuf) +void char_buf_free(struct char_buf* cbuf) { if (cbuf) { free(cbuf->data); @@ -47,7 +47,7 @@ LIBIMOBILEDEVICE_GLUE_API void char_buf_free(struct char_buf* cbuf) } } -LIBIMOBILEDEVICE_GLUE_API void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data) +void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data) { if (!cbuf || !cbuf->data) { return; diff --git a/src/collection.c b/src/collection.c index ef47217..0744427 100644 --- a/src/collection.c +++ b/src/collection.c @@ -37,7 +37,7 @@ #define CAPACITY_STEP 8 -LIBIMOBILEDEVICE_GLUE_API void collection_init(struct collection *col) +void collection_init(struct collection *col) { col->list = malloc(sizeof(void *) * CAPACITY_STEP); assert(col->list); @@ -45,14 +45,14 @@ LIBIMOBILEDEVICE_GLUE_API void collection_init(struct collection *col) col->capacity = CAPACITY_STEP; } -LIBIMOBILEDEVICE_GLUE_API void collection_free(struct collection *col) +void collection_free(struct collection *col) { free(col->list); col->list = NULL; col->capacity = 0; } -LIBIMOBILEDEVICE_GLUE_API void collection_add(struct collection *col, void *element) +void collection_add(struct collection *col, void *element) { int i; for(i=0; i<col->capacity; i++) { @@ -69,7 +69,7 @@ LIBIMOBILEDEVICE_GLUE_API void collection_add(struct collection *col, void *elem col->capacity += CAPACITY_STEP; } -LIBIMOBILEDEVICE_GLUE_API int collection_remove(struct collection *col, void *element) +int collection_remove(struct collection *col, void *element) { int i; for(i=0; i<col->capacity; i++) { @@ -82,7 +82,7 @@ LIBIMOBILEDEVICE_GLUE_API int collection_remove(struct collection *col, void *el return -1; } -LIBIMOBILEDEVICE_GLUE_API int collection_count(struct collection *col) +int collection_count(struct collection *col) { int i, cnt = 0; for(i=0; i<col->capacity; i++) { @@ -92,7 +92,7 @@ LIBIMOBILEDEVICE_GLUE_API int collection_count(struct collection *col) return cnt; } -LIBIMOBILEDEVICE_GLUE_API void collection_copy(struct collection *dest, struct collection *src) +void collection_copy(struct collection *dest, struct collection *src) { if (!dest || !src) return; dest->capacity = src->capacity; diff --git a/src/common.h b/src/common.h index bd22e3d..94b5fdd 100644 --- a/src/common.h +++ b/src/common.h @@ -25,14 +25,18 @@ #include <config.h> #endif -#ifdef WIN32 -#define LIBIMOBILEDEVICE_GLUE_API __declspec( dllexport ) +#ifdef LIMD_GLUE_STATIC + #define LIMD_GLUE_API +#elif defined(_WIN32) + #define LIMD_GLUE_API __declspec( dllexport ) #else -#ifdef HAVE_FVISIBILITY -#define LIBIMOBILEDEVICE_GLUE_API __attribute__((visibility("default"))) -#else -#define LIBIMOBILEDEVICE_GLUE_API -#endif + #if __GNUC__ >= 4 + #define LIMD_GLUE_API __attribute__((visibility("default"))) + #else + #define LIMD_GLUE_API + #endif #endif +#include "libimobiledevice-glue/glue.h" + #endif @@ -78,3 +78,11 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) #else #warning No compiler support for constructor/destructor attributes, some features might not be available. #endif + +const char* libimobiledevice_glue_version() +{ +#ifndef PACKAGE_VERSION +#error PACKAGE_VERSION is not defined! +#endif + return PACKAGE_VERSION; +} diff --git a/src/nskeyedarchive.c b/src/nskeyedarchive.c new file mode 100644 index 0000000..b3dbdc4 --- /dev/null +++ b/src/nskeyedarchive.c @@ -0,0 +1,1228 @@ +/* + * nskeyedarchive.c + * Helper code to work with plist files containing NSKeyedArchiver data. + * + * Copyright (c) 2019 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <plist/plist.h> +#include "common.h" +#include "libimobiledevice-glue/nskeyedarchive.h" + +#define NS_KEYED_ARCHIVER_NAME "NSKeyedArchiver" +#define NS_KEYED_ARCHIVER_VERSION 100000 + +struct nskeyedarchive_st { + plist_t dict; + uint64_t uid; +}; + +nskeyedarchive_t nskeyedarchive_new(void) +{ + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "$version", plist_new_uint(NS_KEYED_ARCHIVER_VERSION)); + plist_t objects = plist_new_array(); + plist_array_append_item(objects, plist_new_string("$null")); + plist_dict_set_item(dict, "$objects", objects); + plist_dict_set_item(dict, "$archiver", plist_new_string(NS_KEYED_ARCHIVER_NAME)); + + nskeyedarchive_t nskeyed = (nskeyedarchive_t)malloc(sizeof(struct nskeyedarchive_st)); + nskeyed->dict = dict; + nskeyed->uid = 1; + return nskeyed; +} + +void nskeyedarchive_free(nskeyedarchive_t ka) +{ + if (ka) { + if (ka->dict) { + plist_free(ka->dict); + } + free(ka); + } +} + +void nskeyedarchive_set_top_ref_key_name(nskeyedarchive_t ka, const char* keyname) +{ + if (!ka) { + return; + } + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (top) { + plist_dict_iter iter = NULL; + plist_dict_new_iter(top, &iter); + if (iter) { + plist_t node = NULL; + char* keyn = NULL; + plist_dict_next_item(top, iter, &keyn, &node); + plist_t keynode = plist_dict_item_get_key(node); + plist_set_key_val(keynode, keyname); + free(keyn); + free(iter); + } + } +} + +plist_t nskeyedarchive_get_objects(nskeyedarchive_t ka) +{ + plist_t objects = plist_dict_get_item(ka->dict, "$objects"); + if (!objects || (plist_get_node_type(objects) != PLIST_ARRAY)) { + fprintf(stderr, "ERROR: $objects node not found!\n"); + return NULL; + } + + return objects; +} + +plist_t nskeyedarchive_get_object_by_uid(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t objects = nskeyedarchive_get_objects(ka); + if (!objects) { + return NULL; + } + + plist_t obj = plist_array_get_item(objects, (uint32_t)uid); + if (!obj) { + fprintf(stderr, "ERROR: unable to get object node with uid %llu\n", (long long)uid); + return NULL; + } + + return obj; +} + +plist_t nskeyedarchive_get_class_by_uid(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t ret = nskeyedarchive_get_object_by_uid(ka, uid); + if (ret && (plist_get_node_type(ret) != PLIST_DICT)) { + fprintf(stderr, "ERROR: the uid %llu does not reference a valid class with node type PLIST_DICT!\n", (long long)uid); + return NULL; + } + + return ret; +} + +void nskeyedarchive_append_object(nskeyedarchive_t ka, plist_t object) +{ + plist_t objects = nskeyedarchive_get_objects(ka); + if (objects && plist_get_node_type(objects) != PLIST_ARRAY) { + fprintf(stderr, "ERROR: unable to append object\n"); + return; + } + + plist_array_append_item(objects, object); +} + +static void nskeyedarchive_append_class_v(nskeyedarchive_t ka, const char* classname, va_list* va) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!classname) { + fprintf(stderr, "%s: ERROR: missing classname!\n", __func__); + return; + } + + char* arg = NULL; + plist_t classes = NULL; + do { + arg = (char*)va_arg(*va, char*); + if (!arg) { + break; + } + if (!classes) { + classes = plist_new_array(); + plist_array_append_item(classes, plist_new_string(classname)); + } + plist_array_append_item(classes, plist_new_string(arg)); + } while (arg); + + plist_t cls = plist_new_dict(); + plist_dict_set_item(cls, "$class", plist_new_uid(++ka->uid)); + + nskeyedarchive_append_object(ka, cls); + + cls = plist_new_dict(); + if (classes) { + plist_dict_set_item(cls, "$classes", classes); + } + plist_dict_set_item(cls, "$classname", plist_new_string(classname)); + + nskeyedarchive_append_object(ka, cls); +} + +void nskeyedarchive_append_class(nskeyedarchive_t ka, const char* classname, ...) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!classname) { + fprintf(stderr, "%s: ERROR: missing classname!\n", __func__); + return; + } + + va_list va; + va_start(va, classname); + nskeyedarchive_append_class_v(ka, classname, &va); + va_end(va); +} + +void nskeyedarchive_add_top_class_uid(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (!top) { + top = plist_new_dict(); + plist_dict_set_item(top, "$0", plist_new_uid(uid)); + plist_dict_set_item(ka->dict, "$top", top); + } else { + uint32_t num = plist_dict_get_size(top); + char newkey[8]; + sprintf(newkey, "$%d", num); + plist_dict_set_item(top, newkey, plist_new_uid(uid)); + } +} + +uint64_t nskeyedarchive_add_top_class(nskeyedarchive_t ka, const char* classname, ...) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return 0; + } else if (!classname) { + fprintf(stderr, "%s: ERROR: missing classname!\n", __func__); + return 0; + } + + uint64_t uid = ka->uid; + + va_list va; + va_start(va, classname); + nskeyedarchive_append_class_v(ka, classname, &va); + va_end(va); + + nskeyedarchive_add_top_class_uid(ka, uid); + return uid; +} + +static void nskeyedarchive_set_class_property_v(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, va_list* va); + +static void nskeyedarchive_nsarray_append(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, ...); + +static void nskeyedarchive_nsarray_append_v(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, va_list* va) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!array) { + fprintf(stderr, "%s: ERROR: missing plist!\n", __func__); + return; + } + + uint64_t newuid; + + switch (type) { + case NSTYPE_INTEGER: + plist_array_append_item(array, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_INTREF: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_BOOLEAN: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int))); + break; + case NSTYPE_CHARS: + plist_array_append_item(array, plist_new_string(va_arg(*va, char*))); + break; + case NSTYPE_STRING: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_string(va_arg(*va, char*))); + break; + case NSTYPE_REAL: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_real(va_arg(*va, double))); + break; + case NSTYPE_NSMUTABLESTRING: + case NSTYPE_NSSTRING: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLESTRING) { + nskeyedarchive_append_class(ka, "NSMutableString", "NSString", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSString", "NSObject", NULL); + } + nskeyedarchive_set_class_property(ka, newuid, "NS.string", NSTYPE_CHARS, va_arg(*va, char*)); + break; + case NSTYPE_NSMUTABLEARRAY: + case NSTYPE_NSARRAY: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEARRAY) { + nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSArray", "NSObject", NULL); + } + { + plist_t arr = plist_new_array(); + do { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append_v(ka, arr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr); + plist_free(arr); + } + break; + case NSTYPE_NSMUTABLEDICTIONARY: + case NSTYPE_NSDICTIONARY: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEDICTIONARY) { + nskeyedarchive_append_class(ka, "NSMutableDictionary", "NSDictionary", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL); + } + { + plist_t keyarr = plist_new_array(); + plist_t valarr = plist_new_array(); + do { + char* key = va_arg(*va, char*); + if (!key) + break; + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key); + nskeyedarchive_nsarray_append_v(ka, valarr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr); + plist_free(keyarr); + plist_free(valarr); + } + break; + case NSTYPE_NSDATE: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSDate", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.time", NSTYPE_REAL, va_arg(*va, double)); + break; + case NSTYPE_NSMUTABLEDATA: + case NSTYPE_NSDATA: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSMutableData", "NSData", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.data", NSTYPE_DATA, va_arg(*va, char*)); + break; + case NSTYPE_NSURL: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSURL", "NSObject", NULL); + { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.base", ptype, va); + ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.relative", ptype, va); + } + break; + case NSTYPE_NSKEYEDARCHIVE: + { + nskeyedarchive_t pka = va_arg(*va, nskeyedarchive_t); + if (!pka) { + fprintf(stderr, "%s: ERROR: no nskeyedarchive argument given for type NSTYPE_NSKEYEDARCHIVE\n", __func__); + return; + } + uint64_t top = nskeyedarchive_get_class_uid(pka, NULL); + if (top != 0) { + plist_array_append_item(array, plist_new_uid(++ka->uid)); + + plist_t object = nskeyedarchive_get_object_by_uid(pka, top); + if (!object) { + fprintf(stderr, "%s: ERROR: can't get object for uid %lld\n", __func__, (long long)top); + return; + } + + plist_t objcopy = plist_copy(object); + nskeyedarchive_append_object(ka, objcopy); + nskeyedarchive_merge_object(ka, pka, objcopy); + } + } + break; + case NSTYPE_FROM_PLIST: + { + plist_t plist = va_arg(*va, plist_t); + if (!plist) { + fprintf(stderr, "%s: ERROR: no plist argument given for NSTYPE_PLIST\n", __func__); + return; + } + switch (plist_get_node_type(plist)) { + case PLIST_STRING: + { + char* str = NULL; + plist_get_string_val(plist, &str); + nskeyedarchive_nsarray_append(ka, array, NSTYPE_NSMUTABLESTRING, str, NULL); + } break; + case PLIST_DICT: + { + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL); + + plist_t keyarr = plist_new_array(); + plist_t valarr = plist_new_array(); + + plist_dict_iter iter = NULL; + plist_dict_new_iter(plist, &iter); + + char *key = NULL; + plist_t node = NULL; + + do { + plist_dict_next_item(plist, iter, &key, &node); + if (key) { + int ptype = 0; + uint8_t bv = 0; + uint64_t u64val = 0; + int intval = 0; + char *str = NULL; + void *val = NULL; + nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key); + switch (plist_get_node_type(node)) { + case PLIST_BOOLEAN: { + ptype = NSTYPE_BOOLEAN; + plist_get_bool_val(node, &bv); + intval = bv; + val = &intval; + } break; + case PLIST_UINT: + ptype = NSTYPE_INTEGER; + plist_get_uint_val(node, &u64val); + val = &u64val; + break; + case PLIST_STRING: + ptype = NSTYPE_STRING; + plist_get_string_val(node, &str); + val = str; + break; + default: + fprintf(stderr, "Unhandled plist type when parsing plist_dict\n"); + break; + } + nskeyedarchive_nsarray_append(ka, valarr, ptype, val); + } + free(key); + } while (node); + + free(iter); + + nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr); + plist_free(keyarr); + plist_free(valarr); + } break; + default: + fprintf(stderr, "%s: ERROR: unhandled plist type %d\n", __func__, plist_get_node_type(plist)); + return; + } + } + break; + default: + fprintf(stderr, "%s: unexpected type %d\n", __func__, type); + break; + } +} + +static void nskeyedarchive_nsarray_append(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, ...) +{ + va_list va; + va_start(va, type); + nskeyedarchive_nsarray_append_v(ka, array, type, &va); + va_end(va); +} + +void nskeyedarchive_nsarray_append_item(nskeyedarchive_t ka, uint64_t uid, enum nskeyedarchive_class_type_t type, ...) +{ + if (!ka) { + return; + } + plist_t objects = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &objects); + if (!objects) { + fprintf(stderr, "ERROR: invalid NSArray object in archive: missing NS.objects property\n"); + return; + } + va_list va; + va_start(va, type); + nskeyedarchive_nsarray_append_v(ka, objects, type, &va); + va_end(va); +} + +void nskeyedarchive_nsdictionary_add_item(nskeyedarchive_t ka, uint64_t uid, const char* key, enum nskeyedarchive_class_type_t type, ...) +{ + if (!ka) { + return; + } + plist_t keys = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.keys", &keys); + if (!keys) { + fprintf(stderr, "ERROR: invalid NSDictionary object in archive: missing NS.keys property\n"); + return; + } + plist_t objects = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &objects); + if (!objects) { + fprintf(stderr, "ERROR: invalid NSDictionary object in archive: missing NS.objects property\n"); + return; + } + va_list va; + va_start(va, type); + nskeyedarchive_nsarray_append(ka, keys, NSTYPE_STRING, key); + nskeyedarchive_nsarray_append_v(ka, objects, type, &va); + va_end(va); +} + +void nskeyedarchive_append_class_type_v(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, va_list* va) +{ + uint64_t newuid = 0; + + switch (type) { + case NSTYPE_INTEGER: + fprintf(stderr, "%s: ERROR: NSTYPE_INTEGER is not an object type, can't add it as class!.\n", __func__); + break; + case NSTYPE_INTREF: + nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_BOOLEAN: + nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int))); + break; + case NSTYPE_CHARS: + fprintf(stderr, "%s: ERROR: NSTYPE_CHARS is not an object type, can't add it as class!\n", __func__); + break; + case NSTYPE_STRING: + { + char* strval = va_arg(*va, char*); + if (strval) { + if (strcmp(strval, "$null") != 0) { + nskeyedarchive_append_object(ka, plist_new_string(strval)); + } else { + // make $null the top class if required + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (!top) { + top = plist_new_dict(); + plist_dict_set_item(top, "$0", plist_new_uid(0)); + plist_dict_set_item(ka->dict, "$top", top); + } + } + } + } + break; + case NSTYPE_REAL: + nskeyedarchive_append_object(ka, plist_new_real(va_arg(*va, double))); + break; + case NSTYPE_ARRAY: + fprintf(stderr, "%s: ERROR: NSTYPE_ARRAY is not an object type, can't add it as class!\n", __func__); + return; + case NSTYPE_NSMUTABLESTRING: + case NSTYPE_NSSTRING: + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLESTRING) { + nskeyedarchive_append_class(ka, "NSMutableString", "NSString", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSString", "NSObject", NULL); + } + nskeyedarchive_set_class_property(ka, newuid, "NS.string", NSTYPE_STRING, va_arg(*va, char*)); + break; + case NSTYPE_NSMUTABLEARRAY: + case NSTYPE_NSARRAY: + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEARRAY) { + nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSArray", "NSObject", NULL); + } + { + plist_t arr = plist_new_array(); + do { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append_v(ka, arr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr); + plist_free(arr); + } + break; + case NSTYPE_NSMUTABLEDICTIONARY: + case NSTYPE_NSDICTIONARY: + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEDICTIONARY) { + nskeyedarchive_append_class(ka, "NSMutableDictionary", "NSDictionary", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL); + } + { + plist_t keyarr = plist_new_array(); + plist_t valarr = plist_new_array(); + do { + char* key = va_arg(*va, char*); + if (!key) + break; + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key); + nskeyedarchive_nsarray_append_v(ka, valarr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr); + plist_free(keyarr); + plist_free(valarr); + } + break; + case NSTYPE_NSDATE: + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSDate", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.time", NSTYPE_REAL, va_arg(*va, double)); + break; + case NSTYPE_NSMUTABLEDATA: + case NSTYPE_NSDATA: + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSMutableData", "NSData", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.data", NSTYPE_DATA, va_arg(*va, char*)); + break; + case NSTYPE_NSURL: + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSURL", "NSObject", NULL); + { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.base", ptype, va); + ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.relative", ptype, va); + } + break; + default: + fprintf(stderr, "unexpected class type %d\n", type); + return; + } + + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (!top) { + top = plist_new_dict(); + plist_dict_set_item(top, "$0", plist_new_uid(1)); + plist_dict_set_item(ka->dict, "$top", top); + } +} + +void nskeyedarchive_append_class_type(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, ...) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!type) { + fprintf(stderr, "%s: ERROR: invalid class type!\n", __func__); + return; + } + + va_list va; + va_start(va, type); + nskeyedarchive_append_class_type_v(ka, type, &va); + va_end(va); +} + +void nskeyedarchive_merge_object(nskeyedarchive_t ka, nskeyedarchive_t pka, plist_t object) +{ + if (!ka || !pka || !object) { + return; + } + + switch (plist_get_node_type(object)) { + case PLIST_DICT: + { + plist_dict_iter iter = NULL; + plist_dict_new_iter(object, &iter); + if (iter) { + plist_t val; + char* key; + do { + key = NULL; + val = NULL; + plist_dict_next_item(object, iter, &key, &val); + if (key) { + switch (plist_get_node_type(val)) { + case PLIST_UID: + { + uint64_t thisuid = 0; + plist_get_uid_val(val, &thisuid); + if (thisuid > 0) { + // remap thisuid to ka->uid+1 + plist_t nextobj = nskeyedarchive_get_object_by_uid(pka, thisuid); + plist_set_uid_val(val, ++ka->uid); + plist_t nextcopy = plist_copy(nextobj); + nskeyedarchive_append_object(ka, nextcopy); + nskeyedarchive_merge_object(ka, pka, nextcopy); + } + } + break; + case PLIST_DICT: + case PLIST_ARRAY: + nskeyedarchive_merge_object(ka, pka, val); + break; + default: + break; + } + free(key); + } + } while (val); + free(iter); + } + } + break; + case PLIST_ARRAY: + { + uint32_t i; + for (i = 0; i < plist_array_get_size(object); i++) { + plist_t val = plist_array_get_item(object, i); + switch (plist_get_node_type(val)) { + case PLIST_UID: + { + uint64_t thisuid = 0; + plist_get_uid_val(val, &thisuid); + if (thisuid > 0) { + // remap thisuid to ka->uid+1 + plist_t nextobj = nskeyedarchive_get_object_by_uid(pka, thisuid); + plist_set_uid_val(val, ++ka->uid); + plist_t nextcopy = plist_copy(nextobj); + nskeyedarchive_append_object(ka, nextcopy); + nskeyedarchive_merge_object(ka, pka, nextcopy); + } + } + break; + case PLIST_ARRAY: + case PLIST_DICT: + nskeyedarchive_merge_object(ka, pka, val); + break; + default: + break; + } + } + } + break; + default: + break; + } + +} + +static void nskeyedarchive_set_class_property_v(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, va_list* va) +{ + plist_t a = nskeyedarchive_get_class_by_uid(ka, uid); + if (a == NULL) + return; + + uint64_t newuid; + + switch (proptype) { + case NSTYPE_INTEGER: + plist_dict_set_item(a, propname, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_INTREF: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_BOOLEAN: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int))); + break; + case NSTYPE_CHARS: + plist_dict_set_item(a, propname, plist_new_string(va_arg(*va, char*))); + break; + case NSTYPE_STRING: + { + char* strval = va_arg(*va, char*); + if (strval && (strcmp(strval, "$null") == 0)) { + plist_dict_set_item(a, propname, plist_new_uid(0)); + } else { + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_string(strval)); + } + } + break; + case NSTYPE_REAL: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_ARRAY: + plist_dict_set_item(a, propname, plist_copy(va_arg(*va, plist_t))); + break; + case NSTYPE_NSMUTABLESTRING: + case NSTYPE_NSSTRING: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSMUTABLEARRAY: + case NSTYPE_NSARRAY: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSMUTABLEDICTIONARY: + case NSTYPE_NSDICTIONARY: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSDATE: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSMUTABLEDATA: + case NSTYPE_NSDATA: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSURL: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSKEYEDARCHIVE: + { + nskeyedarchive_t pka = va_arg(*va, nskeyedarchive_t); + if (!pka) { + fprintf(stderr, "%s: ERROR: no nskeyedarchive argument given for type NSTYPE_NSKEYEDARCHIVE\n", __func__); + return; + } + uint64_t top = nskeyedarchive_get_class_uid(pka, NULL); + if (top != 0) { + plist_t object = nskeyedarchive_get_object_by_uid(pka, top); + if (!object) { + fprintf(stderr, "%s: ERROR: can't get object for uid %lld\n", __func__, (long long)top); + return; + } + + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + plist_t objcopy = plist_copy(object); + nskeyedarchive_append_object(ka, objcopy); + nskeyedarchive_merge_object(ka, pka, objcopy); + } else { + plist_dict_set_item(a, propname, plist_new_uid(0)); + } + } + break; + case NSTYPE_FROM_PLIST: + { + plist_t plist = va_arg(*va, plist_t); + if (!plist) { + fprintf(stderr, "%s: ERROR: no plist argument given for NSTYPE_PLIST\n", __func__); + return; + } + switch (plist_get_node_type(plist)) { + case PLIST_ARRAY: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL); + { + plist_t arr = plist_new_array(); + uint32_t ai; + for (ai = 0; ai < plist_array_get_size(plist); ai++) { + plist_t ae = plist_array_get_item(plist, ai); + nskeyedarchive_nsarray_append(ka, arr, NSTYPE_FROM_PLIST, ae); + } + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr); + } + break; + default: + fprintf(stderr, "%s: sorry, plist type %d is not implemented for conversion.\n", __func__, plist_get_node_type(plist)); + break; + } + } + break; + default: + fprintf(stderr, "unexpected property type %d\n", proptype); + break; + } +} + +void nskeyedarchive_set_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, ...) +{ + plist_t a = nskeyedarchive_get_class_by_uid(ka, uid); + if (a == NULL) { + return; + } + + va_list va; + va_start(va, proptype); + nskeyedarchive_set_class_property_v(ka, uid, propname, proptype, &va); + va_end(va); +} + + +void nskeyedarchive_print(nskeyedarchive_t ka) +{ + char* xml = NULL; + uint32_t xlen = 0; + plist_to_xml(ka->dict, &xml, &xlen); + puts(xml); + free(xml); +} + +plist_t nskeyedarchive_get_plist_ref(nskeyedarchive_t ka) +{ + return ka->dict; +} + +nskeyedarchive_t nskeyedarchive_new_from_plist(plist_t plist) +{ + if (!plist || (plist_get_node_type(plist) != PLIST_DICT)) { + fprintf(stderr, "%s: ERROR: invalid parameter, PLIST_DICT expected\n", __func__); + return NULL; + } + + plist_t node; + char* strval = NULL; + uint64_t uintval = 0; + + // check for $archiver key + node = plist_dict_get_item(plist, "$archiver"); + if (node && (plist_get_node_type(node) == PLIST_STRING)) { + plist_get_string_val(node, &strval); + } + if (!strval || (strcmp(strval, "NSKeyedArchiver") != 0)) { + fprintf(stderr, "%s: ERROR: plist is not in NSKeyedArchiver format ($archiver key not found or invalid)!\n", __func__); + if (strval) + free(strval); + return NULL; + } + if (strval) + free(strval); + strval = NULL; + + // check for $version key + node = plist_dict_get_item(plist, "$version"); + if (node && (plist_get_node_type(node) == PLIST_UINT)) { + plist_get_uint_val(node, &uintval); + } + if (uintval != 100000) { + fprintf(stderr, "%s: ERROR: unexpected NSKeyedArchiver version encountered (%lld != 100000)!\n", __func__, (long long int)uintval); + return NULL; + } + uintval = 0; + + // check for $top key + node = plist_dict_get_item(plist, "$top"); + if (!node || (plist_get_node_type(node) != PLIST_DICT)) { + fprintf(stderr, "%s: ERROR: $top node not found\n", __func__); + return NULL; + } + plist_t topuid = plist_dict_get_item(node, "$0"); + if (!topuid) { + topuid = plist_dict_get_item(node, "root"); + } + if (!topuid || (plist_get_node_type(topuid) != PLIST_UID)) { + fprintf(stderr, "%s: ERROR: uid '$0' or 'root' not found in $top dict!\n", __func__); + return NULL; + } + + uintval = -1LL; + plist_get_uid_val(topuid, (uint64_t*)&uintval); + + if (uintval == (uint64_t)-1LL) { + fprintf(stderr, "%s: ERROR: could not get UID value.\n", __func__); + return NULL; + } + + // check for $objects key + plist_t objects = plist_dict_get_item(plist, "$objects"); + if (!objects || (plist_get_node_type(objects) != PLIST_ARRAY)) { + fprintf(stderr, "%s: ERROR: $objects node not found!\n", __func__); + return NULL; + } + plist_t obj = plist_array_get_item(objects, (uint32_t)uintval); + if (!obj) { + fprintf(stderr, "%s: ERROR: can't get object node\n", __func__); + return NULL; + } + + nskeyedarchive_t archive = (nskeyedarchive_t)malloc(sizeof(struct nskeyedarchive_st)); + archive->dict = plist_copy(plist); + archive->uid = plist_array_get_size(objects) - 1; + + return archive; +} + +nskeyedarchive_t nskeyedarchive_new_from_data(const void* data, uint32_t size) +{ + if (!data || (size < 8)) { + fprintf(stderr, "%s: ERROR: invalid parameter\n", __func__); + return NULL; + } + + + plist_t plist = NULL; + if (memcmp(data, "bplist00", 8) == 0) { + plist_from_bin(data, size, &plist); + } else if ((memcmp(data, "<?xml", 5) == 0) || (memcmp(data, "<plist", 6) == 0)) { + plist_from_xml(data, size, &plist); + } else { + // fail silently + return NULL; + } + if (!plist) { + fprintf(stderr, "%s: ERROR: Can't parse plist from data\n", __func__); + return NULL; + } + + nskeyedarchive_t ka = nskeyedarchive_new_from_plist(plist); + plist_free(plist); + return ka; +} + +uint64_t nskeyedarchive_get_class_uid(nskeyedarchive_t ka, const char* classref) +{ + uint64_t uintval = -1LL; + if (!ka) { + return uintval; + } + if (!ka->dict) { + return uintval; + } + + plist_t node; + + node = plist_dict_get_item(ka->dict, "$top"); + if (!node || (plist_get_node_type(node) != PLIST_DICT)) { + fprintf(stderr, "%s: ERROR: $top node not found\n", __func__); + return 0; + } + plist_t topuid = plist_dict_get_item(node, (classref) ? classref : "$0"); + if (!topuid && !classref) { + topuid = plist_dict_get_item(node, "root"); + } + if (!topuid || (plist_get_node_type(topuid) != PLIST_UID)) { + fprintf(stderr, "%s: ERROR: uid for '%s' not found in $top dict!\n", __func__, classref); + return 0; + } + + plist_get_uid_val(topuid, (uint64_t*)&uintval); + + return uintval; +} + +const char* nskeyedarchive_get_classname(nskeyedarchive_t ka, uint64_t uid) +{ + if (!ka) { + return NULL; + } + if (!ka->dict) { + return NULL; + } + + plist_t obj = nskeyedarchive_get_object_by_uid(ka, uid); + if (!obj) { + return NULL; + } + + plist_t classuid = plist_dict_get_item(obj, "$class"); + if (plist_get_node_type(classuid) != PLIST_UID) { + fprintf(stderr, "ERROR: $class is not a uid node\n"); + return NULL; + } + + uint64_t uintval = 0; + plist_get_uid_val(classuid, &uintval); + if (uintval == 0) { + fprintf(stderr, "ERROR: can't get $class uid val\n"); + return NULL; + } + + plist_t cls = nskeyedarchive_get_class_by_uid(ka, uintval); + if (!cls) { + return NULL; + } + + plist_t classname = plist_dict_get_item(cls, "$classname"); + if (!classname || (plist_get_node_type(classname) != PLIST_STRING)) { + fprintf(stderr, "ERROR: invalid $classname in class dict\n"); + return NULL; + } + + return plist_get_string_ptr(classname, NULL); +} + +int nskeyedarchive_get_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, plist_t* value) +{ + if (!ka || !ka->dict || !value) { + return -1; + } + + plist_t obj = nskeyedarchive_get_class_by_uid(ka, uid); + if (!obj) { + return -1; + } + + *value = plist_dict_get_item(obj, propname); + if (!*value) { + return -1; + } + + return 0; +} + +int nskeyedarchive_get_class_uint64_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, uint64_t* value) +{ + plist_t prop = NULL; + + nskeyedarchive_get_class_property(ka, uid, propname, &prop); + if (!prop) { + fprintf(stderr, "%s: ERROR: no such property '%s'\n", __func__, propname); + return -1; + } + + if (plist_get_node_type(prop) == PLIST_UID) { + uint64_t uintval = 0; + plist_get_uid_val(prop, &uintval); + prop = nskeyedarchive_get_object_by_uid(ka, uintval); + } + + if (plist_get_node_type(prop) != PLIST_UINT) { + fprintf(stderr, "%s: ERROR: property '%s' is not of type integer.\n", __func__, propname); + return -1; + } + + plist_get_uint_val(prop, value); + + return 0; +} + +int nskeyedarchive_get_class_int_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, int* value) +{ + uint64_t uintval = 0; + int res = nskeyedarchive_get_class_uint64_property(ka, uid, propname, &uintval); + if (res < 0) { + return res; + } + *value = (int)uintval; + return 0; +} + +int nskeyedarchive_get_class_string_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, char** value) +{ + plist_t node = NULL; + + nskeyedarchive_get_class_property(ka, uid, propname, &node); + if (!node || (plist_get_node_type(node) != PLIST_UID)) { + return -1; + } + + uint64_t uintval = 0; + plist_get_uid_val(node, &uintval); + + plist_t prop = nskeyedarchive_get_object_by_uid(ka, uintval); + if (!prop || (plist_get_node_type(prop) != PLIST_STRING)) { + fprintf(stderr, "%s: ERROR: property '%s' is not a string\n", __func__, propname); + return -1; + } + + plist_get_string_val(prop, value); + + return 0; +} + +static plist_t _nska_parse_object(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t obj = nskeyedarchive_get_object_by_uid(ka, uid); + switch (plist_get_node_type(obj)) { + case PLIST_BOOLEAN: + case PLIST_INT: + case PLIST_STRING: + return plist_copy(obj); + default: + break; + } + + const char* clsn = nskeyedarchive_get_classname(ka, uid); + plist_t pl = NULL; + if (!strcmp(clsn, "NSMutableDictionary") || !strcmp(clsn, "NSDictionary")) { + plist_t keys = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.keys", &keys); + plist_t vals = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &vals); + + uint32_t count = plist_array_get_size(keys); + if (count != plist_array_get_size(vals)) { + printf("ERROR: %s: inconsistent number of keys vs. values in dictionary object\n", __func__); + return NULL; + } + pl = plist_new_dict(); + uint32_t i = 0; + for (i = 0; i < count; i++) { + plist_t knode = plist_array_get_item(keys, i); + plist_t vnode = plist_array_get_item(vals, i); + + uint64_t subuid = 0; + plist_get_uid_val(knode, &subuid); + plist_t newkey = _nska_parse_object(ka, subuid); + + subuid = 0; + plist_get_uid_val(vnode, &subuid); + plist_t newval = _nska_parse_object(ka, subuid); + + if (!PLIST_IS_STRING(newkey)) { + printf("ERROR: %s: key node is not of string type.\n", __func__); + return NULL; + } + + plist_dict_set_item(pl, plist_get_string_ptr(newkey, NULL), newval); + plist_free(newkey); + } + } else if (!strcmp(clsn, "NSMutableArray") || !strcmp(clsn, "NSArray")) { + plist_t vals = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &vals); + + uint32_t count = plist_array_get_size(vals); + pl = plist_new_array(); + uint32_t i = 0; + for (i = 0; i < count; i++) { + plist_t vnode = plist_array_get_item(vals, i); + uint64_t subuid = 0; + plist_get_uid_val(vnode, &subuid); + plist_t newval = _nska_parse_object(ka, subuid); + plist_array_append_item(pl, newval); + } + } else { + printf("ERROR: %s: unhandled class type '%s'\n", __func__, clsn); + } + return pl; +} + +plist_t nskeyedarchive_to_plist(nskeyedarchive_t ka) +{ + uint64_t obj_uid = nskeyedarchive_get_class_uid(ka, NULL); + return _nska_parse_object(ka, obj_uid); +} diff --git a/src/opack.c b/src/opack.c index 9e7fa73..8a9264a 100644 --- a/src/opack.c +++ b/src/opack.c @@ -206,7 +206,7 @@ static void opack_encode_node(plist_t node, struct char_buf* cbuf) if (len >> 32) { uint8_t blen = 0x94; char_buf_append(cbuf, 1, &blen); - uint32_t u64val = htole64(len); + uint64_t u64val = htole64(len); char_buf_append(cbuf, 8, (unsigned char*)&u64val); } else { uint8_t blen = 0x93; @@ -237,7 +237,7 @@ static void opack_encode_node(plist_t node, struct char_buf* cbuf) } } -LIBIMOBILEDEVICE_GLUE_API void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len) +void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len) { if (!plist || !out || !out_len) { return; @@ -304,11 +304,11 @@ static int opack_decode_obj(unsigned char** p, unsigned char* end, plist_t* plis } else if (type == 0x32) { uint32_t u32val = *(uint32_t*)*p; value = (int32_t)le32toh(u32val); - (p)+=4; + (*p)+=4; } else if (type == 0x33) { uint64_t u64val = *(uint64_t*)*p; value = le64toh(u64val); - (p)+=8; + (*p)+=8; } else { fprintf(stderr, "%s: ERROR: Invalid encoded byte '%02x'\n", __func__, type); *p = end; @@ -462,7 +462,7 @@ static int opack_decode_obj(unsigned char** p, unsigned char* end, plist_t* plis return 0; } -LIBIMOBILEDEVICE_GLUE_API int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out) +int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out) { if (!buf || buf_len == 0 || !plist_out) { return -1; diff --git a/src/socket.c b/src/socket.c index 1029a71..5276b1e 100644 --- a/src/socket.c +++ b/src/socket.c @@ -65,6 +65,9 @@ static int wsa_init = 0; #endif #include "common.h" #include "libimobiledevice-glue/socket.h" +#ifdef HAVE_POLL +#include <sys/poll.h> +#endif #define RECV_TIMEOUT 20000 #define SEND_TIMEOUT 10000 @@ -82,12 +85,12 @@ static int wsa_init = 0; static int verbose = 0; -LIBIMOBILEDEVICE_GLUE_API void socket_set_verbose(int level) +void socket_set_verbose(int level) { verbose = level; } -LIBIMOBILEDEVICE_GLUE_API const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size) +const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size) { #ifdef WIN32 WSADATA wsa_data; @@ -140,8 +143,186 @@ LIBIMOBILEDEVICE_GLUE_API const char *socket_addr_to_string(struct sockaddr *add return NULL; } +enum poll_status +{ + poll_status_success, + poll_status_timeout, + poll_status_error +}; + +#ifdef WIN32 +static inline __attribute__((always_inline)) int WSAError_to_errno(int wsaerr) +{ + switch (wsaerr) { + case WSAEINVAL: + return EINVAL; + case WSAENOTSOCK: + return ENOTSOCK; + case WSAENOTCONN: + return ENOTCONN; + case WSAESHUTDOWN: + return ENOTCONN; + case WSAECONNRESET: + return ECONNRESET; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAENETDOWN: + return ENETDOWN; + case WSAENETRESET: + return ENETRESET; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAEWOULDBLOCK: + return EWOULDBLOCK; + case WSAEINPROGRESS: + return EINPROGRESS; + case WSAENOBUFS: + return ENOBUFS; + case WSAEINTR: + return EINTR; + case WSAEACCES: + return EACCES; + case WSAEFAULT: + return EFAULT; + default: + break; + } + return wsaerr; +} +#endif + +// timeout of -1 means infinity +static inline __attribute__((always_inline)) enum poll_status poll_wrapper(int fd, fd_mode mode, int timeout) +{ +#ifdef HAVE_POLL + // https://man7.org/linux/man-pages/man2/select.2.html + // Correspondence between select() and poll() notifications + // #define POLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN | + // EPOLLHUP | EPOLLERR) + // /* Ready for reading */ + // #define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT | + // EPOLLERR) + // /* Ready for writing */ + // #define POLLEX_SET (EPOLLPRI) + // /* Exceptional condition */ + + short events; + switch (mode) { + case FDM_READ: + events = POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR; + break; + case FDM_WRITE: + events = POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR; + break; + case FDM_EXCEPT: + events = POLLPRI; + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: fd_mode %d unsupported\n", __func__, mode); + return poll_status_error; + } + while (1) { + struct pollfd pfd = { + .fd = fd, + .events = events, + }; + switch (poll(&pfd, 1, timeout)) { + case 1: + if((pfd.revents & (POLLNVAL | POLLERR)) != 0) + { + if (verbose >= 2) + fprintf(stderr, "%s: poll unexpected events: %d\n", __func__, (int)pfd.revents); + return poll_status_error; + } + return poll_status_success; + case 0: + return poll_status_timeout; + case -1: + if(errno == EINTR) + { + if (verbose >= 2) + fprintf(stderr, "%s: EINTR\n", __func__); + continue; + } + // fallthrough + default: + if (verbose >= 2) + fprintf(stderr, "%s: poll failed: %s\n", __func__, strerror(errno)); + return poll_status_error; + } + } +#else + fd_set fds; + int sret; + int eagain; + struct timeval to; + struct timeval *pto; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + sret = poll_status_error; + + do { + if (timeout > 0) { + to.tv_sec = (time_t) (timeout / 1000); + to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); + pto = &to; + } else { + pto = NULL; + } + eagain = 0; + switch (mode) { + case FDM_READ: + sret = select(fd + 1, &fds, NULL, NULL, pto); + break; + case FDM_WRITE: + sret = select(fd + 1, NULL, &fds, NULL, pto); + break; + case FDM_EXCEPT: + sret = select(fd + 1, NULL, NULL, &fds, pto); + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: fd_mode %d unsupported\n", __func__, mode); + return poll_status_error; + } + + if (sret == 1) { + return poll_status_success; + } else if (sret == 0) { + return poll_status_timeout; + } else { + switch (errno) { + case EINTR: + // interrupt signal in select + if (verbose >= 2) + fprintf(stderr, "%s: EINTR\n", __func__); + eagain = 1; + break; + case EAGAIN: + if (verbose >= 2) + fprintf(stderr, "%s: EAGAIN\n", __func__); + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: select failed: %s\n", __func__, strerror(errno)); + return poll_status_error; + } + } + } while (eagain); + + return sret; +#endif +} + #ifndef WIN32 -LIBIMOBILEDEVICE_GLUE_API int socket_create_unix(const char *filename) +int socket_create_unix(const char *filename) { struct sockaddr_un name; int sock; @@ -187,7 +368,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_create_unix(const char *filename) return sock; } -LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) +int socket_connect_unix(const char *filename) { struct sockaddr_un name; int sfd = -1; @@ -246,20 +427,22 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) break; } if (errno == EINPROGRESS) { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sfd, &fds); - - struct timeval timeout; - timeout.tv_sec = CONNECT_TIMEOUT / 1000; - timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000; - if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); if (so_error == 0) { + errno = 0; break; } + errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { + errno = so_error; + } } } socket_close(sfd); @@ -276,7 +459,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) } #endif -LIBIMOBILEDEVICE_GLUE_API int socket_create(const char* addr, uint16_t port) +int socket_create(const char* addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -303,9 +486,6 @@ LIBIMOBILEDEVICE_GLUE_API int socket_create(const char* addr, uint16_t port) sprintf(portstr, "%d", port); - if (!addr) { - addr = "localhost"; - } res = getaddrinfo(addr, portstr, &hints, &result); if (res != 0) { fprintf(stderr, "%s: getaddrinfo: %s\n", __func__, gai_strerror(res)); @@ -699,7 +879,7 @@ static int getifaddrs(struct ifaddrs** ifap) #endif #endif -LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf[6]) +int get_primary_mac_address(unsigned char mac_addr_buf[6]) { int result = -1; struct ifaddrs *ifaddr = NULL, *ifa = NULL; @@ -742,6 +922,12 @@ LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf result = 0; break; } +#elif defined(__CYGWIN__) + if (ifa->ifa_data) { + memcpy(mac_addr_buf, ((struct ifaddrs_hwdata *)ifa->ifa_data)->ifa_hwaddr.sa_data, 6); + result = 0; + break; + } #else #error get_primary_mac_address is not supported on this platform. #endif @@ -840,7 +1026,7 @@ static int32_t _sockaddr_in6_scope_id(struct sockaddr_in6* addr) } #endif -LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_t port) +int socket_connect_addr(struct sockaddr* addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -924,14 +1110,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ if (errno == EINPROGRESS) #endif { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sfd, &fds); - - struct timeval timeout; - timeout.tv_sec = CONNECT_TIMEOUT / 1000; - timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000; - if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); @@ -939,7 +1118,20 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ errno = 0; break; } +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } } } socket_close(sfd); @@ -970,7 +1162,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ return sfd; } -LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) +int socket_connect(const char *addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -993,11 +1185,6 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) int flags = 0; #endif - if (!addr) { - errno = EINVAL; - return -1; - } - memset(&hints, '\0', sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -1048,20 +1235,28 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) if (errno == EINPROGRESS) #endif { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sfd, &fds); - - struct timeval timeout; - timeout.tv_sec = CONNECT_TIMEOUT / 1000; - timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000; - if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); if (so_error == 0) { + errno = 0; break; } +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } } } socket_close(sfd); @@ -1090,75 +1285,39 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) return sfd; } -LIBIMOBILEDEVICE_GLUE_API int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout) +int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout) { - fd_set fds; - int sret; - int eagain; - struct timeval to; - struct timeval *pto; - if (fd < 0) { if (verbose >= 2) fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd); - return -1; + return -EINVAL; } - FD_ZERO(&fds); - FD_SET(fd, &fds); - - sret = -1; - - do { - if (timeout > 0) { - to.tv_sec = (time_t) (timeout / 1000); - to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); - pto = &to; - } else { - pto = NULL; - } - eagain = 0; - switch (fdm) { - case FDM_READ: - sret = select(fd + 1, &fds, NULL, NULL, pto); - break; - case FDM_WRITE: - sret = select(fd + 1, NULL, &fds, NULL, pto); - break; - case FDM_EXCEPT: - sret = select(fd + 1, NULL, NULL, &fds, pto); - break; - default: - return -1; - } + int timeout_ms; + if (timeout > 0) { + timeout_ms = (int)timeout; + if (timeout_ms <= 0) + timeout_ms = -1; + } else { + timeout_ms = -1; + } - if (sret < 0) { - switch (errno) { - case EINTR: - // interrupt signal in select - if (verbose >= 2) - fprintf(stderr, "%s: EINTR\n", __func__); - eagain = 1; - break; - case EAGAIN: - if (verbose >= 2) - fprintf(stderr, "%s: EAGAIN\n", __func__); - break; - default: - if (verbose >= 2) - fprintf(stderr, "%s: select failed: %s\n", __func__, - strerror(errno)); - return -1; - } - } else if (sret == 0) { + switch (poll_wrapper(fd, fdm, timeout_ms)) { + case poll_status_success: + return 1; + case poll_status_timeout: return -ETIMEDOUT; - } - } while (eagain); + case poll_status_error: + default: + if (verbose >= 2) + fprintf(stderr, "%s: poll_wrapper failed\n", __func__); + return -ECONNRESET; + } - return sret; + return -ECONNRESET; } -LIBIMOBILEDEVICE_GLUE_API int socket_accept(int fd, uint16_t port) +int socket_accept(int fd, uint16_t port) { #ifdef WIN32 int addr_len; @@ -1174,12 +1333,12 @@ LIBIMOBILEDEVICE_GLUE_API int socket_accept(int fd, uint16_t port) return result; } -LIBIMOBILEDEVICE_GLUE_API int socket_shutdown(int fd, int how) +int socket_shutdown(int fd, int how) { return shutdown(fd, how); } -LIBIMOBILEDEVICE_GLUE_API int socket_close(int fd) { +int socket_close(int fd) { #ifdef WIN32 return closesocket(fd); #else @@ -1187,17 +1346,17 @@ LIBIMOBILEDEVICE_GLUE_API int socket_close(int fd) { #endif } -LIBIMOBILEDEVICE_GLUE_API int socket_receive(int fd, void *data, size_t length) +int socket_receive(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, 0, RECV_TIMEOUT); } -LIBIMOBILEDEVICE_GLUE_API int socket_peek(int fd, void *data, size_t length) +int socket_peek(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); } -LIBIMOBILEDEVICE_GLUE_API int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout) +int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout) { int res; int result; @@ -1209,19 +1368,22 @@ LIBIMOBILEDEVICE_GLUE_API int socket_receive_timeout(int fd, void *data, size_t } // if we get here, there _is_ data available result = recv(fd, data, length, flags); - if (res > 0 && result == 0) { + if (result == 0) { // but this is an error condition if (verbose >= 3) fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd); return -ECONNRESET; } if (result < 0) { +#ifdef WIN32 + errno = WSAError_to_errno(WSAGetLastError()); +#endif return -errno; } return result; } -LIBIMOBILEDEVICE_GLUE_API int socket_send(int fd, void *data, size_t length) +int socket_send(int fd, void *data, size_t length) { int flags = 0; int res = socket_check_fd(fd, FDM_WRITE, SEND_TIMEOUT); @@ -1231,10 +1393,17 @@ LIBIMOBILEDEVICE_GLUE_API int socket_send(int fd, void *data, size_t length) #ifdef MSG_NOSIGNAL flags |= MSG_NOSIGNAL; #endif - return send(fd, data, length, flags); + int s = (int)send(fd, data, length, flags); + if (s < 0) { +#ifdef WIN32 + errno = WSAError_to_errno(WSAGetLastError()); +#endif + return -errno; + } + return s; } -LIBIMOBILEDEVICE_GLUE_API int socket_get_socket_port(int fd, uint16_t *port) +int socket_get_socket_port(int fd, uint16_t *port) { #ifdef WIN32 int addr_len; diff --git a/src/termcolors.c b/src/termcolors.c index 710c9df..5c436f2 100644 --- a/src/termcolors.c +++ b/src/termcolors.c @@ -92,7 +92,7 @@ static int bgcolor_map[8] = { static int WIN32_LEGACY_MODE = 0; #endif -LIBIMOBILEDEVICE_GLUE_API void term_colors_init() +void term_colors_init() { #ifdef WIN32 DWORD conmode = 0; @@ -126,12 +126,12 @@ LIBIMOBILEDEVICE_GLUE_API void term_colors_init() } } -LIBIMOBILEDEVICE_GLUE_API void term_colors_set_enabled(int en) +void term_colors_set_enabled(int en) { use_colors = en; } -LIBIMOBILEDEVICE_GLUE_API int cvfprintf(FILE* stream, const char* fmt, va_list vargs) +int cvfprintf(FILE* stream, const char* fmt, va_list vargs) { int res = 0; int colorize = use_colors; @@ -298,7 +298,7 @@ LIBIMOBILEDEVICE_GLUE_API int cvfprintf(FILE* stream, const char* fmt, va_list v return res; } -LIBIMOBILEDEVICE_GLUE_API int cfprintf(FILE* stream, const char* fmt, ...) +int cfprintf(FILE* stream, const char* fmt, ...) { int res = 0; va_list va; @@ -308,7 +308,7 @@ LIBIMOBILEDEVICE_GLUE_API int cfprintf(FILE* stream, const char* fmt, ...) return res; } -LIBIMOBILEDEVICE_GLUE_API int cprintf(const char* fmt, ...) +int cprintf(const char* fmt, ...) { int res = 0; va_list va; diff --git a/src/thread.c b/src/thread.c index 6efacec..50639dd 100644 --- a/src/thread.c +++ b/src/thread.c @@ -22,13 +22,16 @@ #ifdef HAVE_CONFIG_H #include <config.h> #endif +#ifdef WIN32 +#include <windows.h> +#endif #include "common.h" #include "libimobiledevice-glue/thread.h" -LIBIMOBILEDEVICE_GLUE_API int thread_new(THREAD_T *thread, thread_func_t thread_func, void* data) +int thread_new(THREAD_T *thread, thread_func_t thread_func, void* data) { #ifdef WIN32 - HANDLE th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, data, 0, NULL); + HANDLE th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(void*)thread_func, data, 0, NULL); if (th == NULL) { return -1; } @@ -40,7 +43,7 @@ LIBIMOBILEDEVICE_GLUE_API int thread_new(THREAD_T *thread, thread_func_t thread_ #endif } -LIBIMOBILEDEVICE_GLUE_API void thread_detach(THREAD_T thread) +void thread_detach(THREAD_T thread) { #ifdef WIN32 CloseHandle(thread); @@ -49,14 +52,14 @@ LIBIMOBILEDEVICE_GLUE_API void thread_detach(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API void thread_free(THREAD_T thread) +void thread_free(THREAD_T thread) { #ifdef WIN32 CloseHandle(thread); #endif } -LIBIMOBILEDEVICE_GLUE_API int thread_join(THREAD_T thread) +int thread_join(THREAD_T thread) { /* wait for thread to complete */ #ifdef WIN32 @@ -66,7 +69,7 @@ LIBIMOBILEDEVICE_GLUE_API int thread_join(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API int thread_alive(THREAD_T thread) +int thread_alive(THREAD_T thread) { if (!thread) return 0; @@ -77,7 +80,7 @@ LIBIMOBILEDEVICE_GLUE_API int thread_alive(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API int thread_cancel(THREAD_T thread) +int thread_cancel(THREAD_T thread) { #ifdef WIN32 return -1; @@ -90,43 +93,43 @@ LIBIMOBILEDEVICE_GLUE_API int thread_cancel(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_init(mutex_t* mutex) +void mutex_init(mutex_t* mutex) { #ifdef WIN32 - InitializeCriticalSection(mutex); + InitializeCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_init(mutex, NULL); #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_destroy(mutex_t* mutex) +void mutex_destroy(mutex_t* mutex) { #ifdef WIN32 - DeleteCriticalSection(mutex); + DeleteCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_destroy(mutex); #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_lock(mutex_t* mutex) +void mutex_lock(mutex_t* mutex) { #ifdef WIN32 - EnterCriticalSection(mutex); + EnterCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_lock(mutex); #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_unlock(mutex_t* mutex) +void mutex_unlock(mutex_t* mutex) { #ifdef WIN32 - LeaveCriticalSection(mutex); + LeaveCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_unlock(mutex); #endif } -LIBIMOBILEDEVICE_GLUE_API void thread_once(thread_once_t *once_control, void (*init_routine)(void)) +void thread_once(thread_once_t *once_control, void (*init_routine)(void)) { #ifdef WIN32 while (InterlockedExchange(&(once_control->lock), 1) != 0) { @@ -142,7 +145,7 @@ LIBIMOBILEDEVICE_GLUE_API void thread_once(thread_once_t *once_control, void (*i #endif } -LIBIMOBILEDEVICE_GLUE_API void cond_init(cond_t* cond) +void cond_init(cond_t* cond) { #ifdef WIN32 cond->sem = CreateSemaphore(NULL, 0, 32767, NULL); @@ -151,7 +154,7 @@ LIBIMOBILEDEVICE_GLUE_API void cond_init(cond_t* cond) #endif } -LIBIMOBILEDEVICE_GLUE_API void cond_destroy(cond_t* cond) +void cond_destroy(cond_t* cond) { #ifdef WIN32 CloseHandle(cond->sem); @@ -160,7 +163,7 @@ LIBIMOBILEDEVICE_GLUE_API void cond_destroy(cond_t* cond) #endif } -LIBIMOBILEDEVICE_GLUE_API int cond_signal(cond_t* cond) +int cond_signal(cond_t* cond) { #ifdef WIN32 int result = 0; @@ -173,7 +176,7 @@ LIBIMOBILEDEVICE_GLUE_API int cond_signal(cond_t* cond) #endif } -LIBIMOBILEDEVICE_GLUE_API int cond_wait(cond_t* cond, mutex_t* mutex) +int cond_wait(cond_t* cond, mutex_t* mutex) { #ifdef WIN32 mutex_unlock(mutex); @@ -189,7 +192,7 @@ LIBIMOBILEDEVICE_GLUE_API int cond_wait(cond_t* cond, mutex_t* mutex) #endif } -LIBIMOBILEDEVICE_GLUE_API int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms) +int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms) { #ifdef WIN32 mutex_unlock(mutex); @@ -32,7 +32,7 @@ #include "libimobiledevice-glue/tlv.h" #include "endianness.h" -LIBIMOBILEDEVICE_GLUE_API tlv_buf_t tlv_buf_new() +tlv_buf_t tlv_buf_new() { tlv_buf_t tlv = malloc(sizeof(struct tlv_buf)); tlv->capacity = 1024; @@ -41,7 +41,7 @@ LIBIMOBILEDEVICE_GLUE_API tlv_buf_t tlv_buf_new() return tlv; } -LIBIMOBILEDEVICE_GLUE_API void tlv_buf_free(tlv_buf_t tlv) +void tlv_buf_free(tlv_buf_t tlv) { if (tlv) { free(tlv->data); @@ -49,7 +49,7 @@ LIBIMOBILEDEVICE_GLUE_API void tlv_buf_free(tlv_buf_t tlv) } } -LIBIMOBILEDEVICE_GLUE_API void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data) +void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data) { if (!tlv || !tlv->data) { return; @@ -86,7 +86,7 @@ LIBIMOBILEDEVICE_GLUE_API void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsign tlv->length = p - tlv->data; } -LIBIMOBILEDEVICE_GLUE_API unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length) +unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length) { unsigned char* p = (unsigned char*)tlv_data; unsigned char* end = (unsigned char*)tlv_end; @@ -102,7 +102,7 @@ LIBIMOBILEDEVICE_GLUE_API unsigned char* tlv_get_data_ptr(const void* tlv_data, return NULL; } -LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value) +int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value) { if (!tlv_data || tlv_length < 2 || !value) { return 0; @@ -136,7 +136,7 @@ LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint(const void* tlv_data, unsigned i return 1; } -LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value) +int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value) { if (!tlv_data || tlv_length < 2 || !value) { return 0; @@ -156,7 +156,7 @@ LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint8(const void* tlv_data, unsigned return 1; } -LIBIMOBILEDEVICE_GLUE_API int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len) +int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len) { if (!tlv_data || tlv_length < 2 || !out || !out_len) { return 0; diff --git a/src/utils.c b/src/utils.c index 9baef7e..ad8e9c4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -3,7 +3,7 @@ * Miscellaneous utilities for string manipulation, * file I/O and plist helper. * - * Copyright (c) 2014-2019 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2014-2023 Nikias Bassen, All Rights Reserved. * Copyright (c) 2013-2014 Martin Szulecki, All Rights Reserved. * Copyright (c) 2013 Federico Mena Quintero * @@ -75,7 +75,7 @@ char *stpcpy(char *s1, const char *s2) * @return a newly allocated string, or NULL if @str is NULL. This will also * return NULL and set errno to ENOMEM if memory is exhausted. */ -LIBIMOBILEDEVICE_GLUE_API char *string_concat(const char *str, ...) +char *string_concat(const char *str, ...) { size_t len; va_list args; @@ -119,7 +119,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_concat(const char *str, ...) return result; } -LIBIMOBILEDEVICE_GLUE_API char *string_append(char* str, ...) +char *string_append(char* str, ...) { size_t len = 0; size_t slen; @@ -163,7 +163,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_append(char* str, ...) return result; } -LIBIMOBILEDEVICE_GLUE_API char *string_build_path(const char *elem, ...) +char *string_build_path(const char *elem, ...) { if (!elem) return NULL; @@ -191,7 +191,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_build_path(const char *elem, ...) return out; } -LIBIMOBILEDEVICE_GLUE_API char *string_format_size(uint64_t size) +char *string_format_size(uint64_t size) { char buf[80]; double sz; @@ -213,7 +213,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_format_size(uint64_t size) return strdup(buf); } -LIBIMOBILEDEVICE_GLUE_API char *string_toupper(char* str) +char *string_toupper(char* str) { char *res = strdup(str); size_t i; @@ -229,7 +229,7 @@ static int get_rand(int min, int max) return retval; } -LIBIMOBILEDEVICE_GLUE_API char *generate_uuid() +char *generate_uuid() { const char *chars = "ABCDEF0123456789"; int i = 0; @@ -251,7 +251,7 @@ LIBIMOBILEDEVICE_GLUE_API char *generate_uuid() return uuid; } -LIBIMOBILEDEVICE_GLUE_API int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) +int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) { FILE *f; uint64_t size; @@ -295,7 +295,7 @@ LIBIMOBILEDEVICE_GLUE_API int buffer_read_from_filename(const char *filename, ch return ret; } -LIBIMOBILEDEVICE_GLUE_API int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length) +int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length) { FILE *f; @@ -318,241 +318,3 @@ LIBIMOBILEDEVICE_GLUE_API int buffer_write_to_filename(const char *filename, con return 0; } } - -LIBIMOBILEDEVICE_GLUE_API int plist_read_from_filename(plist_t *plist, const char *filename) -{ - char *buffer = NULL; - uint64_t length; - - if (!filename) - return 0; - - if (!buffer_read_from_filename(filename, &buffer, &length)) { - return 0; - } - - plist_from_memory(buffer, length, plist); - - free(buffer); - - return 1; -} - -LIBIMOBILEDEVICE_GLUE_API int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format) -{ - char *buffer = NULL; - uint32_t length; - - if (!plist || !filename) - return 0; - - if (format == PLIST_FORMAT_XML) - plist_to_xml(plist, &buffer, &length); - else if (format == PLIST_FORMAT_BINARY) - plist_to_bin(plist, &buffer, &length); - else - return 0; - - int res = buffer_write_to_filename(filename, buffer, length); - - free(buffer); - - return res; -} - -static const char base64_str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char base64_pad = '='; - -static char *base64encode(const unsigned char *buf, size_t size) -{ - if (!buf || !(size > 0)) return NULL; - int outlen = (size / 3) * 4; - char *outbuf = (char*)malloc(outlen+5); // 4 spare bytes + 1 for '\0' - size_t n = 0; - size_t m = 0; - unsigned char input[3]; - unsigned int output[4]; - while (n < size) { - input[0] = buf[n]; - input[1] = (n+1 < size) ? buf[n+1] : 0; - input[2] = (n+2 < size) ? buf[n+2] : 0; - output[0] = input[0] >> 2; - output[1] = ((input[0] & 3) << 4) + (input[1] >> 4); - output[2] = ((input[1] & 15) << 2) + (input[2] >> 6); - output[3] = input[2] & 63; - outbuf[m++] = base64_str[(int)output[0]]; - outbuf[m++] = base64_str[(int)output[1]]; - outbuf[m++] = (n+1 < size) ? base64_str[(int)output[2]] : base64_pad; - outbuf[m++] = (n+2 < size) ? base64_str[(int)output[3]] : base64_pad; - n+=3; - } - outbuf[m] = 0; // 0-termination! - return outbuf; -} - -static void plist_node_print_to_stream(plist_t node, int* indent_level, FILE* stream); - -static void plist_array_print_to_stream(plist_t node, int* indent_level, FILE* stream) -{ - /* iterate over items */ - int i, count; - plist_t subnode = NULL; - - count = plist_array_get_size(node); - - for (i = 0; i < count; i++) { - subnode = plist_array_get_item(node, i); - fprintf(stream, "%*s", *indent_level, ""); - fprintf(stream, "%d: ", i); - plist_node_print_to_stream(subnode, indent_level, stream); - } -} - -static void plist_dict_print_to_stream(plist_t node, int* indent_level, FILE* stream) -{ - /* iterate over key/value pairs */ - plist_dict_iter it = NULL; - - char* key = NULL; - plist_t subnode = NULL; - plist_dict_new_iter(node, &it); - plist_dict_next_item(node, it, &key, &subnode); - while (subnode) - { - fprintf(stream, "%*s", *indent_level, ""); - fprintf(stream, "%s", key); - if (plist_get_node_type(subnode) == PLIST_ARRAY) - fprintf(stream, "[%d]: ", plist_array_get_size(subnode)); - else - fprintf(stream, ": "); - free(key); - key = NULL; - plist_node_print_to_stream(subnode, indent_level, stream); - plist_dict_next_item(node, it, &key, &subnode); - } - free(it); -} - -static void plist_node_print_to_stream(plist_t node, int* indent_level, FILE* stream) -{ - char *s = NULL; - char *data = NULL; - double d; - uint8_t b; - uint64_t u = 0; - struct timeval tv = { 0, 0 }; - - plist_type t; - - if (!node) - return; - - t = plist_get_node_type(node); - - switch (t) { - case PLIST_BOOLEAN: - plist_get_bool_val(node, &b); - fprintf(stream, "%s\n", (b ? "true" : "false")); - break; - - case PLIST_UINT: - plist_get_uint_val(node, &u); - fprintf(stream, "%"PRIu64"\n", u); - break; - - case PLIST_REAL: - plist_get_real_val(node, &d); - fprintf(stream, "%f\n", d); - break; - - case PLIST_STRING: - plist_get_string_val(node, &s); - fprintf(stream, "%s\n", s); - free(s); - break; - - case PLIST_KEY: - plist_get_key_val(node, &s); - fprintf(stream, "%s: ", s); - free(s); - break; - - case PLIST_DATA: - plist_get_data_val(node, &data, &u); - if (u > 0) { - s = base64encode((unsigned char*)data, u); - free(data); - if (s) { - fprintf(stream, "%s\n", s); - free(s); - } else { - fprintf(stream, "\n"); - } - } else { - fprintf(stream, "\n"); - } - break; - - case PLIST_DATE: - plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec); - { - time_t ti = (time_t)tv.tv_sec + MAC_EPOCH; - struct tm *btime = localtime(&ti); - if (btime) { - s = (char*)malloc(24); - memset(s, 0, 24); - if (strftime(s, 24, "%Y-%m-%dT%H:%M:%SZ", btime) <= 0) { - free (s); - s = NULL; - } - } - } - if (s) { - fprintf(stream, "%s\n", s); - free(s); - } else { - fprintf(stream, "\n"); - } - break; - - case PLIST_ARRAY: - fprintf(stream, "\n"); - (*indent_level)++; - plist_array_print_to_stream(node, indent_level, stream); - (*indent_level)--; - break; - - case PLIST_DICT: - fprintf(stream, "\n"); - (*indent_level)++; - plist_dict_print_to_stream(node, indent_level, stream); - (*indent_level)--; - break; - - default: - break; - } -} - -LIBIMOBILEDEVICE_GLUE_API void plist_print_to_stream_with_indentation(plist_t plist, FILE* stream, unsigned int indentation) -{ - if (!plist || !stream) - return; - - int indent = indentation; - switch (plist_get_node_type(plist)) { - case PLIST_DICT: - plist_dict_print_to_stream(plist, &indent, stream); - break; - case PLIST_ARRAY: - plist_array_print_to_stream(plist, &indent, stream); - break; - default: - plist_node_print_to_stream(plist, &indent, stream); - } -} - -LIBIMOBILEDEVICE_GLUE_API void plist_print_to_stream(plist_t plist, FILE* stream) -{ - plist_print_to_stream_with_indentation(plist, stream, 0); -} |