summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/Makefile.am1
-rw-r--r--include/libimobiledevice-glue/nskeyedarchive.h90
-rw-r--r--src/Makefile.am1
-rw-r--r--src/nskeyedarchive.c1228
4 files changed, 1320 insertions, 0 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index a194664..7aca2a0 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -6,6 +6,7 @@ nobase_include_HEADERS = \
libimobiledevice-glue/socket.h \
libimobiledevice-glue/thread.h \
libimobiledevice-glue/utils.h \
+ libimobiledevice-glue/nskeyedarchive.h \
libimobiledevice-glue/collection.h \
libimobiledevice-glue/termcolors.h \
libimobiledevice-glue/cbuf.h \
diff --git a/include/libimobiledevice-glue/nskeyedarchive.h b/include/libimobiledevice-glue/nskeyedarchive.h
new file mode 100644
index 0000000..5aad4d6
--- /dev/null
+++ b/include/libimobiledevice-glue/nskeyedarchive.h
@@ -0,0 +1,90 @@
+/*
+ * nskeyedarchive.h
+ * Helper code to work with plist files containing NSKeyedArchiver data.
+ *
+ * Copyright (c) 2019 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __NSKEYEDARCHIVE_H
+#define __NSKEYEDARCHIVE_H
+
+#include <stdint.h>
+#include <libimobiledevice-glue/glue.h>
+#include <plist/plist.h>
+
+enum nskeyedarchive_class_type_t {
+ NSTYPE_INTEGER = 1,
+ NSTYPE_BOOLEAN,
+ NSTYPE_CHARS,
+ NSTYPE_STRING,
+ NSTYPE_REAL,
+ NSTYPE_ARRAY,
+ NSTYPE_DATA,
+ NSTYPE_INTREF,
+ NSTYPE_NSMUTABLESTRING,
+ NSTYPE_NSSTRING,
+ NSTYPE_NSMUTABLEARRAY,
+ NSTYPE_NSARRAY,
+ NSTYPE_NSMUTABLEDICTIONARY,
+ NSTYPE_NSDICTIONARY,
+ NSTYPE_NSDATE,
+ NSTYPE_NSURL,
+ NSTYPE_NSMUTABLEDATA,
+ NSTYPE_NSDATA,
+ NSTYPE_NSKEYEDARCHIVE,
+ NSTYPE_FROM_PLIST
+};
+
+typedef struct nskeyedarchive_st *nskeyedarchive_t;
+
+LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new(void);
+LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new_from_plist(plist_t plist);
+LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new_from_data(const void* data, uint32_t size);
+LIMD_GLUE_API void nskeyedarchive_free(nskeyedarchive_t ka);
+
+LIMD_GLUE_API void nskeyedarchive_set_top_ref_key_name(nskeyedarchive_t ka, const char* keyname);
+
+LIMD_GLUE_API uint64_t nskeyedarchive_add_top_class(nskeyedarchive_t ka, const char* classname, ...) __attribute__ ((sentinel(0)));
+LIMD_GLUE_API void nskeyedarchive_add_top_class_uid(nskeyedarchive_t ka, uint64_t uid);
+LIMD_GLUE_API void nskeyedarchive_append_class(nskeyedarchive_t ka, const char* classname, ...) __attribute__ ((sentinel(0)));
+LIMD_GLUE_API void nskeyedarchive_append_object(nskeyedarchive_t ka, plist_t object);
+
+LIMD_GLUE_API void nskeyedarchive_nsarray_append_item(nskeyedarchive_t ka, uint64_t uid, enum nskeyedarchive_class_type_t type, ...);
+LIMD_GLUE_API void nskeyedarchive_nsdictionary_add_item(nskeyedarchive_t ka, uint64_t uid, const char* key, enum nskeyedarchive_class_type_t type, ...);
+
+LIMD_GLUE_API void nskeyedarchive_append_class_type_v(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, va_list* va);
+LIMD_GLUE_API void nskeyedarchive_append_class_type(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, ...);
+
+LIMD_GLUE_API void nskeyedarchive_merge_object(nskeyedarchive_t ka, nskeyedarchive_t pka, plist_t object);
+
+LIMD_GLUE_API void nskeyedarchive_print(nskeyedarchive_t ka);
+LIMD_GLUE_API plist_t nskeyedarchive_get_plist_ref(nskeyedarchive_t ka);
+LIMD_GLUE_API plist_t nskeyedarchive_get_object_by_uid(nskeyedarchive_t ka, uint64_t uid);
+LIMD_GLUE_API plist_t nskeyedarchive_get_class_by_uid(nskeyedarchive_t ka, uint64_t uid);
+LIMD_GLUE_API plist_t nskeyedarchive_get_objects(nskeyedarchive_t ka);
+
+LIMD_GLUE_API uint64_t nskeyedarchive_get_class_uid(nskeyedarchive_t ka, const char* classref);
+LIMD_GLUE_API const char* nskeyedarchive_get_classname(nskeyedarchive_t ka, uint64_t uid);
+
+LIMD_GLUE_API void nskeyedarchive_set_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, ...);
+LIMD_GLUE_API int nskeyedarchive_get_class_uint64_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, uint64_t* value);
+LIMD_GLUE_API int nskeyedarchive_get_class_int_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, int* value);
+LIMD_GLUE_API int nskeyedarchive_get_class_string_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, char** value);
+LIMD_GLUE_API int nskeyedarchive_get_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, plist_t* value);
+
+LIMD_GLUE_API plist_t nskeyedarchive_to_plist(nskeyedarchive_t ka);
+
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index b709103..0db7ede 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,6 +12,7 @@ libimobiledevice_glue_1_0_la_SOURCES = \
socket.c \
thread.c \
utils.c \
+ nskeyedarchive.c \
collection.c \
termcolors.c \
cbuf.c \
diff --git a/src/nskeyedarchive.c b/src/nskeyedarchive.c
new file mode 100644
index 0000000..b3dbdc4
--- /dev/null
+++ b/src/nskeyedarchive.c
@@ -0,0 +1,1228 @@
+/*
+ * nskeyedarchive.c
+ * Helper code to work with plist files containing NSKeyedArchiver data.
+ *
+ * Copyright (c) 2019 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <plist/plist.h>
+#include "common.h"
+#include "libimobiledevice-glue/nskeyedarchive.h"
+
+#define NS_KEYED_ARCHIVER_NAME "NSKeyedArchiver"
+#define NS_KEYED_ARCHIVER_VERSION 100000
+
+struct nskeyedarchive_st {
+ plist_t dict;
+ uint64_t uid;
+};
+
+nskeyedarchive_t nskeyedarchive_new(void)
+{
+ plist_t dict = plist_new_dict();
+ plist_dict_set_item(dict, "$version", plist_new_uint(NS_KEYED_ARCHIVER_VERSION));
+ plist_t objects = plist_new_array();
+ plist_array_append_item(objects, plist_new_string("$null"));
+ plist_dict_set_item(dict, "$objects", objects);
+ plist_dict_set_item(dict, "$archiver", plist_new_string(NS_KEYED_ARCHIVER_NAME));
+
+ nskeyedarchive_t nskeyed = (nskeyedarchive_t)malloc(sizeof(struct nskeyedarchive_st));
+ nskeyed->dict = dict;
+ nskeyed->uid = 1;
+ return nskeyed;
+}
+
+void nskeyedarchive_free(nskeyedarchive_t ka)
+{
+ if (ka) {
+ if (ka->dict) {
+ plist_free(ka->dict);
+ }
+ free(ka);
+ }
+}
+
+void nskeyedarchive_set_top_ref_key_name(nskeyedarchive_t ka, const char* keyname)
+{
+ if (!ka) {
+ return;
+ }
+ plist_t top = plist_dict_get_item(ka->dict, "$top");
+ if (top) {
+ plist_dict_iter iter = NULL;
+ plist_dict_new_iter(top, &iter);
+ if (iter) {
+ plist_t node = NULL;
+ char* keyn = NULL;
+ plist_dict_next_item(top, iter, &keyn, &node);
+ plist_t keynode = plist_dict_item_get_key(node);
+ plist_set_key_val(keynode, keyname);
+ free(keyn);
+ free(iter);
+ }
+ }
+}
+
+plist_t nskeyedarchive_get_objects(nskeyedarchive_t ka)
+{
+ plist_t objects = plist_dict_get_item(ka->dict, "$objects");
+ if (!objects || (plist_get_node_type(objects) != PLIST_ARRAY)) {
+ fprintf(stderr, "ERROR: $objects node not found!\n");
+ return NULL;
+ }
+
+ return objects;
+}
+
+plist_t nskeyedarchive_get_object_by_uid(nskeyedarchive_t ka, uint64_t uid)
+{
+ plist_t objects = nskeyedarchive_get_objects(ka);
+ if (!objects) {
+ return NULL;
+ }
+
+ plist_t obj = plist_array_get_item(objects, (uint32_t)uid);
+ if (!obj) {
+ fprintf(stderr, "ERROR: unable to get object node with uid %llu\n", (long long)uid);
+ return NULL;
+ }
+
+ return obj;
+}
+
+plist_t nskeyedarchive_get_class_by_uid(nskeyedarchive_t ka, uint64_t uid)
+{
+ plist_t ret = nskeyedarchive_get_object_by_uid(ka, uid);
+ if (ret && (plist_get_node_type(ret) != PLIST_DICT)) {
+ fprintf(stderr, "ERROR: the uid %llu does not reference a valid class with node type PLIST_DICT!\n", (long long)uid);
+ return NULL;
+ }
+
+ return ret;
+}
+
+void nskeyedarchive_append_object(nskeyedarchive_t ka, plist_t object)
+{
+ plist_t objects = nskeyedarchive_get_objects(ka);
+ if (objects && plist_get_node_type(objects) != PLIST_ARRAY) {
+ fprintf(stderr, "ERROR: unable to append object\n");
+ return;
+ }
+
+ plist_array_append_item(objects, object);
+}
+
+static void nskeyedarchive_append_class_v(nskeyedarchive_t ka, const char* classname, va_list* va)
+{
+ if (!ka) {
+ fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__);
+ return;
+ } else if (!classname) {
+ fprintf(stderr, "%s: ERROR: missing classname!\n", __func__);
+ return;
+ }
+
+ char* arg = NULL;
+ plist_t classes = NULL;
+ do {
+ arg = (char*)va_arg(*va, char*);
+ if (!arg) {
+ break;
+ }
+ if (!classes) {
+ classes = plist_new_array();
+ plist_array_append_item(classes, plist_new_string(classname));
+ }
+ plist_array_append_item(classes, plist_new_string(arg));
+ } while (arg);
+
+ plist_t cls = plist_new_dict();
+ plist_dict_set_item(cls, "$class", plist_new_uid(++ka->uid));
+
+ nskeyedarchive_append_object(ka, cls);
+
+ cls = plist_new_dict();
+ if (classes) {
+ plist_dict_set_item(cls, "$classes", classes);
+ }
+ plist_dict_set_item(cls, "$classname", plist_new_string(classname));
+
+ nskeyedarchive_append_object(ka, cls);
+}
+
+void nskeyedarchive_append_class(nskeyedarchive_t ka, const char* classname, ...)
+{
+ if (!ka) {
+ fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__);
+ return;
+ } else if (!classname) {
+ fprintf(stderr, "%s: ERROR: missing classname!\n", __func__);
+ return;
+ }
+
+ va_list va;
+ va_start(va, classname);
+ nskeyedarchive_append_class_v(ka, classname, &va);
+ va_end(va);
+}
+
+void nskeyedarchive_add_top_class_uid(nskeyedarchive_t ka, uint64_t uid)
+{
+ plist_t top = plist_dict_get_item(ka->dict, "$top");
+ if (!top) {
+ top = plist_new_dict();
+ plist_dict_set_item(top, "$0", plist_new_uid(uid));
+ plist_dict_set_item(ka->dict, "$top", top);
+ } else {
+ uint32_t num = plist_dict_get_size(top);
+ char newkey[8];
+ sprintf(newkey, "$%d", num);
+ plist_dict_set_item(top, newkey, plist_new_uid(uid));
+ }
+}
+
+uint64_t nskeyedarchive_add_top_class(nskeyedarchive_t ka, const char* classname, ...)
+{
+ if (!ka) {
+ fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__);
+ return 0;
+ } else if (!classname) {
+ fprintf(stderr, "%s: ERROR: missing classname!\n", __func__);
+ return 0;
+ }
+
+ uint64_t uid = ka->uid;
+
+ va_list va;
+ va_start(va, classname);
+ nskeyedarchive_append_class_v(ka, classname, &va);
+ va_end(va);
+
+ nskeyedarchive_add_top_class_uid(ka, uid);
+ return uid;
+}
+
+static void nskeyedarchive_set_class_property_v(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, va_list* va);
+
+static void nskeyedarchive_nsarray_append(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, ...);
+
+static void nskeyedarchive_nsarray_append_v(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, va_list* va)
+{
+ if (!ka) {
+ fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__);
+ return;
+ } else if (!array) {
+ fprintf(stderr, "%s: ERROR: missing plist!\n", __func__);
+ return;
+ }
+
+ uint64_t newuid;
+
+ switch (type) {
+ case NSTYPE_INTEGER:
+ plist_array_append_item(array, plist_new_uint(va_arg(*va, int)));
+ break;
+ case NSTYPE_INTREF:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int)));
+ break;
+ case NSTYPE_BOOLEAN:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int)));
+ break;
+ case NSTYPE_CHARS:
+ plist_array_append_item(array, plist_new_string(va_arg(*va, char*)));
+ break;
+ case NSTYPE_STRING:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_object(ka, plist_new_string(va_arg(*va, char*)));
+ break;
+ case NSTYPE_REAL:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_object(ka, plist_new_real(va_arg(*va, double)));
+ break;
+ case NSTYPE_NSMUTABLESTRING:
+ case NSTYPE_NSSTRING:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ if (type == NSTYPE_NSMUTABLESTRING) {
+ nskeyedarchive_append_class(ka, "NSMutableString", "NSString", "NSObject", NULL);
+ } else {
+ nskeyedarchive_append_class(ka, "NSString", "NSObject", NULL);
+ }
+ nskeyedarchive_set_class_property(ka, newuid, "NS.string", NSTYPE_CHARS, va_arg(*va, char*));
+ break;
+ case NSTYPE_NSMUTABLEARRAY:
+ case NSTYPE_NSARRAY:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ if (type == NSTYPE_NSMUTABLEARRAY) {
+ nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL);
+ } else {
+ nskeyedarchive_append_class(ka, "NSArray", "NSObject", NULL);
+ }
+ {
+ plist_t arr = plist_new_array();
+ do {
+ int ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_nsarray_append_v(ka, arr, ptype, va);
+ } while (1);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr);
+ plist_free(arr);
+ }
+ break;
+ case NSTYPE_NSMUTABLEDICTIONARY:
+ case NSTYPE_NSDICTIONARY:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ if (type == NSTYPE_NSMUTABLEDICTIONARY) {
+ nskeyedarchive_append_class(ka, "NSMutableDictionary", "NSDictionary", "NSObject", NULL);
+ } else {
+ nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL);
+ }
+ {
+ plist_t keyarr = plist_new_array();
+ plist_t valarr = plist_new_array();
+ do {
+ char* key = va_arg(*va, char*);
+ if (!key)
+ break;
+ int ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key);
+ nskeyedarchive_nsarray_append_v(ka, valarr, ptype, va);
+ } while (1);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr);
+ plist_free(keyarr);
+ plist_free(valarr);
+ }
+ break;
+ case NSTYPE_NSDATE:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSDate", "NSObject", NULL);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.time", NSTYPE_REAL, va_arg(*va, double));
+ break;
+ case NSTYPE_NSMUTABLEDATA:
+ case NSTYPE_NSDATA:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSMutableData", "NSData", "NSObject", NULL);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.data", NSTYPE_DATA, va_arg(*va, char*));
+ break;
+ case NSTYPE_NSURL:
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSURL", "NSObject", NULL);
+ {
+ int ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_set_class_property_v(ka, newuid, "NS.base", ptype, va);
+ ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_set_class_property_v(ka, newuid, "NS.relative", ptype, va);
+ }
+ break;
+ case NSTYPE_NSKEYEDARCHIVE:
+ {
+ nskeyedarchive_t pka = va_arg(*va, nskeyedarchive_t);
+ if (!pka) {
+ fprintf(stderr, "%s: ERROR: no nskeyedarchive argument given for type NSTYPE_NSKEYEDARCHIVE\n", __func__);
+ return;
+ }
+ uint64_t top = nskeyedarchive_get_class_uid(pka, NULL);
+ if (top != 0) {
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+
+ plist_t object = nskeyedarchive_get_object_by_uid(pka, top);
+ if (!object) {
+ fprintf(stderr, "%s: ERROR: can't get object for uid %lld\n", __func__, (long long)top);
+ return;
+ }
+
+ plist_t objcopy = plist_copy(object);
+ nskeyedarchive_append_object(ka, objcopy);
+ nskeyedarchive_merge_object(ka, pka, objcopy);
+ }
+ }
+ break;
+ case NSTYPE_FROM_PLIST:
+ {
+ plist_t plist = va_arg(*va, plist_t);
+ if (!plist) {
+ fprintf(stderr, "%s: ERROR: no plist argument given for NSTYPE_PLIST\n", __func__);
+ return;
+ }
+ switch (plist_get_node_type(plist)) {
+ case PLIST_STRING:
+ {
+ char* str = NULL;
+ plist_get_string_val(plist, &str);
+ nskeyedarchive_nsarray_append(ka, array, NSTYPE_NSMUTABLESTRING, str, NULL);
+ } break;
+ case PLIST_DICT:
+ {
+ plist_array_append_item(array, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL);
+
+ plist_t keyarr = plist_new_array();
+ plist_t valarr = plist_new_array();
+
+ plist_dict_iter iter = NULL;
+ plist_dict_new_iter(plist, &iter);
+
+ char *key = NULL;
+ plist_t node = NULL;
+
+ do {
+ plist_dict_next_item(plist, iter, &key, &node);
+ if (key) {
+ int ptype = 0;
+ uint8_t bv = 0;
+ uint64_t u64val = 0;
+ int intval = 0;
+ char *str = NULL;
+ void *val = NULL;
+ nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key);
+ switch (plist_get_node_type(node)) {
+ case PLIST_BOOLEAN: {
+ ptype = NSTYPE_BOOLEAN;
+ plist_get_bool_val(node, &bv);
+ intval = bv;
+ val = &intval;
+ } break;
+ case PLIST_UINT:
+ ptype = NSTYPE_INTEGER;
+ plist_get_uint_val(node, &u64val);
+ val = &u64val;
+ break;
+ case PLIST_STRING:
+ ptype = NSTYPE_STRING;
+ plist_get_string_val(node, &str);
+ val = str;
+ break;
+ default:
+ fprintf(stderr, "Unhandled plist type when parsing plist_dict\n");
+ break;
+ }
+ nskeyedarchive_nsarray_append(ka, valarr, ptype, val);
+ }
+ free(key);
+ } while (node);
+
+ free(iter);
+
+ nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr);
+ plist_free(keyarr);
+ plist_free(valarr);
+ } break;
+ default:
+ fprintf(stderr, "%s: ERROR: unhandled plist type %d\n", __func__, plist_get_node_type(plist));
+ return;
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: unexpected type %d\n", __func__, type);
+ break;
+ }
+}
+
+static void nskeyedarchive_nsarray_append(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, ...)
+{
+ va_list va;
+ va_start(va, type);
+ nskeyedarchive_nsarray_append_v(ka, array, type, &va);
+ va_end(va);
+}
+
+void nskeyedarchive_nsarray_append_item(nskeyedarchive_t ka, uint64_t uid, enum nskeyedarchive_class_type_t type, ...)
+{
+ if (!ka) {
+ return;
+ }
+ plist_t objects = NULL;
+ nskeyedarchive_get_class_property(ka, uid, "NS.objects", &objects);
+ if (!objects) {
+ fprintf(stderr, "ERROR: invalid NSArray object in archive: missing NS.objects property\n");
+ return;
+ }
+ va_list va;
+ va_start(va, type);
+ nskeyedarchive_nsarray_append_v(ka, objects, type, &va);
+ va_end(va);
+}
+
+void nskeyedarchive_nsdictionary_add_item(nskeyedarchive_t ka, uint64_t uid, const char* key, enum nskeyedarchive_class_type_t type, ...)
+{
+ if (!ka) {
+ return;
+ }
+ plist_t keys = NULL;
+ nskeyedarchive_get_class_property(ka, uid, "NS.keys", &keys);
+ if (!keys) {
+ fprintf(stderr, "ERROR: invalid NSDictionary object in archive: missing NS.keys property\n");
+ return;
+ }
+ plist_t objects = NULL;
+ nskeyedarchive_get_class_property(ka, uid, "NS.objects", &objects);
+ if (!objects) {
+ fprintf(stderr, "ERROR: invalid NSDictionary object in archive: missing NS.objects property\n");
+ return;
+ }
+ va_list va;
+ va_start(va, type);
+ nskeyedarchive_nsarray_append(ka, keys, NSTYPE_STRING, key);
+ nskeyedarchive_nsarray_append_v(ka, objects, type, &va);
+ va_end(va);
+}
+
+void nskeyedarchive_append_class_type_v(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, va_list* va)
+{
+ uint64_t newuid = 0;
+
+ switch (type) {
+ case NSTYPE_INTEGER:
+ fprintf(stderr, "%s: ERROR: NSTYPE_INTEGER is not an object type, can't add it as class!.\n", __func__);
+ break;
+ case NSTYPE_INTREF:
+ nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int)));
+ break;
+ case NSTYPE_BOOLEAN:
+ nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int)));
+ break;
+ case NSTYPE_CHARS:
+ fprintf(stderr, "%s: ERROR: NSTYPE_CHARS is not an object type, can't add it as class!\n", __func__);
+ break;
+ case NSTYPE_STRING:
+ {
+ char* strval = va_arg(*va, char*);
+ if (strval) {
+ if (strcmp(strval, "$null") != 0) {
+ nskeyedarchive_append_object(ka, plist_new_string(strval));
+ } else {
+ // make $null the top class if required
+ plist_t top = plist_dict_get_item(ka->dict, "$top");
+ if (!top) {
+ top = plist_new_dict();
+ plist_dict_set_item(top, "$0", plist_new_uid(0));
+ plist_dict_set_item(ka->dict, "$top", top);
+ }
+ }
+ }
+ }
+ break;
+ case NSTYPE_REAL:
+ nskeyedarchive_append_object(ka, plist_new_real(va_arg(*va, double)));
+ break;
+ case NSTYPE_ARRAY:
+ fprintf(stderr, "%s: ERROR: NSTYPE_ARRAY is not an object type, can't add it as class!\n", __func__);
+ return;
+ case NSTYPE_NSMUTABLESTRING:
+ case NSTYPE_NSSTRING:
+ newuid = ka->uid;
+ if (type == NSTYPE_NSMUTABLESTRING) {
+ nskeyedarchive_append_class(ka, "NSMutableString", "NSString", "NSObject", NULL);
+ } else {
+ nskeyedarchive_append_class(ka, "NSString", "NSObject", NULL);
+ }
+ nskeyedarchive_set_class_property(ka, newuid, "NS.string", NSTYPE_STRING, va_arg(*va, char*));
+ break;
+ case NSTYPE_NSMUTABLEARRAY:
+ case NSTYPE_NSARRAY:
+ newuid = ka->uid;
+ if (type == NSTYPE_NSMUTABLEARRAY) {
+ nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL);
+ } else {
+ nskeyedarchive_append_class(ka, "NSArray", "NSObject", NULL);
+ }
+ {
+ plist_t arr = plist_new_array();
+ do {
+ int ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_nsarray_append_v(ka, arr, ptype, va);
+ } while (1);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr);
+ plist_free(arr);
+ }
+ break;
+ case NSTYPE_NSMUTABLEDICTIONARY:
+ case NSTYPE_NSDICTIONARY:
+ newuid = ka->uid;
+ if (type == NSTYPE_NSMUTABLEDICTIONARY) {
+ nskeyedarchive_append_class(ka, "NSMutableDictionary", "NSDictionary", "NSObject", NULL);
+ } else {
+ nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL);
+ }
+ {
+ plist_t keyarr = plist_new_array();
+ plist_t valarr = plist_new_array();
+ do {
+ char* key = va_arg(*va, char*);
+ if (!key)
+ break;
+ int ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key);
+ nskeyedarchive_nsarray_append_v(ka, valarr, ptype, va);
+ } while (1);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr);
+ plist_free(keyarr);
+ plist_free(valarr);
+ }
+ break;
+ case NSTYPE_NSDATE:
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSDate", "NSObject", NULL);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.time", NSTYPE_REAL, va_arg(*va, double));
+ break;
+ case NSTYPE_NSMUTABLEDATA:
+ case NSTYPE_NSDATA:
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSMutableData", "NSData", "NSObject", NULL);
+ nskeyedarchive_set_class_property(ka, newuid, "NS.data", NSTYPE_DATA, va_arg(*va, char*));
+ break;
+ case NSTYPE_NSURL:
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSURL", "NSObject", NULL);
+ {
+ int ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_set_class_property_v(ka, newuid, "NS.base", ptype, va);
+ ptype = va_arg(*va, int);
+ if (ptype == 0)
+ break;
+ nskeyedarchive_set_class_property_v(ka, newuid, "NS.relative", ptype, va);
+ }
+ break;
+ default:
+ fprintf(stderr, "unexpected class type %d\n", type);
+ return;
+ }
+
+ plist_t top = plist_dict_get_item(ka->dict, "$top");
+ if (!top) {
+ top = plist_new_dict();
+ plist_dict_set_item(top, "$0", plist_new_uid(1));
+ plist_dict_set_item(ka->dict, "$top", top);
+ }
+}
+
+void nskeyedarchive_append_class_type(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, ...)
+{
+ if (!ka) {
+ fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__);
+ return;
+ } else if (!type) {
+ fprintf(stderr, "%s: ERROR: invalid class type!\n", __func__);
+ return;
+ }
+
+ va_list va;
+ va_start(va, type);
+ nskeyedarchive_append_class_type_v(ka, type, &va);
+ va_end(va);
+}
+
+void nskeyedarchive_merge_object(nskeyedarchive_t ka, nskeyedarchive_t pka, plist_t object)
+{
+ if (!ka || !pka || !object) {
+ return;
+ }
+
+ switch (plist_get_node_type(object)) {
+ case PLIST_DICT:
+ {
+ plist_dict_iter iter = NULL;
+ plist_dict_new_iter(object, &iter);
+ if (iter) {
+ plist_t val;
+ char* key;
+ do {
+ key = NULL;
+ val = NULL;
+ plist_dict_next_item(object, iter, &key, &val);
+ if (key) {
+ switch (plist_get_node_type(val)) {
+ case PLIST_UID:
+ {
+ uint64_t thisuid = 0;
+ plist_get_uid_val(val, &thisuid);
+ if (thisuid > 0) {
+ // remap thisuid to ka->uid+1
+ plist_t nextobj = nskeyedarchive_get_object_by_uid(pka, thisuid);
+ plist_set_uid_val(val, ++ka->uid);
+ plist_t nextcopy = plist_copy(nextobj);
+ nskeyedarchive_append_object(ka, nextcopy);
+ nskeyedarchive_merge_object(ka, pka, nextcopy);
+ }
+ }
+ break;
+ case PLIST_DICT:
+ case PLIST_ARRAY:
+ nskeyedarchive_merge_object(ka, pka, val);
+ break;
+ default:
+ break;
+ }
+ free(key);
+ }
+ } while (val);
+ free(iter);
+ }
+ }
+ break;
+ case PLIST_ARRAY:
+ {
+ uint32_t i;
+ for (i = 0; i < plist_array_get_size(object); i++) {
+ plist_t val = plist_array_get_item(object, i);
+ switch (plist_get_node_type(val)) {
+ case PLIST_UID:
+ {
+ uint64_t thisuid = 0;
+ plist_get_uid_val(val, &thisuid);
+ if (thisuid > 0) {
+ // remap thisuid to ka->uid+1
+ plist_t nextobj = nskeyedarchive_get_object_by_uid(pka, thisuid);
+ plist_set_uid_val(val, ++ka->uid);
+ plist_t nextcopy = plist_copy(nextobj);
+ nskeyedarchive_append_object(ka, nextcopy);
+ nskeyedarchive_merge_object(ka, pka, nextcopy);
+ }
+ }
+ break;
+ case PLIST_ARRAY:
+ case PLIST_DICT:
+ nskeyedarchive_merge_object(ka, pka, val);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void nskeyedarchive_set_class_property_v(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, va_list* va)
+{
+ plist_t a = nskeyedarchive_get_class_by_uid(ka, uid);
+ if (a == NULL)
+ return;
+
+ uint64_t newuid;
+
+ switch (proptype) {
+ case NSTYPE_INTEGER:
+ plist_dict_set_item(a, propname, plist_new_uint(va_arg(*va, int)));
+ break;
+ case NSTYPE_INTREF:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int)));
+ break;
+ case NSTYPE_BOOLEAN:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int)));
+ break;
+ case NSTYPE_CHARS:
+ plist_dict_set_item(a, propname, plist_new_string(va_arg(*va, char*)));
+ break;
+ case NSTYPE_STRING:
+ {
+ char* strval = va_arg(*va, char*);
+ if (strval && (strcmp(strval, "$null") == 0)) {
+ plist_dict_set_item(a, propname, plist_new_uid(0));
+ } else {
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_object(ka, plist_new_string(strval));
+ }
+ }
+ break;
+ case NSTYPE_REAL:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_class_type_v(ka, proptype, va);
+ break;
+ case NSTYPE_ARRAY:
+ plist_dict_set_item(a, propname, plist_copy(va_arg(*va, plist_t)));
+ break;
+ case NSTYPE_NSMUTABLESTRING:
+ case NSTYPE_NSSTRING:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_class_type_v(ka, proptype, va);
+ break;
+ case NSTYPE_NSMUTABLEARRAY:
+ case NSTYPE_NSARRAY:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_class_type_v(ka, proptype, va);
+ break;
+ case NSTYPE_NSMUTABLEDICTIONARY:
+ case NSTYPE_NSDICTIONARY:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_class_type_v(ka, proptype, va);
+ break;
+ case NSTYPE_NSDATE:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_class_type_v(ka, proptype, va);
+ break;
+ case NSTYPE_NSMUTABLEDATA:
+ case NSTYPE_NSDATA:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_class_type_v(ka, proptype, va);
+ break;
+ case NSTYPE_NSURL:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ nskeyedarchive_append_class_type_v(ka, proptype, va);
+ break;
+ case NSTYPE_NSKEYEDARCHIVE:
+ {
+ nskeyedarchive_t pka = va_arg(*va, nskeyedarchive_t);
+ if (!pka) {
+ fprintf(stderr, "%s: ERROR: no nskeyedarchive argument given for type NSTYPE_NSKEYEDARCHIVE\n", __func__);
+ return;
+ }
+ uint64_t top = nskeyedarchive_get_class_uid(pka, NULL);
+ if (top != 0) {
+ plist_t object = nskeyedarchive_get_object_by_uid(pka, top);
+ if (!object) {
+ fprintf(stderr, "%s: ERROR: can't get object for uid %lld\n", __func__, (long long)top);
+ return;
+ }
+
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ plist_t objcopy = plist_copy(object);
+ nskeyedarchive_append_object(ka, objcopy);
+ nskeyedarchive_merge_object(ka, pka, objcopy);
+ } else {
+ plist_dict_set_item(a, propname, plist_new_uid(0));
+ }
+ }
+ break;
+ case NSTYPE_FROM_PLIST:
+ {
+ plist_t plist = va_arg(*va, plist_t);
+ if (!plist) {
+ fprintf(stderr, "%s: ERROR: no plist argument given for NSTYPE_PLIST\n", __func__);
+ return;
+ }
+ switch (plist_get_node_type(plist)) {
+ case PLIST_ARRAY:
+ plist_dict_set_item(a, propname, plist_new_uid(++ka->uid));
+ newuid = ka->uid;
+ nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL);
+ {
+ plist_t arr = plist_new_array();
+ uint32_t ai;
+ for (ai = 0; ai < plist_array_get_size(plist); ai++) {
+ plist_t ae = plist_array_get_item(plist, ai);
+ nskeyedarchive_nsarray_append(ka, arr, NSTYPE_FROM_PLIST, ae);
+ }
+ nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr);
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: sorry, plist type %d is not implemented for conversion.\n", __func__, plist_get_node_type(plist));
+ break;
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "unexpected property type %d\n", proptype);
+ break;
+ }
+}
+
+void nskeyedarchive_set_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, ...)
+{
+ plist_t a = nskeyedarchive_get_class_by_uid(ka, uid);
+ if (a == NULL) {
+ return;
+ }
+
+ va_list va;
+ va_start(va, proptype);
+ nskeyedarchive_set_class_property_v(ka, uid, propname, proptype, &va);
+ va_end(va);
+}
+
+
+void nskeyedarchive_print(nskeyedarchive_t ka)
+{
+ char* xml = NULL;
+ uint32_t xlen = 0;
+ plist_to_xml(ka->dict, &xml, &xlen);
+ puts(xml);
+ free(xml);
+}
+
+plist_t nskeyedarchive_get_plist_ref(nskeyedarchive_t ka)
+{
+ return ka->dict;
+}
+
+nskeyedarchive_t nskeyedarchive_new_from_plist(plist_t plist)
+{
+ if (!plist || (plist_get_node_type(plist) != PLIST_DICT)) {
+ fprintf(stderr, "%s: ERROR: invalid parameter, PLIST_DICT expected\n", __func__);
+ return NULL;
+ }
+
+ plist_t node;
+ char* strval = NULL;
+ uint64_t uintval = 0;
+
+ // check for $archiver key
+ node = plist_dict_get_item(plist, "$archiver");
+ if (node && (plist_get_node_type(node) == PLIST_STRING)) {
+ plist_get_string_val(node, &strval);
+ }
+ if (!strval || (strcmp(strval, "NSKeyedArchiver") != 0)) {
+ fprintf(stderr, "%s: ERROR: plist is not in NSKeyedArchiver format ($archiver key not found or invalid)!\n", __func__);
+ if (strval)
+ free(strval);
+ return NULL;
+ }
+ if (strval)
+ free(strval);
+ strval = NULL;
+
+ // check for $version key
+ node = plist_dict_get_item(plist, "$version");
+ if (node && (plist_get_node_type(node) == PLIST_UINT)) {
+ plist_get_uint_val(node, &uintval);
+ }
+ if (uintval != 100000) {
+ fprintf(stderr, "%s: ERROR: unexpected NSKeyedArchiver version encountered (%lld != 100000)!\n", __func__, (long long int)uintval);
+ return NULL;
+ }
+ uintval = 0;
+
+ // check for $top key
+ node = plist_dict_get_item(plist, "$top");
+ if (!node || (plist_get_node_type(node) != PLIST_DICT)) {
+ fprintf(stderr, "%s: ERROR: $top node not found\n", __func__);
+ return NULL;
+ }
+ plist_t topuid = plist_dict_get_item(node, "$0");
+ if (!topuid) {
+ topuid = plist_dict_get_item(node, "root");
+ }
+ if (!topuid || (plist_get_node_type(topuid) != PLIST_UID)) {
+ fprintf(stderr, "%s: ERROR: uid '$0' or 'root' not found in $top dict!\n", __func__);
+ return NULL;
+ }
+
+ uintval = -1LL;
+ plist_get_uid_val(topuid, (uint64_t*)&uintval);
+
+ if (uintval == (uint64_t)-1LL) {
+ fprintf(stderr, "%s: ERROR: could not get UID value.\n", __func__);
+ return NULL;
+ }
+
+ // check for $objects key
+ plist_t objects = plist_dict_get_item(plist, "$objects");
+ if (!objects || (plist_get_node_type(objects) != PLIST_ARRAY)) {
+ fprintf(stderr, "%s: ERROR: $objects node not found!\n", __func__);
+ return NULL;
+ }
+ plist_t obj = plist_array_get_item(objects, (uint32_t)uintval);
+ if (!obj) {
+ fprintf(stderr, "%s: ERROR: can't get object node\n", __func__);
+ return NULL;
+ }
+
+ nskeyedarchive_t archive = (nskeyedarchive_t)malloc(sizeof(struct nskeyedarchive_st));
+ archive->dict = plist_copy(plist);
+ archive->uid = plist_array_get_size(objects) - 1;
+
+ return archive;
+}
+
+nskeyedarchive_t nskeyedarchive_new_from_data(const void* data, uint32_t size)
+{
+ if (!data || (size < 8)) {
+ fprintf(stderr, "%s: ERROR: invalid parameter\n", __func__);
+ return NULL;
+ }
+
+
+ plist_t plist = NULL;
+ if (memcmp(data, "bplist00", 8) == 0) {
+ plist_from_bin(data, size, &plist);
+ } else if ((memcmp(data, "<?xml", 5) == 0) || (memcmp(data, "<plist", 6) == 0)) {
+ plist_from_xml(data, size, &plist);
+ } else {
+ // fail silently
+ return NULL;
+ }
+ if (!plist) {
+ fprintf(stderr, "%s: ERROR: Can't parse plist from data\n", __func__);
+ return NULL;
+ }
+
+ nskeyedarchive_t ka = nskeyedarchive_new_from_plist(plist);
+ plist_free(plist);
+ return ka;
+}
+
+uint64_t nskeyedarchive_get_class_uid(nskeyedarchive_t ka, const char* classref)
+{
+ uint64_t uintval = -1LL;
+ if (!ka) {
+ return uintval;
+ }
+ if (!ka->dict) {
+ return uintval;
+ }
+
+ plist_t node;
+
+ node = plist_dict_get_item(ka->dict, "$top");
+ if (!node || (plist_get_node_type(node) != PLIST_DICT)) {
+ fprintf(stderr, "%s: ERROR: $top node not found\n", __func__);
+ return 0;
+ }
+ plist_t topuid = plist_dict_get_item(node, (classref) ? classref : "$0");
+ if (!topuid && !classref) {
+ topuid = plist_dict_get_item(node, "root");
+ }
+ if (!topuid || (plist_get_node_type(topuid) != PLIST_UID)) {
+ fprintf(stderr, "%s: ERROR: uid for '%s' not found in $top dict!\n", __func__, classref);
+ return 0;
+ }
+
+ plist_get_uid_val(topuid, (uint64_t*)&uintval);
+
+ return uintval;
+}
+
+const char* nskeyedarchive_get_classname(nskeyedarchive_t ka, uint64_t uid)
+{
+ if (!ka) {
+ return NULL;
+ }
+ if (!ka->dict) {
+ return NULL;
+ }
+
+ plist_t obj = nskeyedarchive_get_object_by_uid(ka, uid);
+ if (!obj) {
+ return NULL;
+ }
+
+ plist_t classuid = plist_dict_get_item(obj, "$class");
+ if (plist_get_node_type(classuid) != PLIST_UID) {
+ fprintf(stderr, "ERROR: $class is not a uid node\n");
+ return NULL;
+ }
+
+ uint64_t uintval = 0;
+ plist_get_uid_val(classuid, &uintval);
+ if (uintval == 0) {
+ fprintf(stderr, "ERROR: can't get $class uid val\n");
+ return NULL;
+ }
+
+ plist_t cls = nskeyedarchive_get_class_by_uid(ka, uintval);
+ if (!cls) {
+ return NULL;
+ }
+
+ plist_t classname = plist_dict_get_item(cls, "$classname");
+ if (!classname || (plist_get_node_type(classname) != PLIST_STRING)) {
+ fprintf(stderr, "ERROR: invalid $classname in class dict\n");
+ return NULL;
+ }
+
+ return plist_get_string_ptr(classname, NULL);
+}
+
+int nskeyedarchive_get_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, plist_t* value)
+{
+ if (!ka || !ka->dict || !value) {
+ return -1;
+ }
+
+ plist_t obj = nskeyedarchive_get_class_by_uid(ka, uid);
+ if (!obj) {
+ return -1;
+ }
+
+ *value = plist_dict_get_item(obj, propname);
+ if (!*value) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int nskeyedarchive_get_class_uint64_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, uint64_t* value)
+{
+ plist_t prop = NULL;
+
+ nskeyedarchive_get_class_property(ka, uid, propname, &prop);
+ if (!prop) {
+ fprintf(stderr, "%s: ERROR: no such property '%s'\n", __func__, propname);
+ return -1;
+ }
+
+ if (plist_get_node_type(prop) == PLIST_UID) {
+ uint64_t uintval = 0;
+ plist_get_uid_val(prop, &uintval);
+ prop = nskeyedarchive_get_object_by_uid(ka, uintval);
+ }
+
+ if (plist_get_node_type(prop) != PLIST_UINT) {
+ fprintf(stderr, "%s: ERROR: property '%s' is not of type integer.\n", __func__, propname);
+ return -1;
+ }
+
+ plist_get_uint_val(prop, value);
+
+ return 0;
+}
+
+int nskeyedarchive_get_class_int_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, int* value)
+{
+ uint64_t uintval = 0;
+ int res = nskeyedarchive_get_class_uint64_property(ka, uid, propname, &uintval);
+ if (res < 0) {
+ return res;
+ }
+ *value = (int)uintval;
+ return 0;
+}
+
+int nskeyedarchive_get_class_string_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, char** value)
+{
+ plist_t node = NULL;
+
+ nskeyedarchive_get_class_property(ka, uid, propname, &node);
+ if (!node || (plist_get_node_type(node) != PLIST_UID)) {
+ return -1;
+ }
+
+ uint64_t uintval = 0;
+ plist_get_uid_val(node, &uintval);
+
+ plist_t prop = nskeyedarchive_get_object_by_uid(ka, uintval);
+ if (!prop || (plist_get_node_type(prop) != PLIST_STRING)) {
+ fprintf(stderr, "%s: ERROR: property '%s' is not a string\n", __func__, propname);
+ return -1;
+ }
+
+ plist_get_string_val(prop, value);
+
+ return 0;
+}
+
+static plist_t _nska_parse_object(nskeyedarchive_t ka, uint64_t uid)
+{
+ plist_t obj = nskeyedarchive_get_object_by_uid(ka, uid);
+ switch (plist_get_node_type(obj)) {
+ case PLIST_BOOLEAN:
+ case PLIST_INT:
+ case PLIST_STRING:
+ return plist_copy(obj);
+ default:
+ break;
+ }
+
+ const char* clsn = nskeyedarchive_get_classname(ka, uid);
+ plist_t pl = NULL;
+ if (!strcmp(clsn, "NSMutableDictionary") || !strcmp(clsn, "NSDictionary")) {
+ plist_t keys = NULL;
+ nskeyedarchive_get_class_property(ka, uid, "NS.keys", &keys);
+ plist_t vals = NULL;
+ nskeyedarchive_get_class_property(ka, uid, "NS.objects", &vals);
+
+ uint32_t count = plist_array_get_size(keys);
+ if (count != plist_array_get_size(vals)) {
+ printf("ERROR: %s: inconsistent number of keys vs. values in dictionary object\n", __func__);
+ return NULL;
+ }
+ pl = plist_new_dict();
+ uint32_t i = 0;
+ for (i = 0; i < count; i++) {
+ plist_t knode = plist_array_get_item(keys, i);
+ plist_t vnode = plist_array_get_item(vals, i);
+
+ uint64_t subuid = 0;
+ plist_get_uid_val(knode, &subuid);
+ plist_t newkey = _nska_parse_object(ka, subuid);
+
+ subuid = 0;
+ plist_get_uid_val(vnode, &subuid);
+ plist_t newval = _nska_parse_object(ka, subuid);
+
+ if (!PLIST_IS_STRING(newkey)) {
+ printf("ERROR: %s: key node is not of string type.\n", __func__);
+ return NULL;
+ }
+
+ plist_dict_set_item(pl, plist_get_string_ptr(newkey, NULL), newval);
+ plist_free(newkey);
+ }
+ } else if (!strcmp(clsn, "NSMutableArray") || !strcmp(clsn, "NSArray")) {
+ plist_t vals = NULL;
+ nskeyedarchive_get_class_property(ka, uid, "NS.objects", &vals);
+
+ uint32_t count = plist_array_get_size(vals);
+ pl = plist_new_array();
+ uint32_t i = 0;
+ for (i = 0; i < count; i++) {
+ plist_t vnode = plist_array_get_item(vals, i);
+ uint64_t subuid = 0;
+ plist_get_uid_val(vnode, &subuid);
+ plist_t newval = _nska_parse_object(ka, subuid);
+ plist_array_append_item(pl, newval);
+ }
+ } else {
+ printf("ERROR: %s: unhandled class type '%s'\n", __func__, clsn);
+ }
+ return pl;
+}
+
+plist_t nskeyedarchive_to_plist(nskeyedarchive_t ka)
+{
+ uint64_t obj_uid = nskeyedarchive_get_class_uid(ka, NULL);
+ return _nska_parse_object(ka, obj_uid);
+}