summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2023-03-27 01:51:20 +0200
committerGravatar Nikias Bassen2023-03-27 01:51:20 +0200
commite86c2e397e7873503f1986faf22a5e592a2c8dc1 (patch)
tree53d247ae6b5146a61be662bb481280c9a0e6421e
parent9525a2a76171e7da82f0e3fd602ad3c7101a30bf (diff)
downloadideviceinstaller-e86c2e397e7873503f1986faf22a5e592a2c8dc1.tar.gz
ideviceinstaller-e86c2e397e7873503f1986faf22a5e592a2c8dc1.tar.bz2
Further rework of command line option handling
-rw-r--r--man/ideviceinstaller.164
-rw-r--r--src/ideviceinstaller.c189
2 files changed, 116 insertions, 137 deletions
diff --git a/man/ideviceinstaller.1 b/man/ideviceinstaller.1
index dbaa0d5..d3849dd 100644
--- a/man/ideviceinstaller.1
+++ b/man/ideviceinstaller.1
@@ -12,11 +12,24 @@ Allows to enumerate, install, upgrade, and uninstall apps on iOS devices.
.SH COMMANDS
.TP
.B list
-List installed apps on the device.
+List installed apps on the device. Options:
.RS
.TP
-\-a|\-\-attribute <attr>
-Specify attribute to return. This argument can be passed multiple times. If omitted and \f[B]-o 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:
+.B \-\-user
+List user apps only (apps installed by the user).
+.B This is the default.
+.TP
+.B \-\-system
+List system apps only (apps available from the system firmware).
+.TP
+.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
.TP
\f[B]StaticDiskUsage\f[] disk usage of installed app
@@ -26,23 +39,12 @@ Specify attribute to return. This argument can be passed multiple times. If omit
\f[B]Path\f[] app installation location
.TP
\f[B]SignerIdentity\f[] code signing identity
+.TP
+NOTE: It is suggested to always add CFBundleIdentifier to allow unique identification of the apps.
.RE
.TP
-\-b|--bundle-identifier <bundleID>
+.B \-b, \-\-bundle\-identifier BUNDLEID
Only query given bundle identifier. This argument can be passed multiple times.
-.TP
-\-o list_user
-list user apps only (apps installed by the user)
-.B This is the default.
-.TP
-\-o list_system
-list system apps only (apps available from the system firmware)
-.TP
-\-o list_all
-list all types of apps
-.TP
-\-o xml
-print output in xml format (PList)
.RE
.TP
.B install PATH
@@ -62,23 +64,23 @@ Upgrade app from a package file specified by PATH.
The following commands are non-functional with iOS 7 or later.
.TP
.B archive BUNDLEID
-Archive app specified by BUNDLEID.
+Archive app specified by BUNDLEID. Options:
.RS
.TP
-\-o uninstall
-uninstall the package after making an archive
+.B \-\-uninstall
+Uninstall the package after making an archive
.TP
-\-o app_only
-archive application data only
+.B \-\-app_only
+Archive application data only
.TP
-\-o docs_only
-archive documents (user data) only
+.B \-\-docs_only
+Archive documents (user data) only
.TP
-\-o copy=PATH
-copy the app archive to directory PATH when done
+.B \-\-copy=PATH
+Copy the app archive to directory PATH when done
.TP
-\-o remove
-only valid when copy=PATH is used: remove after copy
+.B \-\-remove
+Only valid when copy=PATH is used: remove after copy
.RE
.TP
@@ -87,11 +89,11 @@ Restore archived app specified by BUNDLEID.
.TP
.B list-archives
-List archived apps on the device.
+List archived apps on the device. Options:
.RS
.TP
-\-o xml
-print full output as xml plist
+.B \-\-xml
+Print output as XML Property List.
.RE
.TP
diff --git a/src/ideviceinstaller.c b/src/ideviceinstaller.c
index ecf63c6..dfa3e13 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
@@ -93,7 +93,6 @@ const char PKG_PATH[] = "PublicStaging";
const char APPARCH_PATH[] = "ApplicationArchives";
char *udid = NULL;
-char *options = NULL;
char *cmdarg = NULL;
enum cmd_mode {
@@ -122,7 +121,14 @@ int err_occurred = 0;
int notified = 0;
plist_t bundle_ids = NULL;
plist_t return_attrs = NULL;
-int return_attrs_have_CFBundleIdentifier = 0;
+int xml_mode = 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()
{
@@ -414,30 +420,30 @@ static void print_usage(int argc, char **argv, int is_error)
"Manage apps on iOS devices.\n"
"\n"
"COMMANDS:\n"
- " list List installed apps\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"
- " -o list_user list user apps only (this is the default)\n"
- " -o list_system list system apps only\n"
- " -o list_all list all types of apps\n"
- " -o xml print full output as xml plist\n"
" install PATH Install app from package file specified by PATH.\n"
" PATH can also be a .ipcc file for carrier bundles.\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, possible options:\n"
- " -o uninstall uninstall the package after making an archive\n"
- " -o app_only archive application data only\n"
- " -o docs_only archive documents (user data) only\n"
- " -o copy=PATH copy the app archive to directory PATH when done\n"
- " -o 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\n"
- " -o xml print full output as xml plist\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"
@@ -454,24 +460,44 @@ static void print_usage(int argc, char **argv, int is_error)
);
}
+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
+};
+
static void parse_opts(int argc, char **argv)
{
static struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "udid", required_argument, NULL, 'u' },
{ "network", no_argument, NULL, 'n' },
- { "options", required_argument, NULL, 'o' },
{ "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 },
+ { "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:o:nwdvb:a:", longopts, (int*)0);
+ c = getopt_long(argc, argv, "hu:nwdvb:a:", longopts, (int*)0);
if (c == -1) {
break;
}
@@ -501,9 +527,6 @@ static void parse_opts(int argc, char **argv)
return_attrs = plist_new_array();
}
plist_array_append_item(return_attrs, plist_new_string(optarg));
- if (!strcmp(optarg, "CFBundleIdentifier")) {
- return_attrs_have_CFBundleIdentifier = 1;
- }
break;
case 'b':
if (!*optarg) {
@@ -516,18 +539,6 @@ static void parse_opts(int argc, char **argv)
}
plist_array_append_item(bundle_ids, plist_new_string(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;
- }
- break;
case 'w':
use_notifier = 1;
break;
@@ -537,6 +548,36 @@ static void parse_opts(int argc, char **argv)
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:
+ xml_mode = 1;
+ 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, 1);
exit(2);
@@ -795,46 +836,30 @@ 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 && !return_attrs) {
+ if (!xml_mode) {
return_attrs = plist_new_array();
plist_array_append_item(return_attrs, plist_new_string("CFBundleIdentifier"));
- return_attrs_have_CFBundleIdentifier = 1;
plist_array_append_item(return_attrs, plist_new_string("CFBundleShortVersionString"));
plist_array_append_item(return_attrs, plist_new_string("CFBundleDisplayName"));
}
if (return_attrs) {
- if (!return_attrs_have_CFBundleIdentifier) {
- plist_array_insert_item(return_attrs, plist_new_string("CFBundleIdentifier"), 0);
- }
instproxy_client_options_add(client_opts, "ReturnAttributes", return_attrs, NULL);
}
@@ -845,7 +870,6 @@ run_again:
fprintf(stderr, "ERROR: instproxy_browse returnd an invalid plist!\n");
goto leave_cleanup;
}
-
char *xml = NULL;
uint32_t len = 0;
@@ -1228,21 +1252,8 @@ run_again:
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);
@@ -1257,7 +1268,6 @@ run_again:
if (xml_mode) {
char *xml = NULL;
uint32_t len = 0;
-
plist_to_xml(dict, &xml, &len);
if (xml) {
puts(xml);
@@ -1310,35 +1320,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) {
@@ -1355,13 +1338,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);
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);
goto leave_cleanup;
}
@@ -1372,7 +1353,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);
goto leave_cleanup;
}
@@ -1411,7 +1391,6 @@ run_again:
fprintf(stderr, "Out of memory!?\n");
goto leave_cleanup;
}
- free(copy_path);
f = fopen(localfile, "wb");
if (!f) {
@@ -1509,8 +1488,6 @@ run_again:
/* remove archive if requested */
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");
goto leave_cleanup;
@@ -1547,7 +1524,7 @@ leave_cleanup:
idevice_free(device);
free(udid);
- free(options);
+ free(copy_path);
free(bundleidentifier);
plist_free(bundle_ids);
plist_free(return_attrs);