summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml268
-rw-r--r--Makefile.am6
-rw-r--r--NEWS20
-rw-r--r--README59
-rw-r--r--README.md127
-rwxr-xr-xautogen.sh33
-rw-r--r--configure.ac34
-rwxr-xr-xgit-version-gen20
-rw-r--r--m4/as-compiler-flag.m44
-rw-r--r--man/ideviceinstaller.1155
-rw-r--r--src/ideviceinstaller.c889
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
diff --git a/NEWS b/NEWS
index 27b36a1..f90b886 100644
--- a/NEWS
+++ b/NEWS
@@ -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
~~~~~~~~~~~~~
diff --git a/README b/README
deleted file mode 100644
index 008cfad..0000000
--- a/README
+++ /dev/null
@@ -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
diff --git a/autogen.sh b/autogen.sh
index 3292973..5a0ec43 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -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;