summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/libiphone/installation_proxy.h30
-rw-r--r--src/installation_proxy.c271
2 files changed, 165 insertions, 136 deletions
diff --git a/include/libiphone/installation_proxy.h b/include/libiphone/installation_proxy.h
index f796054..b7bbb60 100644
--- a/include/libiphone/installation_proxy.h
+++ b/include/libiphone/installation_proxy.h
@@ -28,6 +28,7 @@ extern "C" {
#endif
#include <libiphone/libiphone.h>
+#include <glib.h>
/* Error Codes */
#define INSTPROXY_E_SUCCESS 0
@@ -41,15 +42,6 @@ extern "C" {
typedef int16_t instproxy_error_t;
-typedef enum {
- INSTPROXY_APPTYPE_ALL = 0,
- INSTPROXY_APPTYPE_SYSTEM = 1,
- INSTPROXY_APPTYPE_USER = 2
-} instproxy_apptype_t;
-
-#define INSTPROXY_ARCHIVE_APP_ONLY (1 << 0)
-#define INSTPROXY_ARCHIVE_SKIP_UNINSTALL (1 << 1)
-
struct instproxy_client_int;
typedef struct instproxy_client_int *instproxy_client_t;
@@ -59,15 +51,19 @@ typedef void (*instproxy_status_cb_t) (const char *operation, plist_t status);
instproxy_error_t instproxy_client_new(iphone_device_t device, uint16_t port, instproxy_client_t *client);
instproxy_error_t instproxy_client_free(instproxy_client_t client);
-instproxy_error_t instproxy_browse(instproxy_client_t client, instproxy_apptype_t apptype, plist_t *result);
-instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb);
-instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb);
-instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb);
+instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result);
+instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb);
+instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb);
+instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb);
+
+instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result);
+instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb);
+instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb);
+instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb);
-instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t *result);
-instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, uint32_t options, instproxy_status_cb_t status_cb);
-instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb);
-instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb);
+plist_t instproxy_client_options_new();
+void instproxy_client_options_add(plist_t client_options, ...) G_GNUC_NULL_TERMINATED;
+void instproxy_client_options_free(plist_t client_options);
#ifdef __cplusplus
}
diff --git a/src/installation_proxy.c b/src/installation_proxy.c
index a99b5cb..e0bccd3 100644
--- a/src/installation_proxy.c
+++ b/src/installation_proxy.c
@@ -143,55 +143,71 @@ instproxy_error_t instproxy_client_free(instproxy_client_t client)
}
/**
+ * Send a command with specified options to the device.
+ * Internally used only.
+ *
+ * @param client The connected installation_proxy client.
+ * @param command The command to execute. Required.
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * @param appid The ApplicationIdentifier to add or NULL if not required.
+ * @param package_path The installation package path or NULL if not required.
+ *
+ * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
+ * an error occured.
+ */
+static instproxy_error_t instproxy_send_command(instproxy_client_t client, const char *command, plist_t client_options, const char *appid, const char *package_path)
+{
+ if (!client || !command || (client_options && (plist_get_node_type(client_options) != PLIST_DICT)))
+ return INSTPROXY_E_INVALID_ARG;
+
+ plist_t dict = plist_new_dict();
+ if (appid) {
+ plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
+ }
+ if (client_options && (plist_dict_get_size(client_options) > 0)) {
+ plist_dict_insert_item(dict, "ClientOptions", plist_copy(client_options));
+ }
+ plist_dict_insert_item(dict, "Command", plist_new_string(command));
+ if (package_path) {
+ plist_dict_insert_item(dict, "PackagePath", plist_new_string(package_path));
+ }
+
+ instproxy_error_t err = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
+ plist_free(dict);
+ return err;
+}
+
+/**
* List installed applications. This function runs synchronously.
*
* @param client The connected installation_proxy client
- * @param apptype The type of applications to list.
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Valid client options include:
+ * "ApplicationType" -> "User"
+ * "ApplicationType" -> "System"
* @param result Pointer that will be set to a plist that will hold an array
* of PLIST_DICT holding information about the applications found.
*
* @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
* an error occured.
*/
-instproxy_error_t instproxy_browse(instproxy_client_t client, instproxy_apptype_t apptype, plist_t *result)
+instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result)
{
if (!client || !client->parent || !result)
return INSTPROXY_E_INVALID_ARG;
instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
- int browsing = 0;
- plist_t apps_array = NULL;
-
- plist_t dict = plist_new_dict();
- if (apptype != INSTPROXY_APPTYPE_ALL) {
- plist_t client_opts = plist_new_dict();
- plist_t p_apptype = NULL;
- switch (apptype) {
- case INSTPROXY_APPTYPE_SYSTEM:
- p_apptype = plist_new_string("System");
- break;
- case INSTPROXY_APPTYPE_USER:
- p_apptype = plist_new_string("User");
- break;
- default:
- debug_info("unknown apptype %d given, using INSTPROXY_APPTYPE_USER instead", apptype);
- p_apptype = plist_new_string("User");
- break;
- }
- plist_dict_insert_item(client_opts, "ApplicationType", p_apptype);
- plist_dict_insert_item(dict, "ClientOptions", client_opts);
- }
- plist_dict_insert_item(dict, "Command", plist_new_string("Browse"));
instproxy_lock(client);
- res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
- plist_free(dict);
+ res = instproxy_send_command(client, "Browse", client_options, NULL, NULL);
if (res != INSTPROXY_E_SUCCESS) {
debug_info("could not send plist");
goto leave_unlock;
}
- apps_array = plist_new_array();
+ int browsing = 0;
+ plist_t apps_array = plist_new_array();
+ plist_t dict = NULL;
do {
browsing = 0;
@@ -393,8 +409,7 @@ static instproxy_error_t instproxy_create_status_updater(instproxy_client_t clie
*
* @param client The connected installation_proxy client
* @param pkg_path Path of the installation package (inside the AFC jail)
- * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
- * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
* @param status_cb Callback function for progress and status information. If
* NULL is passed, this function will run synchronously.
* @param command The command to execute.
@@ -402,42 +417,19 @@ static instproxy_error_t instproxy_create_status_updater(instproxy_client_t clie
* @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
* an error occured.
*/
-static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb, const char *command)
+static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, const char *command)
{
if (!client || !client->parent || !pkg_path) {
return INSTPROXY_E_INVALID_ARG;
}
- if (sinf && (plist_get_node_type(sinf) != PLIST_DATA)) {
- debug_info("(%s): ERROR: sinf data is not a PLIST_DATA node!", command);
- return INSTPROXY_E_INVALID_ARG;
- }
- if (metadata && (plist_get_node_type(metadata) != PLIST_DATA)) {
- debug_info("(%s): ERROR: metadata is not a PLIST_DATA node!", command);
- return INSTPROXY_E_INVALID_ARG;
- }
-
if (client->status_updater) {
return INSTPROXY_E_OP_IN_PROGRESS;
}
- instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
-
- plist_t dict = plist_new_dict();
- if (sinf && metadata) {
- plist_t client_opts = plist_new_dict();
- plist_dict_insert_item(client_opts, "ApplicationSINF", plist_copy(sinf));
- plist_dict_insert_item(client_opts, "iTunesMetadata", plist_copy(metadata));
- plist_dict_insert_item(dict, "ClientOptions", client_opts);
- }
- plist_dict_insert_item(dict, "Command", plist_new_string(command));
- plist_dict_insert_item(dict, "PackagePath", plist_new_string(pkg_path));
-
instproxy_lock(client);
- res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
+ instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path);
instproxy_unlock(client);
- plist_free(dict);
-
if (res != INSTPROXY_E_SUCCESS) {
debug_info("could not send plist, error %d", res);
return res;
@@ -451,8 +443,13 @@ static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client,
*
* @param client The connected installation_proxy client
* @param pkg_path Path of the installation package (inside the AFC jail)
- * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
- * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Valid options include:
+ * "iTunesMetadata" -> PLIST_DATA
+ * "ApplicationSINF" -> PLIST_DATA
+ * "PackageType" -> "Developer"
+ * If PackageType -> Developer is specified, then pkg_path points to
+ * an .app directory instead of an install package.
* @param status_cb Callback function for progress and status information. If
* NULL is passed, this function will run synchronously.
*
@@ -464,9 +461,9 @@ static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client,
* created successfully; any error occuring during the operation has to be
* handled inside the specified callback function.
*/
-instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb)
+instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb)
{
- return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Install");
+ return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Install");
}
/**
@@ -476,8 +473,13 @@ instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_p
*
* @param client The connected installation_proxy client
* @param pkg_path Path of the installation package (inside the AFC jail)
- * @param sinf PLIST_DATA node holding the package's SINF data or NULL.
- * @param metadata PLIST_DATA node holding the packages's Metadata or NULL.
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Valid options include:
+ * "iTunesMetadata" -> PLIST_DATA
+ * "ApplicationSINF" -> PLIST_DATA
+ * "PackageType" -> "Developer"
+ * If PackageType -> Developer is specified, then pkg_path points to
+ * an .app directory instead of an install package.
* @param status_cb Callback function for progress and status information. If
* NULL is passed, this function will run synchronously.
*
@@ -489,9 +491,9 @@ instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_p
* created successfully; any error occuring during the operation has to be
* handled inside the specified callback function.
*/
-instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t sinf, plist_t metadata, instproxy_status_cb_t status_cb)
+instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb)
{
- return instproxy_install_or_upgrade(client, pkg_path, sinf, metadata, status_cb, "Upgrade");
+ return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Upgrade");
}
/**
@@ -499,6 +501,8 @@ instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_p
*
* @param client The connected installation proxy client
* @param appid ApplicationIdentifier of the app to uninstall
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Currently there are no known client options, so pass NULL here.
* @param status_cb Callback function for progress and status information. If
* NULL is passed, this function will run synchronously.
*
@@ -510,7 +514,7 @@ instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_p
* created successfully; any error occuring during the operation has to be
* handled inside the specified callback function.
*/
-instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
+instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb)
{
if (!client || !client->parent || !appid) {
return INSTPROXY_E_INVALID_ARG;
@@ -526,7 +530,7 @@ instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *app
plist_dict_insert_item(dict, "Command", plist_new_string("Uninstall"));
instproxy_lock(client);
- res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
+ res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL);
instproxy_unlock(client);
plist_free(dict);
@@ -545,26 +549,21 @@ instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *app
* @see instproxy_archive
*
* @param client The connected installation_proxy client
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Currently there are no known client options, so pass NULL here.
* @param result Pointer that will be set to a plist containing a PLIST_DICT
* holding information about the archived applications found.
*
* @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
* an error occured.
*/
-instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t *result)
+instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result)
{
if (!client || !client->parent || !result)
return INSTPROXY_E_INVALID_ARG;
- instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
-
- plist_t dict = plist_new_dict();
- plist_dict_insert_item(dict, "Command", plist_new_string("LookupArchives"));
-
instproxy_lock(client);
-
- res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
- plist_free(dict);
+ instproxy_error_t res = instproxy_send_command(client, "LookupArchives", client_options, NULL, NULL);
if (res != INSTPROXY_E_SUCCESS) {
debug_info("could not send plist, error %d", res);
@@ -592,11 +591,10 @@ leave_unlock:
*
* @param client The connected installation proxy client
* @param appid ApplicationIdentifier of the app to archive.
- * @param options This is either 0 for default behaviour (make an archive
- * including app/user settings etc. AND uninstall the application),
- * or one or a combination of the following options:
- * INSTPROXY_ARCHIVE_APP_ONLY (1)
- * INSTPROXY_ARCHIVE_SKIP_UNINSTALL (2)
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Valid options include:
+ * "SkipUninstall" -> Boolean
+ * "ArchiveType" -> "ApplicationOnly"
* @param status_cb Callback function for progress and status information. If
* NULL is passed, this function will run synchronously.
*
@@ -608,7 +606,7 @@ leave_unlock:
* created successfully; any error occuring during the operation has to be
* handled inside the specified callback function.
*/
-instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, uint32_t options, instproxy_status_cb_t status_cb)
+instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb)
{
if (!client || !client->parent || !appid)
return INSTPROXY_E_INVALID_ARG;
@@ -617,28 +615,10 @@ instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid
return INSTPROXY_E_OP_IN_PROGRESS;
}
- instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
-
- plist_t dict = plist_new_dict();
- plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
- if (options > 0) {
- plist_t client_opts = plist_new_dict();
- if (options & INSTPROXY_ARCHIVE_APP_ONLY) {
- plist_dict_insert_item(client_opts, "ArchiveType", plist_new_string("ApplicationOnly"));
- }
- if (options & INSTPROXY_ARCHIVE_SKIP_UNINSTALL) {
- plist_dict_insert_item(client_opts, "SkipUninstall", plist_new_bool(1));
- }
- plist_dict_insert_item(dict, "ClientOptions", client_opts);
- }
- plist_dict_insert_item(dict, "Command", plist_new_string("Archive"));
-
instproxy_lock(client);
- res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
+ instproxy_error_t res = instproxy_send_command(client, "Archive", client_options, appid, NULL);
instproxy_unlock(client);
- plist_free(dict);
-
if (res != INSTPROXY_E_SUCCESS) {
debug_info("could not send plist, error %d", res);
return res;
@@ -653,6 +633,8 @@ instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid
*
* @param client The connected installation proxy client
* @param appid ApplicationIdentifier of the app to restore.
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Currently there are no known client options, so pass NULL here.
* @param status_cb Callback function for progress and status information. If
* NULL is passed, this function will run synchronously.
*
@@ -664,7 +646,7 @@ instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid
* created successfully; any error occuring during the operation has to be
* handled inside the specified callback function.
*/
-instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
+instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb)
{
if (!client || !client->parent || !appid)
return INSTPROXY_E_INVALID_ARG;
@@ -673,18 +655,10 @@ instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid
return INSTPROXY_E_OP_IN_PROGRESS;
}
- instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
-
- plist_t dict = plist_new_dict();
- plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
- plist_dict_insert_item(dict, "Command", plist_new_string("Restore"));
-
instproxy_lock(client);
- res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
+ instproxy_error_t res = instproxy_send_command(client, "Restore", client_options, appid, NULL);
instproxy_unlock(client);
- plist_free(dict);
-
if (res != INSTPROXY_E_SUCCESS) {
debug_info("could not send plist, error %d", res);
return res;
@@ -699,6 +673,8 @@ instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid
*
* @param client The connected installation proxy client
* @param appid ApplicationIdentifier of the archived app to remove.
+ * @param client_options The client options to use, as PLIST_DICT, or NULL.
+ * Currently there are no known client options, so passing NULL is fine.
* @param status_cb Callback function for progress and status information. If
* NULL is passed, this function will run synchronously.
*
@@ -710,7 +686,7 @@ instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid
* created successfully; any error occuring during the operation has to be
* handled inside the specified callback function.
*/
-instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, instproxy_status_cb_t status_cb)
+instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb)
{
if (!client || !client->parent || !appid)
return INSTPROXY_E_INVALID_ARG;
@@ -719,18 +695,10 @@ instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char
return INSTPROXY_E_OP_IN_PROGRESS;
}
- instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
-
- plist_t dict = plist_new_dict();
- plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
- plist_dict_insert_item(dict, "Command", plist_new_string("RemoveArchive"));
-
instproxy_lock(client);
- res = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
+ instproxy_error_t res = instproxy_send_command(client, "RemoveArchive", client_options, appid, NULL);
instproxy_unlock(client);
- plist_free(dict);
-
if (res != INSTPROXY_E_SUCCESS) {
debug_info("could not send plist, error %d", res);
return res;
@@ -738,3 +706,68 @@ instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char
return instproxy_create_status_updater(client, status_cb, "RemoveArchive");
}
+/**
+ * Create a new client_options plist.
+ *
+ * @return A new plist_t of type PLIST_DICT.
+ */
+plist_t instproxy_client_options_new()
+{
+ return plist_new_dict();
+}
+
+/**
+ * Add one or more new key:value pairs to the given client_options.
+ *
+ * @param client_options The client options to modify.
+ * @param ... KEY, VALUE, [KEY, VALUE], NULL
+ *
+ * @note The keys and values passed are expected to be strings, except for
+ * "ApplicationSINF" and "iTunesMetadata" expecting a plist node of type
+ * PLIST_DATA as value, or "SkipUninstall" needing int as value.
+ */
+void instproxy_client_options_add(plist_t client_options, ...)
+{
+ if (!client_options)
+ return;
+ va_list args;
+ va_start(args, client_options);
+ char *arg = va_arg(args, char*);
+ while (arg) {
+ char *key = strdup(arg);
+ if (!strcmp(key, "SkipUninstall")) {
+ int intval = va_arg(args, int);
+ plist_dict_set_item(client_options, key, plist_new_bool(intval));
+ } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata")) {
+ plist_t plistval = va_arg(args, plist_t);
+ if (!plistval) {
+ free(key);
+ break;
+ }
+ plist_dict_set_item(client_options, key, plist_copy(plistval));
+ } else {
+ char *strval = va_arg(args, char*);
+ if (!strval) {
+ free(key);
+ break;
+ }
+ plist_dict_set_item(client_options, key, plist_new_string(strval));
+ }
+ free(key);
+ arg = va_arg(args, char*);
+ }
+ va_end(args);
+}
+
+/**
+ * Free client_options plist.
+ *
+ * @param client_options The client options plist to free. Does nothing if NULL
+ * is passed.
+ */
+void instproxy_client_options_free(plist_t client_options)
+{
+ if (client_options) {
+ plist_free(client_options);
+ }
+}