diff options
-rw-r--r-- | .github/workflows/build.yml | 268 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | NEWS | 20 | ||||
-rw-r--r-- | README | 59 | ||||
-rw-r--r-- | README.md | 127 | ||||
-rwxr-xr-x | autogen.sh | 33 | ||||
-rw-r--r-- | configure.ac | 34 | ||||
-rwxr-xr-x | git-version-gen | 20 | ||||
-rw-r--r-- | m4/as-compiler-flag.m4 | 4 | ||||
-rw-r--r-- | man/ideviceinstaller.1 | 155 | ||||
-rw-r--r-- | src/ideviceinstaller.c | 889 |
11 files changed, 1110 insertions, 505 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9c0508a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,268 @@ +name: build + +on: + push: + schedule: + - cron: '0 0 1 * *' + +jobs: + build-linux-ubuntu: + runs-on: ubuntu-latest + steps: + - name: install dependencies + run: | + sudo apt-get update + sudo apt-get install libzip-dev + - name: prepare environment + run: | + echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV + - name: fetch libplist + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libplist-latest_${{env.target_triplet}} + repo: libimobiledevice/libplist + - name: fetch libusbmuxd + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libusbmuxd-latest_${{env.target_triplet}} + repo: libimobiledevice/libusbmuxd + - name: fetch libimobiledevice-glue + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-glue-latest_${{env.target_triplet}} + repo: libimobiledevice/libimobiledevice-glue + - name: fetch libimobiledevice + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-latest_${{env.target_triplet}} + repo: libimobiledevice/libimobiledevice + - name: install external dependencies + run: | + mkdir extract + for I in *.tar; do + tar -C extract -xvf $I + done + rm -rf extract/lib + sudo cp -r extract/* / + sudo ldconfig + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: autogen + run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig + - name: make + run: make + - name: make install + run: sudo make install + - name: prepare artifact + run: | + mkdir -p dest + DESTDIR=`pwd`/dest make install + tar -C dest -cf ideviceinstaller.tar usr + - name: publish artifact + uses: actions/upload-artifact@v3 + with: + name: ideviceinstaller-latest_${{env.target_triplet}} + path: ideviceinstaller.tar + build-macOS: + runs-on: macOS-latest + steps: + - name: install dependencies + run: | + if test -x "`which port`"; then + sudo port install libtool autoconf automake pkgconfig + else + brew install libtool autoconf automake pkgconfig + fi + shell: bash + - name: fetch libplist + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libplist-latest_macOS + repo: libimobiledevice/libplist + - name: fetch libusbmuxd + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libusbmuxd-latest_macOS + repo: libimobiledevice/libusbmuxd + - name: fetch libimobiledevice-glue + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-glue-latest_macOS + repo: libimobiledevice/libimobiledevice-glue + - name: fetch libimobiledevice + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-latest_macOS + repo: libimobiledevice/libimobiledevice + - name: install external dependencies + run: | + mkdir extract + for I in *.tar; do + tar -C extract -xvf $I + done + sudo cp -r extract/* / + - uses: actions/checkout@v3 + - name: install additional requirements + run: | + SDKDIR=`xcrun --sdk macosx --show-sdk-path 2>/dev/null` + echo "SDKDIR=$SDKDIR" >> $GITHUB_ENV + TESTARCHS="arm64 x86_64" + USEARCHS= + for ARCH in $TESTARCHS; do + if echo "int main(int argc, char **argv) { return 0; }" |clang -arch $ARCH -o /dev/null -isysroot $SDKDIR -x c - 2>/dev/null; then + USEARCHS="$USEARCHS -arch $ARCH" + fi + done + export CFLAGS="$USEARCHS -isysroot $SDKDIR" + echo "Using CFLAGS: $CFLAGS" + echo "BUILD_CFLAGS=$CFLAGS" >> $GITHUB_ENV + mkdir -p lib + curl -o lib/libcrypto.35.tbd -Ls \ + https://gist.github.com/nikias/94c99fd145a75a5104415e5117b0cafa/raw/5209dfbff5a871a14272afe4794e76eb4cf6f062/libcrypto.35.tbd + curl -o lib/libssl.35.tbd -Ls \ + https://gist.github.com/nikias/94c99fd145a75a5104415e5117b0cafa/raw/5209dfbff5a871a14272afe4794e76eb4cf6f062/libssl.35.tbd + LIBRESSL_VER=2.2.7 + FILENAME="libressl-$LIBRESSL_VER.tar.gz" + curl -o $FILENAME -Ls "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/$FILENAME" + mkdir -p deps + tar -C deps -xzf $FILENAME + echo "LIBRESSL_CFLAGS=-I`pwd`/deps/libressl-$LIBRESSL_VER/include" >> $GITHUB_ENV + echo "LIBRESSL_LIBS=-Xlinker `pwd`/lib/libssl.35.tbd -Xlinker `pwd`/lib/libcrypto.35.tbd" >> $GITHUB_ENV + FILENAME="libzip-static.tar.bz2" + curl -o $FILENAME.b64 -Ls "https://gist.github.com/nikias/3da15d03120382f87b44029cd8495a02/raw/99cd8138fed99e8f6530b6f179f787342c698e1f/libzip-1.7.1_static_macOS.tar.bz2" + base64 -D < $FILENAME.b64 > $FILENAME + tar -C deps -xjf $FILENAME + echo "LIBZIP_CFLAGS=-I`pwd`/deps/include" >> $GITHUB_ENV + echo "LIBZIP_LIBS=`pwd`/deps/lib/libzip.a -Xlinker /usr/lib/libbz2.dylib -Xlinker /usr/lib/liblzma.dylib -lz" >> $GITHUB_ENV + - name: autogen + run: | + export CFLAGS="${{env.BUILD_CFLAGS}} -Wno-nullability-completeness -Wno-expansion-to-defined" + echo "Using CFLAGS: $CFLAGS" + ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig \ + openssl_CFLAGS="$LIBRESSL_CFLAGS" openssl_LIBS="$LIBRESSL_LIBS" \ + libzip_CFLAGS="$LIBZIP_CFLAGS -I${{env.SDKDIR}}/usr/include" libzip_LIBS="$LIBZIP_LIBS -lz" \ + libimobiledevice_CFLAGS="-I/usr/local/include ${{env.LIBRESSL_CFLAGS}}" libimobiledevice_LIBS="-L/usr/local/lib -lusbmuxd-2.0 -limobiledevice-glue-1.0 -limobiledevice-1.0 ${{env.LIBRESSL_LIBS}}" + - name: make + run: make + - name: make install + run: sudo make install + - name: prepare artifact + run: | + mkdir -p dest + DESTDIR=`pwd`/dest make install + tar -C dest -cf ideviceinstaller.tar usr + - name: publish artifact + uses: actions/upload-artifact@v3 + with: + name: ideviceinstaller-latest_macOS + path: ideviceinstaller.tar + build-windows: + runs-on: windows-2019 + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: false + matrix: + include: [ + { msystem: MINGW64, arch: x86_64 }, + { msystem: MINGW32, arch: i686 } + ] + steps: + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msystem }} + release: false + update: false + install: >- + base-devel + git + mingw-w64-${{ matrix.arch }}-gcc + make + libtool + autoconf + automake-wrapper + liblzma + - name: prepare environment + run: | + dest=`echo ${{ matrix.msystem }} |tr [:upper:] [:lower:]` + echo "dest=$dest" >> $GITHUB_ENV + echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV + - name: fetch libplist + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libplist-latest_${{ matrix.arch }}-${{ env.dest }} + repo: libimobiledevice/libplist + - name: fetch libusbmuxd + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libusbmuxd-latest_${{ matrix.arch }}-${{ env.dest }} + repo: libimobiledevice/libusbmuxd + - name: fetch libimobiledevice-glue + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }} + repo: libimobiledevice/libimobiledevice-glue + - name: fetch libimobiledevice + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-latest_${{ matrix.arch }}-${{ env.dest }} + repo: libimobiledevice/libimobiledevice + - name: install external dependencies + run: | + mkdir extract + for I in *.tar; do + tar -C extract -xvf $I + done + cp -r extract/* / + - uses: actions/checkout@v3 + - name: install additional requirements + run: | + FILENAME="libzip-1.7.1-static.tar.bz2" + curl -o $FILENAME.b64 -Ls "https://gist.github.com/nikias/3da15d03120382f87b44029cd8495a02/raw/99cd8138fed99e8f6530b6f179f787342c698e1f/libzip-1.7.1_static_${{matrix.arch}}-${{env.dest}}.tar.bz2" + base64 -d < $FILENAME.b64 > $FILENAME + mkdir deps + tar -C deps -xjf $FILENAME + echo "LIBZIP_CFLAGS=-I`pwd`/deps/include" >> $GITHUB_ENV + echo "LIBZIP_LIBS=`pwd`/deps/lib/libzip.a /${{env.dest}}/lib/libbz2.a /${{env.dest}}/lib/liblzma.a " >> $GITHUB_ENV + - name: autogen + run: ./autogen.sh CC=gcc CXX=g++ libzip_CFLAGS="${{env.LIBZIP_CFLAGS}}" libzip_LIBS="${{env.LIBZIP_LIBS}} /${{env.dest}}/lib/libz.a" + - name: make + run: make + - name: make install + run: make install + - name: prepare artifact + run: | + mkdir -p dest + DESTDIR=`pwd`/dest make install + tar -C dest -cf ideviceinstaller.tar ${{ env.dest }} + - name: publish artifact + uses: actions/upload-artifact@v3 + with: + name: ideviceinstaller-latest_${{ matrix.arch }}-${{ env.dest }} + path: ideviceinstaller.tar diff --git a/Makefile.am b/Makefile.am index 7c28996..5888709 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,3 +2,9 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src man +EXTRA_DIST = \ + README.md \ + git-version-gen + +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version @@ -1,3 +1,23 @@ +Version 1.1.1 +~~~~~~~~~~~~~ + +* Changes: + - Bump autoconf requirement to 2.64 + - Bump libzip dependency to 0.10 + - Ignore .DS_Store and hidden files when parsing ZIP files + - Fix device removal detection triggering on any device unplug + - Improve excessive progress output + - Return non-zero exit status on errors + - Remove length check on UDID argument to support newer devices + - Fix win32 build + - Add "-n" option to make waiting on install/uninstall notification optional + - Ignore SIGPIPE signal + - Add "--network" and "--version" options to ideviceactivation tool + - Bump libimobiledevice dependency to 1.3.0 + - Bump libplist dependency to 2.2.0 + - Improve README.md with project description, installation, contributing and + usage sections + Version 1.1.0 ~~~~~~~~~~~~~ @@ -1,59 +0,0 @@ -About -===== - -ideviceinstaller is a tool to interact with the installation_proxy -of an iOS device allowing to install, upgrade, uninstall, archive, restore -and enumerate installed or archived apps. - -It makes use of the fabulous libimobiledevice library that allows communication -with iOS devices. - -Requirements -============ - -Development Packages of: - libimobiledevice - libplist - libzip - -Installation -============ - -To compile run: - ./autogen.sh - make - sudo make install - -Who/What/Where? -=============== - -Home: - http://www.libimobiledevice.org/ - -Code: - git clone http://git.libimobiledevice.org/ideviceinstaller.git - -Code (Mirror): - git clone https://github.com/libimobiledevice/ideviceinstaller.git - -Tickets: - http://github.com/libimobiledevice/ideviceinstaller/issues - -Mailing List: - http://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel - -IRC: - irc://irc.freenode.net#libimobiledevice - -Twitter: - https://twitter.com/libimobiledev - -Credits -======= - -Apple, iPhone, iPod, and iPod Touch are trademarks of Apple Inc. -libimobiledevice is an independent software library and has not been -authorized, sponsored or otherwise approved by Apple Inc. - -README Updated on: - 2015-01-28 diff --git a/README.md b/README.md new file mode 100644 index 0000000..84d4f38 --- /dev/null +++ b/README.md @@ -0,0 +1,127 @@ +# ideviceinstaller + +*A command-line application to manage apps and app archives on iOS devices.* + +![](https://github.com/libimobiledevice/ideviceinstaller/actions/workflows/build.yml/badge.svg) + +## Features + +The ideviceinstaller application allows interacting with the app installation +service of an iOS device. + +It makes use of the fabulous [libimobiledevice library](https://github.com/libimobiledevice/libimobiledevice) that allows +communication with iOS devices. + +Some key features are: + +- **Status:** Install, upgrade, uninstall, and enumerate apps +- **Browse**: Allows to retrieve a list of installed apps with filter options +- **Install**: Supports app package, carrier bundle and developer .app directory +- **Format**: Allows command output in plist, XML, or JSON format +- **Compatibility**: Supports latest device firmware releases +- **Cross-Platform:** Tested on Linux, macOS, Windows and Android platforms + +## Installation / Getting started + +### Debian / Ubuntu Linux + +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 \ + libimobiledevice-dev \ + libzip-dev \ + usbmuxd +``` + +Continue with cloning the actual project repository: +```shell +git clone https://github.com/libimobiledevice/ideviceinstaller.git +cd ideviceinstaller +``` + +Now you can build and install it: +```shell +./autogen.sh +make +sudo make install +``` + +## Usage + +First of all attach your device to your machine. + +Then simply run: +```shell +ideviceinstaller list +``` + +This will print a list of `<appid>` identifiers (bundle identifiers) for use +with other commands (see further below). + +To install an app from a package file use: +```shell +ideviceinstaller install <file> +``` + +To uninstall an app with the `<appid>` from the device use: +```shell +ideviceinstaller uninstall <appid> +``` + +Please consult the usage information or manual page for a full documentation of +available command line options: +```shell +ideviceinstaller --help +man ideviceinstaller +``` + +## Contributing + +We welcome contributions from anyone and are grateful for every pull request! + +If you'd like to contribute, please fork the `master` branch, change, commit and +send a pull request for review. Once approved it can be merged into the main +code base. + +If you plan to contribute larger changes or a major refactoring, please create a +ticket first to discuss the idea upfront to ensure less effort for everyone. + +Please make sure your contribution adheres to: +* Try to follow the code style of the project +* Commit messages should describe the change well without being too short +* Try to split larger changes into individual commits of a common domain +* Use your real name and a valid email address for your commits + +We are still working on the guidelines so bear with us! + +## Links + +* Homepage: https://libimobiledevice.org/ +* Repository: https://git.libimobiledevice.org/ideviceinstaller.git +* Repository (Mirror): https://github.com/libimobiledevice/ideviceinstaller.git +* Issue Tracker: https://github.com/libimobiledevice/ideviceinstaller/issues +* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel +* Twitter: https://twitter.com/libimobiledev + +## License + +This software is licensed under the [GNU General Public License v2.0](https://www.gnu.org/licenses/gpl-2.0.en.html), +also included in the repository in the `COPYING` file. + +## Credits + +Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS, +iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. + +ideviceinstaller is an independent software application and has not been +authorized, sponsored or otherwise approved by Apple Inc. + +README Updated on: 2023-07-20 @@ -1,15 +1,26 @@ #!/bin/sh -gprefix=`which glibtoolize 2>&1 >/dev/null` -if [ $? -eq 0 ]; then - glibtoolize --force -else - libtoolize --force -fi -aclocal -I m4 -autoheader -automake --add-missing -autoconf + +olddir=`pwd` +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +( + cd "$srcdir" + + gprefix=`which glibtoolize 2>&1 >/dev/null` + if [ $? -eq 0 ]; then + glibtoolize --force + else + libtoolize --force + fi + aclocal -I m4 + autoheader + automake --add-missing + autoconf + + cd "$olddir" +) if [ -z "$NOCONFIGURE" ]; then - ./configure "$@" + $srcdir/configure "$@" fi diff --git a/configure.ac b/configure.ac index e137301..be124b9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,26 +1,30 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -AC_PREREQ(2.64) -AC_INIT([ideviceinstaller], [1.1.1], [https://github.com/libimobiledevice/ideviceinstaller/issues],, [http://libimobiledevice.org]) +AC_PREREQ([2.68]) +AC_INIT([ideviceinstaller], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/ideviceinstaller/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/]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) +# 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 + # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O -AC_PROG_LIBTOOL +LT_INIT # Checks for libraries. -PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.2.0) -PKG_CHECK_MODULES(libplist, libplist >= 0.15) +PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.3.0) +PKG_CHECK_MODULES(libplist, libplist-2.0 >= 2.3.0) PKG_CHECK_MODULES(libzip, libzip >= 0.10) # Checks for header files. -AC_HEADER_STDC AC_CHECK_HEADERS([stdint.h stdlib.h string.h]) # Checks for typedefs, structures, and compiler characteristics. @@ -39,16 +43,16 @@ AC_CHECK_FUNCS([strdup strerror asprintf vasprintf]) # Check for lstat AC_MSG_CHECKING([whether lstat is available]) -AC_TRY_LINK([ +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> #include <sys/stat.h> #if defined(HAVE_UNISTD_H) #include <unistd.h> #endif -],[ +]],[[ struct stat st; lstat("/tmp", &st); -], [have_lstat="yes"], [have_lstat="no"]) +]])], [have_lstat="yes"], [have_lstat="no"]) AC_MSG_RESULT([${have_lstat}]) if test "x${have_lstat}" = "xyes" ; then @@ -60,9 +64,19 @@ AC_SUBST(GLOBAL_CFLAGS) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) -AC_OUTPUT([ +AC_CONFIG_FILES([ Makefile src/Makefile man/Makefile ]) +AC_OUTPUT + +echo " +Configuration for $PACKAGE $VERSION: +------------------------------------------- + + Install prefix: .........: $prefix + Now type 'make' to build $PACKAGE $VERSION, + and then 'make install' for installation. +" 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/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4 index 0f660cf..baab5d9 100644 --- a/m4/as-compiler-flag.m4 +++ b/m4/as-compiler-flag.m4 @@ -18,7 +18,7 @@ AC_DEFUN([AS_COMPILER_FLAG], save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" - AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then @@ -44,7 +44,7 @@ AC_DEFUN([AS_COMPILER_FLAGS], do save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $each" - AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then diff --git a/man/ideviceinstaller.1 b/man/ideviceinstaller.1 index 401a3d2..0cd0bee 100644 --- a/man/ideviceinstaller.1 +++ b/man/ideviceinstaller.1 @@ -7,102 +7,133 @@ ideviceinstaller \- Manage apps on iOS devices. .SH DESCRIPTION -Allows to install, upgrade, uninstall, archive, restore and enumerate installed -or archived apps on iOS devices. +Allows to enumerate, install, upgrade, and uninstall apps on iOS devices. -.SH OPTIONS - -.SS General options: +.SH COMMANDS .TP -.B \-d, \-\-debug -enable communication debugging. +.B list +List installed apps on the device. Options: +.RS .TP -.B \-u, \-\-udid UDID -target specific device by its 40-digit device UDID. +.B \-\-user +List user apps only (apps installed by the user). +.B This is the default. .TP -.B \-h, \-\-help -prints usage information - -.SS Commands: +.B \-\-system +List system apps only (apps available from the system firmware). .TP -.B \-l, \-\-list-apps -list apps installed on the device. - +.B \-\-all +List all types of apps. +.TP +.B \-\-xml +Print output as XML Property List. +.TP +.B \-a, \-\-attribute ATTR +Specify attribute to return. This argument can be passed multiple times. If omitted and \f[B]\-\-xml\f[] is *not* specified, the default attributes \f[B]CFBundleIdentifier\f[], \f[B]CFBundleShortVersionString\f[], and \f[B]CFBundleDisplayName\f[] will be used. The attributes can be found in the app's Info.plist, but also some extra attributes exist. Some examples: .RS -.B Additional options: .TP -\-o list_user -list user apps only (apps installed by the user) -.B This is the default. +\f[B]StaticDiskUsage\f[] disk usage of installed app +.TP +\f[B]DynamicDiskUsage\f[] app user data disk usage .TP -\-o list_system -list system apps only (apps available from the system firmware) +\f[B]Path\f[] app installation location .TP -\-o list_all -list all types of apps +\f[B]SignerIdentity\f[] code signing identity .TP -\-o xml -print output in xml format (PList) +NOTE: It is suggested to always add CFBundleIdentifier to allow unique identification of the apps. .RE - .TP -.B \-i, \-\-install ARCHIVE -install app from a package file specified by ARCHIVE. ARCHIVE can also be a -.ipcc file for carrier bundle installation or a .app directory for developer +.B \-b, \-\-bundle\-identifier BUNDLEID +Only query given bundle identifier. This argument can be passed multiple times. +.RE +.TP +.B install PATH +Install app from a package file specified by PATH. PATH can also be a .ipcc +file for carrier bundle installation or a .app directory for developer app installation. +.RS +.TP +.B \-s, \-\-sinf PATH +Pass an external SINF file located at PATH. +.TP +.B \-m, \-\-metadata PATH +Pass an external iTunesMetadata file located at PATH. +.RE .TP -.B \-U, \-\-uninstall APPID -uninstall app specified by APPID. +.B uninstall BUNDLEID +Uninstall app specified by BUNDLEID. .TP -.B \-g, \-\-upgrade ARCHIVE -upgrade app from a package file specified by ARCHIVE. +.B upgrade PATH +Upgrade app from a package file specified by PATH. +.SH LEGACY COMMANDS +The following commands are non-functional with iOS 7 or later. +.TP +.B archive BUNDLEID +Archive app specified by BUNDLEID. Options: +.RS +.TP +.B \-\-uninstall +Uninstall the package after making an archive +.TP +.B \-\-app_only +Archive application data only .TP -.B \-r, \-\-restore APPID -restore archived app specified by APPID. +.B \-\-docs_only +Archive documents (user data) only +.TP +.B \-\-copy=PATH +Copy the app archive to directory PATH when done +.TP +.B \-\-remove +Only valid when copy=PATH is used: remove after copy +.RE .TP -.B \-L, \-\-list-archives -list archived applications on the device. +.B restore BUNDLEID +Restore archived app specified by BUNDLEID. +.TP +.B list-archives +List archived apps on the device. Options: .RS -.B Additional options: .TP -\-o xml -print output in xml format (PList) +.B \-\-xml +Print output as XML Property List. .RE .TP -.B \-a, \-\-archive APPID -archive app specified by APPID. +.B remove-archive BUNDLEID +Remove app archive specified by BUNDLEID. -.RS -.B Additional options: +.SH OPTIONS .TP -\-o uninstall -uninstall the package after making an archive +.B \-u, \-\-udid UDID +Target specific device by UDID. .TP -\-o app_only -archive application data only +.B \-n, \-\-network +Connect to network device. .TP -\-o docs_only -archive documents (user data) only +.B \-w, \-\-notify-wait +Wait for app installed/uninstalled notification before reporting success of operation. .TP -\-o copy=PATH -copy the app archive to directory PATH when done +.B \-h, \-\-help +Print usage information. .TP -\-o remove -only valid when copy=PATH is used: remove after copy -.RE - +.B \-d, \-\-debug +Enable communication debugging. .TP -.B \-R, \-\-remove-archive APPID -remove app archive specified by APPID. +.B \-v, \-\-version +Print version information. -.SH AUTHOR -This manual page was written by Martin Szulecki. +.SH AUTHORS +Nikias Bassen + +Martin Szulecki .SH ON THE WEB -http://libimobiledevice.org +https://libimobiledevice.org + +https://github.com/libimobiledevice/ideviceinstaller diff --git a/src/ideviceinstaller.c b/src/ideviceinstaller.c index 27b4669..c50bacf 100644 --- a/src/ideviceinstaller.c +++ b/src/ideviceinstaller.c @@ -1,7 +1,7 @@ /* * ideviceinstaller - Manage apps on iOS devices. * - * Copyright (C) 2010-2019 Nikias Bassen <nikias@gmx.li> + * Copyright (C) 2010-2023 Nikias Bassen <nikias@gmx.li> * Copyright (C) 2010-2015 Martin Szulecki <m.szulecki@libimobiledevice.org> * * Licensed under the GNU General Public License Version 2 @@ -40,6 +40,9 @@ #ifdef HAVE_UNISTD_H #include <unistd.h> #endif +#ifndef WIN32 +#include <signal.h> +#endif #include <libimobiledevice/libimobiledevice.h> #include <libimobiledevice/lockdown.h> @@ -90,8 +93,9 @@ const char PKG_PATH[] = "PublicStaging"; const char APPARCH_PATH[] = "ApplicationArchives"; char *udid = NULL; -char *options = NULL; -char *appid = NULL; +char *cmdarg = NULL; +char *extsinf = NULL; +char *extmeta = NULL; enum cmd_mode { CMD_NONE = 0, @@ -109,62 +113,88 @@ int cmd = CMD_NONE; char *last_status = NULL; int wait_for_command_complete = 0; +int use_network = 0; int use_notifier = 0; int notification_expected = 0; int is_device_connected = 0; int command_completed = 0; +int ignore_events = 0; int err_occurred = 0; int notified = 0; +plist_t bundle_ids = NULL; +plist_t return_attrs = NULL; +#define FORMAT_XML 1 +#define FORMAT_JSON 2 +int output_format = 0; +int opt_list_user = 0; +int opt_list_system = 0; +char *copy_path = NULL; +int remove_after_copy = 0; +int skip_uninstall = 1; +int app_only = 0; +int docs_only = 0; static void print_apps_header() { - /* output app details header */ - printf("%s", "CFBundleIdentifier"); - printf(", %s", "CFBundleVersion"); - printf(", %s", "CFBundleDisplayName"); + if (!return_attrs) { + return; + } + uint32_t i = 0; + for (i = 0; i < plist_array_get_size(return_attrs); i++) { + plist_t node = plist_array_get_item(return_attrs, i); + if (i > 0) { + printf(", "); + } + printf("%s", plist_get_string_ptr(node, NULL)); + } printf("\n"); } static void print_apps(plist_t apps) { + if (!return_attrs) { + return; + } uint32_t i = 0; for (i = 0; i < plist_array_get_size(apps); i++) { plist_t app = plist_array_get_item(apps, i); - plist_t p_bundle_identifier = plist_dict_get_item(app, "CFBundleIdentifier"); - char *s_bundle_identifier = NULL; - char *s_display_name = NULL; - char *s_version = NULL; - plist_t display_name = plist_dict_get_item(app, "CFBundleDisplayName"); - plist_t version = plist_dict_get_item(app, "CFBundleVersion"); - - if (p_bundle_identifier) { - plist_get_string_val(p_bundle_identifier, &s_bundle_identifier); - } - if (!s_bundle_identifier) { - fprintf(stderr, "ERROR: Failed to get APPID!\n"); - break; - } - - if (version) { - plist_get_string_val(version, &s_version); - } - if (display_name) { - plist_get_string_val(display_name, &s_display_name); - } - if (!s_display_name) { - s_display_name = strdup(s_bundle_identifier); - } - - /* output app details */ - printf("%s", s_bundle_identifier); - if (s_version) { - printf(", \"%s\"", s_version); - free(s_version); + uint32_t j = 0; + for (j = 0; j < plist_array_get_size(return_attrs); j++) { + plist_t node = plist_array_get_item(return_attrs, j); + if (j > 0) { + printf(", "); + } + const char* key = plist_get_string_ptr(node, NULL); + node = plist_dict_get_item(app, key); + if (node) { + if (!strcmp(key, "CFBundleIdentifier")) { + printf("%s", plist_get_string_ptr(node, NULL)); + } else { + uint64_t uval = 0; + switch (plist_get_node_type(node)) { + case PLIST_STRING: + printf("\"%s\"", plist_get_string_ptr(node, NULL)); + break; + case PLIST_INT: + plist_get_uint_val(node, &uval); + printf("%" PRIu64, uval); + break; + case PLIST_BOOLEAN: + printf("%s", plist_bool_val_is_true(node) ? "true" : "false"); + break; + case PLIST_ARRAY: + printf("(array)"); + break; + case PLIST_DICT: + printf("(dict)"); + break; + default: + break; + } + } + } } - printf(", \"%s\"", s_display_name); printf("\n"); - free(s_display_name); - free(s_bundle_identifier); } } @@ -294,8 +324,8 @@ static int zip_get_contents(struct zip *zf, const char *filename, int locate_fla static int zip_get_app_directory(struct zip* zf, char** path) { - int i = 0; - int c = zip_get_num_files(zf); + zip_int64_t i = 0; + zip_int64_t c = (zip_int64_t)zip_get_num_entries(zf, 0); int len = 0; const char* name = NULL; @@ -325,6 +355,12 @@ static int zip_get_app_directory(struct zip* zf, char** path) len = p - name + 1; + /* make sure app directory endwith .app */ + if (len < 12 || strncmp(p - 4, ".app", 4)) + { + continue; + } + if (path != NULL) { free(*path); *path = NULL; @@ -342,11 +378,18 @@ static int zip_get_app_directory(struct zip* zf, char** path) } } while(i < c); + if (*path == NULL) { + return -1; + } + return 0; } static void idevice_event_callback(const idevice_event_t* event, void* userdata) { + if (ignore_events) { + return; + } if (event->event == IDEVICE_DEVICE_REMOVE) { if (!strcmp(udid, event->udid)) { fprintf(stderr, "ideviceinstaller: Device removed\n"); @@ -358,6 +401,7 @@ static void idevice_event_callback(const idevice_event_t* event, void* userdata) static void idevice_wait_for_command_to_complete() { is_device_connected = 1; + ignore_events = 0; /* subscribe to make sure to exit on device removal */ idevice_event_subscribe(idevice_event_callback, NULL); @@ -373,165 +417,266 @@ static void idevice_wait_for_command_to_complete() wait_ms(50); } + ignore_events = 1; idevice_event_unsubscribe(); } -static void print_usage(int argc, char **argv) +static void print_usage(int argc, char **argv, int is_error) { - char *name = NULL; - - name = strrchr(argv[0], '/'); - printf("Usage: %s OPTIONS\n", (name ? name + 1 : argv[0])); - printf("Manage apps on iOS devices.\n\n"); - printf - (" -u, --udid UDID\tTarget specific device by UDID.\n" - " -l, --list-apps\tList apps, possible options:\n" - " -o list_user\t- list user apps only (this is the default)\n" - " -o list_system\t- list system apps only\n" - " -o list_all\t- list all types of apps\n" - " -o xml\t\t- print full output as xml plist\n" - " -i, --install ARCHIVE\tInstall app from package file specified by ARCHIVE.\n" - " \tARCHIVE can also be a .ipcc file for carrier bundles.\n" - " -U, --uninstall APPID\tUninstall app specified by APPID.\n" - " -g, --upgrade ARCHIVE\tUpgrade app from package file specified by ARCHIVE.\n" - " -L, --list-archives\tList archived applications, possible options:\n" - " -o xml\t\t- print full output as xml plist\n" - " -a, --archive APPID\tArchive app specified by APPID, possible options:\n" - " -o uninstall\t- uninstall the package after making an archive\n" - " -o app_only\t- archive application data only\n" - " -o docs_only\t- archive documents (user data) only\n" - " -o copy=PATH\t- copy the app archive to directory PATH when done\n" - " -o remove\t- only valid when copy=PATH is used: remove after copy\n" - " -r, --restore APPID\tRestore archived app specified by APPID\n" - " -R, --remove-archive APPID Remove app archive specified by APPID\n" - " -o, --options\t\tPass additional options to the specified command.\n" - " -n, --notify-wait\t\tWait for app installed/uninstalled notification\n" - " \t\tto before reporting success of operation\n" - " -h, --help\t\tprints usage information\n" - " -d, --debug\t\tenable communication debugging\n" "\n"); - printf("Homepage: <http://libimobiledevice.org>\n"); + char *name = strrchr(argv[0], '/'); + fprintf((is_error) ? stderr : stdout, "Usage: %s OPTIONS\n", (name ? name + 1 : argv[0])); + fprintf((is_error) ? stderr : stdout, + "\n" + "Manage apps on iOS devices.\n" + "\n" + "COMMANDS:\n" + " list List installed apps. Options:\n" + " --user List user apps only (this is the default)\n" + " --system List system apps only\n" + " --all List all types of apps\n" + " --xml Print output as XML Property List\n" + " -a, --attribute ATTR Specify attribute to return - see man page\n" + " (can be passed multiple times)\n" + " -b, --bundle-identifier BUNDLEID Only query given bundle identifier\n" + " (can be passed multiple times)\n" + " install PATH Install app from package file specified by PATH.\n" + " PATH can also be a .ipcc file for carrier bundles.\n" + " -s, --sinf PATH Pass an external SINF file\n" + " -m, --metadata PATH Pass an external iTunesMetadata file\n" + " uninstall BUNDLEID Uninstall app specified by BUNDLEID.\n" + " upgrade PATH Upgrade app from package file specified by PATH.\n" + "\n" + "LEGACY COMMANDS (non-functional with iOS 7 or later):\n" + " archive BUNDLEID Archive app specified by BUNDLEID. Options:\n" + " --uninstall Uninstall the package after making an archive\n" + " --app-only Archive application data only\n" + " --docs-only Archive documents (user data) only\n" + " --copy=PATH Copy the app archive to directory PATH when done\n" + " --remove Only valid when copy=PATH is used: remove after copy\n" + " restore BUNDLEID Restore archived app specified by BUNDLEID\n" + " list-archives List archived apps. Options:\n" + " --xml Print output as XML Property List\n" + " remove-archive BUNDLEID Remove app archive specified by BUNDLEID\n" + "\n" + "OPTIONS:\n" + " -u, --udid UDID Target specific device by UDID\n" + " -n, --network Connect to network device\n" + " -w, --notify-wait Wait for app installed/uninstalled notification\n" + " before reporting success of operation\n" + " -h, --help Print usage information\n" + " -d, --debug Enable communication debugging\n" + " -v, --version Print version information\n" + "\n" + "Homepage: <" PACKAGE_URL ">\n" + "Bug Reports: <" PACKAGE_BUGREPORT ">\n" + ); } +enum numerical_opts { + LIST_USER = 1, + LIST_SYSTEM, + LIST_ALL, + ARCHIVE_UNINSTALL, + ARCHIVE_APP_ONLY, + ARCHIVE_DOCS_ONLY, + ARCHIVE_COPY_PATH, + ARCHIVE_COPY_REMOVE, + OUTPUT_XML, + OUTPUT_JSON +}; + static void parse_opts(int argc, char **argv) { static struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "udid", required_argument, NULL, 'u' }, - { "list-apps", no_argument, NULL, 'l' }, - { "install", required_argument, NULL, 'i' }, - { "uninstall", required_argument, NULL, 'U' }, - { "upgrade", required_argument, NULL, 'g' }, - { "list-archives", no_argument, NULL, 'L' }, - { "archive", required_argument, NULL, 'a' }, - { "restore", required_argument, NULL, 'r' }, - { "remove-archive", required_argument, NULL, 'R' }, - { "options", required_argument, NULL, 'o' }, - { "notify-wait", no_argument, NULL, 'n' }, + { "network", no_argument, NULL, 'n' }, + { "notify-wait", no_argument, NULL, 'w' }, { "debug", no_argument, NULL, 'd' }, + { "version", no_argument, NULL, 'v' }, + { "bundle-identifier", required_argument, NULL, 'b' }, + { "attribute", required_argument, NULL, 'a' }, + { "user", no_argument, NULL, LIST_USER }, + { "system", no_argument, NULL, LIST_SYSTEM }, + { "all", no_argument, NULL, LIST_ALL }, + { "xml", no_argument, NULL, OUTPUT_XML }, + { "json", no_argument, NULL, OUTPUT_JSON }, + { "sinf", required_argument, NULL, 's' }, + { "metadata", required_argument, NULL, 'm' }, + { "uninstall", no_argument, NULL, ARCHIVE_UNINSTALL }, + { "app-only", no_argument, NULL, ARCHIVE_APP_ONLY }, + { "docs-only", no_argument, NULL, ARCHIVE_DOCS_ONLY }, + { "copy", required_argument, NULL, ARCHIVE_COPY_PATH }, + { "remove", no_argument, NULL, ARCHIVE_COPY_REMOVE }, { NULL, 0, NULL, 0 } }; int c; while (1) { - c = getopt_long(argc, argv, "hU:li:u:g:La:r:R:o:nd", longopts, - (int *) 0); + c = getopt_long(argc, argv, "hu:nwdvb:a:s:m:", longopts, (int*)0); if (c == -1) { break; } - /* verify if multiple modes have been supplied */ - switch (c) { - case 'l': - case 'i': - case 'g': - case 'L': - case 'a': - case 'r': - case 'R': - if (cmd != CMD_NONE) { - printf("ERROR: A mode has already been supplied. Multiple modes are not supported.\n"); - print_usage(argc, argv); - exit(2); - } - break; - default: - break; - } - switch (c) { case 'h': - print_usage(argc, argv); + print_usage(argc, argv, 0); exit(0); case 'u': if (!*optarg) { printf("ERROR: UDID must not be empty!\n"); - print_usage(argc, argv); + print_usage(argc, argv, 1); exit(2); } udid = strdup(optarg); break; - case 'l': - cmd = CMD_LIST_APPS; - break; - case 'i': - cmd = CMD_INSTALL; - appid = strdup(optarg); - break; - case 'U': - cmd = CMD_UNINSTALL; - appid = strdup(optarg); - break; - case 'g': - cmd = CMD_UPGRADE; - appid = strdup(optarg); - break; - case 'L': - cmd = CMD_LIST_ARCHIVES; + case 'n': + use_network = 1; break; case 'a': - cmd = CMD_ARCHIVE; - appid = strdup(optarg); + if (!*optarg) { + printf("ERROR: attribute must not be empty!\n"); + print_usage(argc, argv, 1); + exit(2); + } + if (return_attrs == NULL) { + return_attrs = plist_new_array(); + } + plist_array_append_item(return_attrs, plist_new_string(optarg)); break; - case 'r': - cmd = CMD_RESTORE; - appid = strdup(optarg); + case 'b': + if (!*optarg) { + printf("ERROR: bundle identifier must not be empty!\n"); + print_usage(argc, argv, 1); + exit(2); + } + if (bundle_ids == NULL) { + bundle_ids = plist_new_array(); + } + plist_array_append_item(bundle_ids, plist_new_string(optarg)); break; - case 'R': - cmd = CMD_REMOVE_ARCHIVE; - appid = strdup(optarg); + case 's': + if (!*optarg) { + printf("ERROR: path for --sinf must not be empty!\n"); + print_usage(argc, argv, 1); + exit(2); + } + extsinf = strdup(optarg); break; - case 'o': - if (!options) { - options = strdup(optarg); - } else { - char *newopts = malloc(strlen(options) + strlen(optarg) + 2); - strcpy(newopts, options); - free(options); - strcat(newopts, ","); - strcat(newopts, optarg); - options = newopts; + case 'm': + if (!*optarg) { + printf("ERROR: path for --metadata must not be empty!\n"); + print_usage(argc, argv, 1); + exit(2); } + extmeta = strdup(optarg); break; - case 'n': + case 'w': use_notifier = 1; break; case 'd': idevice_set_debug_level(1); break; + case 'v': + printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + exit(0); + case LIST_USER: + opt_list_user = 1; + break; + case LIST_SYSTEM: + opt_list_system = 1; + break; + case LIST_ALL: + opt_list_user = 1; + opt_list_system = 1; + break; + case OUTPUT_XML: + output_format = FORMAT_XML; + break; + case OUTPUT_JSON: + output_format = FORMAT_JSON; + break; + case ARCHIVE_UNINSTALL: + skip_uninstall = 0; + break; + case ARCHIVE_APP_ONLY: + app_only = 1; + docs_only = 0; + break; + case ARCHIVE_DOCS_ONLY: + docs_only = 1; + app_only = 0; + break; + case ARCHIVE_COPY_PATH: + copy_path = strdup(optarg); + break; + case ARCHIVE_COPY_REMOVE: + remove_after_copy = 1; + break; default: - print_usage(argc, argv); + print_usage(argc, argv, 1); exit(2); } } - if (cmd == CMD_NONE) { - printf("ERROR: No mode/command was supplied.\n"); - } + argv += optind; + argc -= optind; - if (cmd == CMD_NONE || optind <= 1 || (argc - optind > 0)) { - print_usage(argc, argv); + if (argc == 0) { + fprintf(stderr, "ERROR: Missing command.\n\n"); + print_usage(argc+optind, argv-optind, 1); exit(2); } + + char *cmdstr = argv[0]; + + if (!strcmp(cmdstr, "list")) { + cmd = CMD_LIST_APPS; + } else if (!strcmp(cmdstr, "install")) { + cmd = CMD_INSTALL; + } else if (!strcmp(cmdstr, "upgrade")) { + cmd = CMD_UPGRADE; + } else if (!strcmp(cmdstr, "uninstall") || !strcmp(cmdstr, "remove")) { + cmd = CMD_UNINSTALL; + } else if (!strcmp(cmdstr, "archives") || !strcmp(cmdstr, "list-archives")) { + cmd = CMD_LIST_ARCHIVES; + } else if (!strcmp(cmdstr, "archive")) { + cmd = CMD_ARCHIVE; + } else if (!strcmp(cmdstr, "restore")) { + cmd = CMD_RESTORE; + } else if (!strcmp(cmdstr, "remove-archive")) { + cmd = CMD_REMOVE_ARCHIVE; + } + + switch (cmd) { + case CMD_LIST_APPS: + case CMD_LIST_ARCHIVES: + break; + case CMD_INSTALL: + case CMD_UPGRADE: + if (argc < 2) { + fprintf(stderr, "ERROR: Missing filename for '%s' command.\n\n", cmdstr); + print_usage(argc+optind, argv-optind, 1); + exit(2); + } + cmdarg = argv[1]; + break; + case CMD_UNINSTALL: + case CMD_ARCHIVE: + case CMD_RESTORE: + case CMD_REMOVE_ARCHIVE: + if (argc < 2) { + fprintf(stderr, "ERROR: Missing bundle ID for '%s' command.\n\n", cmdstr); + print_usage(argc+optind, argv-optind, 1); + exit(2); + } + cmdarg = argv[1]; + break; + default: + fprintf(stderr, "ERROR: Invalid command '%s'.\n\n", cmdstr); + print_usage(argc+optind, argv-optind, 1); + exit(2); + } } static int afc_upload_file(afc_client_t afc, const char* filename, const char* dstfn) @@ -542,7 +687,7 @@ static int afc_upload_file(afc_client_t afc, const char* filename, const char* d f = fopen(filename, "rb"); if (!f) { - fprintf(stderr, "fopen: %s: %s\n", appid, strerror(errno)); + fprintf(stderr, "fopen: %s: %s\n", filename, strerror(errno)); return -1; } @@ -629,6 +774,37 @@ static void afc_upload_dir(afc_client_t afc, const char* path, const char* afcpa } } +static char *buf_from_file(const char *filename, size_t *size) +{ + struct stat st; + FILE *fp = NULL; + + if (stat(filename, &st) == -1 || (fp = fopen(filename, "r")) == NULL) { + return NULL; + } + size_t filesize = st.st_size; + if (filesize == 0) { + return NULL; + } + char *ibuf = malloc(filesize * sizeof(char)); + if (ibuf == NULL) { + return NULL; + } + size_t amount = fread(ibuf, 1, filesize, fp); + if (amount != filesize) { + fprintf(stderr, "ERROR: could not read %" PRIu64 " bytes from %s\n", (uint64_t)filesize, filename); + free(ibuf); + return NULL; + } + fclose(fp); + + if (size) { + *size = filesize; + } + + return ibuf; +} + int main(int argc, char **argv) { idevice_t device = NULL; @@ -638,36 +814,40 @@ int main(int argc, char **argv) np_client_t np = NULL; afc_client_t afc = NULL; lockdownd_service_descriptor_t service = NULL; - int res = 0; + int res = EXIT_FAILURE; char *bundleidentifier = NULL; +#ifndef WIN32 + signal(SIGPIPE, SIG_IGN); +#endif parse_opts(argc, argv); argc -= optind; argv += optind; - if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) { - fprintf(stderr, "No iOS device found, is it plugged in?\n"); - return -1; + if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) { + if (udid) { + fprintf(stderr, "No device found with udid %s.\n", udid); + } else { + fprintf(stderr, "No device found.\n"); + } + return EXIT_FAILURE; } if (!udid) { idevice_get_udid(device, &udid); } - if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &client, "ideviceinstaller")) { - fprintf(stderr, "Could not connect to lockdownd. Exiting.\n"); - res = -1; + lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &client, "ideviceinstaller"); + if (lerr != LOCKDOWN_E_SUCCESS) { + fprintf(stderr, "Could not connect to lockdownd: %s. Exiting.\n", lockdownd_strerror(lerr)); goto leave_cleanup; } if (use_notifier) { - if ((lockdownd_start_service - (client, "com.apple.mobile.notification_proxy", - &service) != LOCKDOWN_E_SUCCESS) || !service) { - fprintf(stderr, - "Could not start com.apple.mobile.notification_proxy!\n"); - res = -1; + lerr =lockdownd_start_service(client, "com.apple.mobile.notification_proxy", &service); + if (lerr != LOCKDOWN_E_SUCCESS) { + fprintf(stderr, "Could not start com.apple.mobile.notification_proxy: %s\n", lockdownd_strerror(lerr)); goto leave_cleanup; } @@ -680,7 +860,6 @@ int main(int argc, char **argv) if (nperr != NP_E_SUCCESS) { fprintf(stderr, "Could not connect to notification_proxy!\n"); - res = -1; goto leave_cleanup; } @@ -697,11 +876,9 @@ run_again: } service = NULL; - if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy", - &service) != LOCKDOWN_E_SUCCESS) || !service) { - fprintf(stderr, - "Could not start com.apple.mobile.installation_proxy!\n"); - res = -1; + lerr = lockdownd_start_service(client, "com.apple.mobile.installation_proxy", &service); + if (lerr != LOCKDOWN_E_SUCCESS) { + fprintf(stderr, "Could not start com.apple.mobile.installation_proxy: %s\n", lockdownd_strerror(lerr)); goto leave_cleanup; } @@ -714,7 +891,6 @@ run_again: if (err != INSTPROXY_E_SUCCESS) { fprintf(stderr, "Could not connect to installation_proxy!\n"); - res = -1; goto leave_cleanup; } @@ -726,60 +902,89 @@ run_again: notification_expected = 0; if (cmd == CMD_LIST_APPS) { - int xml_mode = 0; plist_t client_opts = instproxy_client_options_new(); instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL); plist_t apps = NULL; - /* look for options */ - if (options) { - char *opts = strdup(options); - char *elem = strtok(opts, ","); - while (elem) { - if (!strcmp(elem, "list_system")) { - instproxy_client_options_add(client_opts, "ApplicationType", "System", NULL); - } else if (!strcmp(elem, "list_all")) { - plist_dict_remove_item(client_opts, "ApplicationType"); - } else if (!strcmp(elem, "list_user")) { - /* do nothing, we're already set */ - } else if (!strcmp(elem, "xml")) { - xml_mode = 1; - } - elem = strtok(NULL, ","); - } - free(opts); + if (opt_list_system && opt_list_user) { + plist_dict_remove_item(client_opts, "ApplicationType"); + } else if (opt_list_system) { + instproxy_client_options_add(client_opts, "ApplicationType", "System", NULL); + } else if (opt_list_user) { + instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL); + } + + if (bundle_ids) { + plist_dict_set_item(client_opts, "BundleIDs", plist_copy(bundle_ids)); } - if (!xml_mode) { - instproxy_client_options_set_return_attributes(client_opts, - "CFBundleIdentifier", - "CFBundleDisplayName", - "CFBundleVersion", - "StaticDiskUsage", - "DynamicDiskUsage", - NULL - ); + if (!output_format && !return_attrs) { + return_attrs = plist_new_array(); + plist_array_append_item(return_attrs, plist_new_string("CFBundleIdentifier")); + plist_array_append_item(return_attrs, plist_new_string("CFBundleShortVersionString")); + plist_array_append_item(return_attrs, plist_new_string("CFBundleDisplayName")); } - if (xml_mode) { + if (return_attrs) { + instproxy_client_options_add(client_opts, "ReturnAttributes", return_attrs, NULL); + } + + if (output_format) { err = instproxy_browse(ipc, client_opts, &apps); if (!apps || (plist_get_node_type(apps) != PLIST_ARRAY)) { - fprintf(stderr, - "ERROR: instproxy_browse returnd an invalid plist!\n"); - res = -1; + fprintf(stderr, "ERROR: instproxy_browse returnd an invalid plist!\n"); goto leave_cleanup; } - - char *xml = NULL; + char *buf = NULL; uint32_t len = 0; - - plist_to_xml(apps, &xml, &len); - if (xml) { - puts(xml); - free(xml); + if (output_format == FORMAT_XML) { + plist_err_t perr = plist_to_xml(apps, &buf, &len); + if (perr != PLIST_ERR_SUCCESS) { + fprintf(stderr, "ERROR: Failed to convert data to XML format (%d).\n", perr); + } + } else if (output_format == FORMAT_JSON) { + /* for JSON, we need to convert some stuff since it doesn't support PLIST_DATA nodes */ + plist_array_iter aiter = NULL; + plist_array_new_iter(apps, &aiter); + plist_t entry = NULL; + do { + plist_array_next_item(apps, aiter, &entry); + if (!entry) break; + plist_t items = plist_dict_get_item(entry, "UIApplicationShortcutItems"); + plist_array_iter inner = NULL; + plist_array_new_iter(items, &inner); + plist_t item = NULL; + do { + plist_array_next_item(items, inner, &item); + if (!item) break; + plist_t userinfo = plist_dict_get_item(item, "UIApplicationShortcutItemUserInfo"); + if (userinfo) { + plist_t data_node = plist_dict_get_item(userinfo, "data"); + + if (data_node) { + char *strbuf = NULL; + uint32_t buflen = 0; + plist_write_to_string(data_node, &strbuf, &buflen, PLIST_FORMAT_LIMD, PLIST_OPT_NO_NEWLINE); + plist_set_string_val(data_node, strbuf); + free(strbuf); + } + } + } while (item); + free(inner); + } while (entry); + free(aiter); + plist_err_t perr = plist_to_json(apps, &buf, &len, 1); + if (perr != PLIST_ERR_SUCCESS) { + fprintf(stderr, "ERROR: Failed to convert data to JSON format (%d).\n", perr); + } + } + if (buf) { + puts(buf); + free(buf); } plist_free(apps); + res = 0; goto leave_cleanup; } @@ -793,7 +998,6 @@ run_again: instproxy_client_options_free(client_opts); if (err != INSTPROXY_E_SUCCESS) { fprintf(stderr, "ERROR: instproxy_browse returned %d\n", err); - res = -1; goto leave_cleanup; } @@ -810,10 +1014,9 @@ run_again: lockdownd_service_descriptor_free(service); service = NULL; - if ((lockdownd_start_service(client, "com.apple.afc", &service) != - LOCKDOWN_E_SUCCESS) || !service) { - fprintf(stderr, "Could not start com.apple.afc!\n"); - res = -1; + lerr = lockdownd_start_service(client, "com.apple.afc", &service); + if (lerr != LOCKDOWN_E_SUCCESS) { + fprintf(stderr, "Could not start com.apple.afc: %s\n", lockdownd_strerror(lerr)); goto leave_cleanup; } @@ -822,13 +1025,11 @@ run_again: if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) { fprintf(stderr, "Could not connect to AFC!\n"); - res = -1; goto leave_cleanup; } - if (stat(appid, &fst) != 0) { - fprintf(stderr, "ERROR: stat: %s: %s\n", appid, strerror(errno)); - res = -1; + if (stat(cmdarg, &fst) != 0) { + fprintf(stderr, "ERROR: stat: %s: %s\n", cmdarg, strerror(errno)); goto leave_cleanup; } @@ -853,15 +1054,14 @@ run_again: int errp = 0; struct zip *zf = NULL; - if ((strlen(appid) > 5) && (strcmp(&appid[strlen(appid)-5], ".ipcc") == 0)) { - zf = zip_open(appid, 0, &errp); + if ((strlen(cmdarg) > 5) && (strcmp(&cmdarg[strlen(cmdarg)-5], ".ipcc") == 0)) { + zf = zip_open(cmdarg, 0, &errp); if (!zf) { - fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp); - res = -1; + fprintf(stderr, "ERROR: zip_open: %s: %d\n", cmdarg, errp); goto leave_cleanup; } - char* ipcc = strdup(appid); + char* ipcc = strdup(cmdarg); if ((asprintf(&pkgname, "%s/%s", PKG_PATH, basename(ipcc)) > 0) && pkgname) { afc_make_directory(afc, pkgname); } @@ -869,8 +1069,8 @@ run_again: printf("Uploading %s package contents... ", basename(ipcc)); /* extract the contents of the .ipcc file to PublicStaging/<name>.ipcc directory */ - zip_uint64_t numzf = zip_get_num_entries(zf, 0); - zip_uint64_t i = 0; + zip_int64_t numzf = (zip_int64_t)zip_get_num_entries(zf, 0); + zip_int64_t i = 0; for (i = 0; numzf > 0 && i < numzf; i++) { const char* zname = zip_get_name(zf, i, 0); char* dstpath = NULL; @@ -930,7 +1130,6 @@ run_again: afc_file_close(afc, af); zip_fclose(zfile); free(dstpath); - res = -1; goto leave_cleanup; } } @@ -952,21 +1151,20 @@ run_again: /* upload developer app directory */ instproxy_client_options_add(client_opts, "PackageType", "Developer", NULL); - if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(appid)) < 0) { + if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(cmdarg)) < 0) { fprintf(stderr, "ERROR: Out of memory allocating pkgname!?\n"); - res = -1; goto leave_cleanup; } - printf("Uploading %s package contents... ", basename(appid)); - afc_upload_dir(afc, appid, pkgname); + printf("Uploading %s package contents... ", basename(cmdarg)); + afc_upload_dir(afc, cmdarg, pkgname); printf("DONE.\n"); /* extract the CFBundleIdentifier from the package */ /* construct full filename to Info.plist */ - char *filename = (char*)malloc(strlen(appid)+11+1); - strcpy(filename, appid); + char *filename = (char*)malloc(strlen(cmdarg)+11+1); + strcpy(filename, cmdarg); strcat(filename, "/Info.plist"); struct stat st; @@ -975,7 +1173,6 @@ run_again: if (stat(filename, &st) == -1 || (fp = fopen(filename, "r")) == NULL) { fprintf(stderr, "ERROR: could not locate %s in app!\n", filename); free(filename); - res = -1; goto leave_cleanup; } size_t filesize = st.st_size; @@ -984,23 +1181,17 @@ run_again: if (amount != filesize) { fprintf(stderr, "ERROR: could not read %u bytes from %s\n", (uint32_t)filesize, filename); free(filename); - res = -1; goto leave_cleanup; } fclose(fp); free(filename); plist_t info = NULL; - if (memcmp(ibuf, "bplist00", 8) == 0) { - plist_from_bin(ibuf, filesize, &info); - } else { - plist_from_xml(ibuf, filesize, &info); - } + plist_from_memory(ibuf, filesize, &info, NULL); free(ibuf); if (!info) { fprintf(stderr, "ERROR: could not parse Info.plist!\n"); - res = -1; goto leave_cleanup; } @@ -1011,28 +1202,45 @@ run_again: plist_free(info); info = NULL; } else { - zf = zip_open(appid, 0, &errp); + zf = zip_open(cmdarg, 0, &errp); if (!zf) { - fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp); - res = -1; + fprintf(stderr, "ERROR: zip_open: %s: %d\n", cmdarg, errp); goto leave_cleanup; } - /* extract iTunesMetadata.plist from package */ char *zbuf = NULL; uint32_t len = 0; plist_t meta_dict = NULL; - if (zip_get_contents(zf, ITUNES_METADATA_PLIST_FILENAME, 0, &zbuf, &len) == 0) { - meta = plist_new_data(zbuf, len); - if (memcmp(zbuf, "bplist00", 8) == 0) { - plist_from_bin(zbuf, len, &meta_dict); - } else { - plist_from_xml(zbuf, len, &meta_dict); + + if (extmeta) { + size_t flen = 0; + zbuf = buf_from_file(extmeta, &flen); + if (zbuf && flen) { + meta = plist_new_data(zbuf, flen); + plist_from_memory(zbuf, flen, &meta_dict, NULL); + free(zbuf); } - } else { - fprintf(stderr, "WARNING: could not locate %s in archive!\n", ITUNES_METADATA_PLIST_FILENAME); + if (!meta_dict) { + plist_free(meta); + meta = NULL; + fprintf(stderr, "WARNING: could not load external iTunesMetadata %s!\n", extmeta); + } + zbuf = NULL; + } + + if (!meta && !meta_dict) { + /* extract iTunesMetadata.plist from package */ + if (zip_get_contents(zf, ITUNES_METADATA_PLIST_FILENAME, 0, &zbuf, &len) == 0) { + meta = plist_new_data(zbuf, len); + plist_from_memory(zbuf, len, &meta_dict, NULL); + } + if (!meta_dict) { + plist_free(meta); + meta = NULL; + fprintf(stderr, "WARNING: could not locate %s in archive!\n", ITUNES_METADATA_PLIST_FILENAME); + } + free(zbuf); } - free(zbuf); /* determine .app directory in archive */ zbuf = NULL; @@ -1042,8 +1250,7 @@ run_again: char* app_directory_name = NULL; if (zip_get_app_directory(zf, &app_directory_name)) { - fprintf(stderr, "Unable to locate app directory in archive!\n"); - res = -1; + fprintf(stderr, "ERROR: Unable to locate .app directory in archive. Make sure it is inside a 'Payload' directory.\n"); goto leave_cleanup; } @@ -1059,22 +1266,16 @@ run_again: free(filename); zip_unchange_all(zf); zip_close(zf); - res = -1; goto leave_cleanup; } free(filename); - if (memcmp(zbuf, "bplist00", 8) == 0) { - plist_from_bin(zbuf, len, &info); - } else { - plist_from_xml(zbuf, len, &info); - } + plist_from_memory(zbuf, len, &info, NULL); free(zbuf); if (!info) { fprintf(stderr, "Could not parse Info.plist!\n"); zip_unchange_all(zf); zip_close(zf); - res = -1; goto leave_cleanup; } @@ -1096,40 +1297,52 @@ run_again: fprintf(stderr, "Could not determine value for CFBundleExecutable!\n"); zip_unchange_all(zf); zip_close(zf); - res = -1; goto leave_cleanup; } - char *sinfname = NULL; - if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundleexecutable, bundleexecutable) < 0) { - fprintf(stderr, "Out of memory!?\n"); - res = -1; - goto leave_cleanup; + if (extsinf) { + size_t flen = 0; + zbuf = buf_from_file(extsinf, &flen); + if (zbuf && flen) { + sinf = plist_new_data(zbuf, flen); + free(zbuf); + } else { + fprintf(stderr, "WARNING: could not load external SINF %s!\n", extsinf); + } + zbuf = NULL; } - free(bundleexecutable); - /* extract .sinf from package */ - zbuf = NULL; - len = 0; - if (zip_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) { - sinf = plist_new_data(zbuf, len); - } else { - fprintf(stderr, "WARNING: could not locate %s in archive!\n", sinfname); + if (!sinf) { + char *sinfname = NULL; + if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundleexecutable, bundleexecutable) < 0) { + fprintf(stderr, "Out of memory!?\n"); + goto leave_cleanup; + } + free(bundleexecutable); + + /* extract .sinf from package */ + zbuf = NULL; + len = 0; + if (zip_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) { + sinf = plist_new_data(zbuf, len); + } else { + fprintf(stderr, "WARNING: could not locate %s in archive!\n", sinfname); + } + free(sinfname); + free(zbuf); } - free(sinfname); - free(zbuf); /* copy archive to device */ pkgname = NULL; if (asprintf(&pkgname, "%s/%s", PKG_PATH, bundleidentifier) < 0) { fprintf(stderr, "Out of memory!?\n"); - res = -1; goto leave_cleanup; } - printf("Copying '%s' to device... ", appid); + printf("Copying '%s' to device... ", cmdarg); - if (afc_upload_file(afc, appid, pkgname) < 0) { + if (afc_upload_file(afc, cmdarg, pkgname) < 0) { + printf("FAILED\n"); free(pkgname); goto leave_cleanup; } @@ -1164,48 +1377,41 @@ run_again: wait_for_command_complete = 1; notification_expected = 1; } else if (cmd == CMD_UNINSTALL) { - printf("Uninstalling '%s'\n", appid); - instproxy_uninstall(ipc, appid, NULL, status_cb, NULL); + printf("Uninstalling '%s'\n", cmdarg); + instproxy_uninstall(ipc, cmdarg, NULL, status_cb, NULL); wait_for_command_complete = 1; notification_expected = 0; } else if (cmd == CMD_LIST_ARCHIVES) { - int xml_mode = 0; plist_t dict = NULL; - /* look for options */ - if (options) { - char *opts = strdup(options); - char *elem = strtok(opts, ","); - while (elem) { - if (!strcmp(elem, "xml")) { - xml_mode = 1; - } - elem = strtok(NULL, ","); - } - } - err = instproxy_lookup_archives(ipc, NULL, &dict); if (err != INSTPROXY_E_SUCCESS) { fprintf(stderr, "ERROR: lookup_archives returned %d\n", err); - res = -1; goto leave_cleanup; } if (!dict) { - fprintf(stderr, - "ERROR: lookup_archives did not return a plist!?\n"); - res = -1; + fprintf(stderr, "ERROR: lookup_archives did not return a plist!?\n"); goto leave_cleanup; } - if (xml_mode) { - char *xml = NULL; + if (output_format) { + char *buf = NULL; uint32_t len = 0; - - plist_to_xml(dict, &xml, &len); - if (xml) { - puts(xml); - free(xml); + if (output_format == FORMAT_XML) { + plist_err_t perr = plist_to_xml(dict, &buf, &len); + if (perr != PLIST_ERR_SUCCESS) { + fprintf(stderr, "ERROR: Failed to convert data to XML format (%d).\n", perr); + } + } else if (output_format == FORMAT_JSON) { + plist_err_t perr = plist_to_json(dict, &buf, &len, 1); + if (perr != PLIST_ERR_SUCCESS) { + fprintf(stderr, "ERROR: Failed to convert data to JSON format (%d).\n", perr); + } + } + if (buf) { + puts(buf); + free(buf); } plist_free(dict); goto leave_cleanup; @@ -1231,7 +1437,7 @@ run_again: plist_t dispName = plist_dict_get_item(node, "CFBundleDisplayName"); plist_t version = - plist_dict_get_item(node, "CFBundleVersion"); + plist_dict_get_item(node, "CFBundleShortVersionString"); if (dispName) { plist_get_string_val(dispName, &s_dispName); } @@ -1254,35 +1460,8 @@ run_again: while (node); plist_free(dict); } else if (cmd == CMD_ARCHIVE) { - char *copy_path = NULL; - int remove_after_copy = 0; - int skip_uninstall = 1; - int app_only = 0; - int docs_only = 0; plist_t client_opts = NULL; - /* look for options */ - if (options) { - char *opts = strdup(options); - char *elem = strtok(opts, ","); - while (elem) { - if (!strcmp(elem, "uninstall")) { - skip_uninstall = 0; - } else if (!strcmp(elem, "app_only")) { - app_only = 1; - docs_only = 0; - } else if (!strcmp(elem, "docs_only")) { - docs_only = 1; - app_only = 0; - } else if ((strlen(elem) > 5) && !strncmp(elem, "copy=", 5)) { - copy_path = strdup(elem+5); - } else if (!strcmp(elem, "remove")) { - remove_after_copy = 1; - } - elem = strtok(NULL, ","); - } - } - if (skip_uninstall || app_only || docs_only) { client_opts = instproxy_client_options_new(); if (skip_uninstall) { @@ -1299,15 +1478,11 @@ run_again: struct stat fst; if (stat(copy_path, &fst) != 0) { fprintf(stderr, "ERROR: stat: %s: %s\n", copy_path, strerror(errno)); - free(copy_path); - res = -1; goto leave_cleanup; } if (!S_ISDIR(fst.st_mode)) { fprintf(stderr, "ERROR: '%s' is not a directory as expected.\n", copy_path); - free(copy_path); - res = -1; goto leave_cleanup; } @@ -1318,8 +1493,6 @@ run_again: if ((lockdownd_start_service(client, "com.apple.afc", &service) != LOCKDOWN_E_SUCCESS) || !service) { fprintf(stderr, "Could not start com.apple.afc!\n"); - free(copy_path); - res = -1; goto leave_cleanup; } @@ -1328,12 +1501,11 @@ run_again: if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) { fprintf(stderr, "Could not connect to AFC!\n"); - res = -1; goto leave_cleanup; } } - instproxy_archive(ipc, appid, client_opts, status_cb, NULL); + instproxy_archive(ipc, cmdarg, client_opts, status_cb, NULL); instproxy_client_options_free(client_opts); wait_for_command_complete = 1; @@ -1349,19 +1521,16 @@ run_again: if (err_occurred) { afc_client_free(afc); afc = NULL; - res = -1; goto leave_cleanup; } FILE *f = NULL; uint64_t af = 0; /* local filename */ char *localfile = NULL; - if (asprintf(&localfile, "%s/%s.ipa", copy_path, appid) < 0) { + if (asprintf(&localfile, "%s/%s.ipa", copy_path, cmdarg) < 0) { fprintf(stderr, "Out of memory!?\n"); - res = -1; goto leave_cleanup; } - free(copy_path); f = fopen(localfile, "wb"); if (!f) { @@ -1372,7 +1541,7 @@ run_again: /* remote filename */ char *remotefile = NULL; - if (asprintf(&remotefile, "%s/%s.zip", APPARCH_PATH, appid) < 0) { + if (asprintf(&remotefile, "%s/%s.zip", APPARCH_PATH, cmdarg) < 0) { fprintf(stderr, "Out of memory!?\n"); goto leave_cleanup; } @@ -1384,7 +1553,6 @@ run_again: fclose(f); free(remotefile); free(localfile); - res = -1; goto leave_cleanup; } @@ -1415,7 +1583,6 @@ run_again: fprintf(stderr, "ERROR: could not open '%s' on device for reading!\n", remotefile); free(remotefile); free(localfile); - res = -1; goto leave_cleanup; } @@ -1459,13 +1626,10 @@ run_again: if (remove_after_copy) { /* remove archive if requested */ - printf("Removing '%s'\n", appid); + printf("Removing '%s'\n", cmdarg); cmd = CMD_REMOVE_ARCHIVE; - free(options); - options = NULL; if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &client, "ideviceinstaller")) { fprintf(stderr, "Could not connect to lockdownd. Exiting.\n"); - res = -1; goto leave_cleanup; } goto run_again; @@ -1473,16 +1637,15 @@ run_again: } goto leave_cleanup; } else if (cmd == CMD_RESTORE) { - instproxy_restore(ipc, appid, NULL, status_cb, NULL); + instproxy_restore(ipc, cmdarg, NULL, status_cb, NULL); wait_for_command_complete = 1; notification_expected = 1; } else if (cmd == CMD_REMOVE_ARCHIVE) { - instproxy_remove_archive(ipc, appid, NULL, status_cb, NULL); + instproxy_remove_archive(ipc, cmdarg, NULL, status_cb, NULL); wait_for_command_complete = 1; } else { - printf - ("ERROR: no command selected?! This should not be reached!\n"); - res = -2; + printf("ERROR: no command selected?! This should not be reached!\n"); + res = 2; goto leave_cleanup; } @@ -1491,6 +1654,7 @@ run_again: client = NULL; idevice_wait_for_command_to_complete(); + res = 0; leave_cleanup: np_client_free(np); @@ -1500,9 +1664,12 @@ leave_cleanup: idevice_free(device); free(udid); - free(appid); - free(options); + free(copy_path); + free(extsinf); + free(extmeta); free(bundleidentifier); + plist_free(bundle_ids); + plist_free(return_attrs); if (err_occurred && !res) { res = 128; |