summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/base
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/base')
-rw-r--r--third_party/heimdal/lib/base/Makefile.am95
-rw-r--r--third_party/heimdal/lib/base/NTMakefile134
-rw-r--r--third_party/heimdal/lib/base/array.c478
-rw-r--r--third_party/heimdal/lib/base/baselocl.h98
-rw-r--r--third_party/heimdal/lib/base/bool.c59
-rw-r--r--third_party/heimdal/lib/base/bsearch.c888
-rw-r--r--third_party/heimdal/lib/base/common_plugin.h105
-rw-r--r--third_party/heimdal/lib/base/config_file.c1458
-rw-r--r--third_party/heimdal/lib/base/config_reg.c658
-rw-r--r--third_party/heimdal/lib/base/context.c394
-rw-r--r--third_party/heimdal/lib/base/data.c166
-rw-r--r--third_party/heimdal/lib/base/db.c1721
-rw-r--r--third_party/heimdal/lib/base/dict.c305
-rw-r--r--third_party/heimdal/lib/base/dll.c325
-rw-r--r--third_party/heimdal/lib/base/error.c178
-rw-r--r--third_party/heimdal/lib/base/error_string.c177
-rw-r--r--third_party/heimdal/lib/base/expand_path.c725
-rw-r--r--third_party/heimdal/lib/base/heim_err.et57
-rw-r--r--third_party/heimdal/lib/base/heimbase-atomics.h255
-rw-r--r--third_party/heimdal/lib/base/heimbase-svc.h83
-rw-r--r--third_party/heimdal/lib/base/heimbase.c1081
-rw-r--r--third_party/heimdal/lib/base/heimbase.h527
-rw-r--r--third_party/heimdal/lib/base/heimbasepriv.h115
-rw-r--r--third_party/heimdal/lib/base/json.c811
-rw-r--r--third_party/heimdal/lib/base/log.c1074
-rw-r--r--third_party/heimdal/lib/base/null.c53
-rw-r--r--third_party/heimdal/lib/base/number.c136
-rw-r--r--third_party/heimdal/lib/base/plugin.c785
-rw-r--r--third_party/heimdal/lib/base/roken_rename.h61
-rw-r--r--third_party/heimdal/lib/base/string.c260
-rw-r--r--third_party/heimdal/lib/base/test_base.c962
-rw-r--r--third_party/heimdal/lib/base/version-script.map208
-rw-r--r--third_party/heimdal/lib/base/warn.c169
33 files changed, 14601 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/base/Makefile.am b/third_party/heimdal/lib/base/Makefile.am
new file mode 100644
index 0000000..18300b3
--- /dev/null
+++ b/third_party/heimdal/lib/base/Makefile.am
@@ -0,0 +1,95 @@
+
+include $(top_srcdir)/Makefile.am.common
+
+if do_roken_rename
+ES = base64.c
+endif
+
+IMPLEMENT_TLS=
+if MAINTAINER_MODE
+IMPLEMENT_TLS += dll.c
+AM_CPPFLAGS += -DHEIM_BASE_MAINTAINER
+endif
+
+AM_CPPFLAGS += $(ROKEN_RENAME) -I../com_err -I$(srcdir)/../com_err
+
+lib_LTLIBRARIES = libheimbase.la
+check_PROGRAMS = test_base
+
+libheimbase_la_LDFLAGS = -version-info 1:0:0
+
+if FRAMEWORK_COREFOUNDATION
+libheimbase_la_LDFLAGS += -framework CoreFoundation
+endif
+
+TESTS = test_base
+
+if versionscript
+libheimbase_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+endif
+
+libheimbase_la_LIBADD = $(PTHREAD_LIBADD) $(LIB_dlopen) $(LIB_com_err)
+
+include_HEADERS = heimbase.h common_plugin.h heimbase-atomics.h heimbase-svc.h
+
+ERR_FILES = heim_err.c
+
+dist_libheimbase_la_SOURCES = \
+ array.c \
+ baselocl.h \
+ bsearch.c \
+ bool.c \
+ common_plugin.h \
+ config_file.c \
+ context.c \
+ data.c \
+ db.c \
+ dict.c \
+ $(IMPLEMENT_TLS) \
+ error.c \
+ error_string.c \
+ expand_path.c \
+ heimbase.c \
+ heimbasepriv.h \
+ json.c \
+ log.c \
+ null.c \
+ number.c \
+ plugin.c \
+ roken_rename.h \
+ string.c \
+ warn.c
+
+nodist_libheimbase_la_SOURCES = $(ES) $(ERR_FILES)
+
+$(libheimbase_la_OBJECTS): $(srcdir)/heimbase-protos.h heim_err.h
+
+$(srcdir)/heimbase-protos.h: $(dist_libheimbase_la_SOURCES)
+ cd $(srcdir) && \
+ perl ../../cf/make-proto.pl -q -P comment -o heimbase-protos.h $(dist_libheimbase_la_SOURCES) || \
+ rm -f heimbase-protos.h
+
+$(srcdir)/heimbase-private.h: $(dist_libheimbase_la_SOURCES)
+ cd $(srcdir) && \
+ perl ../../cf/make-proto.pl -q -P comment -p heimbase-private.h $(dist_libheimbase_la_SOURCES) || \
+ rm -f heimbase-private.h
+
+# install these?
+
+libheimbase_la_DEPENDENCIES = version-script.map
+
+test_base_LDADD = libheimbase.la $(LIB_roken)
+
+CLEANFILES = base64.c test_db.json heim_err.c heim_err.h
+
+EXTRA_DIST = NTMakefile version-script.map config_reg.c heim_err.et
+
+dist_include_HEADERS = heimbase-protos.h
+
+nodist_include_HEADERS = heim_err.h
+
+heim_err.h: heim_err.et
+
+base64.c:
+ rm -f base64.c
+ $(LN_S) $(srcdir)/../roken/base64.c .
diff --git a/third_party/heimdal/lib/base/NTMakefile b/third_party/heimdal/lib/base/NTMakefile
new file mode 100644
index 0000000..49cd8b8
--- /dev/null
+++ b/third_party/heimdal/lib/base/NTMakefile
@@ -0,0 +1,134 @@
+########################################################################
+#
+# Copyright (c) 2010, Secure Endpoints Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+RELDIR=lib\base
+
+intcflags=-I$(SRCDIR) -I$(OBJ)
+
+!include ../../windows/NTMakefile.w32
+
+INCFILES= \
+ $(INCDIR)\heimbase.h \
+ $(INCDIR)\heimbase-protos.h \
+ $(INCDIR)\heimbase-atomics.h \
+ $(INCDIR)\heimbase-svc.h \
+ $(INCDIR)\heim_err.h \
+ $(INCDIR)\common_plugin.h
+
+test_binaries = $(OBJ)\test_base.exe
+
+libheimbase_SOURCES = \
+ array.c \
+ bool.c \
+ bsearch.c \
+ config_file.c \
+ config_reg.c \
+ context.c \
+ data.c \
+ db.c \
+ dict.c \
+ dll.c \
+ error.c \
+ error_string.c \
+ expand_path.c \
+ heimbase.c \
+ json.c \
+ log.c \
+ null.c \
+ number.c \
+ plugin.c \
+ string.c \
+ warn.c
+
+libheimbase_OBJS = \
+ $(OBJ)\array.obj \
+ $(OBJ)\bool.obj \
+ $(OBJ)\bsearch.obj \
+ $(OBJ)\config_file.obj \
+ $(OBJ)\config_reg.obj \
+ $(OBJ)\context.obj \
+ $(OBJ)\data.obj \
+ $(OBJ)\db.obj \
+ $(OBJ)\dict.obj \
+ $(OBJ)\dll.obj \
+ $(OBJ)\error.obj \
+ $(OBJ)\error_string.obj \
+ $(OBJ)\expand_path.obj \
+ $(OBJ)\heimbase.obj \
+ $(OBJ)\json.obj \
+ $(OBJ)\log.obj \
+ $(OBJ)\null.obj \
+ $(OBJ)\number.obj \
+ $(OBJ)\plugin.obj \
+ $(OBJ)\string.obj \
+ $(OBJ)\warn.obj
+
+libheimbase_gen_OBJS= $(OBJ)\heim_err.obj
+
+$(LIBHEIMBASE): $(libheimbase_OBJS) $(libheimbase_gen_OBJS)
+ $(LIBCON_C) -OUT:$@ $(LIBROKEN) $(LIBCOMERR) $(PTHREAD_LIB) Secur32.lib Shell32.lib Advapi32.lib Shlwapi.lib @<<
+$(libheimbase_OBJS: =
+)
+$(libheimbase_gen_OBJS: =
+)
+<<
+
+$(OBJ)\heimbase-protos.h: $(libheimbase_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl -E KRB5_LIB -q -P remove -o $(OBJ)\heimbase-protos.h $(libheimbase_SOURCES) || $(RM) -f $(OBJ)\heimbase-protos.h
+
+$(OBJ)\heimbase-private.h: $(libheimbase_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl -q -P remove -p $(OBJ)\heimbase-private.h $(libheimbase_SOURCES) || $(RM) -f $(OBJ)\heimbase-private.h
+
+$(OBJ)\heim_err.c $(OBJ)\heim_err.h: heim_err.et
+ cd $(OBJ)
+ $(BINDIR)\compile_et.exe $(SRCDIR)\heim_err.et
+ cd $(SRCDIR)
+
+test:: test-binaries test-run
+
+test-run:
+ cd $(OBJ)
+ -test_base.exe
+ cd $(SRCDIR)
+
+all:: $(INCFILES) $(LIBHEIMBASE)
+
+clean::
+ -$(RM) $(INCFILES)
+
+test-binaries: $(test_binaries)
+
+$(test_binaries): $$(@R).obj $(LIBHEIMBASE) $(LIBVERS) $(LIBROKEN)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
+$(test_binaries:.exe=.obj): $$(@B).c
+ $(C2OBJ_C) -Fo$@ -Fd$(@D)\ $** -DBlah
diff --git a/third_party/heimdal/lib/base/array.c b/third_party/heimdal/lib/base/array.c
new file mode 100644
index 0000000..994fa7d
--- /dev/null
+++ b/third_party/heimdal/lib/base/array.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+/*
+ *
+ */
+
+struct heim_array_data {
+ size_t len;
+ heim_object_t *val;
+ size_t allocated_len;
+ heim_object_t *allocated;
+};
+
+static void HEIM_CALLCONV
+array_dealloc(heim_object_t ptr)
+{
+ heim_array_t array = ptr;
+ size_t n;
+ for (n = 0; n < array->len; n++)
+ heim_release(array->val[n]);
+ free(array->allocated);
+}
+
+struct heim_type_data array_object = {
+ HEIM_TID_ARRAY,
+ "array-object",
+ NULL,
+ array_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/**
+ * Allocate an array
+ *
+ * @return A new allocated array, free with heim_release()
+ */
+
+heim_array_t
+heim_array_create(void)
+{
+ heim_array_t array;
+
+ array = _heim_alloc_object(&array_object, sizeof(*array));
+ if (array == NULL)
+ return NULL;
+
+ array->allocated = NULL;
+ array->allocated_len = 0;
+ array->val = NULL;
+ array->len = 0;
+
+ return array;
+}
+
+/**
+ * Get type id of an dict
+ *
+ * @return the type id
+ */
+
+heim_tid_t
+heim_array_get_type_id(void)
+{
+ return HEIM_TID_ARRAY;
+}
+
+/**
+ * Append object to array
+ *
+ * @param array array to add too
+ * @param object the object to add
+ *
+ * @return zero if added, errno otherwise
+ */
+
+int
+heim_array_append_value(heim_array_t array, heim_object_t object)
+{
+ heim_object_t *ptr;
+ size_t leading = array->val - array->allocated; /* unused leading slots */
+ size_t trailing = array->allocated_len - array->len - leading;
+ size_t new_len;
+
+ if (trailing > 0) {
+ /* We have pre-allocated space; use it */
+ array->val[array->len++] = heim_retain(object);
+ return 0;
+ }
+
+ if (leading > (array->len + 1)) {
+ /*
+ * We must have appending to, and deleting at index 0 from this
+ * array a lot; don't want to grow forever!
+ */
+ (void) memmove(&array->allocated[0], &array->val[0],
+ array->len * sizeof(array->val[0]));
+ array->val = array->allocated;
+
+ /* We have pre-allocated space; use it */
+ array->val[array->len++] = heim_retain(object);
+ return 0;
+ }
+
+ /* Pre-allocate extra .5 times number of used slots */
+ new_len = leading + array->len + 1 + (array->len >> 1);
+ ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
+ if (ptr == NULL)
+ return ENOMEM;
+ array->allocated = ptr;
+ array->allocated_len = new_len;
+ array->val = &ptr[leading];
+ array->val[array->len++] = heim_retain(object);
+
+ return 0;
+}
+
+/*
+ * Internal function to insert at index 0, taking care to optimize the
+ * case where we're always inserting at index 0, particularly the case
+ * where we insert at index 0 and delete from the right end.
+ */
+static int
+heim_array_prepend_value(heim_array_t array, heim_object_t object)
+{
+ heim_object_t *ptr;
+ size_t leading = array->val - array->allocated; /* unused leading slots */
+ size_t trailing = array->allocated_len - array->len - leading;
+ size_t new_len;
+
+ if (leading > 0) {
+ /* We have pre-allocated space; use it */
+ array->val--;
+ array->val[0] = heim_retain(object);
+ array->len++;
+ return 0;
+ }
+ if (trailing > (array->len + 1)) {
+ /*
+ * We must have prepending to, and deleting at index
+ * array->len - 1 from this array a lot; don't want to grow
+ * forever!
+ */
+ (void) memmove(&array->allocated[array->len], &array->val[0],
+ array->len * sizeof(array->val[0]));
+ array->val = &array->allocated[array->len];
+
+ /* We have pre-allocated space; use it */
+ array->val--;
+ array->val[0] = heim_retain(object);
+ array->len++;
+ return 0;
+ }
+ /* Pre-allocate extra .5 times number of used slots */
+ new_len = array->len + 1 + trailing + (array->len >> 1);
+ ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
+ if (ptr == NULL)
+ return ENOMEM;
+ (void) memmove(&ptr[1], &ptr[0], array->len * sizeof (array->val[0]));
+ array->allocated = ptr;
+ array->allocated_len = new_len;
+ array->val = &ptr[0];
+ array->val[0] = heim_retain(object);
+ array->len++;
+
+ return 0;
+}
+
+/**
+ * Insert an object at a given index in an array
+ *
+ * @param array array to add too
+ * @param idx index where to add element (-1 == append, -2 next to last, ...)
+ * @param object the object to add
+ *
+ * @return zero if added, errno otherwise
+ */
+
+int
+heim_array_insert_value(heim_array_t array, size_t idx, heim_object_t object)
+{
+ int ret;
+
+ if (idx == 0)
+ return heim_array_prepend_value(array, object);
+ else if (idx > array->len)
+ heim_abort("index too large");
+
+ /*
+ * We cheat: append this element then rotate elements around so we
+ * have this new element at the desired location, unless we're truly
+ * appending the new element. This means reusing array growth in
+ * heim_array_append_value() instead of duplicating that here.
+ */
+ ret = heim_array_append_value(array, object);
+ if (ret != 0 || idx == (array->len - 1))
+ return ret;
+ /*
+ * Shift to the right by one all the elements after idx, then set
+ * [idx] to the new object.
+ */
+ (void) memmove(&array->val[idx + 1], &array->val[idx],
+ (array->len - idx - 1) * sizeof(array->val[0]));
+ array->val[idx] = heim_retain(object);
+
+ return 0;
+}
+
+/**
+ * Iterate over all objects in array
+ *
+ * @param array array to iterate over
+ * @param ctx context passed to fn
+ * @param fn function to call on each object
+ */
+
+void
+heim_array_iterate_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
+{
+ size_t n;
+ int stop = 0;
+ for (n = 0; n < array->len; n++) {
+ fn(array->val[n], ctx, &stop);
+ if (stop)
+ return;
+ }
+}
+
+#ifdef __BLOCKS__
+/**
+ * Iterate over all objects in array
+ *
+ * @param array array to iterate over
+ * @param fn block to call on each object
+ */
+
+void
+heim_array_iterate(heim_array_t array, void (^fn)(heim_object_t, int *))
+{
+ size_t n;
+ int stop = 0;
+ for (n = 0; n < array->len; n++) {
+ fn(array->val[n], &stop);
+ if (stop)
+ return;
+ }
+}
+#endif
+
+/**
+ * Iterate over all objects in array, backwards
+ *
+ * @param array array to iterate over
+ * @param ctx context passed to fn
+ * @param fn function to call on each object
+ */
+
+void
+heim_array_iterate_reverse_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
+{
+ size_t n;
+ int stop = 0;
+
+ for (n = array->len; n > 0; n--) {
+ fn(array->val[n - 1], ctx, &stop);
+ if (stop)
+ return;
+ }
+}
+
+#ifdef __BLOCKS__
+/**
+ * Iterate over all objects in array, backwards
+ *
+ * @param array array to iterate over
+ * @param fn block to call on each object
+ */
+
+void
+heim_array_iterate_reverse(heim_array_t array, void (^fn)(heim_object_t, int *))
+{
+ size_t n;
+ int stop = 0;
+ for (n = array->len; n > 0; n--) {
+ fn(array->val[n - 1], &stop);
+ if (stop)
+ return;
+ }
+}
+#endif
+
+/**
+ * Get length of array
+ *
+ * @param array array to get length of
+ *
+ * @return length of array
+ */
+
+size_t
+heim_array_get_length(heim_array_t array)
+{
+ return array->len;
+}
+
+/**
+ * Get value of element at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ *
+ * @return a not-retained copy of the object
+ */
+
+heim_object_t
+heim_array_get_value(heim_array_t array, size_t idx)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ return array->val[idx];
+}
+
+/**
+ * Get value of element at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ *
+ * @return a retained copy of the object
+ */
+
+heim_object_t
+heim_array_copy_value(heim_array_t array, size_t idx)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ return heim_retain(array->val[idx]);
+}
+
+/**
+ * Set value at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ * @param value value to set
+ *
+ */
+
+void
+heim_array_set_value(heim_array_t array, size_t idx, heim_object_t value)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ heim_release(array->val[idx]);
+ array->val[idx] = heim_retain(value);
+}
+
+/**
+ * Delete value at idx
+ *
+ * @param array the array to modify
+ * @param idx the key to delete
+ */
+
+void
+heim_array_delete_value(heim_array_t array, size_t idx)
+{
+ heim_object_t obj;
+ if (idx >= array->len)
+ heim_abort("index too large");
+ obj = array->val[idx];
+
+ array->len--;
+
+ /*
+ * Deleting the first or last elements is cheap, as we leave
+ * allocated space for opportunistic reuse later; no realloc(), no
+ * memmove(). All others require a memmove().
+ *
+ * If we ever need to optimize deletion of non-last/ non-first
+ * element we can use a tagged object type to signify "deleted
+ * value" so we can leave holes in the array, avoid memmove()s on
+ * delete, and opportunistically re-use those holes on insert.
+ */
+ if (idx == 0)
+ array->val++;
+ else if (idx < array->len)
+ (void) memmove(&array->val[idx], &array->val[idx + 1],
+ (array->len - idx) * sizeof(array->val[0]));
+
+ heim_release(obj);
+}
+
+/**
+ * Filter out entres of array when function return true
+ *
+ * @param array the array to modify
+ * @param fn filter function
+ */
+
+void
+heim_array_filter_f(heim_array_t array, void *ctx, heim_array_filter_f_t fn)
+{
+ size_t n = 0;
+
+ while (n < array->len) {
+ if (fn(array->val[n], ctx)) {
+ heim_array_delete_value(array, n);
+ } else {
+ n++;
+ }
+ }
+}
+
+#ifdef __BLOCKS__
+
+/**
+ * Filter out entres of array when block return true
+ *
+ * @param array the array to modify
+ * @param block filter block
+ */
+
+void
+heim_array_filter(heim_array_t array, int (^block)(heim_object_t))
+{
+ size_t n = 0;
+
+ while (n < array->len) {
+ if (block(array->val[n])) {
+ heim_array_delete_value(array, n);
+ } else {
+ n++;
+ }
+ }
+}
+
+#endif /* __BLOCKS__ */
diff --git a/third_party/heimdal/lib/base/baselocl.h b/third_party/heimdal/lib/base/baselocl.h
new file mode 100644
index 0000000..7ca6439
--- /dev/null
+++ b/third_party/heimdal/lib/base/baselocl.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2010 - 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <roken.h>
+
+#define ISTILDE(x) (x == '~')
+#ifdef _WIN32
+# define ISPATHSEP(x) (x == '/' || x =='\\')
+#else
+# define ISPATHSEP(x) (x == '/')
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define HEIMDAL_TEXTDOMAIN "heimdal_krb5"
+
+#ifdef LIBINTL
+#include <libintl.h>
+#define N_(x,y) dgettext(HEIMDAL_TEXTDOMAIN, x)
+#else
+#define N_(x,y) (x)
+#define bindtextdomain(package, localedir)
+#endif
+
+#include "heimqueue.h"
+#include "heim_threads.h"
+#include "heimbase.h"
+#include "heimbasepriv.h"
+
+#ifdef HAVE_DISPATCH_DISPATCH_H
+#include <dispatch/dispatch.h>
+#endif
+
+/* tagged strings/object/XXX */
+#define heim_base_is_tagged(x) (((uintptr_t)(x)) & 0x3)
+
+#define heim_base_is_tagged_object(x) ((((uintptr_t)(x)) & 0x3) == 1)
+#define heim_base_make_tagged_object(x, tid) \
+ ((heim_object_t)((((uintptr_t)(x)) << 5) | ((tid) << 2) | 0x1))
+#define heim_base_tagged_object_tid(x) ((((uintptr_t)(x)) & 0x1f) >> 2)
+#define heim_base_tagged_object_value(x) (((uintptr_t)(x)) >> 5)
+
+/*
+ *
+ */
+
+#undef HEIMDAL_NORETURN_ATTRIBUTE
+#define HEIMDAL_NORETURN_ATTRIBUTE
+#undef HEIMDAL_PRINTF_ATTRIBUTE
+#define HEIMDAL_PRINTF_ATTRIBUTE(x)
+
+struct heim_context_s {
+ heim_log_facility *log_dest;
+ heim_log_facility *warn_dest;
+ heim_log_facility *debug_dest;
+ char *time_fmt;
+ unsigned int log_utc:1;
+ unsigned int homedir_access:1;
+ struct et_list *et_list;
+ char *error_string;
+ heim_error_code error_code;
+};
diff --git a/third_party/heimdal/lib/base/bool.c b/third_party/heimdal/lib/base/bool.c
new file mode 100644
index 0000000..f7b66ee
--- /dev/null
+++ b/third_party/heimdal/lib/base/bool.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct heim_type_data _heim_bool_object = {
+ HEIM_TID_BOOL,
+ "bool-object",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_bool_t
+heim_bool_create(int val)
+{
+ return heim_base_make_tagged_object(!!val, HEIM_TID_BOOL);
+}
+
+int
+heim_bool_val(heim_bool_t ptr)
+{
+ return heim_base_tagged_object_value(ptr);
+}
diff --git a/third_party/heimdal/lib/base/bsearch.c b/third_party/heimdal/lib/base/bsearch.c
new file mode 100644
index 0000000..268cc01
--- /dev/null
+++ b/third_party/heimdal/lib/base/bsearch.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "baselocl.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <errno.h>
+#include <assert.h>
+
+/*
+ * This file contains functions for binary searching flat text in memory
+ * and in text files where each line is a [variable length] record.
+ * Each record has a key and an optional value separated from the key by
+ * unquoted whitespace. Whitespace in the key, and leading whitespace
+ * for the value, can be quoted with backslashes (but CR and LF must be
+ * quoted in such a way that they don't appear in the quoted result).
+ *
+ * Binary searching a tree are normally a dead simple algorithm. It
+ * turns out that binary searching flat text with *variable* length
+ * records is... tricky. There's no indexes to record beginning bytes,
+ * thus any index selected during the search is likely to fall in the
+ * middle of a record. When deciding to search a left sub-tree one
+ * might fail to find the last record in that sub-tree on account of the
+ * right boundary falling in the middle of it -- the chosen solution to
+ * this makes left sub-tree searches slightly less efficient than right
+ * sub-tree searches.
+ *
+ * If binary searching flat text in memory is tricky, using block-wise
+ * I/O instead is trickier! But it's necessary in order to support
+ * large files (which we either can't or wouldn't want to read or map
+ * into memory). Each block we read has to be large enough that the
+ * largest record can fit in it. And each block might start and/or end
+ * in the middle of a record. Here it is the right sub-tree searches
+ * that are less efficient than left sub-tree searches.
+ *
+ * bsearch_common() contains the common text block binary search code.
+ *
+ * _bsearch_text() is the interface for searching in-core text.
+ * _bsearch_file() is the interface for block-wise searching files.
+ */
+
+struct bsearch_file_handle {
+ int fd; /* file descriptor */
+ char *cache; /* cache bytes */
+ char *page; /* one double-size page worth of bytes */
+ size_t file_sz; /* file size */
+ size_t cache_sz; /* cache size */
+ size_t page_sz; /* page size */
+};
+
+/* Find a new-line */
+static const char *
+find_line(const char *buf, size_t i, size_t right)
+{
+ if (i == 0)
+ return &buf[i];
+ for (; i < right; i++) {
+ if (buf[i] == '\n') {
+ if ((i + 1) < right)
+ return &buf[i + 1];
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Common routine for binary searching text in core.
+ *
+ * Perform a binary search of a char array containing a block from a
+ * text file where each line is a record (LF and CRLF supported). Each
+ * record consists of a key followed by an optional value separated from
+ * the key by whitespace. Whitespace can be quoted with backslashes.
+ * It's the caller's responsibility to encode/decode keys/values if
+ * quoting is desired; newlines should be encoded such that a newline
+ * does not appear in the result.
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * Inputs:
+ *
+ * @buf String to search
+ * @sz Size of string to search
+ * @key Key string to search for
+ * @buf_is_start True if the buffer starts with a record, false if it
+ * starts in the middle of a record or if the caller
+ * doesn't know.
+ *
+ * Outputs:
+ *
+ * @value Location to store a copy of the value (caller must free)
+ * @location Record location if found else the location where the
+ * record should be inserted (index into @buf)
+ * @cmp Set to less than or greater than 0 to indicate that a
+ * key not found would have fit in an earlier or later
+ * part of a file. Callers should use this to decide
+ * whether to read a block to the left or to the right and
+ * search that.
+ * @loops Location to store a count of bisections required for
+ * search (useful for confirming logarithmic performance)
+ */
+static int
+bsearch_common(const char *buf, size_t sz, const char *key,
+ int buf_is_start, char **value, size_t *location,
+ int *cmp, size_t *loops)
+{
+ const char *linep;
+ size_t key_start, key_len; /* key string in buf */
+ size_t val_start, val_len; /* value string in buf */
+ int key_cmp = -1;
+ size_t k;
+ size_t l; /* left side of buffer for binary search */
+ size_t r; /* right side of buffer for binary search */
+ size_t rmax; /* right side of buffer for binary search */
+ size_t i; /* index into buffer, typically in the middle of l and r */
+ size_t loop_count = 0;
+ int ret = -1;
+
+ if (value)
+ *value = NULL;
+ if (cmp)
+ *cmp = 0;
+ if (loops)
+ *loops = 0;
+
+ /* Binary search; file should be sorted */
+ for (l = 0, r = rmax = sz, i = sz >> 1; i >= l && i < rmax; loop_count++) {
+ heim_assert(i < sz, "invalid aname2lname db index");
+
+ /* buf[i] is likely in the middle of a line; find the next line */
+ linep = find_line(buf, i, rmax);
+ k = linep ? linep - buf : i;
+ if (linep == NULL || k >= rmax) {
+ /*
+ * No new line found to the right; search to the left then
+ * but don't change rmax (this isn't optimal, but it's
+ * simple).
+ */
+ if (i == l)
+ break;
+ r = i;
+ i = l + ((r - l) >> 1);
+ continue;
+ }
+ i = k;
+ heim_assert(i >= l && i < rmax, "invalid aname2lname db index");
+
+ /* Got a line; check it */
+
+ /* Search for and split on unquoted whitespace */
+ val_start = 0;
+ for (key_start = i, key_len = 0, val_len = 0, k = i; k < rmax; k++) {
+ if (buf[k] == '\\') {
+ k++;
+ continue;
+ }
+ if (buf[k] == '\r' || buf[k] == '\n') {
+ /* We now know where the key ends, and there's no value */
+ key_len = k - i;
+ break;
+ }
+ if (!isspace((unsigned char)buf[k]))
+ continue;
+
+ while (k < rmax && isspace((unsigned char)buf[k])) {
+ key_len = k - i;
+ k++;
+ }
+ if (k < rmax)
+ val_start = k;
+ /* Find end of value */
+ for (; k < rmax && buf[k] != '\0'; k++) {
+ if (buf[k] == '\r' || buf[k] == '\n') {
+ val_len = k - val_start;
+ break;
+ }
+ }
+ break;
+ }
+
+ /*
+ * The following logic is for dealing with partial buffers,
+ * which we use for block-wise binary searches of large files
+ */
+ if (key_start == 0 && !buf_is_start) {
+ /*
+ * We're at the beginning of a block that might have started
+ * in the middle of a record whose "key" might well compare
+ * as greater than the key we're looking for, so we don't
+ * bother comparing -- we know key_cmp must be -1 here.
+ */
+ key_cmp = -1;
+ break;
+ }
+ if ((val_len && buf[val_start + val_len] != '\n') ||
+ (!val_len && buf[key_start + key_len] != '\n')) {
+ /*
+ * We're at the end of a block that ends in the middle of a
+ * record whose "key" might well compare as less than the
+ * key we're looking for, so we don't bother comparing -- we
+ * know key_cmp must be >= 0 but we can't tell. Our caller
+ * will end up reading a double-size block to handle this.
+ */
+ key_cmp = 1;
+ break;
+ }
+
+ key_cmp = strncmp(key, &buf[key_start], key_len);
+ if (key_cmp == 0 && strlen(key) != key_len)
+ key_cmp = 1;
+ if (key_cmp < 0) {
+ /* search left */
+ r = rmax = (linep - buf);
+ i = l + ((r - l) >> 1);
+ if (location)
+ *location = key_start;
+ } else if (key_cmp > 0) {
+ /* search right */
+ if (l == i)
+ break; /* not found */
+ l = i;
+ i = l + ((r - l) >> 1);
+ if (location)
+ *location = val_start + val_len;
+ } else {
+ /* match! */
+ if (location)
+ *location = key_start;
+ ret = 0;
+ if (val_len && value) {
+ /* Avoid strndup() so we don't need libroken here yet */
+ if ((*value = malloc(val_len + 1))) {
+ (void) memcpy(*value, &buf[val_start], val_len);
+ (*value)[val_len] = '\0';
+ } else {
+ ret = errno;
+ }
+ }
+ break;
+ }
+ }
+
+ if (cmp)
+ *cmp = key_cmp;
+ if (loops)
+ *loops = loop_count;
+
+ return ret;
+}
+
+/*
+ * Binary search a char array containing sorted text records separated
+ * by new-lines (or CRLF). Each record consists of a key and an
+ * optional value following the key, separated from the key by unquoted
+ * whitespace.
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * Inputs:
+ *
+ * @buf Char array pointer
+ * @buf_sz Size of buf
+ * @key Key to search for
+ *
+ * Outputs:
+ *
+ * @value Location where to put the value, if any (caller must free)
+ * @location Record location if found else the location where the record
+ * should be inserted (index into @buf)
+ * @loops Location where to put a number of loops (or comparisons)
+ * needed for the search (useful for benchmarking)
+ */
+int
+_bsearch_text(const char *buf, size_t buf_sz, const char *key,
+ char **value, size_t *location, size_t *loops)
+{
+ return bsearch_common(buf, buf_sz, key, 1, value, location, NULL, loops);
+}
+
+#define MAX_BLOCK_SIZE (1024 * 1024)
+#define DEFAULT_MAX_FILE_SIZE (1024 * 1024)
+/*
+ * Open a file for binary searching. The file will be read in entirely
+ * if it is smaller than @max_sz, else a cache of @max_sz bytes will be
+ * allocated.
+ *
+ * Returns 0 on success, else an error number or -1 if the file is empty.
+ *
+ * Inputs:
+ *
+ * @fname Name of file to open
+ * @max_sz Maximum size of cache to allocate, in bytes (if zero, default)
+ * @page_sz Page size (must be a power of two, larger than 256, smaller
+ * than 1MB; if zero use default)
+ *
+ * Outputs:
+ *
+ * @bfh Handle for use with _bsearch_file() and _bsearch_file_close()
+ * @reads Number of reads performed
+ */
+int
+_bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
+ bsearch_file_handle *bfh, size_t *reads)
+{
+ bsearch_file_handle new_bfh = NULL;
+ struct stat st;
+ size_t i;
+ int fd;
+ int ret;
+
+ *bfh = NULL;
+
+ if (reads)
+ *reads = 0;
+
+ fd = open(fname, O_RDONLY);
+ if (fd == -1)
+ return errno;
+
+ if (fstat(fd, &st) == -1) {
+ ret = errno;
+ goto err;
+ }
+
+ if (st.st_size == 0) {
+ ret = -1; /* no data -> no binary search */
+ goto err;
+ }
+
+ /* Validate / default arguments */
+ if (max_sz == 0)
+ max_sz = DEFAULT_MAX_FILE_SIZE;
+ for (i = page_sz; i; i >>= 1) {
+ /* Make sure page_sz is a power of two */
+ if ((i % 2) && (i >> 1)) {
+ page_sz = 0;
+ break;
+ }
+ }
+ if (page_sz == 0)
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+ page_sz = st.st_blksize;
+#else
+ page_sz = 4096;
+#endif
+ for (i = page_sz; i; i >>= 1) {
+ /* Make sure page_sz is a power of two */
+ if ((i % 2) && (i >> 1)) {
+ /* Can't happen! Filesystems always use powers of two! */
+ page_sz = 4096;
+ break;
+ }
+ }
+ if (page_sz > MAX_BLOCK_SIZE)
+ page_sz = MAX_BLOCK_SIZE;
+
+ new_bfh = calloc(1, sizeof (*new_bfh));
+ if (new_bfh == NULL) {
+ ret = ENOMEM;
+ goto err;
+ }
+
+ new_bfh->fd = fd;
+ new_bfh->page_sz = page_sz;
+ new_bfh->file_sz = st.st_size;
+
+ if (max_sz >= st.st_size) {
+ /* Whole-file method */
+ new_bfh->cache = malloc(st.st_size + 1);
+ if (new_bfh->cache) {
+ new_bfh->cache[st.st_size] = '\0';
+ new_bfh->cache_sz = st.st_size;
+ ret = read(fd, new_bfh->cache, st.st_size);
+ if (ret < 0) {
+ ret = errno;
+ goto err;
+ }
+ if (ret != st.st_size) {
+ ret = EIO; /* XXX ??? */
+ goto err;
+ }
+ if (reads)
+ *reads = 1;
+ (void) close(fd);
+ new_bfh->fd = -1;
+ *bfh = new_bfh;
+ return 0;
+ }
+ }
+
+ /* Block-size method, or above malloc() failed */
+ new_bfh->page = malloc(new_bfh->page_sz << 1);
+ if (new_bfh->page == NULL) {
+ /* Can't even allocate a single double-size page! */
+ ret = ENOMEM;
+ goto err;
+ }
+
+ new_bfh->cache_sz = max_sz < st.st_size ? max_sz : st.st_size;
+ new_bfh->cache = malloc(new_bfh->cache_sz);
+ *bfh = new_bfh;
+
+ /*
+ * malloc() may have failed because we were asking for a lot of
+ * memory, but we may still be able to operate without a cache,
+ * so let's not fail.
+ */
+ if (new_bfh->cache == NULL) {
+ new_bfh->cache_sz = 0;
+ return 0;
+ }
+
+ /* Initialize cache */
+ for (i = 0; i < new_bfh->cache_sz; i += new_bfh->page_sz)
+ new_bfh->cache[i] = '\0';
+ return 0;
+
+err:
+ (void) close(fd);
+ if (new_bfh) {
+ free(new_bfh->page);
+ free(new_bfh->cache);
+ free(new_bfh);
+ }
+ return ret;
+}
+
+/*
+ * Indicate whether the given binary search file handle will be searched
+ * with block-wise method.
+ */
+void
+_bsearch_file_info(bsearch_file_handle bfh,
+ size_t *page_sz, size_t *max_sz, int *blockwise)
+{
+ if (page_sz)
+ *page_sz = bfh->page_sz;
+ if (max_sz)
+ *max_sz = bfh->cache_sz;
+ if (blockwise)
+ *blockwise = (bfh->file_sz != bfh->cache_sz);
+}
+
+/*
+ * Close the given binary file search handle.
+ *
+ * Inputs:
+ *
+ * @bfh Pointer to variable containing handle to close.
+ */
+void
+_bsearch_file_close(bsearch_file_handle *bfh)
+{
+ if (!*bfh)
+ return;
+ if ((*bfh)->fd >= 0)
+ (void) close((*bfh)->fd);
+ if ((*bfh)->page)
+ free((*bfh)->page);
+ if ((*bfh)->cache)
+ free((*bfh)->cache);
+ free(*bfh);
+ *bfh = NULL;
+}
+
+/*
+ * Private function to get a page from a cache. The cache is a char
+ * array of 2^n - 1 double-size page worth of bytes, where n is the
+ * number of tree levels that the cache stores. The cache can be
+ * smaller than n implies.
+ *
+ * The page may or may not be valid. If the first byte of it is NUL
+ * then it's not valid, else it is.
+ *
+ * Returns 1 if page is in cache and valid, 0 if the cache is too small
+ * or the page is invalid. The page address is output in @buf if the
+ * cache is large enough to contain it regardless of whether the page is
+ * valid.
+ *
+ * Inputs:
+ *
+ * @bfh Binary search file handle
+ * @level Level in the tree that we want a page for
+ * @page_idx Page number in the given level (0..2^level - 1)
+ *
+ * Outputs:
+ *
+ * @buf Set to address of page if the cache is large enough
+ */
+static int
+get_page_from_cache(bsearch_file_handle bfh, size_t level, size_t page_idx,
+ char **buf)
+{
+ size_t idx = 0;
+ size_t page_sz;
+
+ page_sz = bfh->page_sz << 1; /* we use double-size pages in the cache */
+
+ *buf = NULL;
+
+ /*
+ * Compute index into cache. The cache is basically an array of
+ * double-size pages. The first (zeroth) double-size page in the
+ * cache will be the middle page of the file -- the root of the
+ * tree. The next two double-size pages will be the left and right
+ * pages of the second level in the tree. The next four double-size
+ * pages will be the four pages at the next level. And so on for as
+ * many pages as fit in the cache.
+ *
+ * The page index is the number of the page at the given level. We
+ * then compute (2^level - 1 + page index) * 2page size, check that
+ * we have that in the cache, check that the page has been read (it
+ * doesn't start with NUL).
+ */
+ if (level)
+ idx = (1 << level) - 1 + page_idx;
+ if (((idx + 1) * page_sz * 2) > bfh->cache_sz)
+ return 0;
+
+ *buf = &bfh->cache[idx * page_sz * 2];
+ if (bfh->cache[idx * page_sz * 2] == '\0')
+ return 0; /* cache[idx] == NUL -> page not loaded in cache */
+ return 1;
+}
+
+/*
+ * Private function to read a page of @page_sz from @fd at offset @off
+ * into @buf, outputing the number of bytes read, which will be the same
+ * as @page_sz unless the page being read is the last page, in which
+ * case the number of remaining bytes in the file will be output.
+ *
+ * Returns 0 on success or an errno value otherwise (EIO if reads are
+ * short).
+ *
+ * Inputs:
+ *
+ * @bfh Binary search file handle
+ * @level Level in the binary search tree that we're at
+ * @page_idx Page "index" at the @level of the tree that we want
+ * @page Actual page number that we want
+ * want_double Whether we need a page or double page read
+ *
+ * Outputs:
+ *
+ * @buf Page read or cached
+ * @bytes Bytes read (may be less than page or double page size in
+ * the case of the last page, of course)
+ */
+static int
+read_page(bsearch_file_handle bfh, size_t level, size_t page_idx, size_t page,
+ int want_double, const char **buf, size_t *bytes)
+{
+ int ret;
+ off_t off;
+ size_t expected;
+ size_t wanted;
+ char *page_buf;
+
+ /* Figure out where we're reading and how much */
+ off = page * bfh->page_sz;
+ if (off < 0)
+ return EOVERFLOW;
+
+ wanted = bfh->page_sz << want_double;
+ expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
+
+ if (get_page_from_cache(bfh, level, page_idx, &page_buf)) {
+ *buf = page_buf;
+ *bytes = expected;
+ return 0; /* found in cache */
+ }
+
+
+ *bytes = 0;
+ *buf = NULL;
+
+ /* OK, we have to read a page or double-size page */
+
+ if (page_buf)
+ want_double = 1; /* we'll be caching; we cache double-size pages */
+ else
+ page_buf = bfh->page; /* we won't cache this page */
+
+ wanted = bfh->page_sz << want_double;
+ expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
+
+#ifdef HAVE_PREAD
+ ret = pread(bfh->fd, page_buf, expected, off);
+#else
+ if (lseek(bfh->fd, off, SEEK_SET) == (off_t)-1)
+ return errno;
+ ret = read(bfh->fd, page_buf, expected);
+#endif
+ if (ret < 0)
+ return errno;
+
+ if (ret != expected)
+ return EIO; /* XXX ??? */
+
+ *buf = page_buf;
+ *bytes = expected;
+ return 0;
+}
+
+/*
+ * Perform a binary search of a file where each line is a record (LF and
+ * CRLF supported). Each record consists of a key followed by an
+ * optional value separated from the key by whitespace. Whitespace can
+ * be quoted with backslashes. It's the caller's responsibility to
+ * encode/decode keys/values if quoting is desired; newlines should be
+ * encoded such that a newline does not appear in the result.
+ *
+ * The search is done with block-wise I/O (i.e., the whole file is not
+ * read into memory).
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * NOTE: We could improve this by not freeing the buffer, instead
+ * requiring that the caller provide it. Further, we could cache
+ * the top N levels of [double-size] pages (2^N - 1 pages), which
+ * should speed up most searches by reducing the number of reads
+ * by N.
+ *
+ * Inputs:
+ *
+ * @fd File descriptor (file to search)
+ * @page_sz Page size (if zero then the file's st_blksize will be used)
+ * @key Key string to search for
+ *
+ * Outputs:
+ *
+ * @value Location to store a copy of the value (caller must free)
+ * @location Record location if found else the location where the
+ * record should be inserted (index into @buf)
+ * @loops Location to store a count of bisections required for
+ * search (useful for confirming logarithmic performance)
+ * @reads Location to store a count of pages read during search
+ * (useful for confirming logarithmic performance)
+ */
+int
+_bsearch_file(bsearch_file_handle bfh, const char *key,
+ char **value, size_t *location, size_t *loops, size_t *reads)
+{
+ int ret;
+ const char *buf;
+ size_t buf_sz;
+ size_t page, l, r;
+ size_t my_reads = 0;
+ size_t my_loops_total = 0;
+ size_t my_loops;
+ size_t level; /* level in the tree */
+ size_t page_idx = 0; /* page number in the tree level */
+ size_t buf_location;
+ int cmp;
+ int buf_ends_in_eol = 0;
+ int buf_is_start = 0;
+
+ if (reads)
+ *reads = 0;
+ if (value)
+ *value = NULL;
+ if (loops)
+ *loops = 0;
+
+ /* If whole file is in memory then search that and we're done */
+ if (bfh->file_sz == bfh->cache_sz)
+ return _bsearch_text(bfh->cache, bfh->cache_sz, key, value, location, loops);
+
+ /* Else block-wise binary search */
+
+ l = 0;
+ r = (bfh->file_sz / bfh->page_sz) + 1;
+ for (level = 0, page = r >> 1; page >= l && page < r ; level++) {
+ ret = read_page(bfh, level, page_idx, page, 0, &buf, &buf_sz);
+ if (ret != 0)
+ return ret;
+ my_reads++;
+ if (buf[buf_sz - 1] == '\r' || buf[buf_sz - 1] == '\n')
+ buf_ends_in_eol = 1;
+ else
+ buf_ends_in_eol = 0;
+
+ buf_is_start = page == 0 ? 1 : 0;
+ ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
+ value, &buf_location, &cmp, &my_loops);
+ if (ret > 0)
+ return ret;
+ /* Found or no we update stats */
+ my_loops_total += my_loops;
+ if (loops)
+ *loops = my_loops_total;
+ if (reads)
+ *reads = my_reads;
+ if (location)
+ *location = page * bfh->page_sz + buf_location;
+ if (ret == 0)
+ return 0; /* found! */
+ /* Not found */
+ if (cmp < 0) {
+ /* Search left */
+ page_idx <<= 1;
+ r = page;
+ page = l + ((r - l) >> 1);
+ continue;
+ } else {
+ /*
+ * Search right, but first search the current and next
+ * blocks in case that the record we're looking for either
+ * straddles the boundary between this and the next record,
+ * or in case the record starts exactly at the next page.
+ */
+ heim_assert(cmp > 0, "cmp > 0");
+
+ if (!buf_ends_in_eol || page == l || page == (r - 1)) {
+ ret = read_page(bfh, level, page_idx, page, 1, &buf, &buf_sz);
+ if (ret != 0)
+ return ret;
+ my_reads++;
+
+ buf_is_start = page == l ? 1 : 0;
+
+ ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
+ value, &buf_location, &cmp, &my_loops);
+ if (ret > 0)
+ return ret;
+ my_loops_total += my_loops;
+ if (loops)
+ *loops = my_loops_total;
+ if (reads)
+ *reads = my_reads;
+ if (location)
+ *location = page * bfh->page_sz + buf_location;
+ if (ret == 0)
+ return 0;
+ }
+
+ /* Oh well, search right */
+ if (l == page && r == (l + 1))
+ break;
+ page_idx = (page_idx << 1) + 1;
+ l = page;
+ page = l + ((r - l) >> 1);
+ continue;
+ }
+ }
+ return -1;
+}
+
+
+static int
+stdb_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ bsearch_file_handle bfh;
+ char *p;
+ int ret;
+
+ if (error)
+ *error = NULL;
+ if (dbname == NULL || *dbname == '\0') {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ N_("DB name required for sorted-text DB "
+ "plugin", ""));
+ return EINVAL;
+ }
+ p = strrchr(dbname, '.');
+ if (p == NULL || strcmp(p, ".txt") != 0) {
+ if (error)
+ *error = heim_error_create(ENOTSUP,
+ N_("Text file (name ending in .txt) "
+ "required for sorted-text DB plugin",
+ ""));
+ return ENOTSUP;
+ }
+
+ ret = _bsearch_file_open(dbname, 0, 0, &bfh, NULL);
+ if (ret)
+ return ret;
+
+ *db = bfh;
+ return 0;
+}
+
+static int
+stdb_close(void *db, heim_error_t *error)
+{
+ bsearch_file_handle bfh = db;
+
+ if (error)
+ *error = NULL;
+ _bsearch_file_close(&bfh);
+ return 0;
+}
+
+static heim_data_t
+stdb_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ bsearch_file_handle bfh = db;
+ const char *k;
+ char *v = NULL;
+ heim_data_t value;
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (table != HSTR(""))
+ return NULL;
+
+ if (heim_get_tid(key) == HEIM_TID_STRING)
+ k = heim_string_get_utf8((heim_string_t)key);
+ else
+ k = (const char *)heim_data_get_ptr(key);
+ ret = _bsearch_file(bfh, k, &v, NULL, NULL, NULL);
+ if (ret == 0 && v == NULL)
+ ret = -1; /* Quiet lint */
+ if (ret != 0) {
+ if (ret > 0 && error)
+ *error = heim_error_create(ret, "%s", strerror(ret));
+ return NULL;
+ }
+ value = heim_data_create(v, strlen(v));
+ free(v);
+ /* XXX Handle ENOMEM */
+ return value;
+}
+
+struct heim_db_type heim_sorted_text_file_dbtype = {
+ 1, stdb_open, NULL, stdb_close, NULL, NULL, NULL, NULL, NULL, NULL,
+ stdb_copy_value, NULL, NULL, NULL
+};
diff --git a/third_party/heimdal/lib/base/common_plugin.h b/third_party/heimdal/lib/base/common_plugin.h
new file mode 100644
index 0000000..ece1d28
--- /dev/null
+++ b/third_party/heimdal/lib/base/common_plugin.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2018 AuriStor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HEIMDAL_BASE_COMMON_PLUGIN_H
+#define HEIMDAL_BASE_COMMON_PLUGIN_H
+
+#include <heimbase-svc.h>
+
+#ifdef _WIN32
+# ifndef HEIM_CALLCONV
+# define HEIM_CALLCONV __stdcall
+# endif
+# ifndef HEIM_LIB_CALL
+# define HEIM_LIB_CALL __stdcall
+# endif
+#else
+# ifndef HEIM_CALLCONV
+# define HEIM_CALLCONV
+# endif
+# ifndef HEIM_LIB_CALL
+# define HEIM_LIB_CALL
+# endif
+#endif
+#ifndef KRB5_CALLCONV
+# define KRB5_CALLCONV HEIM_CALLCONV
+#endif
+#ifndef KRB5_LIB_CALL
+# define KRB5_LIB_CALL HEIM_LIB_CALL
+#endif
+
+/* For krb5 plugins, this is a krb5_context */
+typedef struct heim_pcontext_s *heim_pcontext;
+
+typedef uintptr_t
+(HEIM_LIB_CALL *heim_get_instance_func_t)(const char *);
+typedef heim_get_instance_func_t krb5_get_instance_t;
+
+/*
+ * All plugin function tables extend the following structure.
+ */
+struct heim_plugin_common_ftable_desc {
+ HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(heim_pcontext);
+};
+typedef struct heim_plugin_common_ftable_desc heim_plugin_common_ftable;
+typedef struct heim_plugin_common_ftable_desc *heim_plugin_common_ftable_p;
+typedef struct heim_plugin_common_ftable_desc * const heim_plugin_common_ftable_cp;
+
+typedef int
+(HEIM_CALLCONV heim_plugin_load_ft)(heim_pcontext context,
+ heim_get_instance_func_t *func,
+ size_t *n_ftables,
+ heim_plugin_common_ftable_cp **ftables);
+
+typedef heim_plugin_load_ft *heim_plugin_load_t;
+
+/* For source backwards-compatibility */
+typedef struct heim_plugin_common_ftable_desc krb5_plugin_common_ftable;
+typedef struct heim_plugin_common_ftable_desc *krb5_plugin_common_ftable_p;
+typedef struct heim_plugin_common_ftable_desc * const krb5_plugin_common_ftable_cp;
+typedef heim_plugin_load_ft krb5_plugin_load_ft;
+typedef heim_plugin_load_ft *krb5_plugin_load_t;
+
+/*
+ * All plugins must export a function named "<type>_plugin_load" with
+ * a signature of:
+ *
+ * int HEIM_CALLCONV
+ * <type>_plugin_load(heim_pcontext context,
+ * heim_get_instance_func_t *func,
+ * size_t *n_ftables,
+ * const heim_plugin_common_ftable *const **ftables);
+ */
+#endif /* HEIMDAL_BASE_COMMON_PLUGIN_H */
diff --git a/third_party/heimdal/lib/base/config_file.c b/third_party/heimdal/lib/base/config_file.c
new file mode 100644
index 0000000..963569c
--- /dev/null
+++ b/third_party/heimdal/lib/base/config_file.c
@@ -0,0 +1,1458 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <assert.h>
+#include <ctype.h>
+#include <parse_time.h>
+
+#if defined(HAVE_FRAMEWORK_COREFOUNDATION)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+/* Gaah! I want a portable funopen */
+struct fileptr {
+ heim_context context;
+ const char *s;
+ FILE *f;
+};
+
+static char *
+config_fgets(char *str, size_t len, struct fileptr *ptr)
+{
+ /* XXX this is not correct, in that they don't do the same if the
+ line is longer than len */
+ if(ptr->f != NULL)
+ return fgets(str, len, ptr->f);
+ else {
+ /* this is almost strsep_copy */
+ const char *p;
+ ssize_t l;
+ if(*ptr->s == '\0')
+ return NULL;
+ p = ptr->s + strcspn(ptr->s, "\n");
+ if(*p == '\n')
+ p++;
+ l = min(len, (size_t)(p - ptr->s));
+ if(len > 0) {
+ memcpy(str, ptr->s, l);
+ str[l] = '\0';
+ }
+ ptr->s = p;
+ return str;
+ }
+}
+
+static heim_error_code parse_section(char *p, heim_config_section **s,
+ heim_config_section **res,
+ const char **err_message);
+static heim_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ heim_config_binding **b,
+ heim_config_binding **parent,
+ const char **err_message);
+static heim_error_code parse_list(struct fileptr *f, unsigned *lineno,
+ heim_config_binding **parent,
+ const char **err_message);
+
+heim_config_section *
+heim_config_get_entry(heim_config_section **parent, const char *name, int type)
+{
+ heim_config_section **q;
+
+ for (q = parent; *q != NULL; q = &(*q)->next)
+ if (type == heim_config_list &&
+ (unsigned)type == (*q)->type &&
+ strcmp(name, (*q)->name) == 0)
+ return *q;
+ *q = calloc(1, sizeof(**q));
+ if (*q == NULL)
+ return NULL;
+ (*q)->name = strdup(name);
+ (*q)->type = type;
+ if ((*q)->name == NULL) {
+ free(*q);
+ *q = NULL;
+ return NULL;
+ }
+ return *q;
+}
+
+/*
+ * Parse a section:
+ *
+ * [section]
+ * foo = bar
+ * b = {
+ * a
+ * }
+ * ...
+ *
+ * starting at the line in `p', storing the resulting structure in
+ * `s' and hooking it into `parent'.
+ * Store the error message in `err_message'.
+ */
+
+static heim_error_code
+parse_section(char *p, heim_config_section **s, heim_config_section **parent,
+ const char **err_message)
+{
+ char *p1;
+ heim_config_section *tmp;
+
+ p1 = strchr (p + 1, ']');
+ if (p1 == NULL) {
+ *err_message = "missing ]";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ *p1 = '\0';
+ tmp = heim_config_get_entry(parent, p + 1, heim_config_list);
+ if(tmp == NULL) {
+ *err_message = "out of memory";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ *s = tmp;
+ return 0;
+}
+
+/*
+ * Parse a brace-enclosed list from `f', hooking in the structure at
+ * `parent'.
+ * Store the error message in `err_message'.
+ */
+
+static heim_error_code
+parse_list(struct fileptr *f, unsigned *lineno, heim_config_binding **parent,
+ const char **err_message)
+{
+ char buf[2048];
+ heim_error_code ret;
+ heim_config_binding *b = NULL;
+ unsigned beg_lineno = *lineno;
+
+ while(config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';' || *p == '\0')
+ continue;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '}')
+ return 0;
+ if (*p == '\0')
+ continue;
+ ret = parse_binding (f, lineno, p, &b, parent, err_message);
+ if (ret)
+ return ret;
+ }
+ *lineno = beg_lineno;
+ *err_message = "unclosed {";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+}
+
+/*
+ *
+ */
+
+static heim_error_code
+parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ heim_config_binding **b, heim_config_binding **parent,
+ const char **err_message)
+{
+ heim_config_binding *tmp;
+ char *p1, *p2;
+ heim_error_code ret = 0;
+
+ p1 = p;
+ while (*p && *p != '=' && !isspace((unsigned char)*p))
+ ++p;
+ if (*p == '\0') {
+ *err_message = "missing =";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ p2 = p;
+ while (isspace((unsigned char)*p))
+ ++p;
+ if (*p != '=') {
+ *err_message = "missing =";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ++p;
+ while(isspace((unsigned char)*p))
+ ++p;
+ *p2 = '\0';
+ if (*p == '{') {
+ tmp = heim_config_get_entry(parent, p1, heim_config_list);
+ if (tmp == NULL) {
+ *err_message = "out of memory";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ret = parse_list (f, lineno, &tmp->u.list, err_message);
+ } else {
+ tmp = heim_config_get_entry(parent, p1, heim_config_string);
+ if (tmp == NULL) {
+ *err_message = "out of memory";
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ p1 = p;
+ p = p1 + strlen(p1);
+ while(p > p1 && isspace((unsigned char)*(p-1)))
+ --p;
+ *p = '\0';
+ tmp->u.string = strdup(p1);
+ }
+ *b = tmp;
+ return ret;
+}
+
+#if defined(HAVE_FRAMEWORK_COREFOUNDATION)
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+#define HAVE_CFPROPERTYLISTCREATEWITHSTREAM 1
+#endif
+
+static char *
+cfstring2cstring(CFStringRef string)
+{
+ CFIndex len;
+ char *str;
+
+ str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
+ if (str)
+ return strdup(str);
+
+ len = CFStringGetLength(string);
+ len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
+ str = malloc(len);
+ if (str == NULL)
+ return NULL;
+
+ if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) {
+ free (str);
+ return NULL;
+ }
+ return str;
+}
+
+static void
+convert_content(const void *key, const void *value, void *context)
+{
+ heim_config_section *tmp, **parent = context;
+ char *k;
+
+ if (CFGetTypeID(key) != CFStringGetTypeID())
+ return;
+
+ k = cfstring2cstring(key);
+ if (k == NULL)
+ return;
+
+ if (CFGetTypeID(value) == CFStringGetTypeID()) {
+ tmp = heim_config_get_entry(parent, k, heim_config_string);
+ tmp->u.string = cfstring2cstring(value);
+ } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
+ tmp = heim_config_get_entry(parent, k, heim_config_list);
+ CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
+ } else {
+ /* log */
+ }
+ free(k);
+}
+
+static heim_error_code
+parse_plist_config(heim_context context, const char *path, heim_config_section **parent)
+{
+ CFReadStreamRef s;
+ CFDictionaryRef d;
+ CFURLRef url;
+
+ url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), 0);
+ if (url == NULL) {
+ heim_clear_error_message(context);
+ return ENOMEM;
+ }
+
+ s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+ CFRelease(url);
+ if (s == NULL) {
+ heim_clear_error_message(context);
+ return ENOMEM;
+ }
+
+ if (!CFReadStreamOpen(s)) {
+ CFRelease(s);
+ heim_clear_error_message(context);
+ return ENOENT;
+ }
+
+#ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
+ d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
+#else
+ d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
+#endif
+ CFRelease(s);
+ if (d == NULL) {
+ heim_clear_error_message(context);
+ return ENOENT;
+ }
+
+ CFDictionaryApplyFunction(d, convert_content, parent);
+ CFRelease(d);
+
+ return 0;
+}
+
+#endif
+
+static int
+is_absolute_path(const char *path)
+{
+ /*
+ * An absolute path is one that refers to an explicit object
+ * without ambiguity.
+ */
+#ifdef WIN32
+ size_t len = strlen(path);
+
+ /* UNC path is by definition absolute */
+ if (len > 2
+ && ISPATHSEP(path[0])
+ && ISPATHSEP(path[1]))
+ return 1;
+
+ /* A drive letter path might be absolute */
+ if (len > 3
+ && isalpha(path[0])
+ && path[1] == ':'
+ && ISPATHSEP(path[2]))
+ return 1;
+
+ /*
+ * if no drive letter but first char is a path
+ * separator then the drive letter must be obtained
+ * from the including file.
+ */
+#else
+ /* UNIX is easy, first char '/' is absolute */
+ if (ISPATHSEP(path[0]))
+ return 1;
+#endif
+ return 0;
+}
+
+/*
+ * Parse the config file `fname', generating the structures into `res'
+ * returning error messages in `err_message'
+ */
+
+static heim_error_code
+heim_config_parse_debug(struct fileptr *f,
+ heim_config_section **res,
+ unsigned *lineno,
+ const char **err_message)
+{
+ heim_config_section *s = NULL;
+ heim_config_binding *b = NULL;
+ char buf[2048];
+ heim_error_code ret;
+
+ *lineno = 0;
+ *err_message = "";
+
+ while (config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';')
+ continue;
+ if (*p == '[') {
+ ret = parse_section(p, &s, res, err_message);
+ if (ret)
+ return ret;
+ b = NULL;
+ } else if (*p == '}') {
+ *err_message = "unmatched }";
+ return 2048;
+ } else if (strncmp(p, "include", sizeof("include") - 1) == 0 &&
+ isspace(p[sizeof("include") - 1])) {
+ p += sizeof("include");
+ while (isspace(*p))
+ p++;
+ if (!is_absolute_path(p)) {
+ heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT,
+ "Configuration include path must be "
+ "absolute");
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ret = heim_config_parse_file_multi(f->context, p, res);
+ if (ret)
+ return ret;
+ } else if (strncmp(p, "includedir", sizeof("includedir") - 1) == 0 &&
+ isspace(p[sizeof("includedir") - 1])) {
+ p += sizeof("includedir");
+ while (isspace(*p))
+ p++;
+ if (!is_absolute_path(p)) {
+ heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT,
+ "Configuration includedir path must be "
+ "absolute");
+ return HEIM_ERR_CONFIG_BADFORMAT;
+ }
+ ret = heim_config_parse_dir_multi(f->context, p, res);
+ if (ret)
+ return ret;
+ } else if(*p != '\0') {
+ if (s == NULL) {
+ *err_message = "binding before section";
+ return 2048;
+ }
+ ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int
+is_plist_file(const char *fname)
+{
+ size_t len = strlen(fname);
+ char suffix[] = ".plist";
+ if (len < sizeof(suffix))
+ return 0;
+ if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * Parse configuration files in the given directory and add the result
+ * into res. Only files whose names consist only of alphanumeric
+ * characters, hyphen, and underscore, will be parsed, though files
+ * ending in ".conf" will also be parsed.
+ *
+ * This interface can be used to parse several configuration directories
+ * into one resulting heim_config_section by calling it repeatably.
+ *
+ * @param context a Kerberos 5 context.
+ * @param dname a directory name to a Kerberos configuration file
+ * @param res the returned result, must be free with heim_free_config_files().
+ * @return Return an error code or 0, see heim_get_error_message().
+ *
+ * @ingroup heim_support
+ */
+
+heim_error_code
+heim_config_parse_dir_multi(heim_context context,
+ const char *dname,
+ heim_config_section **res)
+{
+ struct dirent *entry;
+ heim_error_code ret;
+ DIR *d;
+
+ if ((d = opendir(dname)) == NULL)
+ return errno;
+
+ while ((entry = readdir(d)) != NULL) {
+ char *p = entry->d_name;
+ char *path;
+ int is_valid = 1;
+
+ while (*p) {
+ /*
+ * Here be dragons. The call to heim_config_parse_file_multi()
+ * below expands path tokens. Because of the limitations here
+ * on file naming, we can't have path tokens in the file name,
+ * so we're safe. Anyone changing this if condition here should
+ * be aware.
+ */
+ if (!isalnum(*p) && *p != '_' && *p != '-' &&
+ strcmp(p, ".conf") != 0) {
+ is_valid = 0;
+ break;
+ }
+ p++;
+ }
+ if (!is_valid)
+ continue;
+
+ if (asprintf(&path, "%s/%s", dname, entry->d_name) == -1 ||
+ path == NULL) {
+ (void) closedir(d);
+ return heim_enomem(context);
+ }
+ ret = heim_config_parse_file_multi(context, path, res);
+ free(path);
+ if (ret == ENOMEM) {
+ (void) closedir(d);
+ return ENOMEM;
+ }
+ /* Ignore malformed config files so we don't lock out admins, etc... */
+ }
+ (void) closedir(d);
+ return 0;
+}
+
+/**
+ * Parse a configuration file and add the result into res. This
+ * interface can be used to parse several configuration files into one
+ * resulting heim_config_section by calling it repeatably.
+ *
+ * @param context a Kerberos 5 context.
+ * @param fname a file name to a Kerberos configuration file
+ * @param res the returned result, must be free with heim_free_config_files().
+ * @return Return an error code or 0, see heim_get_error_message().
+ *
+ * @ingroup heim_support
+ */
+
+HEIMDAL_THREAD_LOCAL int config_include_depth = 0;
+
+heim_error_code
+heim_config_parse_file_multi(heim_context context,
+ const char *fname,
+ heim_config_section **res)
+{
+ const char *str;
+ char *newfname = NULL;
+ unsigned lineno = 0;
+ heim_error_code ret = 0;
+ struct fileptr f;
+ struct stat st;
+
+ if (config_include_depth > 5) {
+ heim_warnx(context, "Maximum config file include depth reached; "
+ "not including %s", fname);
+ return 0;
+ }
+ config_include_depth++;
+
+ /**
+ * If the fname starts with "~/" parse configuration file in the
+ * current users home directory. The behavior can be disabled and
+ * enabled by calling heim_set_home_dir_access().
+ */
+ if (ISTILDE(fname[0]) && ISPATHSEP(fname[1])) {
+ if (!heim_context_get_homedir_access(context)) {
+ heim_set_error_message(context, EPERM,
+ "Access to home directory not allowed");
+ ret = EPERM;
+ goto out;
+ }
+ if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
+ newfname == NULL) {
+ ret = heim_enomem(context);
+ goto out;
+ }
+ fname = newfname;
+ }
+
+ if (is_plist_file(fname)) {
+#if defined(HAVE_FRAMEWORK_COREFOUNDATION)
+ ret = parse_plist_config(context, fname, res);
+ if (ret) {
+ heim_set_error_message(context, ret,
+ "Failed to parse plist %s", fname);
+ goto out;
+ }
+#else
+ heim_set_error_message(context, ENOENT,
+ "no support for plist configuration files");
+ ret = ENOENT;
+ goto out;
+#endif
+ } else {
+ char *exp_fname = NULL;
+
+ /*
+ * Note that heim_config_parse_dir_multi() doesn't want tokens
+ * expanded here, but it happens to limit the names of files to
+ * include such that there can be no tokens to expand. Don't
+ * add token expansion for tokens using _, say.
+ */
+ ret = heim_expand_path_tokens(context, fname, 1, &exp_fname, NULL);
+ if (ret)
+ goto out;
+ free(newfname);
+ fname = newfname = exp_fname;
+
+ f.context = context;
+ f.f = fopen(fname, "r");
+ f.s = NULL;
+ if (f.f == NULL || fstat(fileno(f.f), &st) == -1) {
+ if (f.f != NULL)
+ (void) fclose(f.f);
+ ret = errno;
+ heim_set_error_message(context, ret, "open or stat %s: %s",
+ fname, strerror(ret));
+ goto out;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ (void) fclose(f.f);
+ heim_set_error_message(context, EISDIR, "not a regular file %s: %s",
+ fname, strerror(EISDIR));
+ ret = EISDIR;
+ goto out;
+ }
+
+ ret = heim_config_parse_debug(&f, res, &lineno, &str);
+ fclose(f.f);
+ if (ret) {
+ if (ret != HEIM_ERR_CONFIG_BADFORMAT)
+ ret = HEIM_ERR_CONFIG_BADFORMAT;
+ heim_set_error_message(context, ret, "%s:%u: %s",
+ fname, lineno, str);
+ goto out;
+ }
+ }
+
+ out:
+ config_include_depth--;
+ if (ret == HEIM_ERR_CONFIG_BADFORMAT || (ret && config_include_depth > 0)) {
+ heim_warn(context, ret, "Ignoring");
+ if (config_include_depth > 0)
+ ret = 0;
+ }
+ free(newfname);
+ return ret;
+}
+
+heim_error_code
+heim_config_parse_file(heim_context context,
+ const char *fname,
+ heim_config_section **res)
+{
+ *res = NULL;
+ return heim_config_parse_file_multi(context, fname, res);
+}
+
+static void
+free_binding(heim_context context, heim_config_binding *b)
+{
+ heim_config_binding *next_b;
+
+ while (b) {
+ free (b->name);
+ assert(b->type == heim_config_string || b->type == heim_config_list);
+ if (b->type == heim_config_string)
+ free (b->u.string);
+ else
+ free_binding (context, b->u.list);
+ next_b = b->next;
+ free (b);
+ b = next_b;
+ }
+}
+
+/**
+ * Free configuration file section, the result of
+ * heim_config_parse_file() and heim_config_parse_file_multi().
+ *
+ * @param context A Kerberos 5 context
+ * @param s the configuration section to free
+ *
+ * @return returns 0 on successes, otherwise an error code, see
+ * heim_get_error_message()
+ *
+ * @ingroup heim_support
+ */
+
+heim_error_code
+heim_config_file_free(heim_context context, heim_config_section *s)
+{
+ free_binding (context, s);
+ return 0;
+}
+
+#ifndef HEIMDAL_SMALLER
+
+heim_error_code
+heim_config_copy(heim_context context,
+ heim_config_section *c,
+ heim_config_section **head)
+{
+ heim_config_binding *d, *previous = NULL;
+
+ *head = NULL;
+
+ while (c) {
+ d = calloc(1, sizeof(*d));
+
+ if (*head == NULL)
+ *head = d;
+
+ d->name = strdup(c->name);
+ d->type = c->type;
+ assert(d->type == heim_config_string || d->type == heim_config_list);
+ if (d->type == heim_config_string)
+ d->u.string = strdup(c->u.string);
+ else
+ heim_config_copy (context, c->u.list, &d->u.list);
+ if (previous)
+ previous->next = d;
+
+ previous = d;
+ c = c->next;
+ }
+ return 0;
+}
+
+#endif /* HEIMDAL_SMALLER */
+
+const void *
+heim_config_get_next(heim_context context,
+ const heim_config_section *c,
+ const heim_config_binding **pointer,
+ int type,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = heim_config_vget_next(context, c, pointer, type, args);
+ va_end(args);
+ return ret;
+}
+
+static const void *
+vget_next(heim_context context,
+ const heim_config_binding *b,
+ const heim_config_binding **pointer,
+ int type,
+ const char *name,
+ va_list args)
+{
+ const char *p = va_arg(args, const char *);
+
+ while (b != NULL) {
+ if (strcmp(b->name, name) == 0) {
+ if (b->type == (unsigned)type && p == NULL) {
+ *pointer = b;
+ return b->u.generic;
+ } else if (b->type == heim_config_list && p != NULL) {
+ return vget_next(context, b->u.list, pointer, type, p, args);
+ }
+ }
+ b = b->next;
+ }
+ return NULL;
+}
+
+const void *
+heim_config_vget_next(heim_context context,
+ const heim_config_section *c,
+ const heim_config_binding **pointer,
+ int type,
+ va_list args)
+{
+ const heim_config_binding *b;
+ const char *p;
+
+ if (c == NULL)
+ return NULL;
+
+ if (*pointer == NULL) {
+ /* first time here, walk down the tree looking for the right
+ section */
+ p = va_arg(args, const char *);
+ if (p == NULL)
+ return NULL;
+ return vget_next(context, c, pointer, type, p, args);
+ }
+
+ /* we were called again, so just look for more entries with the
+ same name and type */
+ for (b = (*pointer)->next; b != NULL; b = b->next) {
+ if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) {
+ *pointer = b;
+ return b->u.generic;
+ }
+ }
+ return NULL;
+}
+
+const void *
+heim_config_get(heim_context context,
+ const heim_config_section *c,
+ int type,
+ ...)
+{
+ const void *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = heim_config_vget(context, c, type, args);
+ va_end(args);
+ return ret;
+}
+
+
+const void *
+heim_config_vget(heim_context context,
+ const heim_config_section *c,
+ int type,
+ va_list args)
+{
+ const heim_config_binding *foo = NULL;
+
+ return heim_config_vget_next(context, c, &foo, type, args);
+}
+
+/**
+ * Get a list of configuration binding list for more processing
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return NULL if configuration list is not found, a list otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const heim_config_binding *
+heim_config_get_list(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ const heim_config_binding *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = heim_config_vget_list(context, c, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Get a list of configuration binding list for more processing
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return NULL if configuration list is not found, a list otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const heim_config_binding *
+heim_config_vget_list(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget(context, c, heim_config_list, args);
+}
+
+/**
+ * Returns a "const char *" to a string in the configuration database.
+ * The string may not be valid after a reload of the configuration
+ * database so a caller should make a local copy if it needs to keep
+ * the string.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return NULL if configuration string not found, a string otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_get_string(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = heim_config_vget_string(context, c, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Like heim_config_get_string(), but uses a va_list instead of ...
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return NULL if configuration string not found, a string otherwise
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_vget_string(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget(context, c, heim_config_string, args);
+}
+
+/**
+ * Like heim_config_vget_string(), but instead of returning NULL,
+ * instead return a default value.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return a configuration string
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_vget_string_default(heim_context context,
+ const heim_config_section *c,
+ const char *def_value,
+ va_list args)
+{
+ const char *ret;
+
+ ret = heim_config_vget_string(context, c, args);
+ if (ret == NULL)
+ ret = def_value;
+ return ret;
+}
+
+/**
+ * Like heim_config_get_string(), but instead of returning NULL,
+ * instead return a default value.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return a configuration string
+ *
+ * @ingroup heim_support
+ */
+
+const char *
+heim_config_get_string_default(heim_context context,
+ const heim_config_section *c,
+ const char *def_value,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, def_value);
+ ret = heim_config_vget_string_default (context, c, def_value, args);
+ va_end(args);
+ return ret;
+}
+
+static char *
+next_component_string(char * begin, const char * delims, char **state)
+{
+ char * end;
+
+ if (begin == NULL)
+ begin = *state;
+
+ if (*begin == '\0')
+ return NULL;
+
+ end = begin;
+ while (*end == '"') {
+ char * t = strchr(end + 1, '"');
+
+ if (t)
+ end = ++t;
+ else
+ end += strlen(end);
+ }
+
+ if (*end != '\0') {
+ size_t pos;
+
+ pos = strcspn(end, delims);
+ end = end + pos;
+ }
+
+ if (*end != '\0') {
+ *end = '\0';
+ *state = end + 1;
+ if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
+ begin++; *(end - 1) = '\0';
+ }
+ return begin;
+ }
+
+ *state = end;
+ if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
+ begin++; *(end - 1) = '\0';
+ }
+ return begin;
+}
+
+/**
+ * Get a list of configuration strings, free the result with
+ * heim_config_free_strings().
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+char **
+heim_config_vget_strings(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ char **strings = NULL;
+ size_t nstr = 0;
+ const heim_config_binding *b = NULL;
+ const char *p;
+
+ while((p = heim_config_vget_next(context, c, &b,
+ heim_config_string, args))) {
+ char *tmp = strdup(p);
+ char *pos = NULL;
+ char *s;
+ if(tmp == NULL)
+ goto cleanup;
+ s = next_component_string(tmp, " \t", &pos);
+ while(s){
+ char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp2 == NULL) {
+ free(tmp);
+ goto cleanup;
+ }
+ strings = tmp2;
+ strings[nstr] = strdup(s);
+ nstr++;
+ if(strings[nstr-1] == NULL) {
+ free(tmp);
+ goto cleanup;
+ }
+ s = next_component_string(NULL, " \t", &pos);
+ }
+ free(tmp);
+ }
+ if(nstr){
+ char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp == NULL)
+ goto cleanup;
+ strings = tmp;
+ strings[nstr] = NULL;
+ }
+ return strings;
+cleanup:
+ while(nstr--)
+ free(strings[nstr]);
+ free(strings);
+ return NULL;
+
+}
+
+/**
+ * Get a list of configuration strings, free the result with
+ * heim_config_free_strings().
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+char **
+heim_config_get_strings(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ char **ret;
+ va_start(ap, c);
+ ret = heim_config_vget_strings(context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Free the resulting strings from heim_config-get_strings() and
+ * heim_config_vget_strings().
+ *
+ * @param strings strings to free
+ *
+ * @ingroup heim_support
+ */
+
+void
+heim_config_free_strings(char **strings)
+{
+ char **s = strings;
+
+ while (s && *s) {
+ free(*s);
+ s++;
+ }
+ free(strings);
+}
+
+/**
+ * Like heim_config_get_bool_default() but with a va_list list of
+ * configuration selection.
+ *
+ * Configuration value to a boolean value, where yes/true and any
+ * non-zero number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_vget_bool_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ str = heim_config_vget_string(context, c, args);
+ if (str == NULL)
+ return def_value;
+ return !!(strcasecmp(str, "yes") == 0 ||
+ strcasecmp(str, "true") == 0 ||
+ atoi(str));
+}
+
+/**
+ * heim_config_get_bool() will convert the configuration
+ * option value to a boolean value, where yes/true and any non-zero
+ * number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_vget_bool(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget_bool_default(context, c, 0, args);
+}
+
+/**
+ * heim_config_get_bool_default() will convert the configuration
+ * option value to a boolean value, where yes/true and any non-zero
+ * number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_get_bool_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, def_value);
+ ret = heim_config_vget_bool_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Like heim_config_get_bool() but with a va_list list of
+ * configuration selection.
+ *
+ * Configuration value to a boolean value, where yes/true and any
+ * non-zero number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup heim_support
+ */
+
+int
+heim_config_get_bool(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = heim_config_vget_bool (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Get the time from the configuration file using a relative time.
+ *
+ * Like heim_config_get_time_default() but with a va_list list of
+ * configuration selection.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return parsed the time (or def_value on parse error)
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_vget_time_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ time_t t = -1;
+
+ if ((str = heim_config_vget_string(context, c, args)))
+ t = parse_time(str, "s");
+ return t != -1 ? t : def_value;
+}
+
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return parsed the time or -1 on error
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_vget_time(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget_time_default(context, c, -1, args);
+}
+
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return parsed the time (or def_value on parse error)
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_get_time_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ time_t ret;
+
+ va_start(ap, def_value);
+ ret = heim_config_vget_time_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return parsed the time or -1 on error
+ *
+ * @ingroup heim_support
+ */
+
+time_t
+heim_config_get_time(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = heim_config_vget_time(context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+int
+heim_config_vget_int_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ str = heim_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ else {
+ char *endptr;
+ long l;
+ l = strtol(str, &endptr, 0);
+ if (endptr == str)
+ return def_value;
+ else
+ return l;
+ }
+}
+
+int
+heim_config_vget_int(heim_context context,
+ const heim_config_section *c,
+ va_list args)
+{
+ return heim_config_vget_int_default(context, c, -1, args);
+}
+
+int
+heim_config_get_int_default(heim_context context,
+ const heim_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, def_value);
+ ret = heim_config_vget_int_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+int
+heim_config_get_int(heim_context context,
+ const heim_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = heim_config_vget_int (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+#ifndef HEIMDAL_SMALLER
+heim_error_code
+heim_config_parse_string_multi(heim_context context,
+ const char *string,
+ heim_config_section **res)
+{
+ const char *str;
+ unsigned lineno = 0;
+ heim_error_code ret;
+ struct fileptr f;
+
+ f.context = context;
+ f.f = NULL;
+ f.s = string;
+
+ ret = heim_config_parse_debug(&f, res, &lineno, &str);
+ if (ret) {
+ if (ret != HEIM_ERR_CONFIG_BADFORMAT) {
+ ret = HEIM_ERR_CONFIG_BADFORMAT;
+ heim_set_error_message(context, ret, "%s:%u: %s",
+ "<constant>", lineno, str);
+ }
+ return ret;
+ }
+ return 0;
+}
+#endif
diff --git a/third_party/heimdal/lib/base/config_reg.c b/third_party/heimdal/lib/base/config_reg.c
new file mode 100644
index 0000000..cb24e50
--- /dev/null
+++ b/third_party/heimdal/lib/base/config_reg.c
@@ -0,0 +1,658 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include "baselocl.h"
+
+#ifndef _WIN32
+#error config_reg.c is only for Windows
+#endif
+
+#include <shlwapi.h>
+
+#ifndef MAX_DWORD
+#define MAX_DWORD 0xFFFFFFFF
+#endif
+
+/**
+ * Store a string as a registry value of the specified type
+ *
+ * The following registry types are handled:
+ *
+ * - REG_DWORD: The string is converted to a number.
+ *
+ * - REG_SZ: The string is stored as is.
+ *
+ * - REG_EXPAND_SZ: The string is stored as is.
+ *
+ * - REG_MULTI_SZ:
+ *
+ * . If a separator is specified, the input string is broken
+ * up into multiple strings and stored as a multi-sz.
+ *
+ * . If no separator is provided, the input string is stored
+ * as a multi-sz.
+ *
+ * - REG_NONE:
+ *
+ * . If the string is all numeric, it will be stored as a
+ * REG_DWORD.
+ *
+ * . Otherwise, the string is stored as a REG_SZ.
+ *
+ * Other types are rejected.
+ *
+ * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
+ * otherwise a buffer overrun will occur.
+ *
+ * @param [in]valuename Name of the registry value to be modified or created
+ * @param [in]type Type of the value. REG_NONE if unknown
+ * @param [in]data The input string to be stored in the registry.
+ * @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown.
+ * @param [in]separator Separator character for parsing strings.
+ *
+ * @retval 0 if success or non-zero on error.
+ * If non-zero is returned, an error message has been set using
+ * heim_set_error_message().
+ *
+ */
+int
+heim_store_string_to_reg_value(heim_context context,
+ HKEY key, const char *valuename,
+ DWORD type, const char *data, DWORD cb_data,
+ const char *separator)
+{
+ LONG rcode;
+ int dwData;
+ BYTE static_buffer[16384];
+
+ if (data == NULL)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "'data' must not be NULL");
+ return -1;
+ }
+
+ if (cb_data == MAX_DWORD)
+ {
+ cb_data = (DWORD)strlen(data) + 1;
+ }
+ else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
+ cb_data >= sizeof(static_buffer))
+ {
+ if (context)
+ heim_set_error_message(context, 0, "cb_data too big");
+ return -1;
+ }
+ else if (data[cb_data-1] != '\0')
+ {
+ memcpy(static_buffer, data, cb_data);
+ static_buffer[cb_data++] = '\0';
+ if (type == REG_MULTI_SZ)
+ static_buffer[cb_data++] = '\0';
+ data = static_buffer;
+ }
+
+ if (type == REG_NONE)
+ {
+ /*
+ * If input is all numeric, convert to DWORD and save as REG_DWORD.
+ * Otherwise, store as REG_SZ.
+ */
+ if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
+ {
+ type = REG_DWORD;
+ } else {
+ type = REG_SZ;
+ }
+ }
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
+ if (rcode)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ break;
+ case REG_MULTI_SZ:
+ if (separator && *separator)
+ {
+ char *cp;
+
+ if (data != static_buffer)
+ static_buffer[cb_data++] = '\0';
+
+ for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
+ {
+ if (*cp == *separator)
+ *cp = '\0';
+ }
+
+ rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
+ if (rcode)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ }
+ break;
+ case REG_DWORD:
+ if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when parsing %s as number gle 0x%x",
+ data,
+ GetLastError());
+ }
+
+ rcode = RegSetValueEx(key, valuename, 0, type, (BYTE *)&dwData, sizeof(DWORD));
+ if (rcode)
+ {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Parse a registry value as a string
+ *
+ * @see heim_parse_reg_value_as_multi_string()
+ */
+char *
+heim_parse_reg_value_as_string(heim_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cb_data)
+{
+ return heim_parse_reg_value_as_multi_string(context, key, valuename,
+ type, cb_data, " ");
+}
+
+/**
+ * Parse a registry value as a multi string
+ *
+ * The following registry value types are handled:
+ *
+ * - REG_DWORD: The decimal string representation is used as the
+ * value.
+ *
+ * - REG_SZ: The string is used as-is.
+ *
+ * - REG_EXPAND_SZ: Environment variables in the string are expanded
+ * and the result is used as the value.
+ *
+ * - REG_MULTI_SZ: The list of strings is concatenated using the
+ * separator. No quoting is performed.
+ *
+ * Any other value type is rejected.
+ *
+ * @param [in]valuename Name of the registry value to be queried
+ * @param [in]type Type of the value. REG_NONE if unknown
+ * @param [in]cbdata Size of value. 0 if unknown.
+ * @param [in]separator Separator character for concatenating strings.
+ *
+ * @a type and @a cbdata are only considered valid if both are
+ * specified.
+ *
+ * @retval The registry value string, or NULL if there was an error.
+ * If NULL is returned, an error message has been set using
+ * heim_set_error_message().
+ */
+char *
+heim_parse_reg_value_as_multi_string(heim_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cb_data, char *separator)
+{
+ LONG rcode = ERROR_MORE_DATA;
+
+ BYTE static_buffer[16384];
+ BYTE *pbuffer = &static_buffer[0];
+ DWORD cb_alloc = sizeof(static_buffer);
+ char *ret_string = NULL;
+
+ /* If we know a type and cb_data from a previous call to
+ * RegEnumValue(), we use it. Otherwise we use the
+ * static_buffer[] and query directly. We do this to minimize the
+ * number of queries. */
+
+ if (type == REG_NONE || cb_data == 0) {
+
+ pbuffer = &static_buffer[0];
+ cb_alloc = cb_data = sizeof(static_buffer);
+ rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
+
+ if (rcode == ERROR_SUCCESS &&
+
+ ((type != REG_SZ &&
+ type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
+
+ (type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
+ goto have_data;
+
+ if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
+ return NULL;
+ }
+
+ /* Either we don't have the data or we aren't sure of the size
+ * (due to potentially missing terminating NULs). */
+
+ switch (type) {
+ case REG_DWORD:
+ if (cb_data != sizeof(DWORD)) {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected size while reading registry value %s",
+ valuename);
+ return NULL;
+ }
+ break;
+
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+
+ if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
+ goto have_data;
+
+ cb_data += sizeof(char); /* Accout for potential missing NUL
+ * terminator. */
+ break;
+
+ case REG_MULTI_SZ:
+
+ if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
+ (cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
+ goto have_data;
+
+ cb_data += sizeof(char) * 2; /* Potential missing double NUL
+ * terminator. */
+ break;
+
+ default:
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected type while reading registry value %s",
+ valuename);
+ return NULL;
+ }
+
+ if (cb_data <= sizeof(static_buffer))
+ pbuffer = &static_buffer[0];
+ else {
+ pbuffer = malloc(cb_data);
+ if (pbuffer == NULL)
+ return NULL;
+ }
+
+ cb_alloc = cb_data;
+ rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
+
+ if (rcode != ERROR_SUCCESS) {
+
+ /* This can potentially be from a race condition. I.e. some
+ * other process or thread went and modified the registry
+ * value between the time we queried its size and queried for
+ * its value. Ideally we would retry the query in a loop. */
+
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected error while reading registry value %s",
+ valuename);
+ goto done;
+ }
+
+ if (cb_data > cb_alloc || cb_data == 0) {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected size while reading registry value %s",
+ valuename);
+ goto done;
+ }
+
+have_data:
+ switch (type) {
+ case REG_DWORD:
+ asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
+ break;
+
+ case REG_SZ:
+ {
+ char * str = (char *) pbuffer;
+
+ if (str[cb_data - 1] != '\0') {
+ if (cb_data < cb_alloc)
+ str[cb_data] = '\0';
+ else
+ break;
+ }
+
+ if (pbuffer != static_buffer) {
+ ret_string = (char *) pbuffer;
+ pbuffer = NULL;
+ } else {
+ ret_string = strdup((char *) pbuffer);
+ }
+ }
+ break;
+
+ case REG_EXPAND_SZ:
+ {
+ char *str = (char *) pbuffer;
+ char expsz[32768]; /* Size of output buffer for
+ * ExpandEnvironmentStrings() is
+ * limited to 32K. */
+
+ if (str[cb_data - 1] != '\0') {
+ if (cb_data < cb_alloc)
+ str[cb_data] = '\0';
+ else
+ break;
+ }
+
+ if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
+ ret_string = strdup(expsz);
+ } else {
+ if (context)
+ heim_set_error_message(context, 0,
+ "Overflow while expanding environment strings "
+ "for registry value %s", valuename);
+ }
+ }
+ break;
+
+ case REG_MULTI_SZ:
+ {
+ char * str = (char *) pbuffer;
+ char * iter;
+
+ str[cb_alloc - 1] = '\0';
+ str[cb_alloc - 2] = '\0';
+
+ for (iter = str; *iter;) {
+ size_t len = strlen(iter);
+
+ iter += len;
+ if (iter[1] != '\0')
+ *iter++ = *separator;
+ else
+ break;
+ }
+
+ if (pbuffer != static_buffer) {
+ ret_string = str;
+ pbuffer = NULL;
+ } else {
+ ret_string = strdup(str);
+ }
+ }
+ break;
+
+ default:
+ if (context)
+ heim_set_error_message(context, 0,
+ "Unexpected type while reading registry value %s",
+ valuename);
+ }
+
+done:
+ if (pbuffer != static_buffer && pbuffer != NULL)
+ free(pbuffer);
+
+ return ret_string;
+}
+
+/**
+ * Parse a registry value as a configuration value
+ *
+ * @see parse_reg_value_as_string()
+ */
+static heim_error_code
+parse_reg_value(heim_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cbdata, heim_config_section ** parent)
+{
+ char *reg_string = NULL;
+ heim_config_section *value;
+ heim_error_code code = 0;
+
+ reg_string = heim_parse_reg_value_as_string(context, key, valuename, type, cbdata);
+
+ if (reg_string == NULL)
+ return HEIM_ERR_CONFIG_BADFORMAT;
+
+ value = heim_config_get_entry(parent, valuename, heim_config_string);
+ if (value == NULL) {
+ code = ENOMEM;
+ goto done;
+ }
+
+ if (value->u.string != NULL)
+ free(value->u.string);
+
+ value->u.string = reg_string;
+ reg_string = NULL;
+
+done:
+ if (reg_string != NULL)
+ free(reg_string);
+
+ return code;
+}
+
+static heim_error_code
+parse_reg_values(heim_context context,
+ HKEY key,
+ heim_config_section ** parent)
+{
+ DWORD index;
+ LONG rcode;
+
+ for (index = 0; ; index ++) {
+ char name[16385];
+ DWORD cch = sizeof(name)/sizeof(name[0]);
+ DWORD type;
+ DWORD cbdata = 0;
+ heim_error_code code;
+
+ rcode = RegEnumValue(key, index, name, &cch, NULL,
+ &type, NULL, &cbdata);
+ if (rcode != ERROR_SUCCESS)
+ break;
+
+ if (cbdata == 0)
+ continue;
+
+ code = parse_reg_value(context, key, name, type, cbdata, parent);
+ if (code != 0)
+ return code;
+ }
+
+ return 0;
+}
+
+static heim_error_code
+parse_reg_subkeys(heim_context context,
+ HKEY key,
+ heim_config_section ** parent)
+{
+ DWORD index;
+ LONG rcode;
+
+ for (index = 0; ; index ++) {
+ HKEY subkey = NULL;
+ char name[256];
+ DWORD cch = sizeof(name)/sizeof(name[0]);
+ heim_config_section *section = NULL;
+ heim_error_code code;
+
+ rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL);
+ if (rcode != ERROR_SUCCESS)
+ break;
+
+ rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey);
+ if (rcode != ERROR_SUCCESS)
+ continue;
+
+ section = heim_config_get_entry(parent, name, heim_config_list);
+ if (section == NULL) {
+ RegCloseKey(subkey);
+ return ENOMEM;
+ }
+
+ code = parse_reg_values(context, subkey, &section->u.list);
+ if (code) {
+ RegCloseKey(subkey);
+ return code;
+ }
+
+ code = parse_reg_subkeys(context, subkey, &section->u.list);
+ if (code) {
+ RegCloseKey(subkey);
+ return code;
+ }
+
+ RegCloseKey(subkey);
+ }
+
+ return 0;
+}
+
+static heim_error_code
+parse_reg_root(heim_context context,
+ HKEY key,
+ heim_config_section ** parent)
+{
+ heim_config_section *libdefaults = NULL;
+ heim_error_code code = 0;
+
+ libdefaults = heim_config_get_entry(parent, "libdefaults", heim_config_list);
+ if (libdefaults == NULL)
+ return heim_enomem(context);
+
+ code = parse_reg_values(context, key, &libdefaults->u.list);
+ if (code)
+ return code;
+
+ return parse_reg_subkeys(context, key, parent);
+}
+
+static heim_error_code
+load_config_from_regpath(heim_context context,
+ HKEY hk_root,
+ const char* key_path,
+ heim_config_section ** res)
+{
+ HKEY key = NULL;
+ LONG rcode;
+ heim_error_code code = 0;
+
+ rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
+ if (rcode == ERROR_SUCCESS) {
+ code = parse_reg_root(context, key, res);
+ RegCloseKey(key);
+ key = NULL;
+ }
+
+ return code;
+}
+
+/**
+ * Load configuration from registry
+ *
+ * The registry keys 'HKCU\Software\Heimdal' and
+ * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each
+ * registry key corresponds to a configuration section (or bound list)
+ * and each value in a registry key is treated as a bound value. The
+ * set of values that are directly under the Heimdal key are treated
+ * as if they were defined in the [libdefaults] section.
+ *
+ * @see parse_reg_value() for details about how each type of value is handled.
+ */
+heim_error_code
+heim_load_config_from_registry(heim_context context,
+ const char *path0,
+ const char *path1,
+ heim_config_section **res)
+{
+ heim_error_code code;
+
+ if (!path0 && !path1)
+ return EINVAL;
+
+ if (path0) {
+ code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
+ path0, res);
+ if (code)
+ return code;
+ }
+
+ if (path1) {
+ code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
+ path1, res);
+ if (code)
+ return code;
+ }
+
+ if (path0) {
+ code = load_config_from_regpath(context, HKEY_CURRENT_USER,
+ path0, res);
+ if (code)
+ return code;
+ }
+
+ if (path0) {
+ code = load_config_from_regpath(context, HKEY_CURRENT_USER,
+ path1, res);
+ if (code)
+ return code;
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/lib/base/context.c b/third_party/heimdal/lib/base/context.c
new file mode 100644
index 0000000..fb9b442
--- /dev/null
+++ b/third_party/heimdal/lib/base/context.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+#undef __attribute__
+#define __attribute__(X)
+
+heim_context
+heim_context_init(void)
+{
+ heim_context context;
+
+ if ((context = calloc(1, sizeof(*context))) == NULL)
+ return NULL;
+
+ context->homedir_access = !issuid();
+ context->log_utc = 1;
+ context->error_string = NULL;
+ context->debug_dest = NULL;
+ context->warn_dest = NULL;
+ context->log_dest = NULL;
+ context->time_fmt = NULL;
+ context->et_list = NULL;
+ return context;
+}
+
+void
+heim_context_free(heim_context *contextp)
+{
+ heim_context context = *contextp;
+
+ *contextp = NULL;
+ if (!context)
+ return;
+ heim_closelog(context, context->debug_dest);
+ heim_closelog(context, context->warn_dest);
+ heim_closelog(context, context->log_dest);
+ free_error_table(context->et_list);
+ free(context->time_fmt);
+ free(context->error_string);
+ free(context);
+}
+
+heim_error_code
+heim_add_et_list(heim_context context, void (*func)(struct et_list **))
+{
+ (*func)(&context->et_list);
+ return 0;
+}
+
+heim_error_code
+heim_context_set_time_fmt(heim_context context, const char *fmt)
+{
+ char *s;
+
+ if (fmt == NULL) {
+ free(context->time_fmt);
+ return 0;
+ }
+ if ((s = strdup(fmt)) == NULL)
+ return heim_enomem(context);
+ free(context->time_fmt);
+ context->time_fmt = s;
+ return 0;
+}
+
+const char *
+heim_context_get_time_fmt(heim_context context)
+{
+ return context->time_fmt ? context->time_fmt : "%Y-%m-%dT%H:%M:%S";
+}
+
+unsigned int
+heim_context_set_log_utc(heim_context context, unsigned int log_utc)
+{
+ unsigned int old = context->log_utc;
+
+ context->log_utc = log_utc ? 1 : 0;
+ return old;
+}
+
+int
+heim_context_get_log_utc(heim_context context)
+{
+ return context->log_utc;
+}
+
+unsigned int
+heim_context_set_homedir_access(heim_context context, unsigned int homedir_access)
+{
+ unsigned int old = context->homedir_access;
+
+ context->homedir_access = homedir_access ? 1 : 0;
+ return old;
+}
+
+unsigned int
+heim_context_get_homedir_access(heim_context context)
+{
+ return context->homedir_access;
+}
+
+heim_error_code
+heim_enomem(heim_context context)
+{
+ heim_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+}
+
+heim_log_facility *
+heim_get_log_dest(heim_context context)
+{
+ return context->log_dest;
+}
+
+heim_log_facility *
+heim_get_warn_dest(heim_context context)
+{
+ return context->warn_dest;
+}
+
+heim_log_facility *
+heim_get_debug_dest(heim_context context)
+{
+ return context->debug_dest;
+}
+
+heim_error_code
+heim_set_log_dest(heim_context context, heim_log_facility *fac)
+{
+ context->log_dest = fac;
+ return 0;
+}
+
+heim_error_code
+heim_set_warn_dest(heim_context context, heim_log_facility *fac)
+{
+ context->warn_dest = fac;
+ return 0;
+}
+
+heim_error_code
+heim_set_debug_dest(heim_context context, heim_log_facility *fac)
+{
+ context->debug_dest = fac;
+ return 0;
+}
+
+#ifndef PATH_SEP
+# define PATH_SEP ":"
+#endif
+
+static heim_error_code
+add_file(char ***pfilenames, int *len, char *file)
+{
+ char **pp = *pfilenames;
+ int i;
+
+ for(i = 0; i < *len; i++) {
+ if(strcmp(pp[i], file) == 0) {
+ free(file);
+ return 0;
+ }
+ }
+
+ pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp));
+ if (pp == NULL) {
+ free(file);
+ return ENOMEM;
+ }
+
+ pp[*len] = file;
+ pp[*len + 1] = NULL;
+ *pfilenames = pp;
+ *len += 1;
+ return 0;
+}
+
+#ifdef WIN32
+static char *
+get_default_config_config_files_from_registry(const char *envvar)
+{
+ static const char *KeyName = "Software\\Heimdal"; /* XXX #define this */
+ const char *ValueName;
+ char *config_file = NULL;
+ LONG rcode;
+ HKEY key;
+
+ if (stricmp(envvar, "KRB5_CONFIG") == 0)
+ ValueName = "config";
+ else
+ ValueName = envvar;
+
+ rcode = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName, 0, KEY_READ, &key);
+ if (rcode == ERROR_SUCCESS) {
+ config_file = heim_parse_reg_value_as_multi_string(NULL, key, ValueName,
+ REG_NONE, 0, PATH_SEP);
+ RegCloseKey(key);
+ }
+
+ if (config_file)
+ return config_file;
+
+ rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &key);
+ if (rcode == ERROR_SUCCESS) {
+ config_file = heim_parse_reg_value_as_multi_string(NULL, key, ValueName,
+ REG_NONE, 0, PATH_SEP);
+ RegCloseKey(key);
+ }
+
+ return config_file;
+}
+#endif
+
+heim_error_code
+heim_prepend_config_files(const char *filelist,
+ char **pq,
+ char ***ret_pp)
+{
+ heim_error_code ret;
+ const char *p, *q;
+ char **pp;
+ int len;
+ char *fn;
+
+ pp = NULL;
+
+ len = 0;
+ p = filelist;
+ while(1) {
+ ssize_t l;
+ q = p;
+ l = strsep_copy(&q, PATH_SEP, NULL, 0);
+ if(l == -1)
+ break;
+ fn = malloc(l + 1);
+ if(fn == NULL) {
+ heim_free_config_files(pp);
+ return ENOMEM;
+ }
+ (void) strsep_copy(&p, PATH_SEP, fn, l + 1);
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ heim_free_config_files(pp);
+ return ret;
+ }
+ }
+
+ if (pq != NULL) {
+ int i;
+
+ for (i = 0; pq[i] != NULL; i++) {
+ fn = strdup(pq[i]);
+ if (fn == NULL) {
+ heim_free_config_files(pp);
+ return ENOMEM;
+ }
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ heim_free_config_files(pp);
+ return ret;
+ }
+ }
+ }
+
+ *ret_pp = pp;
+ return 0;
+}
+
+heim_error_code
+heim_prepend_config_files_default(const char *prepend,
+ const char *def,
+ const char *envvar,
+ char ***pfilenames)
+{
+ heim_error_code ret;
+ char **defpp, **pp = NULL;
+
+ ret = heim_get_default_config_files(def, envvar, &defpp);
+ if (ret)
+ return ret;
+
+ ret = heim_prepend_config_files(prepend, defpp, &pp);
+ heim_free_config_files(defpp);
+ if (ret) {
+ return ret;
+ }
+ *pfilenames = pp;
+ return 0;
+}
+
+heim_error_code
+heim_get_default_config_files(const char *def,
+ const char *envvar,
+ char ***pfilenames)
+{
+ const char *files = NULL;
+
+ files = secure_getenv(envvar);
+
+#ifdef _WIN32
+ if (files == NULL) {
+ char * reg_files;
+ reg_files = get_default_config_config_files_from_registry(envvar);
+ if (reg_files != NULL) {
+ heim_error_code code;
+
+ code = heim_prepend_config_files(reg_files, NULL, pfilenames);
+ free(reg_files);
+
+ return code;
+ }
+ }
+#endif
+
+ if (files == NULL)
+ files = def;
+ return heim_prepend_config_files(files, NULL, pfilenames);
+}
+
+#ifdef _WIN32
+#define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
+#define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
+#endif
+
+heim_error_code
+heim_set_config_files(heim_context context, char **filenames,
+ heim_config_binding **res)
+{
+ heim_error_code ret = 0;
+
+ *res = NULL;
+ while (filenames != NULL && *filenames != NULL && **filenames != '\0') {
+ ret = heim_config_parse_file_multi(context, *filenames, res);
+ if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM
+ && ret != HEIM_ERR_CONFIG_BADFORMAT) {
+ heim_config_file_free(context, *res);
+ *res = NULL;
+ return ret;
+ }
+ filenames++;
+ }
+
+#ifdef _WIN32
+ /*
+ * We always ignored errors from loading from the registry, so we still do.
+ */
+ heim_load_config_from_registry(context, REGPATH_KERBEROS,
+ REGPATH_HEIMDAL, res);
+
+#endif
+ return 0;
+}
+
+void
+heim_free_config_files(char **filenames)
+{
+ char **p;
+
+ for (p = filenames; p && *p != NULL; p++)
+ free(*p);
+ free(filenames);
+}
diff --git a/third_party/heimdal/lib/base/data.c b/third_party/heimdal/lib/base/data.c
new file mode 100644
index 0000000..cefdde0
--- /dev/null
+++ b/third_party/heimdal/lib/base/data.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <string.h>
+
+static void HEIM_CALLCONV
+data_dealloc(void *ptr)
+{
+ heim_data_t d = ptr;
+ heim_octet_string *os = (heim_octet_string *)d;
+ heim_data_free_f_t *deallocp;
+ heim_data_free_f_t dealloc;
+
+ if (os->data == NULL)
+ return;
+
+ /* Possible string ref */
+ deallocp = _heim_get_isaextra(os, 0);
+ dealloc = *deallocp;
+ if (dealloc != NULL)
+ dealloc(os->data);
+}
+
+static int
+data_cmp(void *a, void *b)
+{
+ heim_octet_string *osa = a, *osb = b;
+ if (osa->length != osb->length)
+ return osa->length - osb->length;
+ return memcmp(osa->data, osb->data, osa->length);
+}
+
+static uintptr_t
+data_hash(void *ptr)
+{
+ heim_octet_string *os = ptr;
+ const unsigned char *s = os->data;
+
+ if (os->length < 4)
+ return os->length;
+
+ return ((unsigned long)s[os->length - 1] << 24)
+ | (s[os->length - 2] << 16) | (s[1] << 8) | s[0];
+}
+
+struct heim_type_data _heim_data_object = {
+ HEIM_TID_DATA,
+ "data-object",
+ NULL,
+ data_dealloc,
+ NULL,
+ data_cmp,
+ data_hash,
+ NULL
+};
+
+/**
+ * Create a data object
+ *
+ * @param string the string to create, must be an utf8 string
+ *
+ * @return string object
+ */
+
+heim_data_t
+heim_data_create(const void *data, size_t length)
+{
+ heim_octet_string *os;
+
+ os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
+ if (os) {
+ os->data = (uint8_t *)os + sizeof(*os);
+ os->length = length;
+ memcpy(os->data, data, length);
+ }
+ return (heim_data_t)os;
+}
+
+heim_data_t
+heim_data_ref_create(const void *data, size_t length,
+ heim_data_free_f_t dealloc)
+{
+ heim_octet_string *os;
+ heim_data_free_f_t *deallocp;
+
+ os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
+ if (os) {
+ os->data = (void *)data;
+ os->length = length;
+ deallocp = _heim_get_isaextra(os, 0);
+ *deallocp = dealloc;
+ }
+ return (heim_data_t)os;
+}
+
+
+/**
+ * Return the type ID of data objects
+ *
+ * @return type id of data objects
+ */
+
+heim_tid_t
+heim_data_get_type_id(void)
+{
+ return HEIM_TID_DATA;
+}
+
+/**
+ * Get the data value of the content.
+ *
+ * @param data the data object to get the value from
+ *
+ * @return a heim_octet_string
+ */
+
+const heim_octet_string *
+heim_data_get_data(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return (const heim_octet_string *)data;
+}
+
+const void *
+heim_data_get_ptr(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return ((const heim_octet_string *)data)->data;
+}
+
+size_t heim_data_get_length(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return ((const heim_octet_string *)data)->length;
+}
diff --git a/third_party/heimdal/lib/base/db.c b/third_party/heimdal/lib/base/db.c
new file mode 100644
index 0000000..b206ff6
--- /dev/null
+++ b/third_party/heimdal/lib/base/db.c
@@ -0,0 +1,1721 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This is a pluggable simple DB abstraction, with a simple get/set/
+ * delete key/value pair interface.
+ *
+ * Plugins may provide any of the following optional features:
+ *
+ * - tables -- multiple attribute/value tables in one DB
+ * - locking
+ * - transactions (i.e., allow any heim_object_t as key or value)
+ * - transcoding of values
+ *
+ * Stackable plugins that provide missing optional features are
+ * possible.
+ *
+ * Any plugin that provides locking will also provide transactions, but
+ * those transactions will not be atomic in the face of failures (a
+ * memory-based rollback log is used).
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <io.h>
+#else
+#include <sys/file.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "baselocl.h"
+#include <base64.h>
+
+#define HEIM_ENOMEM(ep) \
+ (((ep) && !*(ep)) ? \
+ heim_error_get_code((*(ep) = heim_error_create_enomem())) : ENOMEM)
+
+#define HEIM_ERROR_HELPER(ep, ec, args) \
+ (((ep) && !*(ep)) ? \
+ heim_error_get_code((*(ep) = heim_error_create args)) : (ec))
+
+#define HEIM_ERROR(ep, ec, args) \
+ (ec == ENOMEM) ? HEIM_ENOMEM(ep) : HEIM_ERROR_HELPER(ep, ec, args);
+
+static heim_string_t to_base64(heim_data_t, heim_error_t *);
+static heim_data_t from_base64(heim_string_t, heim_error_t *);
+
+static int open_file(const char *, int , int, int *, heim_error_t *);
+static int read_json(const char *, heim_object_t *, heim_error_t *);
+static struct heim_db_type json_dbt;
+
+static void HEIM_CALLCONV db_dealloc(void *ptr);
+
+struct heim_type_data db_object = {
+ HEIM_TID_DB,
+ "db-object",
+ NULL,
+ db_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+static heim_base_once_t db_plugin_init_once = HEIM_BASE_ONCE_INIT;
+
+static heim_dict_t db_plugins;
+
+typedef struct db_plugin {
+ heim_string_t name;
+ heim_db_plug_open_f_t openf;
+ heim_db_plug_clone_f_t clonef;
+ heim_db_plug_close_f_t closef;
+ heim_db_plug_lock_f_t lockf;
+ heim_db_plug_unlock_f_t unlockf;
+ heim_db_plug_sync_f_t syncf;
+ heim_db_plug_begin_f_t beginf;
+ heim_db_plug_commit_f_t commitf;
+ heim_db_plug_rollback_f_t rollbackf;
+ heim_db_plug_copy_value_f_t copyf;
+ heim_db_plug_set_value_f_t setf;
+ heim_db_plug_del_key_f_t delf;
+ heim_db_plug_iter_f_t iterf;
+ void *data;
+} db_plugin_desc, *db_plugin;
+
+struct heim_db_data {
+ db_plugin plug;
+ heim_string_t dbtype;
+ heim_string_t dbname;
+ heim_dict_t options;
+ void *db_data;
+ heim_data_t to_release;
+ heim_error_t error;
+ int ret;
+ unsigned int in_transaction:1;
+ unsigned int ro:1;
+ unsigned int ro_tx:1;
+ heim_dict_t set_keys;
+ heim_dict_t del_keys;
+ heim_string_t current_table;
+};
+
+static int
+db_do_log_actions(heim_db_t db, heim_error_t *error);
+static int
+db_replay_log(heim_db_t db, heim_error_t *error);
+
+static HEIMDAL_MUTEX db_type_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+static void
+db_init_plugins_once(void *arg)
+{
+ db_plugins = heim_retain(arg);
+}
+
+static void HEIM_CALLCONV
+plugin_dealloc(void *arg)
+{
+ db_plugin plug = arg;
+
+ heim_release(plug->name);
+}
+
+/** heim_db_register
+ * @brief Registers a DB type for use with heim_db_create().
+ *
+ * @param dbtype Name of DB type
+ * @param data Private data argument to the dbtype's openf method
+ * @param plugin Structure with DB type methods (function pointers)
+ *
+ * Backends that provide begin/commit/rollback methods must provide ACID
+ * semantics.
+ *
+ * The registered DB type will have ACID semantics for backends that do
+ * not provide begin/commit/rollback methods but do provide lock/unlock
+ * and rdjournal/wrjournal methods (using a replay log journalling
+ * scheme).
+ *
+ * If the registered DB type does not natively provide read vs. write
+ * transaction isolation but does provide a lock method then the DB will
+ * provide read/write transaction isolation.
+ *
+ * @return ENOMEM on failure, else 0.
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_register(const char *dbtype,
+ void *data,
+ struct heim_db_type *plugin)
+{
+ heim_dict_t plugins;
+ heim_string_t s;
+ db_plugin plug, plug2;
+ int ret = 0;
+
+ if ((plugin->beginf != NULL && plugin->commitf == NULL) ||
+ (plugin->beginf != NULL && plugin->rollbackf == NULL) ||
+ (plugin->lockf != NULL && plugin->unlockf == NULL) ||
+ plugin->copyf == NULL)
+ heim_abort("Invalid DB plugin; make sure methods are paired");
+
+ /* Initialize */
+ plugins = heim_dict_create(11);
+ if (plugins == NULL)
+ return ENOMEM;
+ heim_base_once_f(&db_plugin_init_once, plugins, db_init_plugins_once);
+ heim_release(plugins);
+ heim_assert(db_plugins != NULL, "heim_db plugin table initialized");
+
+ s = heim_string_create(dbtype);
+ if (s == NULL)
+ return ENOMEM;
+
+ plug = heim_alloc(sizeof (*plug), "db_plug", plugin_dealloc);
+ if (plug == NULL) {
+ heim_release(s);
+ return ENOMEM;
+ }
+
+ plug->name = heim_retain(s);
+ plug->openf = plugin->openf;
+ plug->clonef = plugin->clonef;
+ plug->closef = plugin->closef;
+ plug->lockf = plugin->lockf;
+ plug->unlockf = plugin->unlockf;
+ plug->syncf = plugin->syncf;
+ plug->beginf = plugin->beginf;
+ plug->commitf = plugin->commitf;
+ plug->rollbackf = plugin->rollbackf;
+ plug->copyf = plugin->copyf;
+ plug->setf = plugin->setf;
+ plug->delf = plugin->delf;
+ plug->iterf = plugin->iterf;
+ plug->data = data;
+
+ HEIMDAL_MUTEX_lock(&db_type_mutex);
+ plug2 = heim_dict_get_value(db_plugins, s);
+ if (plug2 == NULL)
+ ret = heim_dict_set_value(db_plugins, s, plug);
+ HEIMDAL_MUTEX_unlock(&db_type_mutex);
+ heim_release(plug);
+ heim_release(s);
+
+ return ret;
+}
+
+static void HEIM_CALLCONV
+db_dealloc(void *arg)
+{
+ heim_db_t db = arg;
+ heim_assert(!db->in_transaction,
+ "rollback or commit heim_db_t before releasing it");
+ if (db->db_data)
+ (void) db->plug->closef(db->db_data, NULL);
+ heim_release(db->to_release);
+ heim_release(db->dbtype);
+ heim_release(db->dbname);
+ heim_release(db->options);
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ heim_release(db->error);
+}
+
+struct dbtype_iter {
+ heim_db_t db;
+ const char *dbname;
+ heim_dict_t options;
+ heim_error_t *error;
+};
+
+/*
+ * Helper to create a DB handle with the first registered DB type that
+ * can open the given DB. This is useful when the app doesn't know the
+ * DB type a priori. This assumes that DB types can "taste" DBs, either
+ * from the filename extension or from the actual file contents.
+ */
+static void
+dbtype_iter2create_f(heim_object_t dbtype, heim_object_t junk, void *arg)
+{
+ struct dbtype_iter *iter_ctx = arg;
+
+ if (iter_ctx->db != NULL)
+ return;
+ iter_ctx->db = heim_db_create(heim_string_get_utf8(dbtype),
+ iter_ctx->dbname, iter_ctx->options,
+ iter_ctx->error);
+}
+
+/**
+ * Open a database of the given dbtype.
+ *
+ * Database type names can be composed of one or more pseudo-DB types
+ * and one concrete DB type joined with a '+' between each. For
+ * example: "transaction+bdb" might be a Berkeley DB with a layer above
+ * that provides transactions.
+ *
+ * Options may be provided via a dict (an associative array). Existing
+ * options include:
+ *
+ * - "create", with any value (create if DB doesn't exist)
+ * - "exclusive", with any value (exclusive create)
+ * - "truncate", with any value (truncate the DB)
+ * - "read-only", with any value (disallow writes)
+ * - "sync", with any value (make transactions durable)
+ * - "journal-name", with a string value naming a journal file name
+ *
+ * @param dbtype Name of DB type
+ * @param dbname Name of DB (likely a file path)
+ * @param options Options dict
+ * @param db Output open DB handle
+ * @param error Output error object
+ *
+ * @return a DB handle
+ *
+ * @addtogroup heimbase
+ */
+heim_db_t
+heim_db_create(const char *dbtype, const char *dbname,
+ heim_dict_t options, heim_error_t *error)
+{
+ heim_string_t s;
+ char *p;
+ db_plugin plug;
+ heim_db_t db;
+ int ret = 0;
+
+ if (options == NULL) {
+ options = heim_dict_create(11);
+ if (options == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ } else {
+ (void) heim_retain(options);
+ }
+
+ if (db_plugins == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ if (dbtype == NULL || *dbtype == '\0') {
+ struct dbtype_iter iter_ctx = { NULL, dbname, options, error};
+
+ /* Try all dbtypes */
+ heim_dict_iterate_f(db_plugins, &iter_ctx, dbtype_iter2create_f);
+ heim_release(options);
+ return iter_ctx.db;
+ } else if (strstr(dbtype, "json")) {
+ (void) heim_db_register(dbtype, NULL, &json_dbt);
+ }
+
+ /*
+ * Allow for dbtypes that are composed from pseudo-dbtypes chained
+ * to a real DB type with '+'. For example a pseudo-dbtype might
+ * add locking, transactions, transcoding of values, ...
+ */
+ p = strchr(dbtype, '+');
+ if (p != NULL)
+ s = heim_string_create_with_bytes(dbtype, p - dbtype);
+ else
+ s = heim_string_create(dbtype);
+ if (s == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ HEIMDAL_MUTEX_lock(&db_type_mutex);
+ plug = heim_dict_get_value(db_plugins, s);
+ HEIMDAL_MUTEX_unlock(&db_type_mutex);
+ heim_release(s);
+ if (plug == NULL) {
+ if (error)
+ *error = heim_error_create(ENOENT,
+ N_("Heimdal DB plugin not found: %s", ""),
+ dbtype);
+ heim_release(options);
+ return NULL;
+ }
+
+ db = _heim_alloc_object(&db_object, sizeof(*db));
+ if (db == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->plug = plug;
+ db->options = options;
+
+ ret = plug->openf(plug->data, dbtype, dbname, options, &db->db_data, error);
+ if (ret) {
+ heim_release(db);
+ if (error && *error == NULL)
+ *error = heim_error_create(ENOENT,
+ N_("Heimdal DB could not be opened: %s", ""),
+ dbname);
+ return NULL;
+ }
+
+ ret = db_replay_log(db, error);
+ if (ret) {
+ heim_release(db);
+ return NULL;
+ }
+
+ if (plug->clonef == NULL) {
+ db->dbtype = heim_string_create(dbtype);
+ db->dbname = heim_string_create(dbname);
+
+ if (!db->dbtype || ! db->dbname) {
+ heim_release(db);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ }
+
+ return db;
+}
+
+/**
+ * Clone (duplicate) an open DB handle.
+ *
+ * This is useful for multi-threaded applications. Applications must
+ * synchronize access to any given DB handle.
+ *
+ * Returns EBUSY if there is an open transaction for the input db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return a DB handle
+ *
+ * @addtogroup heimbase
+ */
+heim_db_t
+heim_db_clone(heim_db_t db, heim_error_t *error)
+{
+ heim_db_t result;
+ int ret;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ heim_abort("Expected a database");
+ if (db->in_transaction)
+ heim_abort("DB handle is busy");
+
+ if (db->plug->clonef == NULL) {
+ return heim_db_create(heim_string_get_utf8(db->dbtype),
+ heim_string_get_utf8(db->dbname),
+ db->options, error);
+ }
+
+ result = _heim_alloc_object(&db_object, sizeof(*result));
+ if (result == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ result->set_keys = NULL;
+ result->del_keys = NULL;
+ ret = db->plug->clonef(db->db_data, &result->db_data, error);
+ if (ret) {
+ heim_release(result);
+ if (error && !*error)
+ *error = heim_error_create(ENOENT,
+ N_("Could not re-open DB while cloning", ""));
+ return NULL;
+ }
+ db->db_data = NULL;
+ return result;
+}
+
+/**
+ * Open a transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_begin(heim_db_t db, int read_only, heim_error_t *error)
+{
+ int ret;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (db->in_transaction && (read_only || !db->ro_tx || (!read_only && !db->ro_tx)))
+ heim_abort("DB already in transaction");
+
+ if (db->plug->setf == NULL || db->plug->delf == NULL)
+ return EINVAL;
+
+ if (db->plug->beginf) {
+ ret = db->plug->beginf(db->db_data, read_only, error);
+ if (ret)
+ return ret;
+ } else if (!db->in_transaction) {
+ /* Try to emulate transactions */
+
+ if (db->plug->lockf == NULL)
+ return EINVAL; /* can't lock? -> no transactions */
+
+ /* Assume unlock provides sync/durability */
+ ret = db->plug->lockf(db->db_data, read_only, error);
+ if (ret)
+ return ret;
+
+ ret = db_replay_log(db, error);
+ if (ret) {
+ ret = db->plug->unlockf(db->db_data, error);
+ return ret;
+ }
+
+ db->set_keys = heim_dict_create(11);
+ if (db->set_keys == NULL)
+ return ENOMEM;
+ db->del_keys = heim_dict_create(11);
+ if (db->del_keys == NULL) {
+ heim_release(db->set_keys);
+ db->set_keys = NULL;
+ return ENOMEM;
+ }
+ } else {
+ heim_assert(read_only == 0, "Internal error");
+ ret = db->plug->lockf(db->db_data, 0, error);
+ if (ret)
+ return ret;
+ }
+ db->in_transaction = 1;
+ db->ro_tx = !!read_only;
+ return 0;
+}
+
+/**
+ * Commit an open transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_commit(heim_db_t db, heim_error_t *error)
+{
+ int ret, ret2;
+ heim_string_t journal_fname = NULL;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+ if (!db->in_transaction)
+ return 0;
+ if (db->plug->commitf == NULL && db->plug->lockf == NULL)
+ return EINVAL;
+
+ if (db->plug->commitf != NULL) {
+ ret = db->plug->commitf(db->db_data, error);
+ if (ret)
+ (void) db->plug->rollbackf(db->db_data, error);
+
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+ return ret;
+ }
+
+ if (db->ro_tx) {
+ ret = 0;
+ goto done;
+ }
+
+ if (db->options)
+ journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename"));
+
+ if (journal_fname != NULL) {
+ heim_array_t a;
+ heim_string_t journal_contents;
+ size_t len, bytes;
+ int save_errno;
+
+ /* Create contents for replay log */
+ ret = ENOMEM;
+ a = heim_array_create();
+ if (a == NULL)
+ goto err;
+ ret = heim_array_append_value(a, db->set_keys);
+ if (ret) {
+ heim_release(a);
+ goto err;
+ }
+ ret = heim_array_append_value(a, db->del_keys);
+ if (ret) {
+ heim_release(a);
+ goto err;
+ }
+ journal_contents = heim_json_copy_serialize(a, 0, error);
+ heim_release(a);
+
+ /* Write replay log */
+ if (journal_fname != NULL) {
+ int fd;
+
+ ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error);
+ if (ret) {
+ heim_release(journal_contents);
+ goto err;
+ }
+ len = strlen(heim_string_get_utf8(journal_contents));
+ bytes = write(fd, heim_string_get_utf8(journal_contents), len);
+ save_errno = errno;
+ heim_release(journal_contents);
+ ret = close(fd);
+ if (bytes != len) {
+ /* Truncate replay log */
+ (void) open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error);
+ ret = save_errno;
+ goto err;
+ }
+ if (ret)
+ goto err;
+ }
+ }
+
+ /* Apply logged actions */
+ ret = db_do_log_actions(db, error);
+ if (ret)
+ return ret;
+
+ if (db->plug->syncf != NULL) {
+ /* fsync() or whatever */
+ ret = db->plug->syncf(db->db_data, error);
+ if (ret)
+ return ret;
+ }
+
+ /* Truncate replay log and we're done */
+ if (journal_fname != NULL) {
+ int fd;
+
+ ret2 = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error);
+ if (ret2 == 0)
+ (void) close(fd);
+ }
+
+ /*
+ * Clean up; if we failed to remore the replay log that's OK, we'll
+ * handle that again in heim_db_commit()
+ */
+done:
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+
+ ret2 = db->plug->unlockf(db->db_data, error);
+ if (ret == 0)
+ ret = ret2;
+
+ return ret;
+
+err:
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Error while committing transaction: %s", ""),
+ strerror(ret)));
+}
+
+/**
+ * Rollback an open transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_rollback(heim_db_t db, heim_error_t *error)
+{
+ int ret = 0;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+ if (!db->in_transaction)
+ return 0;
+
+ if (db->plug->rollbackf != NULL)
+ ret = db->plug->rollbackf(db->db_data, error);
+ else if (db->plug->unlockf != NULL)
+ ret = db->plug->unlockf(db->db_data, error);
+
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+
+ return ret;
+}
+
+/**
+ * Get type ID of heim_db_t objects.
+ *
+ * @addtogroup heimbase
+ */
+heim_tid_t
+heim_db_get_type_id(void)
+{
+ return HEIM_TID_DB;
+}
+
+heim_data_t
+_heim_db_get_value(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_release(db->to_release);
+ db->to_release = heim_db_copy_value(db, table, key, error);
+ return db->to_release;
+}
+
+/**
+ * Lookup a key's value in the DB.
+ *
+ * Returns 0 on success, -1 if the key does not exist in the DB, or a
+ * system error number on failure.
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param error Output error object
+ *
+ * @return the value (retained), if there is one for the given key
+ *
+ * @addtogroup heimbase
+ */
+heim_data_t
+heim_db_copy_value(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_object_t v;
+ heim_data_t result;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return NULL;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (db->in_transaction) {
+ heim_string_t key64;
+
+ key64 = to_base64(key, error);
+ if (key64 == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ v = heim_path_copy(db->set_keys, error, table, key64, NULL);
+ if (v != NULL) {
+ heim_release(key64);
+ return v;
+ }
+ v = heim_path_copy(db->del_keys, error, table, key64, NULL); /* can't be NULL */
+ heim_release(key64);
+ if (v != NULL)
+ return NULL;
+ }
+
+ result = db->plug->copyf(db->db_data, table, key, error);
+
+ return result;
+}
+
+/**
+ * Set a key's value in the DB.
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param value Value (if NULL the key will be deleted, but empty is OK)
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_set_value(heim_db_t db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ heim_string_t key64 = NULL;
+ int ret;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (value == NULL)
+ /* Use heim_null_t instead of NULL */
+ return heim_db_delete_key(db, table, key, error);
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (heim_get_tid(key) != HEIM_TID_DATA)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("DB keys must be data", "")));
+
+ if (db->plug->setf == NULL)
+ return EBADF;
+
+ if (!db->in_transaction) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ heim_assert(db->in_transaction, "Internal error");
+ ret = heim_db_set_value(db, table, key, value, error);
+ if (ret) {
+ (void) heim_db_rollback(db, NULL);
+ return ret;
+ }
+ return heim_db_commit(db, error);
+ }
+
+ /* Transaction emulation */
+ heim_assert(db->set_keys != NULL, "Internal error");
+ key64 = to_base64(key, error);
+ if (key64 == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (db->ro_tx) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ }
+ ret = heim_path_create(db->set_keys, 29, value, error, table, key64, NULL);
+ if (ret)
+ goto err;
+ heim_path_delete(db->del_keys, error, table, key64, NULL);
+ heim_release(key64);
+
+ return 0;
+
+err:
+ heim_release(key64);
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Could not set a dict value while while "
+ "setting a DB value", "")));
+}
+
+/**
+ * Delete a key and its value from the DB
+ *
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_delete_key(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_string_t key64 = NULL;
+ int ret;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (db->plug->delf == NULL)
+ return EBADF;
+
+ if (!db->in_transaction) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ heim_assert(db->in_transaction, "Internal error");
+ ret = heim_db_delete_key(db, table, key, error);
+ if (ret) {
+ (void) heim_db_rollback(db, NULL);
+ return ret;
+ }
+ return heim_db_commit(db, error);
+ }
+
+ /* Transaction emulation */
+ heim_assert(db->set_keys != NULL, "Internal error");
+ key64 = to_base64(key, error);
+ if (key64 == NULL)
+ return HEIM_ENOMEM(error);
+ if (db->ro_tx) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ }
+ ret = heim_path_create(db->del_keys, 29, heim_number_create(1), error, table, key64, NULL);
+ if (ret)
+ goto err;
+ heim_path_delete(db->set_keys, error, table, key64, NULL);
+ heim_release(key64);
+
+ return 0;
+
+err:
+ heim_release(key64);
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Could not set a dict value while while "
+ "deleting a DB value", "")));
+}
+
+/**
+ * Iterate a callback function over keys and values from a DB.
+ *
+ * @param db Open DB handle
+ * @param iter_data Callback function's private data
+ * @param iter_f Callback function, called once per-key/value pair
+ * @param error Output error object
+ *
+ * @addtogroup heimbase
+ */
+void
+heim_db_iterate_f(heim_db_t db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ if (error != NULL)
+ *error = NULL;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return;
+
+ if (!db->in_transaction)
+ db->plug->iterf(db->db_data, table, iter_data, iter_f, error);
+}
+
+static void
+db_replay_log_table_set_keys_iter(heim_object_t key, heim_object_t value,
+ void *arg)
+{
+ heim_db_t db = arg;
+ heim_data_t k, v;
+
+ if (db->ret)
+ return;
+
+ k = from_base64((heim_string_t)key, &db->error);
+ if (k == NULL) {
+ db->ret = ENOMEM;
+ return;
+ }
+ v = (heim_data_t)value;
+
+ db->ret = db->plug->setf(db->db_data, db->current_table, k, v, &db->error);
+ heim_release(k);
+}
+
+static void
+db_replay_log_table_del_keys_iter(heim_object_t key, heim_object_t value,
+ void *arg)
+{
+ heim_db_t db = arg;
+ heim_data_t k;
+
+ if (db->ret) {
+ db->ret = ENOMEM;
+ return;
+ }
+
+ k = from_base64((heim_string_t)key, &db->error);
+ if (k == NULL)
+ return;
+
+ db->ret = db->plug->delf(db->db_data, db->current_table, k, &db->error);
+ heim_release(k);
+}
+
+static void
+db_replay_log_set_keys_iter(heim_object_t table, heim_object_t table_dict,
+ void *arg)
+{
+ heim_db_t db = arg;
+
+ if (db->ret)
+ return;
+
+ db->current_table = table;
+ heim_dict_iterate_f(table_dict, db, db_replay_log_table_set_keys_iter);
+}
+
+static void
+db_replay_log_del_keys_iter(heim_object_t table, heim_object_t table_dict,
+ void *arg)
+{
+ heim_db_t db = arg;
+
+ if (db->ret)
+ return;
+
+ db->current_table = table;
+ heim_dict_iterate_f(table_dict, db, db_replay_log_table_del_keys_iter);
+}
+
+static int
+db_do_log_actions(heim_db_t db, heim_error_t *error)
+{
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ db->ret = 0;
+ db->error = NULL;
+ if (db->set_keys != NULL)
+ heim_dict_iterate_f(db->set_keys, db, db_replay_log_set_keys_iter);
+ if (db->del_keys != NULL)
+ heim_dict_iterate_f(db->del_keys, db, db_replay_log_del_keys_iter);
+
+ ret = db->ret;
+ db->ret = 0;
+ if (error && db->error) {
+ *error = db->error;
+ db->error = NULL;
+ } else {
+ heim_release(db->error);
+ db->error = NULL;
+ }
+ return ret;
+}
+
+static int
+db_replay_log(heim_db_t db, heim_error_t *error)
+{
+ int ret;
+ heim_string_t journal_fname = NULL;
+ heim_object_t journal;
+ size_t len;
+
+ heim_assert(!db->in_transaction, "DB transaction not open");
+ heim_assert(db->set_keys == NULL && db->set_keys == NULL, "DB transaction not open");
+
+ if (error)
+ *error = NULL;
+
+ if (db->options == NULL)
+ return 0;
+
+ journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename"));
+ if (journal_fname == NULL)
+ return 0;
+
+ ret = read_json(heim_string_get_utf8(journal_fname), &journal, error);
+ if (ret == ENOENT) {
+ heim_release(journal_fname);
+ return 0;
+ }
+ if (ret == 0 && journal == NULL) {
+ heim_release(journal_fname);
+ return 0;
+ }
+ if (ret != 0) {
+ heim_release(journal_fname);
+ return ret;
+ }
+
+ if (heim_get_tid(journal) != HEIM_TID_ARRAY) {
+ heim_release(journal_fname);
+ return HEIM_ERROR(error, EINVAL,
+ (ret, N_("Invalid journal contents; delete journal",
+ "")));
+ }
+
+ len = heim_array_get_length(journal);
+
+ if (len > 0)
+ db->set_keys = heim_array_get_value(journal, 0);
+ if (len > 1)
+ db->del_keys = heim_array_get_value(journal, 1);
+ ret = db_do_log_actions(db, error);
+ if (ret) {
+ heim_release(journal_fname);
+ return ret;
+ }
+
+ /* Truncate replay log and we're done */
+ ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error);
+ heim_release(journal_fname);
+ if (ret)
+ return ret;
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+
+ return 0;
+}
+
+static
+heim_string_t to_base64(heim_data_t data, heim_error_t *error)
+{
+ char *b64 = NULL;
+ heim_string_t s = NULL;
+ const heim_octet_string *d;
+ int ret;
+
+ d = heim_data_get_data(data);
+ ret = rk_base64_encode(d->data, d->length, &b64);
+ if (ret < 0 || b64 == NULL)
+ goto enomem;
+ s = heim_string_ref_create(b64, free);
+ if (s == NULL)
+ goto enomem;
+ return s;
+
+enomem:
+ free(b64);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+}
+
+static
+heim_data_t from_base64(heim_string_t s, heim_error_t *error)
+{
+ ssize_t len = -1;
+ void *buf;
+ heim_data_t d;
+
+ buf = malloc(strlen(heim_string_get_utf8(s)));
+ if (buf)
+ len = rk_base64_decode(heim_string_get_utf8(s), buf);
+ if (len > -1 && (d = heim_data_ref_create(buf, len, free)))
+ return d;
+ free(buf);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+}
+
+
+static int
+open_file(const char *dbname, int for_write, int excl, int *fd_out, heim_error_t *error)
+{
+#ifdef WIN32
+ HANDLE hFile;
+ int ret = 0;
+
+ if (fd_out)
+ *fd_out = -1;
+
+ if (for_write)
+ hFile = CreateFile(dbname, GENERIC_WRITE | GENERIC_READ, 0,
+ NULL, /* we'll close as soon as we read */
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ else
+ hFile = CreateFile(dbname, GENERIC_READ, FILE_SHARE_READ,
+ NULL, /* we'll close as soon as we read */
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ ret = GetLastError();
+ _set_errno(ret); /* CreateFile() does not set errno */
+ goto err;
+ }
+ if (fd_out == NULL) {
+ (void) CloseHandle(hFile);
+ return 0;
+ }
+
+ *fd_out = _open_osfhandle((intptr_t) hFile, 0);
+ if (*fd_out < 0) {
+ ret = errno;
+ (void) CloseHandle(hFile);
+ goto err;
+ }
+
+ /* No need to lock given share deny mode */
+ return 0;
+
+err:
+ if (error != NULL) {
+ char *s = NULL;
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ 0, ret, 0, (LPTSTR) &s, 0, NULL);
+ *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""),
+ dbname, s ? s : "<error formatting error>");
+ LocalFree(s);
+ }
+ return ret;
+#else
+ int ret = 0;
+ int fd;
+
+ if (fd_out)
+ *fd_out = -1;
+
+ if (for_write && excl)
+ fd = open(dbname, O_CREAT | O_EXCL | O_WRONLY, 0600);
+ else if (for_write)
+ fd = open(dbname, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ else
+ fd = open(dbname, O_RDONLY);
+ if (fd < 0) {
+ if (error != NULL)
+ *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""),
+ dbname, strerror(errno));
+ return errno;
+ }
+
+ if (fd_out == NULL) {
+ (void) close(fd);
+ return 0;
+ }
+
+ ret = flock(fd, for_write ? LOCK_EX : LOCK_SH);
+ if (ret == -1) {
+ /* Note that we if O_EXCL we're leaving the [lock] file around */
+ (void) close(fd);
+ return HEIM_ERROR(error, errno,
+ (errno, N_("Could not lock JSON file %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+
+ *fd_out = fd;
+
+ return 0;
+#endif
+}
+
+static int
+read_json(const char *dbname, heim_object_t *out, heim_error_t *error)
+{
+ struct stat st;
+ char *str = NULL;
+ int ret;
+ int fd = -1;
+ ssize_t bytes;
+
+ *out = NULL;
+ ret = open_file(dbname, 0, 0, &fd, error);
+ if (ret)
+ return ret;
+
+ ret = fstat(fd, &st);
+ if (ret == -1) {
+ (void) close(fd);
+ return HEIM_ERROR(error, errno,
+ (ret, N_("Could not stat JSON DB %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+
+ if (st.st_size == 0) {
+ (void) close(fd);
+ return 0;
+ }
+
+ str = malloc(st.st_size + 1);
+ if (str == NULL) {
+ (void) close(fd);
+ return HEIM_ENOMEM(error);
+ }
+
+ bytes = read(fd, str, st.st_size);
+ (void) close(fd);
+ if (bytes != st.st_size) {
+ free(str);
+ if (bytes >= 0)
+ errno = EINVAL; /* ?? */
+ return HEIM_ERROR(error, errno,
+ (ret, N_("Could not read JSON DB %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+ str[st.st_size] = '\0';
+ *out = heim_json_create(str, 10, 0, error);
+ free(str);
+ if (*out == NULL)
+ return (error && *error) ? heim_error_get_code(*error) : EINVAL;
+ return 0;
+}
+
+typedef struct json_db {
+ heim_dict_t dict;
+ heim_string_t dbname;
+ heim_string_t bkpname;
+ int fd;
+ time_t last_read_time;
+ unsigned int read_only:1;
+ unsigned int locked:1;
+ unsigned int locked_needs_unlink:1;
+} *json_db_t;
+
+static int
+json_db_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ json_db_t jsondb;
+ heim_dict_t contents = NULL;
+ heim_string_t dbname_s = NULL;
+ heim_string_t bkpname_s = NULL;
+
+ if (error)
+ *error = NULL;
+ if (dbtype && *dbtype && strcmp(dbtype, "json") != 0)
+ return HEIM_ERROR(error, EINVAL, (EINVAL, N_("Wrong DB type", "")));
+ if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0) {
+ char *ext = strrchr(dbname, '.');
+ char *bkpname;
+ size_t len;
+ int ret;
+
+ if (ext == NULL || strcmp(ext, ".json") != 0)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB files must end in .json",
+ "")));
+
+ if (options) {
+ heim_object_t vc, ve, vt;
+
+ vc = heim_dict_get_value(options, HSTR("create"));
+ ve = heim_dict_get_value(options, HSTR("exclusive"));
+ vt = heim_dict_get_value(options, HSTR("truncate"));
+ if (vc && vt) {
+ ret = open_file(dbname, 1, ve ? 1 : 0, NULL, error);
+ if (ret)
+ return ret;
+ } else if (vc || ve || vt) {
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("Invalid JSON DB open options",
+ "")));
+ }
+ /*
+ * We don't want cloned handles to truncate the DB, eh?
+ *
+ * We should really just create a copy of the options dict
+ * rather than modify the caller's! But for that it'd be
+ * nicer to have copy utilities in heimbase, something like
+ * this:
+ *
+ * heim_object_t heim_copy(heim_object_t src, int depth,
+ * heim_error_t *error);
+ *
+ * so that options = heim_copy(options, 1); means copy the
+ * dict but nothing else (whereas depth == 0 would mean
+ * heim_retain(), and depth > 1 would be copy that many
+ * levels).
+ */
+ heim_dict_delete_key(options, HSTR("create"));
+ heim_dict_delete_key(options, HSTR("exclusive"));
+ heim_dict_delete_key(options, HSTR("truncate"));
+ }
+ dbname_s = heim_string_create(dbname);
+ if (dbname_s == NULL)
+ return HEIM_ENOMEM(error);
+
+ len = snprintf(NULL, 0, "%s~", dbname);
+ bkpname = malloc(len + 2);
+ if (bkpname == NULL) {
+ heim_release(dbname_s);
+ return HEIM_ENOMEM(error);
+ }
+ (void) snprintf(bkpname, len + 1, "%s~", dbname);
+ bkpname_s = heim_string_create(bkpname);
+ free(bkpname);
+ if (bkpname_s == NULL) {
+ heim_release(dbname_s);
+ return HEIM_ENOMEM(error);
+ }
+
+ ret = read_json(dbname, (heim_object_t *)&contents, error);
+ if (ret) {
+ heim_release(bkpname_s);
+ heim_release(dbname_s);
+ return ret;
+ }
+
+ if (contents != NULL && heim_get_tid(contents) != HEIM_TID_DICT) {
+ heim_release(bkpname_s);
+ heim_release(dbname_s);
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB contents not valid JSON",
+ "")));
+ }
+ }
+
+ jsondb = heim_alloc(sizeof (*jsondb), "json_db", NULL);
+ if (jsondb == NULL) {
+ heim_release(contents);
+ heim_release(dbname_s);
+ heim_release(bkpname_s);
+ return ENOMEM;
+ }
+
+ jsondb->last_read_time = time(NULL);
+ jsondb->fd = -1;
+ jsondb->dbname = dbname_s;
+ jsondb->bkpname = bkpname_s;
+ jsondb->read_only = 0;
+
+ if (contents != NULL)
+ jsondb->dict = contents;
+ else {
+ jsondb->dict = heim_dict_create(29);
+ if (jsondb->dict == NULL) {
+ heim_release(jsondb);
+ return ENOMEM;
+ }
+ }
+
+ *db = jsondb;
+ return 0;
+}
+
+static int
+json_db_close(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+
+ if (error)
+ *error = NULL;
+ if (jsondb->fd > -1)
+ (void) close(jsondb->fd);
+ jsondb->fd = -1;
+ heim_release(jsondb->dbname);
+ heim_release(jsondb->bkpname);
+ heim_release(jsondb->dict);
+ heim_release(jsondb);
+ return 0;
+}
+
+static int
+json_db_lock(void *db, int read_only, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ int ret;
+
+ heim_assert(jsondb->fd == -1 || (jsondb->read_only && !read_only),
+ "DB locks are not recursive");
+
+ jsondb->read_only = read_only ? 1 : 0;
+ if (jsondb->fd > -1)
+ return 0;
+
+ ret = open_file(heim_string_get_utf8(jsondb->bkpname), 1, 1, &jsondb->fd, error);
+ if (ret == 0) {
+ jsondb->locked_needs_unlink = 1;
+ jsondb->locked = 1;
+ }
+ return ret;
+}
+
+static int
+json_db_unlock(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ int ret = 0;
+
+ heim_assert(jsondb->locked, "DB not locked when unlock attempted");
+ if (jsondb->fd > -1)
+ ret = close(jsondb->fd);
+ jsondb->fd = -1;
+ jsondb->read_only = 0;
+ jsondb->locked = 0;
+ if (jsondb->locked_needs_unlink)
+ unlink(heim_string_get_utf8(jsondb->bkpname));
+ jsondb->locked_needs_unlink = 0;
+ return ret;
+}
+
+static int
+json_db_sync(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ size_t len, bytes;
+ heim_error_t e;
+ heim_string_t json;
+ const char *json_text = NULL;
+ int ret = 0;
+ int fd = -1;
+#ifdef WIN32
+ int tries = 3;
+#endif
+
+ heim_assert(jsondb->fd > -1, "DB not locked when sync attempted");
+
+ json = heim_json_copy_serialize(jsondb->dict, 0, &e);
+ if (json == NULL) {
+ if (error)
+ *error = e;
+ else
+ heim_release(e);
+ return heim_error_get_code(e);
+ }
+
+ json_text = heim_string_get_utf8(json);
+ len = strlen(json_text);
+ errno = 0;
+
+#ifdef WIN32
+ while (tries--) {
+ ret = open_file(heim_string_get_utf8(jsondb->dbname), 1, 0, &fd, error);
+ if (ret == 0)
+ break;
+ sleep(1);
+ }
+ if (ret) {
+ heim_release(json);
+ return ret;
+ }
+#else
+ fd = jsondb->fd;
+#endif /* WIN32 */
+
+ bytes = write(fd, json_text, len);
+ heim_release(json);
+ if (bytes != len)
+ return errno ? errno : EIO;
+ ret = fsync(fd);
+ if (ret)
+ return ret;
+
+#ifdef WIN32
+ ret = close(fd);
+ if (ret)
+ return GetLastError();
+#else
+ ret = rename(heim_string_get_utf8(jsondb->bkpname), heim_string_get_utf8(jsondb->dbname));
+ if (ret == 0) {
+ jsondb->locked_needs_unlink = 0;
+ return 0;
+ }
+#endif /* WIN32 */
+
+ return errno;
+}
+
+static heim_data_t
+json_db_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+ struct stat st;
+ heim_data_t result;
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length) {
+ HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB requires keys that are actually "
+ "strings", "")));
+ return NULL;
+ }
+
+ if (stat(heim_string_get_utf8(jsondb->dbname), &st) == -1) {
+ HEIM_ERROR(error, errno,
+ (errno, N_("Could not stat JSON DB file", "")));
+ return NULL;
+ }
+
+ if (st.st_mtime > jsondb->last_read_time ||
+ st.st_ctime > jsondb->last_read_time) {
+ heim_dict_t contents = NULL;
+ int ret;
+
+ /* Ignore file is gone (ENOENT) */
+ ret = read_json(heim_string_get_utf8(jsondb->dbname),
+ (heim_object_t *)&contents, error);
+ if (ret)
+ return NULL;
+ if (contents == NULL)
+ contents = heim_dict_create(29);
+ heim_release(jsondb->dict);
+ jsondb->dict = contents;
+ jsondb->last_read_time = time(NULL);
+ }
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL) {
+ (void) HEIM_ENOMEM(error);
+ return NULL;
+ }
+
+ result = heim_path_copy(jsondb->dict, error, table, key_string, NULL);
+ heim_release(key_string);
+ return result;
+}
+
+static int
+json_db_set_value(void *db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL,
+ N_("JSON DB requires keys that are actually strings",
+ "")));
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (table == NULL)
+ table = HSTR("");
+
+ ret = heim_path_create(jsondb->dict, 29, value, error, table, key_string, NULL);
+ heim_release(key_string);
+ return ret;
+}
+
+static int
+json_db_del_key(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL,
+ N_("JSON DB requires keys that are actually strings",
+ "")));
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (table == NULL)
+ table = HSTR("");
+
+ heim_path_delete(jsondb->dict, error, table, key_string, NULL);
+ heim_release(key_string);
+ return 0;
+}
+
+struct json_db_iter_ctx {
+ heim_db_iterator_f_t iter_f;
+ void *iter_ctx;
+};
+
+static void json_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct json_db_iter_ctx *ctx = arg;
+ const char *key_string;
+ heim_data_t key_data;
+
+ key_string = heim_string_get_utf8((heim_string_t)key);
+ key_data = heim_data_ref_create(key_string, strlen(key_string), NULL);
+ ctx->iter_f(key_data, (heim_object_t)value, ctx->iter_ctx);
+ heim_release(key_data);
+}
+
+static void
+json_db_iter(void *db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ struct json_db_iter_ctx ctx;
+ heim_dict_t table_dict;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ table_dict = heim_dict_get_value(jsondb->dict, table);
+ if (table_dict == NULL)
+ return;
+
+ ctx.iter_ctx = iter_data;
+ ctx.iter_f = iter_f;
+
+ heim_dict_iterate_f(table_dict, &ctx, json_db_iter_f);
+}
+
+static struct heim_db_type json_dbt = {
+ 1, json_db_open, NULL, json_db_close,
+ json_db_lock, json_db_unlock, json_db_sync,
+ NULL, NULL, NULL,
+ json_db_copy_value, json_db_set_value,
+ json_db_del_key, json_db_iter
+};
+
diff --git a/third_party/heimdal/lib/base/dict.c b/third_party/heimdal/lib/base/dict.c
new file mode 100644
index 0000000..86be109
--- /dev/null
+++ b/third_party/heimdal/lib/base/dict.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2002, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct hashentry {
+ struct hashentry **prev;
+ struct hashentry *next;
+ heim_object_t key;
+ heim_object_t value;
+};
+
+struct heim_dict_data {
+ size_t size;
+ struct hashentry **tab;
+};
+
+static void HEIM_CALLCONV
+dict_dealloc(void *ptr)
+{
+ heim_dict_t dict = ptr;
+ struct hashentry **h, *g, *i;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h) {
+ for (g = h[0]; g; g = i) {
+ i = g->next;
+ heim_release(g->key);
+ heim_release(g->value);
+ free(g);
+ }
+ }
+ free(dict->tab);
+}
+
+struct heim_type_data dict_object = {
+ HEIM_TID_DICT,
+ "dict-object",
+ NULL,
+ dict_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static size_t
+isprime(size_t p)
+{
+ size_t q, i;
+
+ for(i = 2 ; i < p; i++) {
+ q = p / i;
+
+ if (i * q == p)
+ return 0;
+ if (i * i > p)
+ return 1;
+ }
+ return 1;
+}
+
+static size_t
+findprime(size_t p)
+{
+ if (p % 2 == 0)
+ p++;
+
+ while (isprime(p) == 0)
+ p += 2;
+
+ return p;
+}
+
+/**
+ * Allocate an array
+ *
+ * @return A new allocated array, free with heim_release()
+ */
+
+heim_dict_t
+heim_dict_create(size_t size)
+{
+ heim_dict_t dict;
+
+ dict = _heim_alloc_object(&dict_object, sizeof(*dict));
+ if (dict == NULL)
+ return NULL;
+
+ dict->size = findprime(size);
+ if (dict->size == 0) {
+ heim_release(dict);
+ return NULL;
+ }
+
+ dict->tab = calloc(dict->size, sizeof(dict->tab[0]));
+ if (dict->tab == NULL) {
+ dict->size = 0;
+ heim_release(dict);
+ return NULL;
+ }
+
+ return dict;
+}
+
+/**
+ * Get type id of an dict
+ *
+ * @return the type id
+ */
+
+heim_tid_t
+heim_dict_get_type_id(void)
+{
+ return HEIM_TID_DICT;
+}
+
+/* Intern search function */
+
+static struct hashentry *
+_search(heim_dict_t dict, heim_object_t ptr)
+{
+ uintptr_t v = heim_get_hash(ptr);
+ struct hashentry *p;
+
+ for (p = dict->tab[v % dict->size]; p != NULL; p = p->next)
+ if (heim_cmp(ptr, p->key) == 0)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * Search for element in hash table
+ *
+ * @value dict the dict to search in
+ * @value key the key to search for
+ *
+ * @return a not-retained copy of the value for key or NULL if not found
+ */
+
+heim_object_t
+heim_dict_get_value(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *p;
+ p = _search(dict, key);
+ if (p == NULL)
+ return NULL;
+
+ return p->value;
+}
+
+/**
+ * Search for element in hash table
+ *
+ * @value dict the dict to search in
+ * @value key the key to search for
+ *
+ * @return a retained copy of the value for key or NULL if not found
+ */
+
+heim_object_t
+heim_dict_copy_value(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *p;
+ p = _search(dict, key);
+ if (p == NULL)
+ return NULL;
+
+ return heim_retain(p->value);
+}
+
+/**
+ * Add key and value to dict
+ *
+ * @value dict the dict to add too
+ * @value key the key to add
+ * @value value the value to add
+ *
+ * @return 0 if added, errno if not
+ */
+
+int
+heim_dict_set_value(heim_dict_t dict, heim_object_t key, heim_object_t value)
+{
+ struct hashentry **tabptr, *h;
+
+ h = _search(dict, key);
+ if (h) {
+ heim_release(h->value);
+ h->value = heim_retain(value);
+ } else {
+ uintptr_t v;
+
+ h = malloc(sizeof(*h));
+ if (h == NULL)
+ return ENOMEM;
+
+ h->key = heim_retain(key);
+ h->value = heim_retain(value);
+
+ v = heim_get_hash(key);
+
+ tabptr = &dict->tab[v % dict->size];
+ h->next = *tabptr;
+ *tabptr = h;
+ h->prev = tabptr;
+ if (h->next)
+ h->next->prev = &h->next;
+ }
+
+ return 0;
+}
+
+/**
+ * Delete element with key key
+ *
+ * @value dict the dict to delete from
+ * @value key the key to delete
+ */
+
+void
+heim_dict_delete_key(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *h = _search(dict, key);
+
+ if (h == NULL)
+ return;
+
+ heim_release(h->key);
+ heim_release(h->value);
+
+ if ((*(h->prev) = h->next) != NULL)
+ h->next->prev = h->prev;
+
+ free(h);
+}
+
+/**
+ * Do something for each element
+ *
+ * @value dict the dict to interate over
+ * @value func the function to search for
+ * @value arg argument to func
+ */
+
+void
+heim_dict_iterate_f(heim_dict_t dict, void *arg, heim_dict_iterator_f_t func)
+{
+ struct hashentry **h, *g;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
+ for (g = *h; g; g = g->next)
+ func(g->key, g->value, arg);
+}
+
+#ifdef __BLOCKS__
+/**
+ * Do something for each element
+ *
+ * @value dict the dict to interate over
+ * @value func the function to search for
+ */
+
+void
+heim_dict_iterate(heim_dict_t dict, void (^func)(heim_object_t, heim_object_t))
+{
+ struct hashentry **h, *g;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
+ for (g = *h; g; g = g->next)
+ func(g->key, g->value);
+}
+#endif
diff --git a/third_party/heimdal/lib/base/dll.c b/third_party/heimdal/lib/base/dll.c
new file mode 100644
index 0000000..59c3913
--- /dev/null
+++ b/third_party/heimdal/lib/base/dll.c
@@ -0,0 +1,325 @@
+/***********************************************************************
+ * Copyright (c) 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+/*
+ * This is an implementation of thread-specific storage with
+ * destructors. WIN32 doesn't quite have this. Instead it has
+ * DllMain(), an entry point in every DLL that gets called to notify the
+ * DLL of thread/process "attach"/"detach" events.
+ *
+ * We use __thread (or __declspec(thread)) for the thread-local itself
+ * and DllMain() DLL_THREAD_DETACH events to drive destruction of
+ * thread-local values.
+ *
+ * When building in maintainer mode on non-Windows pthread systems this
+ * uses a single pthread key instead to implement multiple keys. This
+ * keeps the code from rotting when modified by non-Windows developers.
+ */
+
+#include "baselocl.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef HEIM_WIN32_TLS
+#include <assert.h>
+#include <err.h>
+#include <heim_threads.h>
+
+#ifndef WIN32
+#include <pthread.h>
+#endif
+
+/* Logical array of keys that grows lock-lessly */
+typedef struct tls_keys tls_keys;
+struct tls_keys {
+ void (**keys_dtors)(void *); /* array of destructors */
+ size_t keys_start_idx; /* index of first destructor */
+ size_t keys_num;
+ tls_keys *keys_next;
+};
+
+/*
+ * Well, not quite locklessly. We need synchronization primitives to do
+ * this locklessly. An atomic CAS will do.
+ */
+static HEIMDAL_MUTEX tls_key_defs_lock = HEIMDAL_MUTEX_INITIALIZER;
+static tls_keys *tls_key_defs;
+
+/* Logical array of values (per-thread; no locking needed here) */
+struct tls_values {
+ void **values; /* realloc()ed */
+ size_t values_num;
+};
+
+static HEIMDAL_THREAD_LOCAL struct tls_values values;
+
+static char dead_key;
+#define DEAD_KEY ((void *)&dead_key)
+
+void
+heim_w32_service_thread_detach(void *unused)
+{
+ tls_keys *key_defs;
+ void (*dtor)(void*);
+ size_t i;
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = tls_key_defs;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ if (key_defs == NULL)
+ return;
+
+ for (i = 0; i < values.values_num; i++) {
+ assert(i >= key_defs->keys_start_idx);
+ if (i >= key_defs->keys_start_idx + key_defs->keys_num) {
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = key_defs->keys_next;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ assert(key_defs != NULL);
+ assert(i >= key_defs->keys_start_idx);
+ assert(i < key_defs->keys_start_idx + key_defs->keys_num);
+ }
+ dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx];
+ if (values.values[i] != NULL && dtor != NULL && dtor != DEAD_KEY)
+ dtor(values.values[i]);
+ values.values[i] = NULL;
+ }
+}
+
+#if !defined(WIN32)
+static pthread_key_t pt_key;
+pthread_once_t pt_once = PTHREAD_ONCE_INIT;
+
+static void
+atexit_del_tls_for_thread(void)
+{
+ heim_w32_service_thread_detach(NULL);
+}
+
+static void
+create_pt_key(void)
+{
+ int ret;
+
+ /* The main thread may not execute TLS destructors */
+ atexit(atexit_del_tls_for_thread);
+ ret = pthread_key_create(&pt_key, heim_w32_service_thread_detach);
+ if (ret != 0)
+ err(1, "pthread_key_create() failed");
+}
+
+#endif
+
+int
+heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
+{
+ tls_keys *key_defs, *new_key_defs;
+ size_t i, k;
+ int ret = ENOMEM;
+
+#if !defined(WIN32)
+ (void) pthread_once(&pt_once, create_pt_key);
+ (void) pthread_setspecific(pt_key, DEAD_KEY);
+#endif
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ if (tls_key_defs == NULL) {
+ /* First key */
+ new_key_defs = calloc(1, sizeof(*new_key_defs));
+ if (new_key_defs == NULL) {
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return ENOMEM;
+ }
+ new_key_defs->keys_num = 8;
+ new_key_defs->keys_dtors = calloc(new_key_defs->keys_num,
+ sizeof(*new_key_defs->keys_dtors));
+ if (new_key_defs->keys_dtors == NULL) {
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ free(new_key_defs);
+ return ENOMEM;
+ }
+ tls_key_defs = new_key_defs;
+ new_key_defs->keys_dtors[0] = dtor;
+ for (i = 1; i < new_key_defs->keys_num; i++)
+ new_key_defs->keys_dtors[i] = NULL;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return 0;
+ }
+
+ for (key_defs = tls_key_defs;
+ key_defs != NULL;
+ key_defs = key_defs->keys_next) {
+ k = key_defs->keys_start_idx;
+ for (i = 0; i < key_defs->keys_num; i++, k++) {
+ if (key_defs->keys_dtors[i] == NULL) {
+ /* Found free slot; use it */
+ key_defs->keys_dtors[i] = dtor;
+ *key = k;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return 0;
+ }
+ }
+ if (key_defs->keys_next != NULL)
+ continue;
+
+ /* Grow the registration array */
+ /* XXX DRY */
+ new_key_defs = calloc(1, sizeof(*new_key_defs));
+ if (new_key_defs == NULL)
+ break;
+
+ new_key_defs->keys_dtors =
+ calloc(key_defs->keys_num + key_defs->keys_num / 2,
+ sizeof(*new_key_defs->keys_dtors));
+ if (new_key_defs->keys_dtors == NULL) {
+ free(new_key_defs);
+ break;
+ }
+ new_key_defs->keys_start_idx = key_defs->keys_start_idx +
+ key_defs->keys_num;
+ new_key_defs->keys_num = key_defs->keys_num + key_defs->keys_num / 2;
+ new_key_defs->keys_dtors[i] = dtor;
+ for (i = 1; i < new_key_defs->keys_num; i++)
+ new_key_defs->keys_dtors[i] = NULL;
+ key_defs->keys_next = new_key_defs;
+ ret = 0;
+ break;
+ }
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return ret;
+}
+
+static void
+key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
+ size_t *dtor_idx, void (**dtor)(void *))
+{
+ tls_keys *key_defs;
+
+ if (kd != NULL)
+ *kd = NULL;
+ if (dtor_idx != NULL)
+ *dtor_idx = 0;
+ if (dtor != NULL)
+ *dtor = NULL;
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = tls_key_defs;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ while (key_defs != NULL) {
+ if (key >= key_defs->keys_start_idx &&
+ key < key_defs->keys_start_idx + key_defs->keys_num) {
+ if (kd != NULL)
+ *kd = key_defs;
+ if (dtor_idx != NULL)
+ *dtor_idx = key - key_defs->keys_start_idx;
+ if (dtor != NULL)
+ *dtor = key_defs->keys_dtors[key - key_defs->keys_start_idx];
+ return;
+ }
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = key_defs->keys_next;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ assert(key_defs != NULL);
+ assert(key >= key_defs->keys_start_idx);
+ }
+}
+
+int
+heim_w32_delete_key(HEIM_PRIV_thread_key key)
+{
+ tls_keys *key_defs;
+ size_t dtor_idx;
+
+ key_lookup(key, &key_defs, &dtor_idx, NULL);
+ if (key_defs == NULL)
+ return EINVAL;
+ key_defs->keys_dtors[dtor_idx] = DEAD_KEY;
+ return 0;
+}
+
+int
+heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
+{
+ void **new_values;
+ size_t new_num;
+ void (*dtor)(void *);
+ size_t i;
+
+#if !defined(WIN32)
+ (void) pthread_setspecific(pt_key, DEAD_KEY);
+#endif
+
+ key_lookup(key, NULL, NULL, &dtor);
+ if (dtor == NULL)
+ return EINVAL;
+
+ if (key >= values.values_num) {
+ if (values.values_num == 0) {
+ values.values = NULL;
+ new_num = 8;
+ } else {
+ new_num = (values.values_num + values.values_num / 2);
+ }
+ new_values = realloc(values.values, sizeof(void *) * new_num);
+ if (new_values == NULL)
+ return ENOMEM;
+ for (i = values.values_num; i < new_num; i++)
+ new_values[i] = NULL;
+ values.values = new_values;
+ values.values_num = new_num;
+ }
+
+ assert(key < values.values_num);
+
+ if (values.values[key] != NULL && dtor != NULL && dtor != DEAD_KEY)
+ dtor(values.values[key]);
+
+ values.values[key] = value;
+ return 0;
+}
+
+void *
+heim_w32_getspecific(HEIM_PRIV_thread_key key)
+{
+ if (key >= values.values_num)
+ return NULL;
+ return values.values[key];
+}
+
+#else
+static char dummy;
+#endif /* HEIM_WIN32_TLS */
diff --git a/third_party/heimdal/lib/base/error.c b/third_party/heimdal/lib/base/error.c
new file mode 100644
index 0000000..6ba3bea
--- /dev/null
+++ b/third_party/heimdal/lib/base/error.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct heim_error {
+ int error_code;
+ heim_string_t msg;
+ struct heim_error *next;
+};
+
+static void HEIM_CALLCONV
+error_dealloc(void *ptr)
+{
+ struct heim_error *p = ptr;
+ heim_release(p->msg);
+ heim_release(p->next);
+}
+
+static int
+error_cmp(void *a, void *b)
+{
+ struct heim_error *ap = a, *bp = b;
+ if (ap->error_code == bp->error_code)
+ return 0;
+ return heim_cmp(ap->msg, bp->msg);
+}
+
+static uintptr_t
+error_hash(void *ptr)
+{
+ struct heim_error *p = ptr;
+ return p->error_code;
+}
+
+struct heim_type_data _heim_error_object = {
+ HEIM_TID_ERROR,
+ "error-object",
+ NULL,
+ error_dealloc,
+ NULL,
+ error_cmp,
+ error_hash,
+ NULL
+};
+
+heim_error_t
+heim_error_create_enomem(void)
+{
+ /* This is an immediate object; see heim_number_create() */
+ return (heim_error_t)heim_number_create(ENOMEM);
+}
+
+
+void
+heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
+{
+ if (error) {
+ va_list ap;
+ va_start(ap, fmt);
+ *error = heim_error_createv(error_code, fmt, ap);
+ va_end(ap);
+ }
+}
+
+heim_error_t
+heim_error_create(int error_code, const char *fmt, ...)
+{
+ heim_error_t e;
+ va_list ap;
+
+ va_start(ap, fmt);
+ e = heim_error_createv(error_code, fmt, ap);
+ va_end(ap);
+
+ return e;
+}
+
+heim_error_t
+heim_error_createv(int error_code, const char *fmt, va_list ap)
+{
+ heim_error_t e;
+ char *str;
+ int len;
+ int save_errno = errno;
+
+ str = malloc(1024);
+ errno = save_errno;
+ if (str == NULL)
+ return heim_error_create_enomem();
+ len = vsnprintf(str, 1024, fmt, ap);
+ errno = save_errno;
+ if (len < 0) {
+ free(str);
+ return NULL; /* XXX We should have a special heim_error_t for this */
+ }
+
+ e = _heim_alloc_object(&_heim_error_object, sizeof(struct heim_error));
+ if (e) {
+ e->msg = heim_string_create(str);
+ e->error_code = error_code;
+ }
+ free(str);
+
+ errno = save_errno;
+ return e;
+}
+
+heim_string_t
+heim_error_copy_string(heim_error_t error)
+{
+ if (heim_get_tid(error) != HEIM_TID_ERROR) {
+ if (heim_get_tid(error) == heim_number_get_type_id())
+ return __heim_string_constant(strerror(heim_number_get_int((heim_number_t)error)));
+ heim_abort("invalid heim_error_t");
+ }
+ /* XXX concat all strings */
+ return heim_retain(error->msg);
+}
+
+int
+heim_error_get_code(heim_error_t error)
+{
+ if (error == NULL)
+ return -1;
+ if (heim_get_tid(error) != HEIM_TID_ERROR) {
+ if (heim_get_tid(error) == heim_number_get_type_id())
+ return heim_number_get_int((heim_number_t)error);
+ heim_abort("invalid heim_error_t");
+ }
+ return error->error_code;
+}
+
+heim_error_t
+heim_error_append(heim_error_t top, heim_error_t append)
+{
+ if (heim_get_tid(top) != HEIM_TID_ERROR) {
+ if (heim_get_tid(top) == heim_number_get_type_id())
+ return top;
+ heim_abort("invalid heim_error_t");
+ }
+ if (top->next)
+ heim_release(top->next);
+ top->next = heim_retain(append);
+ return top;
+}
diff --git a/third_party/heimdal/lib/base/error_string.c b/third_party/heimdal/lib/base/error_string.c
new file mode 100644
index 0000000..a562833
--- /dev/null
+++ b/third_party/heimdal/lib/base/error_string.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2001, 2003, 2005 - 2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+#undef __attribute__
+#define __attribute__(x)
+
+void
+heim_clear_error_message(heim_context context)
+{
+ if (!context)
+ return;
+ if (context->error_string)
+ free(context->error_string);
+ context->error_code = 0;
+ context->error_string = NULL;
+}
+
+void
+heim_set_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (context)
+ heim_vset_error_message(context, ret, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_vset_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, va_list args)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ int r;
+
+ if (context == NULL)
+ return;
+ if (context->error_string) {
+ free(context->error_string);
+ context->error_string = NULL;
+ }
+ context->error_code = ret;
+ r = vasprintf(&context->error_string, fmt, args);
+ if (r < 0)
+ context->error_string = NULL;
+ if (context->error_string)
+ heim_debug(context, 200, "error message: %s: %d", context->error_string, ret);
+}
+
+void
+heim_prepend_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ heim_vprepend_error_message(context, ret, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_vprepend_error_message(heim_context context, heim_error_code ret,
+ const char *fmt, va_list args)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ char *str = NULL, *str2 = NULL;
+
+ if (context == NULL || context->error_code != ret ||
+ vasprintf(&str, fmt, args) < 0 || str == NULL)
+ return;
+ if (context->error_string) {
+ int e;
+
+ e = asprintf(&str2, "%s: %s", str, context->error_string);
+ free(context->error_string);
+ if (e < 0 || str2 == NULL)
+ context->error_string = NULL;
+ else
+ context->error_string = str2;
+ free(str);
+ } else
+ context->error_string = str;
+}
+
+const char *
+heim_get_error_message(heim_context context, heim_error_code code)
+{
+ const char *cstr = NULL;
+ char *str = NULL;
+ char buf[128];
+ int free_context = 0;
+
+ if (code == 0)
+ return strdup("Success");
+
+ /*
+ * The MIT version of this function ignores the krb5_context
+ * and several widely deployed applications call krb5_get_error_message()
+ * with a NULL context in order to translate an error code as a
+ * replacement for error_message(). Another reason a NULL context
+ * might be provided is if the krb5_init_context() call itself
+ * failed.
+ */
+ if (context &&
+ context->error_string &&
+ (code == context->error_code || context->error_code == 0) &&
+ (cstr = strdup(context->error_string)))
+ return cstr;
+
+ if (context == NULL && (context = heim_context_init()))
+ free_context = 1;
+ if (context)
+ cstr = com_right_r(context->et_list, code, buf, sizeof(buf));
+ if (free_context)
+ heim_context_free(&context);
+
+ if (cstr || (cstr = error_message(code)))
+ return strdup(cstr);
+ if (asprintf(&str, "<unknown error: %d>", (int)code) == -1 || str == NULL)
+ return NULL;
+ return str;
+}
+
+const char *
+heim_get_error_string(heim_context context)
+{
+ if (context && context->error_string)
+ return strdup(context->error_string);
+ return NULL;
+}
+
+int
+heim_have_error_string(heim_context context)
+{
+ return context && context->error_string != NULL;
+}
+
+void
+heim_free_error_message(heim_context context, const char *msg)
+{
+ free(rk_UNCONST(msg));
+}
diff --git a/third_party/heimdal/lib/base/expand_path.c b/third_party/heimdal/lib/base/expand_path.c
new file mode 100644
index 0000000..cf24991
--- /dev/null
+++ b/third_party/heimdal/lib/base/expand_path.c
@@ -0,0 +1,725 @@
+
+/***********************************************************************
+ * Copyright (c) 2009-2020, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include "baselocl.h"
+
+#include <stdarg.h>
+
+typedef int PTYPE;
+
+#ifdef _WIN32
+#include <shlobj.h>
+#include <sddl.h>
+
+/*
+ * Expand a %{TEMP} token
+ *
+ * The %{TEMP} token expands to the temporary path for the current
+ * user as returned by GetTempPath().
+ *
+ * @note: Since the GetTempPath() function relies on the TMP or TEMP
+ * environment variables, this function will failover to the system
+ * temporary directory until the user profile is loaded. In addition,
+ * the returned path may or may not exist.
+ */
+static heim_error_code
+expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR tpath[MAX_PATH];
+ size_t len;
+
+ if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
+ heim_set_error_message(context, EINVAL,
+ "Failed to get temporary path (GLE=%d)",
+ GetLastError());
+ return EINVAL;
+ }
+
+ len = strlen(tpath);
+
+ if (len > 0 && tpath[len - 1] == '\\')
+ tpath[len - 1] = '\0';
+
+ *ret = strdup(tpath);
+
+ if (*ret == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+
+/*
+ * Expand a %{BINDIR} token
+ *
+ * This is also used to expand a few other tokens on Windows, since
+ * most of the executable binaries end up in the same directory. The
+ * "bin" directory is considered to be the directory in which the
+ * containing DLL is located.
+ */
+static heim_error_code
+expand_bin_dir(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR path[MAX_PATH];
+ TCHAR *lastSlash;
+ DWORD nc;
+
+ nc = GetModuleFileName((HINSTANCE)&__ImageBase, path,
+ sizeof(path)/sizeof(path[0]));
+ if (nc == 0 ||
+ nc == sizeof(path)/sizeof(path[0])) {
+ return EINVAL;
+ }
+
+ lastSlash = strrchr(path, '\\');
+ if (lastSlash != NULL) {
+ TCHAR *fslash = strrchr(lastSlash, '/');
+
+ if (fslash != NULL)
+ lastSlash = fslash;
+
+ *lastSlash = '\0';
+ }
+
+ if (postfix) {
+ if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
+ return EINVAL;
+ }
+
+ *ret = strdup(path);
+ if (*ret == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+/*
+ * Expand a %{USERID} token
+ *
+ * The %{USERID} token expands to the string representation of the
+ * user's SID. The user account that will be used is the account
+ * corresponding to the current thread's security token. This means
+ * that:
+ *
+ * - If the current thread token has the anonymous impersonation
+ * level, the call will fail.
+ *
+ * - If the current thread is impersonating a token at
+ * SecurityIdentification level the call will fail.
+ *
+ */
+static heim_error_code
+expand_userid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ int rv = EINVAL;
+ HANDLE hThread = NULL;
+ HANDLE hToken = NULL;
+ PTOKEN_OWNER pOwner = NULL;
+ DWORD len = 0;
+ LPTSTR strSid = NULL;
+
+ hThread = GetCurrentThread();
+
+ if (!OpenThreadToken(hThread, TOKEN_QUERY,
+ FALSE, /* Open the thread token as the
+ current thread user. */
+ &hToken)) {
+
+ DWORD le = GetLastError();
+
+ if (le == ERROR_NO_TOKEN) {
+ HANDLE hProcess = GetCurrentProcess();
+
+ le = 0;
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
+ le = GetLastError();
+ }
+
+ if (le != 0) {
+ heim_set_error_message(context, rv,
+ "Can't open thread token (GLE=%d)", le);
+ goto _exit;
+ }
+ }
+
+ if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ heim_set_error_message(context, rv,
+ "Unexpected error reading token information (GLE=%d)",
+ GetLastError());
+ goto _exit;
+ }
+
+ if (len == 0) {
+ heim_set_error_message(context, rv,
+ "GetTokenInformation() returned truncated buffer");
+ goto _exit;
+ }
+
+ pOwner = malloc(len);
+ if (pOwner == NULL) {
+ heim_set_error_message(context, rv, "Out of memory");
+ goto _exit;
+ }
+ } else {
+ heim_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
+ goto _exit;
+ }
+
+ if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
+ heim_set_error_message(context, rv,
+ "GetTokenInformation() failed. GLE=%d",
+ GetLastError());
+ goto _exit;
+ }
+
+ if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
+ heim_set_error_message(context, rv,
+ "Can't convert SID to string. GLE=%d",
+ GetLastError());
+ goto _exit;
+ }
+
+ *ret = strdup(strSid);
+ if (*ret == NULL)
+ heim_set_error_message(context, rv, "Out of memory");
+
+ rv = 0;
+
+ _exit:
+ if (hToken != NULL)
+ CloseHandle(hToken);
+
+ if (pOwner != NULL)
+ free (pOwner);
+
+ if (strSid != NULL)
+ LocalFree(strSid);
+
+ return rv;
+}
+
+/*
+ * Expand a folder identified by a CSIDL
+ */
+
+static heim_error_code
+expand_csidl(heim_context context, PTYPE folder, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR path[MAX_PATH];
+ size_t len;
+
+ if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
+ heim_set_error_message(context, EINVAL, "Unable to determine folder path");
+ return EINVAL;
+ }
+
+ len = strlen(path);
+
+ if (len > 0 && path[len - 1] == '\\')
+ path[len - 1] = '\0';
+
+ if (postfix &&
+ strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
+ return heim_enomem(context);
+
+ *ret = strdup(path);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+#else
+
+static heim_error_code
+expand_path(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ *ret = strdup(postfix);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ const char *p = NULL;
+
+ p = secure_getenv("TEMP");
+
+ if (p)
+ *ret = strdup(p);
+ else
+ *ret = strdup("/tmp");
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_userid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ int ret = asprintf(str, "%ld", (unsigned long)getuid());
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_euid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ int ret = asprintf(str, "%ld", (unsigned long)geteuid());
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+#endif /* _WIN32 */
+
+static heim_error_code
+expand_home(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char homedir[MAX_PATH];
+ int ret;
+
+ if (roken_get_homedir(homedir, sizeof(homedir)))
+ ret = asprintf(str, "%s", homedir);
+ else
+ ret = asprintf(str, "/unknown");
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_username(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char user[128];
+ const char *username = roken_get_username(user, sizeof(user));
+
+ if (username == NULL) {
+ heim_set_error_message(context, ENOTTY,
+ N_("unable to figure out current principal",
+ ""));
+ return ENOTTY; /* XXX */
+ }
+
+ *str = strdup(username);
+ if (*str == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+static heim_error_code
+expand_loginname(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char user[128];
+ const char *username = roken_get_loginname(user, sizeof(user));
+
+ if (username == NULL) {
+ heim_set_error_message(context, ENOTTY,
+ N_("unable to figure out current principal",
+ ""));
+ return ENOTTY; /* XXX */
+ }
+
+ *str = strdup(username);
+ if (*str == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+static heim_error_code
+expand_strftime(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ size_t len;
+ time_t t;
+ char buf[1024];
+
+ t = time(NULL);
+ len = strftime(buf, sizeof(buf), arg, localtime(&t));
+ if (len == 0 || len >= sizeof(buf))
+ return heim_enomem(context);
+ *ret = strdup(buf);
+ return 0;
+}
+
+/**
+ * Expand an extra token
+ */
+
+static heim_error_code
+expand_extra_token(heim_context context, const char *value, char **ret)
+{
+ *ret = strdup(value);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+/**
+ * Expand a %{null} token
+ *
+ * The expansion of a %{null} token is always the empty string.
+ */
+
+static heim_error_code
+expand_null(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ *ret = strdup("");
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+
+static const struct {
+ const char * tok;
+ int ftype;
+#define FTYPE_CSIDL 0
+#define FTYPE_SPECIAL 1
+
+ PTYPE param;
+ const char * postfix;
+
+ int (*exp_func)(heim_context, PTYPE, const char *, const char *, char **);
+
+#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
+#define SPECIAL(f) SPECIALP(f, NULL)
+
+} tokens[] = {
+#ifdef _WIN32
+#define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl
+#define CSIDL(C) CSIDLP(C, NULL)
+
+ {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
+ {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
+ {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
+ {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
+ {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
+ {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
+ {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
+ {"LIBDIR", SPECIAL(expand_bin_dir)},
+ {"BINDIR", SPECIAL(expand_bin_dir)},
+ {"LIBEXEC", SPECIAL(expand_bin_dir)},
+ {"SBINDIR", SPECIAL(expand_bin_dir)},
+#else
+ {"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, expand_path},
+ {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path},
+ {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path},
+ {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, expand_path},
+ {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path},
+ {"USERCONFIG", SPECIAL(expand_home)}, /* same as %{HOME} on not-Windows */
+ {"euid", SPECIAL(expand_euid)},
+ {"ruid", SPECIAL(expand_userid)},
+ {"loginname", SPECIAL(expand_loginname)},
+#endif
+ {"username", SPECIAL(expand_username)},
+ {"TEMP", SPECIAL(expand_temp_folder)},
+ {"USERID", SPECIAL(expand_userid)},
+ {"uid", SPECIAL(expand_userid)},
+ {"null", SPECIAL(expand_null)},
+ {"strftime", SPECIAL(expand_strftime)},
+ {"HOME", SPECIAL(expand_home)},
+};
+
+static heim_error_code
+expand_token(heim_context context,
+ const char *token,
+ const char *token_end,
+ char **extra_tokens,
+ char **ret)
+{
+ heim_error_code errcode;
+ size_t i;
+ char **p;
+ const char *colon;
+
+ *ret = NULL;
+
+ if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
+ token_end - token <= 2) {
+ heim_set_error_message(context, EINVAL,"Invalid token.");
+ return EINVAL;
+ }
+
+ for (p = extra_tokens; p && p[0]; p += 2) {
+ if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
+ return expand_extra_token(context, p[1], ret);
+ }
+
+ for (colon=token+2; colon < token_end; colon++)
+ if (*colon == ':')
+ break;
+
+ for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
+ if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) {
+ char *arg = NULL;
+
+ errcode = 0;
+ if (*colon == ':') {
+ int asprintf_ret = asprintf(&arg, "%.*s",
+ (int)(token_end - colon - 1),
+ colon + 1);
+ if (asprintf_ret < 0 || !arg)
+ errcode = ENOMEM;
+ }
+ if (!errcode)
+ errcode = tokens[i].exp_func(context, tokens[i].param,
+ tokens[i].postfix, arg, ret);
+ free(arg);
+ return errcode;
+ }
+
+ heim_set_error_message(context, EINVAL, "Invalid token.");
+ return EINVAL;
+}
+
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Params:
+ *
+ * @context A heim_context
+ * @path_in The path to expand tokens from
+ * @filepath True if this is a filesystem path (converts slashes to
+ * backslashes on Windows)
+ * @ppath_out The expanded path
+ * @... Variable number of pairs of strings, the first of each
+ * being a token (e.g., "luser") and the second a string to
+ * replace it with. The list is terminated by a NULL.
+ */
+heim_error_code
+heim_expand_path_tokens(heim_context context,
+ const char *path_in,
+ int filepath,
+ char **ppath_out,
+ ...)
+{
+ heim_error_code ret;
+ va_list ap;
+
+ va_start(ap, ppath_out);
+ ret = heim_expand_path_tokensv(context, path_in, filepath, ppath_out, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static void
+free_extra_tokens(char **extra_tokens)
+{
+ char **p;
+
+ for (p = extra_tokens; p && *p; p++)
+ free(*p);
+ free(extra_tokens);
+}
+
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Inputs:
+ *
+ * @context A heim_context
+ * @path_in The path to expand tokens from
+ * @filepath True if this is a filesystem path (converts slashes to
+ * backslashes on Windows)
+ * @ppath_out The expanded path
+ * @ap A NULL-terminated va_list of pairs of strings, the first of each
+ * being a token (e.g., "luser") and the second a string to replace
+ * it with.
+ *
+ * Outputs:
+ *
+ * @ppath_out Path with expanded tokens (caller must free() this)
+ */
+heim_error_code
+heim_expand_path_tokensv(heim_context context,
+ const char *path_in,
+ int filepath,
+ char **ppath_out, va_list ap)
+{
+ char *tok_begin, *tok_end, *append;
+ char **extra_tokens = NULL;
+ const char *path_left;
+ size_t nargs = 0;
+ size_t len = 0;
+ va_list ap2;
+
+ if (path_in == NULL || *path_in == '\0') {
+ *ppath_out = strdup("");
+ return 0;
+ }
+
+ *ppath_out = NULL;
+
+#if defined(_MSC_VER)
+ ap2 = ap; /* Come on! See SO #558223 */
+#else
+ va_copy(ap2, ap);
+#endif
+ while (va_arg(ap2, const char *)) {
+ nargs++;
+ va_arg(ap2, const char *);
+ }
+ va_end(ap2);
+ nargs *= 2;
+
+ /* Get extra tokens */
+ if (nargs) {
+ size_t i;
+
+ extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
+ if (extra_tokens == NULL)
+ return heim_enomem(context);
+ for (i = 0; i < nargs; i++) {
+ const char *s = va_arg(ap, const char *); /* token key */
+ if (s == NULL)
+ break;
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i++] == NULL) {
+ free_extra_tokens(extra_tokens);
+ return heim_enomem(context);
+ }
+ s = va_arg(ap, const char *); /* token value */
+ if (s == NULL)
+ s = "";
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i] == NULL) {
+ free_extra_tokens(extra_tokens);
+ return heim_enomem(context);
+ }
+ }
+ }
+
+ for (path_left = path_in; path_left && *path_left; ) {
+
+ tok_begin = strstr(path_left, "%{");
+
+ if (tok_begin && tok_begin != path_left) {
+
+ append = malloc((tok_begin - path_left) + 1);
+ if (append) {
+ memcpy(append, path_left, tok_begin - path_left);
+ append[tok_begin - path_left] = '\0';
+ }
+ path_left = tok_begin;
+
+ } else if (tok_begin) {
+
+ tok_end = strchr(tok_begin, '}');
+ if (tok_end == NULL) {
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ heim_set_error_message(context, EINVAL, "variable missing }");
+ return EINVAL;
+ }
+
+ if (expand_token(context, tok_begin, tok_end, extra_tokens,
+ &append)) {
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return EINVAL;
+ }
+
+ path_left = tok_end + 1;
+ } else {
+
+ append = strdup(path_left);
+ path_left = NULL;
+
+ }
+
+ if (append == NULL) {
+
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return heim_enomem(context);
+
+ }
+
+ {
+ size_t append_len = strlen(append);
+ char * new_str = realloc(*ppath_out, len + append_len + 1);
+
+ if (new_str == NULL) {
+ free_extra_tokens(extra_tokens);
+ free(append);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return heim_enomem(context);
+ }
+
+ *ppath_out = new_str;
+ memcpy(*ppath_out + len, append, append_len + 1);
+ len = len + append_len;
+ free(append);
+ }
+ }
+
+#ifdef _WIN32
+ /* Also deal with slashes */
+ if (filepath && *ppath_out) {
+ char * c;
+
+ for (c = *ppath_out; *c; c++)
+ if (*c == '/')
+ *c = '\\';
+ }
+#endif
+
+ free_extra_tokens(extra_tokens);
+ return 0;
+}
diff --git a/third_party/heimdal/lib/base/heim_err.et b/third_party/heimdal/lib/base/heim_err.et
new file mode 100644
index 0000000..5532d9b
--- /dev/null
+++ b/third_party/heimdal/lib/base/heim_err.et
@@ -0,0 +1,57 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table heim
+
+prefix HEIM_ERR
+
+error_code LOG_PARSE, "Error parsing log destination"
+error_code V4_PRINC_NO_CONV, "Failed to convert v4 principal"
+error_code SALTTYPE_NOSUPP, "Salt type is not supported by enctype"
+error_code NOHOST, "Host not found"
+error_code OPNOTSUPP, "Operation not supported"
+error_code EOF, "End of file"
+error_code BAD_MKEY, "Failed to get the master key"
+error_code SERVICE_NOMATCH, "Unacceptable service used"
+error_code NOT_SEEKABLE, "File descriptor not seekable"
+error_code TOO_BIG, "Offset too large"
+error_code BAD_HDBENT_ENCODING, "Invalid HDB entry encoding"
+error_code RANDOM_OFFLINE, "No random source available"
+error_code CONFIG_BADFORMAT, "Improper format of configuration file"
+error_code PA_CONTINUE_NEEDED, "Need to continue preauth stepping"
+error_code PA_CANT_CONTINUE, "Can't continue with this preauth"
+error_code NO_MORE_PA_MECHS, "No more PA mechanisms available"
+
+index 64
+prefix HEIM_PKINIT
+error_code NO_CERTIFICATE, "Certificate missing"
+error_code NO_PRIVATE_KEY, "Private key missing"
+error_code NO_VALID_CA, "No valid certificate authority"
+error_code CERTIFICATE_INVALID, "Certificate invalid"
+error_code PRIVATE_KEY_INVALID, "Private key invalid"
+
+index 128
+prefix HEIM_EAI
+#error_code NOERROR, "no error"
+error_code UNKNOWN, "unknown error from getaddrinfo"
+error_code ADDRFAMILY, "address family for nodename not supported"
+error_code AGAIN, "temporary failure in name resolution"
+error_code BADFLAGS, "invalid value for ai_flags"
+error_code FAIL, "non-recoverable failure in name resolution"
+error_code FAMILY, "ai_family not supported"
+error_code MEMORY, "memory allocation failure"
+error_code NODATA, "no address associated with nodename"
+error_code NONAME, "nodename nor servname provided, or not known"
+error_code SERVICE, "servname not supported for ai_socktype"
+error_code SOCKTYPE, "ai_socktype not supported"
+error_code SYSTEM, "system error returned in errno"
+
+index 192
+prefix HEIM_NET
+error_code CONN_REFUSED, "connection refused"
+
+end
diff --git a/third_party/heimdal/lib/base/heimbase-atomics.h b/third_party/heimdal/lib/base/heimbase-atomics.h
new file mode 100644
index 0000000..58b72ac
--- /dev/null
+++ b/third_party/heimdal/lib/base/heimbase-atomics.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HEIM_BASE_ATOMICS_H
+#define HEIM_BASE_ATOMICS_H 1
+
+#include "config.h"
+
+/*
+ * Atomic operations
+ */
+
+#if defined(HAVE_STDATOMIC_H)
+
+#include <stdatomic.h>
+
+#define heim_base_atomic_init(t, v) atomic_init(t, v)
+#define heim_base_atomic_load(x) atomic_load((x))
+#define heim_base_atomic_store(t, v) atomic_store((t), (v))
+
+#define heim_base_atomic(T) _Atomic(T)
+
+#define heim_base_atomic_inc(x) (atomic_fetch_add((x), 1) + 1)
+#define heim_base_atomic_dec(x) (atomic_fetch_sub((x), 1) - 1)
+#define heim_base_atomic_integer_type heim_base_atomic(unsigned int)
+#define heim_base_atomic_integer_max UINT_MAX
+
+#define heim_base_exchange_pointer(t,v) atomic_exchange((t), (v))
+#define heim_base_exchange_32(t,v) atomic_exchange((t), (v))
+#define heim_base_exchange_64(t,v) atomic_exchange((t), (v))
+
+#elif defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH)
+
+#define heim_base_atomic_barrier() __sync_synchronize()
+
+#define heim_base_atomic_inc(x) __sync_add_and_fetch((x), 1)
+#define heim_base_atomic_dec(x) __sync_sub_and_fetch((x), 1)
+#define heim_base_atomic_integer_type unsigned int
+#define heim_base_atomic_integer_max UINT_MAX
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if __has_builtin(__sync_swap)
+#define heim_base_exchange_pointer(t,v) __sync_swap((t), (v))
+#else
+/* FIXME: some targets may only write the value 1 into *t */
+#define heim_base_exchange_pointer(t,v) __sync_lock_test_and_set((t), (v))
+#endif
+
+#define heim_base_exchange_32(t,v) heim_base_exchange_pointer((t), (v))
+#define heim_base_exchange_64(t,v) heim_base_exchange_pointer((t), (v))
+
+#elif defined(__sun)
+
+#include <sys/atomic.h>
+
+#define heim_base_atomic_barrier() __machine_rw_barrier()
+
+#define heim_base_atomic_inc(x) atomic_inc_uint_nv((volatile uint_t *)(x))
+#define heim_base_atomic_dec(x) atomic_dec_uint_nv((volatile uint_t *)(x))
+#define heim_base_atomic_integer_type uint_t
+#define heim_base_atomic_integer_max UINT_MAX
+
+#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((volatile void *)(t), (void *)(v))
+#define heim_base_exchange_32(t,v) atomic_swap_32((volatile uint32_t *)(t), (v))
+#define heim_base_exchange_64(t,v) atomic_swap_64((volatile uint64_t *)(t), (v))
+
+#elif defined(_AIX)
+
+#include <sys/atomic_op.h>
+
+#define heim_base_atomic_barrier() __isync()
+
+#define heim_base_atomic_inc(x) (fetch_and_add((atomic_p)(x)) + 1)
+#define heim_base_atomic_dec(x) (fetch_and_add((atomic_p)(x)) - 1)
+#define heim_base_atomic_integer_type unsigned int
+#define heim_base_atomic_integer_max UINT_MAX
+
+static inline void *
+heim_base_exchange_pointer(void *p, void *newval)
+{
+ void *val = *(void **)p;
+
+ while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval))
+ ;
+
+ return val;
+}
+
+static inline uint32_t
+heim_base_exchange_32(uint32_t *p, uint32_t newval)
+{
+ uint32_t val = *p;
+
+ while (!compare_and_swap((atomic_p)p, (int *)&val, (int)newval))
+ ;
+
+ return val;
+}
+
+static inline uint64_t
+heim_base_exchange_64(uint64_t *p, uint64_t newval)
+{
+ uint64_t val = *p;
+
+ while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval))
+ ;
+
+ return val;
+}
+
+#elif defined(_WIN32)
+
+#define heim_base_atomic_barrier() MemoryBarrier()
+
+#define heim_base_atomic_inc(x) InterlockedIncrement(x)
+#define heim_base_atomic_dec(x) InterlockedDecrement(x)
+#define heim_base_atomic_integer_type LONG
+#define heim_base_atomic_integer_max MAXLONG
+
+#define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((PVOID volatile *)(t), (PVOID)(v))
+#define heim_base_exchange_32(t,v) ((ULONG)InterlockedExchange((LONG volatile *)(t), (LONG)(v)))
+#define heim_base_exchange_64(t,v) ((ULONG64)InterlockedExchange64((LONG64 violatile *)(t), (LONG64)(v)))
+
+#else
+
+#include <heim_threads.h>
+
+#define HEIM_BASE_NEED_ATOMIC_MUTEX 1
+extern HEIMDAL_MUTEX _heim_base_mutex;
+
+#define heim_base_atomic_integer_type unsigned int
+#define heim_base_atomic_integer_max UINT_MAX
+
+static inline heim_base_atomic_integer_type
+heim_base_atomic_inc(heim_base_atomic_integer_type *x)
+{
+ heim_base_atomic_integer_type t;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ t = ++(*x);
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return t;
+}
+
+static inline heim_base_atomic_integer_type
+heim_base_atomic_dec(heim_base_atomic_integer_type *x)
+{
+ heim_base_atomic_integer_type t;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ t = --(*x);
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return t;
+}
+
+static inline void *
+heim_base_exchange_pointer(void *target, void *value)
+{
+ void *old;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ old = *(void **)target;
+ *(void **)target = value;
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return old;
+}
+
+static inline uint32_t
+heim_base_exchange_32(uint32_t *p, uint32_t newval)
+{
+ uint32_t old;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ old = *p;
+ *p = newval;
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return old;
+}
+
+static inline uint64_t
+heim_base_exchange_64(uint64_t *p, uint64_t newval)
+{
+ uint64_t old;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ old = *p;
+ *p = newval;
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return old;
+}
+
+#endif /* defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) */
+
+#ifndef heim_base_atomic
+#define heim_base_atomic(T) T
+#endif
+
+#ifndef heim_base_atomic_barrier
+#define heim_base_atomic_barrier()
+#endif
+
+#ifndef heim_base_atomic_load
+#define heim_base_atomic_load(x) (heim_base_atomic_barrier(), *(x))
+#endif
+
+#ifndef heim_base_atomic_init
+#define heim_base_atomic_init(t, v) do { (*(t) = (v)); } while (0)
+#endif
+
+#ifndef heim_base_atomic_store
+#define heim_base_atomic_store(t, v) do { \
+ (*(t) = (v)); \
+ heim_base_atomic_barrier(); \
+ } while (0)
+#endif
+
+#if SIZEOF_TIME_T == 8
+#define heim_base_exchange_time_t(t,v) heim_base_exchange_64((t), (v))
+#elif SIZEOF_TIME_T == 4
+#define heim_base_exchange_time_t(t,v) heim_base_exchange_32((t), (v))
+#else
+#error set SIZEOF_TIME_T for your platform
+#endif
+
+#endif /* HEIM_BASE_ATOMICS_H */
diff --git a/third_party/heimdal/lib/base/heimbase-svc.h b/third_party/heimdal/lib/base/heimbase-svc.h
new file mode 100644
index 0000000..0e7454c
--- /dev/null
+++ b/third_party/heimdal/lib/base/heimbase-svc.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HEIMBASE_SVC_H
+#define HEIMBASE_SVC_H 1
+
+#include <heimbase.h>
+
+/*
+ * This file is meant to be included in services, which can
+ *
+ * #define heim_pcontext krb5_context
+ *
+ * or whatever is appropriate.
+ */
+
+#define HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS \
+ /* Input */ \
+ heim_pcontext context; \
+ heim_pconfig config; \
+ heim_context hcontext; \
+ heim_log_facility *logf; \
+ const char *from; \
+ struct sockaddr *addr; \
+ int datagram_reply; \
+ heim_octet_string request; \
+ \
+ /* Output */ \
+ heim_octet_string *reply; \
+ unsigned int use_request_t:1; \
+ \
+ /* Common state, to be freed in process.c */ \
+ struct timeval tv_start; \
+ struct timeval tv_end; \
+ const char *reqtype; \
+ char *cname; \
+ char *sname; \
+ const char *e_text; \
+ char *e_text_buf; \
+ heim_string_t reason; \
+ /* auditing key/value store */ \
+ heim_dict_t kv; \
+ heim_dict_t attributes; \
+ int32_t error_code
+
+#define HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(CONTEXT_TYPE) \
+ int minor_version; \
+ int (HEIM_LIB_CALL *init)(CONTEXT_TYPE, void **); \
+ void (HEIM_LIB_CALL *fini)(void *)
+
+#endif /* HEIMBASE_SVC_H */
diff --git a/third_party/heimdal/lib/base/heimbase.c b/third_party/heimdal/lib/base/heimbase.c
new file mode 100644
index 0000000..1e6805a
--- /dev/null
+++ b/third_party/heimdal/lib/base/heimbase.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <syslog.h>
+
+static heim_base_atomic_integer_type tidglobal = HEIM_TID_USER;
+
+struct heim_base {
+ heim_type_t isa;
+ heim_base_atomic_integer_type ref_cnt;
+ HEIM_TAILQ_ENTRY(heim_base) autorel;
+ heim_auto_release_t autorelpool;
+ uintptr_t isaextra[3];
+};
+
+/* specialized version of base */
+struct heim_base_mem {
+ heim_type_t isa;
+ heim_base_atomic_integer_type ref_cnt;
+ HEIM_TAILQ_ENTRY(heim_base) autorel;
+ heim_auto_release_t autorelpool;
+ const char *name;
+ void (HEIM_CALLCONV *dealloc)(void *);
+ uintptr_t isaextra[1];
+};
+
+#define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1)
+#define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1))
+
+#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX
+HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
+#endif
+
+/*
+ * Auto release structure
+ */
+
+struct heim_auto_release {
+ HEIM_TAILQ_HEAD(, heim_base) pool;
+ HEIMDAL_MUTEX pool_mutex;
+ struct heim_auto_release *parent;
+};
+
+
+/**
+ * Retain object (i.e., take a reference)
+ *
+ * @param object to be released, NULL is ok
+ *
+ * @return the same object as passed in
+ */
+
+heim_object_t
+heim_retain(heim_object_t ptr)
+{
+ struct heim_base *p;
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return ptr;
+
+ p = PTR2BASE(ptr);
+
+ if (heim_base_atomic_load(&p->ref_cnt) == heim_base_atomic_integer_max)
+ return ptr;
+
+ if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
+ heim_abort("resurection");
+ return ptr;
+}
+
+/**
+ * Release object, free if reference count reaches zero
+ *
+ * @param object to be released
+ */
+
+void
+heim_release(void *ptr)
+{
+ heim_base_atomic_integer_type old;
+ struct heim_base *p;
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return;
+
+ p = PTR2BASE(ptr);
+
+ if (heim_base_atomic_load(&p->ref_cnt) == heim_base_atomic_integer_max)
+ return;
+
+ old = heim_base_atomic_dec(&p->ref_cnt) + 1;
+
+ if (old > 1)
+ return;
+
+ if (old == 1) {
+ heim_auto_release_t ar = p->autorelpool;
+ /* remove from autorel pool list */
+ if (ar) {
+ p->autorelpool = NULL;
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+ }
+ if (p->isa->dealloc)
+ p->isa->dealloc(ptr);
+ free(p);
+ } else
+ heim_abort("over release");
+}
+
+/**
+ * If used require wrapped in autorelease pool
+ */
+
+heim_string_t
+heim_description(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ if (p->isa->desc == NULL)
+ return heim_auto_release(heim_string_ref_create(p->isa->name, NULL));
+ return heim_auto_release(p->isa->desc(ptr));
+}
+
+
+void
+_heim_make_permanent(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ heim_base_atomic_store(&p->ref_cnt, heim_base_atomic_integer_max);
+}
+
+
+static heim_type_t tagged_isa[9] = {
+ &_heim_number_object,
+ &_heim_null_object,
+ &_heim_bool_object,
+
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_type_t
+_heim_get_isa(heim_object_t ptr)
+{
+ struct heim_base *p;
+ if (heim_base_is_tagged(ptr)) {
+ if (heim_base_is_tagged_object(ptr))
+ return tagged_isa[heim_base_tagged_object_tid(ptr)];
+ heim_abort("not a supported tagged type");
+ }
+ p = PTR2BASE(ptr);
+ return p->isa;
+}
+
+/**
+ * Get type ID of object
+ *
+ * @param object object to get type id of
+ *
+ * @return type id of object
+ */
+
+heim_tid_t
+heim_get_tid(heim_object_t ptr)
+{
+ heim_type_t isa = _heim_get_isa(ptr);
+ return isa->tid;
+}
+
+/**
+ * Get hash value of object
+ *
+ * @param object object to get hash value for
+ *
+ * @return a hash value
+ */
+
+uintptr_t
+heim_get_hash(heim_object_t ptr)
+{
+ heim_type_t isa = _heim_get_isa(ptr);
+ if (isa->hash)
+ return isa->hash(ptr);
+ return (uintptr_t)ptr;
+}
+
+/**
+ * Compare two objects, returns 0 if equal, can use used for qsort()
+ * and friends.
+ *
+ * @param a first object to compare
+ * @param b first object to compare
+ *
+ * @return 0 if objects are equal
+ */
+
+int
+heim_cmp(heim_object_t a, heim_object_t b)
+{
+ heim_tid_t ta, tb;
+ heim_type_t isa;
+
+ ta = heim_get_tid(a);
+ tb = heim_get_tid(b);
+
+ if (ta != tb)
+ return ta - tb;
+
+ isa = _heim_get_isa(a);
+
+ if (isa->cmp)
+ return isa->cmp(a, b);
+
+ return (uintptr_t)a - (uintptr_t)b;
+}
+
+/*
+ * Private - allocates an memory object
+ */
+
+static void HEIM_CALLCONV
+memory_dealloc(void *ptr)
+{
+ struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr);
+ if (p->dealloc)
+ p->dealloc(ptr);
+}
+
+struct heim_type_data memory_object = {
+ HEIM_TID_MEMORY,
+ "memory-object",
+ NULL,
+ memory_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/**
+ * Allocate memory for an object of anonymous type
+ *
+ * @param size size of object to be allocated
+ * @param name name of ad-hoc type
+ * @param dealloc destructor function
+ *
+ * Objects allocated with this interface do not serialize.
+ *
+ * @return allocated object
+ */
+
+void *
+heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
+{
+ /* XXX use posix_memalign */
+
+ struct heim_base_mem *p = calloc(1, size + sizeof(*p));
+ if (p == NULL)
+ return NULL;
+ p->isa = &memory_object;
+ p->ref_cnt = 1;
+ p->name = name;
+ p->dealloc = dealloc;
+ return BASE2PTR(p);
+}
+
+heim_type_t
+_heim_create_type(const char *name,
+ heim_type_init init,
+ heim_type_dealloc dealloc,
+ heim_type_copy copy,
+ heim_type_cmp cmp,
+ heim_type_hash hash,
+ heim_type_description desc)
+{
+ heim_type_t type;
+
+ type = calloc(1, sizeof(*type));
+ if (type == NULL)
+ return NULL;
+
+ type->tid = heim_base_atomic_inc(&tidglobal);
+ type->name = name;
+ type->init = init;
+ type->dealloc = dealloc;
+ type->copy = copy;
+ type->cmp = cmp;
+ type->hash = hash;
+ type->desc = desc;
+
+ return type;
+}
+
+heim_object_t
+_heim_alloc_object(heim_type_t type, size_t size)
+{
+ /* XXX should use posix_memalign */
+ struct heim_base *p = calloc(1, size + sizeof(*p));
+ if (p == NULL)
+ return NULL;
+ p->isa = type;
+ p->ref_cnt = 1;
+
+ return BASE2PTR(p);
+}
+
+void *
+_heim_get_isaextra(heim_object_t ptr, size_t idx)
+{
+ struct heim_base *p;
+
+ heim_assert(ptr != NULL, "internal error");
+ p = (struct heim_base *)PTR2BASE(ptr);
+ if (p->isa == &memory_object)
+ return NULL;
+ heim_assert(idx < 3, "invalid private heim_base extra data index");
+ return &p->isaextra[idx];
+}
+
+heim_tid_t
+_heim_type_get_tid(heim_type_t type)
+{
+ return type->tid;
+}
+
+#if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
+static pthread_once_t once_arg_key_once = PTHREAD_ONCE_INIT;
+static pthread_key_t once_arg_key;
+
+static void
+once_arg_key_once_init(void)
+{
+ errno = pthread_key_create(&once_arg_key, NULL);
+ if (errno != 0) {
+ fprintf(stderr,
+ "Error: pthread_key_create() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+}
+
+struct once_callback {
+ void (*fn)(void *);
+ void *data;
+};
+
+static void
+once_callback_caller(void)
+{
+ struct once_callback *once_callback = pthread_getspecific(once_arg_key);
+
+ if (once_callback == NULL) {
+ fprintf(stderr, "Error: pthread_once() calls callback on "
+ "different thread?! Cannot continue.\n");
+ abort();
+ }
+ once_callback->fn(once_callback->data);
+}
+#endif
+
+/**
+ * Call func once and only once
+ *
+ * @param once pointer to a heim_base_once_t
+ * @param ctx context passed to func
+ * @param func function to be called
+ */
+
+void
+heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
+{
+#if defined(WIN32)
+ /*
+ * With a libroken wrapper for some CAS function and a libroken yield()
+ * wrapper we could make this the default implementation when we have
+ * neither Grand Central nor POSX threads.
+ *
+ * We could also adapt the double-checked lock pattern with CAS
+ * providing the necessary memory barriers in the absence of
+ * portable explicit memory barrier APIs.
+ */
+ /*
+ * We use CAS operations in large part to provide implied memory
+ * barriers.
+ *
+ * State 0 means that func() has never executed.
+ * State 1 means that func() is executing.
+ * State 2 means that func() has completed execution.
+ */
+ if (InterlockedCompareExchange(once, 1L, 0L) == 0L) {
+ /* State is now 1 */
+ (*func)(ctx);
+ (void)InterlockedExchange(once, 2L);
+ /* State is now 2 */
+ } else {
+ /*
+ * The InterlockedCompareExchange is being used to fetch
+ * the current state under a full memory barrier. As long
+ * as the current state is 1 continue to spin.
+ */
+ while (InterlockedCompareExchange(once, 2L, 0L) == 1L)
+ SwitchToThread();
+ }
+#elif defined(HAVE_DISPATCH_DISPATCH_H)
+ dispatch_once_f(once, ctx, func);
+#elif defined(ENABLE_PTHREAD_SUPPORT)
+ struct once_callback once_callback;
+
+ once_callback.fn = func;
+ once_callback.data = ctx;
+
+ errno = pthread_once(&once_arg_key_once, once_arg_key_once_init);
+ if (errno != 0) {
+ fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+ errno = pthread_setspecific(once_arg_key, &once_callback);
+ if (errno != 0) {
+ fprintf(stderr,
+ "Error: pthread_setspecific() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+ errno = pthread_once(once, once_callback_caller);
+ if (errno != 0) {
+ fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+#else
+ static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
+ HEIMDAL_MUTEX_lock(&mutex);
+ if (*once == 0) {
+ *once = 1;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ func(ctx);
+ HEIMDAL_MUTEX_lock(&mutex);
+ *once = 2;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ } else if (*once == 2) {
+ HEIMDAL_MUTEX_unlock(&mutex);
+ } else {
+ HEIMDAL_MUTEX_unlock(&mutex);
+ while (1) {
+ struct timeval tv = { 0, 1000 };
+ select(0, NULL, NULL, NULL, &tv);
+ HEIMDAL_MUTEX_lock(&mutex);
+ if (*once == 2)
+ break;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ }
+ HEIMDAL_MUTEX_unlock(&mutex);
+ }
+#endif
+}
+
+/**
+ * Abort and log the failure (using syslog)
+ */
+
+void
+heim_abort(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ heim_abortv(fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * Abort and log the failure (using syslog)
+ */
+
+void
+heim_abortv(const char *fmt, va_list ap)
+{
+ static char str[1024];
+
+ vsnprintf(str, sizeof(str), fmt, ap);
+ syslog(LOG_ERR, "heim_abort: %s", str);
+ abort();
+}
+
+/*
+ *
+ */
+
+static int ar_created = 0;
+static HEIMDAL_thread_key ar_key;
+
+struct ar_tls {
+ struct heim_auto_release *head;
+ struct heim_auto_release *current;
+ HEIMDAL_MUTEX tls_mutex;
+};
+
+static void
+ar_tls_delete(void *ptr)
+{
+ struct ar_tls *tls = ptr;
+ heim_auto_release_t next = NULL;
+
+ if (tls == NULL)
+ return;
+ for (; tls->current != NULL; tls->current = next) {
+ next = tls->current->parent;
+ heim_release(tls->current);
+ }
+ free(tls);
+}
+
+static void
+init_ar_tls(void *ptr)
+{
+ int ret;
+ HEIMDAL_key_create(&ar_key, ar_tls_delete, ret);
+ if (ret == 0)
+ ar_created = 1;
+}
+
+static struct ar_tls *
+autorel_tls(void)
+{
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
+ struct ar_tls *arp;
+ int ret;
+
+ heim_base_once_f(&once, NULL, init_ar_tls);
+ if (!ar_created)
+ return NULL;
+
+ arp = HEIMDAL_getspecific(ar_key);
+ if (arp == NULL) {
+
+ arp = calloc(1, sizeof(*arp));
+ if (arp == NULL)
+ return NULL;
+ HEIMDAL_setspecific(ar_key, arp, ret);
+ if (ret) {
+ free(arp);
+ return NULL;
+ }
+ }
+ return arp;
+
+}
+
+static void HEIM_CALLCONV
+autorel_dealloc(void *ptr)
+{
+ heim_auto_release_t ar = ptr;
+ struct ar_tls *tls;
+
+ tls = autorel_tls();
+ if (tls == NULL)
+ heim_abort("autorelease pool released on thread w/o autorelease inited");
+
+ heim_auto_release_drain(ar);
+
+ if (!HEIM_TAILQ_EMPTY(&ar->pool))
+ heim_abort("pool not empty after draining");
+
+ HEIMDAL_MUTEX_lock(&tls->tls_mutex);
+ if (tls->current != ptr)
+ heim_abort("autorelease not releaseing top pool");
+
+ tls->current = ar->parent;
+ HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
+}
+
+static int
+autorel_cmp(void *a, void *b)
+{
+ return (a == b);
+}
+
+static uintptr_t
+autorel_hash(void *ptr)
+{
+ return (uintptr_t)ptr;
+}
+
+
+static struct heim_type_data _heim_autorel_object = {
+ HEIM_TID_AUTORELEASE,
+ "autorelease-pool",
+ NULL,
+ autorel_dealloc,
+ NULL,
+ autorel_cmp,
+ autorel_hash,
+ NULL
+};
+
+/**
+ * Create thread-specific object auto-release pool
+ *
+ * Objects placed on the per-thread auto-release pool (with
+ * heim_auto_release()) can be released in one fell swoop by calling
+ * heim_auto_release_drain().
+ */
+
+heim_auto_release_t
+heim_auto_release_create(void)
+{
+ struct ar_tls *tls = autorel_tls();
+ heim_auto_release_t ar;
+
+ if (tls == NULL)
+ heim_abort("Failed to create/get autorelease head");
+
+ ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release));
+ if (ar) {
+ HEIMDAL_MUTEX_lock(&tls->tls_mutex);
+ if (tls->head == NULL)
+ tls->head = ar;
+ ar->parent = tls->current;
+ tls->current = ar;
+ HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
+ }
+
+ return ar;
+}
+
+/**
+ * Place the current object on the thread's auto-release pool
+ *
+ * @param ptr object
+ */
+
+heim_object_t
+heim_auto_release(heim_object_t ptr)
+{
+ struct heim_base *p;
+ struct ar_tls *tls = autorel_tls();
+ heim_auto_release_t ar;
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return ptr;
+
+ p = PTR2BASE(ptr);
+
+ /* drop from old pool */
+ if ((ar = p->autorelpool) != NULL) {
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
+ p->autorelpool = NULL;
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+ }
+
+ if (tls == NULL || (ar = tls->current) == NULL)
+ heim_abort("no auto relase pool in place, would leak");
+
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel);
+ p->autorelpool = ar;
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+
+ return ptr;
+}
+
+/**
+ * Release all objects on the given auto-release pool
+ */
+
+void
+heim_auto_release_drain(heim_auto_release_t autorel)
+{
+ heim_object_t obj;
+
+ /* release all elements on the tail queue */
+
+ HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
+ while(!HEIM_TAILQ_EMPTY(&autorel->pool)) {
+ obj = HEIM_TAILQ_FIRST(&autorel->pool);
+ HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
+ heim_release(BASE2PTR(obj));
+ HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
+ }
+ HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
+}
+
+/*
+ * Helper for heim_path_vget() and heim_path_delete(). On success
+ * outputs the node named by the path and the parent node and key
+ * (useful for heim_path_delete()).
+ */
+
+static heim_object_t
+heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key,
+ heim_error_t *error, va_list ap)
+{
+ heim_object_t path_element;
+ heim_object_t node, next_node;
+ heim_tid_t node_type;
+
+ *parent = NULL;
+ *key = NULL;
+ if (ptr == NULL)
+ return NULL;
+
+ for (node = ptr; node != NULL; ) {
+ path_element = va_arg(ap, heim_object_t);
+ if (path_element == NULL) {
+ *parent = node;
+ *key = path_element;
+ return node;
+ }
+
+ node_type = heim_get_tid(node);
+ switch (node_type) {
+ case HEIM_TID_ARRAY:
+ case HEIM_TID_DICT:
+ case HEIM_TID_DB:
+ break;
+ default:
+ if (node == ptr)
+ heim_abort("heim_path_get() only operates on container types");
+ return NULL;
+ }
+
+ if (node_type == HEIM_TID_DICT) {
+ next_node = heim_dict_get_value(node, path_element);
+ } else if (node_type == HEIM_TID_DB) {
+ next_node = _heim_db_get_value(node, NULL, path_element, NULL);
+ } else {
+ int idx = -1;
+
+ /* node_type == HEIM_TID_ARRAY */
+ if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
+ idx = heim_number_get_int(path_element);
+ if (idx < 0) {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path_get() path elements "
+ "for array nodes must be "
+ "numeric and positive");
+ return NULL;
+ }
+ next_node = heim_array_get_value(node, idx);
+ }
+ node = next_node;
+ }
+ return NULL;
+}
+
+/**
+ * Get a node in a heim_object tree by path
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ap NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return object (not retained) if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_vget(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t p, k;
+
+ return heim_path_vget2(ptr, &p, &k, error, ap);
+}
+
+/**
+ * Get a node in a tree by path, with retained reference
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ap NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return retained object if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_vcopy(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t p, k;
+
+ return heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
+}
+
+/**
+ * Get a node in a tree by path
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ... NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return object (not retained) if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_get(heim_object_t ptr, heim_error_t *error, ...)
+{
+ heim_object_t o;
+ heim_object_t p, k;
+ va_list ap;
+
+ if (ptr == NULL)
+ return NULL;
+
+ va_start(ap, error);
+ o = heim_path_vget2(ptr, &p, &k, error, ap);
+ va_end(ap);
+ return o;
+}
+
+/**
+ * Get a node in a tree by path, with retained reference
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ... NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return retained object if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_copy(heim_object_t ptr, heim_error_t *error, ...)
+{
+ heim_object_t o;
+ heim_object_t p, k;
+ va_list ap;
+
+ if (ptr == NULL)
+ return NULL;
+
+ va_start(ap, error);
+ o = heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
+ va_end(ap);
+ return o;
+}
+
+/**
+ * Create a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param size the size of the heim_dict_t nodes to be created
+ * @param leaf leaf node to be added, if any
+ * @param error error (output)
+ * @param ap NULL-terminated of path component objects
+ *
+ * Create a path of heim_dict_t interior nodes in a given heim_object_t
+ * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
+ * then the leaf is not deleted).
+ *
+ * @return 0 on success, else a system error
+ *
+ * @addtogroup heimbase
+ */
+
+int
+heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, va_list ap)
+{
+ heim_object_t path_element = va_arg(ap, heim_object_t);
+ heim_object_t next_path_element = NULL;
+ heim_object_t node = ptr;
+ heim_object_t next_node = NULL;
+ heim_tid_t node_type;
+ int ret = 0;
+
+ if (ptr == NULL)
+ heim_abort("heim_path_vcreate() does not create root nodes");
+
+ while (path_element != NULL) {
+ next_path_element = va_arg(ap, heim_object_t);
+ node_type = heim_get_tid(node);
+
+ if (node_type == HEIM_TID_DICT) {
+ next_node = heim_dict_get_value(node, path_element);
+ } else if (node_type == HEIM_TID_ARRAY) {
+ int idx = -1;
+
+ if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
+ idx = heim_number_get_int(path_element);
+ if (idx < 0) {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path() path elements for "
+ "array nodes must be numeric "
+ "and positive");
+ return EINVAL;
+ }
+ if (idx < heim_array_get_length(node))
+ next_node = heim_array_get_value(node, idx);
+ else
+ next_node = NULL;
+ } else if (node_type == HEIM_TID_DB && next_path_element != NULL) {
+ if (error)
+ *error = heim_error_create(EINVAL, "Interior node is a DB");
+ return EINVAL;
+ }
+
+ if (next_path_element == NULL)
+ break;
+
+ /* Create missing interior node */
+ if (next_node == NULL) {
+ next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */
+ if (next_node == NULL) {
+ ret = ENOMEM;
+ goto err;
+ }
+
+ if (node_type == HEIM_TID_DICT) {
+ ret = heim_dict_set_value(node, path_element, next_node);
+ } else if (node_type == HEIM_TID_ARRAY &&
+ heim_number_get_int(path_element) <= heim_array_get_length(node)) {
+ ret = heim_array_insert_value(node,
+ heim_number_get_int(path_element),
+ next_node);
+ } else {
+ ret = EINVAL;
+ if (error)
+ *error = heim_error_create(ret, "Node in path not a "
+ "container");
+ }
+ heim_release(next_node);
+ if (ret)
+ goto err;
+ }
+
+ path_element = next_path_element;
+ node = next_node;
+ next_node = NULL;
+ }
+
+ if (path_element == NULL)
+ goto err;
+
+ /* Add the leaf */
+ if (leaf != NULL) {
+ if (node_type == HEIM_TID_DICT)
+ ret = heim_dict_set_value(node, path_element, leaf);
+ else
+ ret = heim_array_insert_value(node,
+ heim_number_get_int(path_element),
+ leaf);
+ }
+ return ret;
+
+err:
+ if (error && !*error) {
+ if (ret == ENOMEM)
+ *error = heim_error_create_enomem();
+ else
+ *error = heim_error_create(ret, "Could not set "
+ "dict value");
+ }
+ return ret;
+}
+
+/**
+ * Create a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param size the size of the heim_dict_t nodes to be created
+ * @param leaf leaf node to be added, if any
+ * @param error error (output)
+ * @param ... NULL-terminated list of path component objects
+ *
+ * Create a path of heim_dict_t interior nodes in a given heim_object_t
+ * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
+ * then the leaf is not deleted).
+ *
+ * @return 0 on success, else a system error
+ *
+ * @addtogroup heimbase
+ */
+
+int
+heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, error);
+ ret = heim_path_vcreate(ptr, size, leaf, error, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Delete leaf node named by a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param error error (output)
+ * @param ap NULL-terminated list of path component objects
+ *
+ * @addtogroup heimbase
+ */
+
+void
+heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t parent, key, child;
+
+ child = heim_path_vget2(ptr, &parent, &key, error, ap);
+ if (child != NULL) {
+ if (heim_get_tid(parent) == HEIM_TID_DICT)
+ heim_dict_delete_key(parent, key);
+ else if (heim_get_tid(parent) == HEIM_TID_DB)
+ heim_db_delete_key(parent, NULL, key, error);
+ else if (heim_get_tid(parent) == HEIM_TID_ARRAY)
+ heim_array_delete_value(parent, heim_number_get_int(key));
+ heim_release(child);
+ }
+}
+
+/**
+ * Delete leaf node named by a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param error error (output)
+ * @param ap NULL-terminated list of path component objects
+ *
+ * @addtogroup heimbase
+ */
+
+void
+heim_path_delete(heim_object_t ptr, heim_error_t *error, ...)
+{
+ va_list ap;
+
+ va_start(ap, error);
+ heim_path_vdelete(ptr, error, ap);
+ va_end(ap);
+ return;
+}
+
diff --git a/third_party/heimdal/lib/base/heimbase.h b/third_party/heimdal/lib/base/heimbase.h
new file mode 100644
index 0000000..3706fc8
--- /dev/null
+++ b/third_party/heimdal/lib/base/heimbase.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HEIM_BASE_H
+#define HEIM_BASE_H 1
+
+#include "config.h"
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
+#include <pthread.h>
+#endif
+#include <krb5-types.h>
+#include <stdarg.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+#ifndef false
+#define false 0
+#endif
+#ifndef true
+#define true 1
+#endif
+#endif
+
+#include <stdint.h>
+
+#include <heim_err.h>
+
+#ifdef _WIN32
+#define HEIM_CALLCONV __stdcall
+#define HEIM_LIB_CALL __stdcall
+#else
+#define HEIM_CALLCONV
+#define HEIM_LIB_CALL
+#endif
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+#define HEIM_BASE_API_VERSION 20130210
+
+/*
+ * Generic facilities (moved from lib/krb5/.
+ */
+
+typedef int32_t heim_error_code;
+typedef struct heim_context_s *heim_context;
+typedef struct heim_pcontext_s *heim_pcontext;
+
+typedef void (HEIM_CALLCONV *heim_log_log_func_t)(heim_context,
+ const char *,
+ const char *,
+ void *);
+typedef void (HEIM_CALLCONV *heim_log_close_func_t)(void *);
+
+typedef struct heim_log_facility_s heim_log_facility;
+
+typedef uintptr_t
+(HEIM_LIB_CALL *heim_get_instance_func_t)(const char *);
+
+#define HEIM_PLUGIN_INVOKE_ALL 1
+
+struct heim_plugin_data {
+ const char *module;
+ const char *name;
+ int min_version;
+ const char **deps;
+ heim_get_instance_func_t get_instance;
+};
+
+/*
+ * heim_config_binding is identical to struct krb5_config_binding
+ * within krb5.h. Its format is public and used by callers of
+ * krb5_config_get_list() and krb5_config_vget_list().
+ */
+enum heim_config_type {
+ heim_config_string,
+ heim_config_list,
+};
+struct heim_config_binding {
+ enum heim_config_type type;
+ char *name;
+ struct heim_config_binding *next;
+ union {
+ char *string;
+ struct heim_config_binding *list;
+ void *generic;
+ } u;
+};
+typedef struct heim_config_binding heim_config_binding;
+typedef struct heim_config_binding heim_config_section;
+
+/*
+ * CF-like, JSON APIs
+ */
+
+typedef void * heim_object_t;
+typedef unsigned int heim_tid_t;
+typedef heim_object_t heim_bool_t;
+typedef heim_object_t heim_null_t;
+#ifdef WIN32
+typedef LONG heim_base_once_t;
+#define HEIM_BASE_ONCE_INIT 0
+#elif defined(HAVE_DISPATCH_DISPATCH_H)
+typedef long heim_base_once_t; /* XXX arch dependant */
+#define HEIM_BASE_ONCE_INIT 0
+#elif defined(ENABLE_PTHREAD_SUPPORT)
+typedef pthread_once_t heim_base_once_t;
+#define HEIM_BASE_ONCE_INIT PTHREAD_ONCE_INIT
+#else
+typedef long heim_base_once_t; /* XXX arch dependant */
+#define HEIM_BASE_ONCE_INIT 0
+#endif
+
+#if !defined(__has_extension)
+#define __has_extension(x) 0
+#endif
+
+#define HEIM_REQUIRE_GNUC(m,n,p) \
+ (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) >= \
+ (((m) * 10000) + ((n) * 100) + (p)))
+
+
+#if __has_extension(__builtin_expect) || HEIM_REQUIRE_GNUC(3,0,0)
+#define heim_builtin_expect(_op,_res) __builtin_expect(_op,_res)
+#else
+#define heim_builtin_expect(_op,_res) (_op)
+#endif
+
+
+heim_object_t heim_retain(heim_object_t);
+void heim_release(heim_object_t);
+
+void heim_show(heim_object_t);
+
+typedef void (HEIM_CALLCONV *heim_type_dealloc)(void *);
+
+void *
+heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc);
+
+heim_tid_t
+heim_get_tid(heim_object_t object);
+
+int
+heim_cmp(heim_object_t a, heim_object_t b);
+
+uintptr_t
+heim_get_hash(heim_object_t ptr);
+
+void
+heim_base_once_f(heim_base_once_t *, void *, void (*)(void *));
+
+void
+heim_abort(const char *fmt, ...)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2));
+
+void
+heim_abortv(const char *fmt, va_list ap)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0));
+
+#define heim_assert(e,t) \
+ (heim_builtin_expect(!(e), 0) ? heim_abort(t ":" #e) : (void)0)
+
+/*
+ *
+ */
+
+heim_null_t
+heim_null_create(void);
+
+heim_bool_t
+heim_bool_create(int);
+
+int
+heim_bool_val(heim_bool_t);
+
+/*
+ * Array
+ */
+
+typedef struct heim_array_data *heim_array_t;
+
+heim_array_t heim_array_create(void);
+heim_tid_t heim_array_get_type_id(void);
+
+typedef void (*heim_array_iterator_f_t)(heim_object_t, void *, int *);
+typedef int (*heim_array_filter_f_t)(heim_object_t, void *);
+
+int heim_array_append_value(heim_array_t, heim_object_t);
+int heim_array_insert_value(heim_array_t, size_t idx, heim_object_t);
+void heim_array_iterate_f(heim_array_t, void *, heim_array_iterator_f_t);
+void heim_array_iterate_reverse_f(heim_array_t, void *, heim_array_iterator_f_t);
+#ifdef __BLOCKS__
+void heim_array_iterate(heim_array_t, void (^)(heim_object_t, int *));
+void heim_array_iterate_reverse(heim_array_t, void (^)(heim_object_t, int *));
+#endif
+size_t heim_array_get_length(heim_array_t);
+heim_object_t
+ heim_array_get_value(heim_array_t, size_t);
+heim_object_t
+ heim_array_copy_value(heim_array_t, size_t);
+void heim_array_set_value(heim_array_t, size_t, heim_object_t);
+void heim_array_delete_value(heim_array_t, size_t);
+void heim_array_filter_f(heim_array_t, void *, heim_array_filter_f_t);
+#ifdef __BLOCKS__
+void heim_array_filter(heim_array_t, int (^)(heim_object_t));
+#endif
+
+/*
+ * Dict
+ */
+
+typedef struct heim_dict_data *heim_dict_t;
+
+heim_dict_t heim_dict_create(size_t size);
+heim_tid_t heim_dict_get_type_id(void);
+
+typedef void (*heim_dict_iterator_f_t)(heim_object_t, heim_object_t, void *);
+
+int heim_dict_set_value(heim_dict_t, heim_object_t, heim_object_t);
+void heim_dict_iterate_f(heim_dict_t, void *, heim_dict_iterator_f_t);
+#ifdef __BLOCKS__
+void heim_dict_iterate(heim_dict_t, void (^)(heim_object_t, heim_object_t));
+#endif
+
+heim_object_t
+ heim_dict_get_value(heim_dict_t, heim_object_t);
+heim_object_t
+ heim_dict_copy_value(heim_dict_t, heim_object_t);
+void heim_dict_delete_key(heim_dict_t, heim_object_t);
+
+/*
+ * String
+ */
+
+typedef struct heim_string_data *heim_string_t;
+typedef void (*heim_string_free_f_t)(void *);
+
+heim_string_t heim_string_create(const char *);
+heim_string_t heim_string_ref_create(const char *, heim_string_free_f_t);
+heim_string_t heim_string_create_with_bytes(const void *, size_t);
+heim_string_t heim_string_ref_create_with_bytes(const void *, size_t,
+ heim_string_free_f_t);
+heim_string_t heim_string_create_with_format(const char *, ...);
+heim_tid_t heim_string_get_type_id(void);
+const char * heim_string_get_utf8(heim_string_t);
+
+#define HSTR(_str) (__heim_string_constant("" _str ""))
+heim_string_t __heim_string_constant(const char *);
+
+/*
+ * Errors
+ */
+
+typedef struct heim_error * heim_error_t;
+
+heim_error_t heim_error_create_enomem(void);
+
+heim_error_t heim_error_create(int, const char *, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3));
+
+void heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4));
+
+heim_error_t heim_error_createv(int, const char *, va_list)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0));
+
+heim_string_t heim_error_copy_string(heim_error_t);
+int heim_error_get_code(heim_error_t);
+
+heim_error_t heim_error_append(heim_error_t, heim_error_t);
+
+/*
+ * Path
+ */
+
+heim_object_t heim_path_get(heim_object_t ptr, heim_error_t *error, ...);
+heim_object_t heim_path_copy(heim_object_t ptr, heim_error_t *error, ...);
+heim_object_t heim_path_vget(heim_object_t ptr, heim_error_t *error,
+ va_list ap);
+heim_object_t heim_path_vcopy(heim_object_t ptr, heim_error_t *error,
+ va_list ap);
+
+int heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, va_list ap);
+int heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, ...);
+
+void heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap);
+void heim_path_delete(heim_object_t ptr, heim_error_t *error, ...);
+
+/*
+ * Data (octet strings)
+ */
+
+#ifndef __HEIM_BASE_DATA__
+#define __HEIM_BASE_DATA__
+struct heim_base_data {
+ size_t length;
+ void *data;
+};
+typedef struct heim_base_data heim_octet_string;
+#endif
+
+typedef struct heim_base_data * heim_data_t;
+typedef void (*heim_data_free_f_t)(void *);
+
+heim_data_t heim_data_create(const void *, size_t);
+heim_data_t heim_data_ref_create(const void *, size_t, heim_data_free_f_t);
+heim_tid_t heim_data_get_type_id(void);
+const heim_octet_string *
+ heim_data_get_data(heim_data_t);
+const void * heim_data_get_ptr(heim_data_t);
+size_t heim_data_get_length(heim_data_t);
+
+/*
+ * DB
+ */
+
+typedef struct heim_db_data *heim_db_t;
+
+typedef void (*heim_db_iterator_f_t)(heim_data_t, heim_data_t, void *);
+
+typedef int (*heim_db_plug_open_f_t)(void *, const char *, const char *,
+ heim_dict_t, void **, heim_error_t *);
+typedef int (*heim_db_plug_clone_f_t)(void *, void **, heim_error_t *);
+typedef int (*heim_db_plug_close_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_lock_f_t)(void *, int, heim_error_t *);
+typedef int (*heim_db_plug_unlock_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_sync_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_begin_f_t)(void *, int, heim_error_t *);
+typedef int (*heim_db_plug_commit_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_rollback_f_t)(void *, heim_error_t *);
+typedef heim_data_t (*heim_db_plug_copy_value_f_t)(void *, heim_string_t,
+ heim_data_t,
+ heim_error_t *);
+typedef int (*heim_db_plug_set_value_f_t)(void *, heim_string_t, heim_data_t,
+ heim_data_t, heim_error_t *);
+typedef int (*heim_db_plug_del_key_f_t)(void *, heim_string_t, heim_data_t,
+ heim_error_t *);
+typedef void (*heim_db_plug_iter_f_t)(void *, heim_string_t, void *,
+ heim_db_iterator_f_t, heim_error_t *);
+
+struct heim_db_type {
+ int version;
+ heim_db_plug_open_f_t openf;
+ heim_db_plug_clone_f_t clonef;
+ heim_db_plug_close_f_t closef;
+ heim_db_plug_lock_f_t lockf;
+ heim_db_plug_unlock_f_t unlockf;
+ heim_db_plug_sync_f_t syncf;
+ heim_db_plug_begin_f_t beginf;
+ heim_db_plug_commit_f_t commitf;
+ heim_db_plug_rollback_f_t rollbackf;
+ heim_db_plug_copy_value_f_t copyf;
+ heim_db_plug_set_value_f_t setf;
+ heim_db_plug_del_key_f_t delf;
+ heim_db_plug_iter_f_t iterf;
+};
+
+extern struct heim_db_type heim_sorted_text_file_dbtype;
+
+#define HEIM_DB_TYPE_VERSION_01 1
+
+int heim_db_register(const char *dbtype,
+ void *data,
+ struct heim_db_type *plugin);
+
+heim_db_t heim_db_create(const char *dbtype, const char *dbname,
+ heim_dict_t options, heim_error_t *error);
+heim_db_t heim_db_clone(heim_db_t, heim_error_t *);
+int heim_db_begin(heim_db_t, int, heim_error_t *);
+int heim_db_commit(heim_db_t, heim_error_t *);
+int heim_db_rollback(heim_db_t, heim_error_t *);
+heim_tid_t heim_db_get_type_id(void);
+
+int heim_db_set_value(heim_db_t, heim_string_t, heim_data_t, heim_data_t,
+ heim_error_t *);
+heim_data_t heim_db_copy_value(heim_db_t, heim_string_t, heim_data_t,
+ heim_error_t *);
+int heim_db_delete_key(heim_db_t, heim_string_t, heim_data_t,
+ heim_error_t *);
+void heim_db_iterate_f(heim_db_t, heim_string_t, void *,
+ heim_db_iterator_f_t, heim_error_t *);
+#ifdef __BLOCKS__
+void heim_db_iterate(heim_db_t, heim_string_t,
+ void (^)(heim_data_t, heim_data_t), heim_error_t *);
+#endif
+
+
+/*
+ * Number
+ */
+
+typedef struct heim_number_data *heim_number_t;
+
+heim_number_t heim_number_create(int64_t);
+heim_tid_t heim_number_get_type_id(void);
+int heim_number_get_int(heim_number_t);
+int64_t heim_number_get_long(heim_number_t);
+
+/*
+ *
+ */
+
+typedef struct heim_auto_release * heim_auto_release_t;
+
+heim_auto_release_t heim_auto_release_create(void);
+void heim_auto_release_drain(heim_auto_release_t);
+heim_object_t heim_auto_release(heim_object_t);
+
+/*
+ * JSON
+ */
+typedef enum heim_json_flags {
+ HEIM_JSON_F_NO_C_NULL = 1,
+ HEIM_JSON_F_STRICT_STRINGS = 2,
+ HEIM_JSON_F_NO_DATA = 4,
+ HEIM_JSON_F_NO_DATA_DICT = 8,
+ HEIM_JSON_F_STRICT_DICT = 16,
+ HEIM_JSON_F_STRICT = 31,
+ HEIM_JSON_F_CNULL2JSNULL = 32,
+ HEIM_JSON_F_TRY_DECODE_DATA = 64,
+ HEIM_JSON_F_ONE_LINE = 128
+} heim_json_flags_t;
+
+heim_object_t heim_json_create(const char *, size_t, heim_json_flags_t,
+ heim_error_t *);
+heim_object_t heim_json_create_with_bytes(const void *, size_t, size_t,
+ heim_json_flags_t,
+ heim_error_t *);
+heim_string_t heim_json_copy_serialize(heim_object_t, heim_json_flags_t,
+ heim_error_t *);
+
+
+/*
+ * Debug
+ */
+
+heim_string_t
+heim_description(heim_object_t ptr);
+
+/*
+ * Binary search.
+ *
+ * Note: these are private until integrated into the heimbase object system.
+ */
+typedef struct bsearch_file_handle *bsearch_file_handle;
+int _bsearch_text(const char *buf, size_t buf_sz, const char *key,
+ char **value, size_t *location, size_t *loops);
+int _bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
+ bsearch_file_handle *bfh, size_t *reads);
+int _bsearch_file(bsearch_file_handle bfh, const char *key, char **value,
+ size_t *location, size_t *loops, size_t *reads);
+void _bsearch_file_info(bsearch_file_handle bfh, size_t *page_sz,
+ size_t *max_sz, int *blockwise);
+void _bsearch_file_close(bsearch_file_handle *bfh);
+
+/*
+ * Thread-specific keys
+ */
+
+int heim_w32_key_create(unsigned long *, void (*)(void *));
+int heim_w32_delete_key(unsigned long);
+int heim_w32_setspecific(unsigned long, void *);
+void *heim_w32_getspecific(unsigned long);
+void heim_w32_service_thread_detach(void *);
+
+#include <heim_threads.h>
+#include "heimbase-atomics.h"
+#include <com_err.h>
+
+/*
+ * Service logging facility (moved from kdc/).
+ */
+
+#define HEIM_SVC_AUDIT_EATWHITE 0x1
+#define HEIM_SVC_AUDIT_VIS 0x2
+#define HEIM_SVC_AUDIT_VISLAST 0x4
+
+typedef struct heim_svc_req_desc_common_s *heim_svc_req_desc;
+
+#include <heimbase-protos.h>
+
+#endif /* HEIM_BASE_H */
diff --git a/third_party/heimdal/lib/base/heimbasepriv.h b/third_party/heimdal/lib/base/heimbasepriv.h
new file mode 100644
index 0000000..b9f63e5
--- /dev/null
+++ b/third_party/heimdal/lib/base/heimbasepriv.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(HEIM_BASE_MAINTAINER) && defined(ENABLE_PTHREAD_SUPPORT)
+#define HEIM_WIN32_TLS
+#elif defined(WIN32)
+#define HEIM_WIN32_TLS
+#endif
+
+typedef void (*heim_type_init)(void *);
+typedef heim_object_t (*heim_type_copy)(void *);
+typedef int (*heim_type_cmp)(void *, void *);
+typedef uintptr_t (*heim_type_hash)(void *);
+typedef heim_string_t (*heim_type_description)(void *);
+
+typedef struct heim_type_data *heim_type_t;
+
+enum {
+ HEIM_TID_NUMBER = 0,
+ HEIM_TID_NULL = 1,
+ HEIM_TID_BOOL = 2,
+ HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */
+ HEIM_TID_MEMORY = 128,
+ HEIM_TID_ARRAY = 129,
+ HEIM_TID_DICT = 130,
+ HEIM_TID_STRING = 131,
+ HEIM_TID_AUTORELEASE = 132,
+ HEIM_TID_ERROR = 133,
+ HEIM_TID_DATA = 134,
+ HEIM_TID_DB = 135,
+ HEIM_TID_PA_AUTH_MECH = 136,
+ HEIM_TID_PAC = 137,
+ HEIM_TID_USER = 255
+
+};
+
+struct heim_type_data {
+ heim_tid_t tid;
+ const char *name;
+ heim_type_init init;
+ heim_type_dealloc dealloc;
+ heim_type_copy copy;
+ heim_type_cmp cmp;
+ heim_type_hash hash;
+ heim_type_description desc;
+};
+
+heim_type_t _heim_get_isa(heim_object_t);
+
+heim_type_t
+_heim_create_type(const char *name,
+ heim_type_init init,
+ heim_type_dealloc dealloc,
+ heim_type_copy copy,
+ heim_type_cmp cmp,
+ heim_type_hash hash,
+ heim_type_description desc);
+
+heim_object_t
+_heim_alloc_object(heim_type_t type, size_t size);
+
+void *
+_heim_get_isaextra(heim_object_t o, size_t idx);
+
+heim_tid_t
+_heim_type_get_tid(heim_type_t type);
+
+void
+_heim_make_permanent(heim_object_t ptr);
+
+heim_data_t
+_heim_db_get_value(heim_db_t, heim_string_t, heim_data_t, heim_error_t *);
+
+
+/* tagged tid */
+extern struct heim_type_data _heim_null_object;
+extern struct heim_type_data _heim_bool_object;
+extern struct heim_type_data _heim_number_object;
+extern struct heim_type_data _heim_string_object;
diff --git a/third_party/heimdal/lib/base/json.c b/third_party/heimdal/lib/base/json.c
new file mode 100644
index 0000000..2ef371b
--- /dev/null
+++ b/third_party/heimdal/lib/base/json.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <ctype.h>
+#include <base64.h>
+
+static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT;
+static heim_string_t heim_tid_data_uuid_key = NULL;
+static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void
+json_init_once(void *arg)
+{
+ heim_tid_data_uuid_key = __heim_string_constant("heimdal-type-data-76d7fca2-d0da-4b20-a126-1a10f8a0eae6");
+}
+
+struct twojson {
+ void *ctx;
+ void (*out)(void *, const char *);
+ size_t indent;
+ heim_json_flags_t flags;
+ int ret;
+ int first;
+};
+
+struct heim_strbuf {
+ char *str;
+ size_t len;
+ size_t alloced;
+ int enomem;
+ heim_json_flags_t flags;
+};
+
+static int
+base2json(heim_object_t, struct twojson *);
+
+static void
+indent(struct twojson *j)
+{
+ size_t i = j->indent;
+ if (j->flags & HEIM_JSON_F_ONE_LINE)
+ return;
+ while (i--)
+ j->out(j->ctx, "\t");
+}
+
+static void
+array2json(heim_object_t value, void *ctx, int *stop)
+{
+ struct twojson *j = ctx;
+ if (j->ret)
+ return;
+ if (j->first) {
+ j->first = 0;
+ } else {
+ j->out(j->ctx, NULL); /* eat previous '\n' if possible */
+ j->out(j->ctx, ",\n");
+ }
+ j->ret = base2json(value, j);
+}
+
+static void
+dict2json(heim_object_t key, heim_object_t value, void *ctx)
+{
+ struct twojson *j = ctx;
+ if (j->ret)
+ return;
+ if (j->first) {
+ j->first = 0;
+ } else {
+ j->out(j->ctx, NULL); /* eat previous '\n' if possible */
+ j->out(j->ctx, ",\n");
+ }
+ j->ret = base2json(key, j);
+ if (j->ret)
+ return;
+ j->out(j->ctx, " : \n");
+ j->indent++;
+ j->ret = base2json(value, j);
+ if (j->ret)
+ return;
+ j->indent--;
+}
+
+static int
+base2json(heim_object_t obj, struct twojson *j)
+{
+ heim_tid_t type;
+ int first = 0;
+
+ if (obj == NULL) {
+ if (j->flags & HEIM_JSON_F_CNULL2JSNULL) {
+ obj = heim_null_create();
+ } else if (j->flags & HEIM_JSON_F_NO_C_NULL) {
+ return EINVAL;
+ } else {
+ indent(j);
+ j->out(j->ctx, "<NULL>\n"); /* This is NOT valid JSON! */
+ return 0;
+ }
+ }
+
+ type = heim_get_tid(obj);
+ switch (type) {
+ case HEIM_TID_ARRAY:
+ indent(j);
+ j->out(j->ctx, "[\n");
+ j->indent++;
+ first = j->first;
+ j->first = 1;
+ heim_array_iterate_f(obj, j, array2json);
+ j->indent--;
+ if (!j->first)
+ j->out(j->ctx, "\n");
+ indent(j);
+ j->out(j->ctx, "]\n");
+ j->first = first;
+ break;
+
+ case HEIM_TID_DICT:
+ indent(j);
+ j->out(j->ctx, "{\n");
+ j->indent++;
+ first = j->first;
+ j->first = 1;
+ heim_dict_iterate_f(obj, j, dict2json);
+ j->indent--;
+ if (!j->first)
+ j->out(j->ctx, "\n");
+ indent(j);
+ j->out(j->ctx, "}\n");
+ j->first = first;
+ break;
+
+ case HEIM_TID_STRING:
+ indent(j);
+ j->out(j->ctx, "\"");
+ j->out(j->ctx, heim_string_get_utf8(obj));
+ j->out(j->ctx, "\"");
+ break;
+
+ case HEIM_TID_DATA: {
+ heim_dict_t d;
+ heim_string_t v;
+ const heim_octet_string *data;
+ char *b64 = NULL;
+ int ret;
+
+ if (j->flags & HEIM_JSON_F_NO_DATA)
+ return EINVAL; /* JSON doesn't do binary */
+
+ data = heim_data_get_data(obj);
+ ret = rk_base64_encode(data->data, data->length, &b64);
+ if (ret < 0 || b64 == NULL)
+ return ENOMEM;
+
+ if (j->flags & HEIM_JSON_F_NO_DATA_DICT) {
+ indent(j);
+ j->out(j->ctx, "\"");
+ j->out(j->ctx, b64); /* base64-encode; hope there's no aliasing */
+ j->out(j->ctx, "\"");
+ free(b64);
+ } else {
+ /*
+ * JSON has no way to represent binary data, therefore the
+ * following is a Heimdal-specific convention.
+ *
+ * We encode binary data as a dict with a single very magic
+ * key with a base64-encoded value. The magic key includes
+ * a uuid, so we're not likely to alias accidentally.
+ */
+ d = heim_dict_create(2);
+ if (d == NULL) {
+ free(b64);
+ return ENOMEM;
+ }
+ v = heim_string_ref_create(b64, free);
+ if (v == NULL) {
+ free(b64);
+ heim_release(d);
+ return ENOMEM;
+ }
+ ret = heim_dict_set_value(d, heim_tid_data_uuid_key, v);
+ heim_release(v);
+ if (ret) {
+ heim_release(d);
+ return ENOMEM;
+ }
+ ret = base2json(d, j);
+ heim_release(d);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+
+ case HEIM_TID_NUMBER: {
+ char num[32];
+ indent(j);
+ snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
+ j->out(j->ctx, num);
+ break;
+ }
+ case HEIM_TID_NULL:
+ indent(j);
+ j->out(j->ctx, "null");
+ break;
+ case HEIM_TID_BOOL:
+ indent(j);
+ j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
+ void (*out)(void *, const char *))
+{
+ struct twojson j;
+
+ if (flags & HEIM_JSON_F_STRICT_STRINGS)
+ return ENOTSUP; /* Sorry, not yet! */
+
+ heim_base_once_f(&heim_json_once, NULL, json_init_once);
+
+ j.indent = 0;
+ j.ctx = ctx;
+ j.out = out;
+ j.flags = flags;
+ j.ret = 0;
+ j.first = 1;
+
+ return base2json(obj, &j);
+}
+
+
+/*
+ *
+ */
+
+struct parse_ctx {
+ unsigned long lineno;
+ const uint8_t *p;
+ const uint8_t *pstart;
+ const uint8_t *pend;
+ heim_error_t error;
+ size_t depth;
+ heim_json_flags_t flags;
+};
+
+
+static heim_object_t
+parse_value(struct parse_ctx *ctx);
+
+/*
+ * This function eats whitespace, but, critically, it also succeeds
+ * only if there's anything left to parse.
+ */
+static int
+white_spaces(struct parse_ctx *ctx)
+{
+ while (ctx->p < ctx->pend) {
+ uint8_t c = *ctx->p;
+ if (c == ' ' || c == '\t' || c == '\r') {
+
+ } else if (c == '\n') {
+ ctx->lineno++;
+ } else
+ return 0;
+ (ctx->p)++;
+ }
+ return -1;
+}
+
+static int
+is_number(uint8_t n)
+{
+ return ('0' <= n && n <= '9');
+}
+
+static heim_number_t
+parse_number(struct parse_ctx *ctx)
+{
+ int number = 0, neg = 1;
+
+ if (ctx->p >= ctx->pend)
+ return NULL;
+
+ if (*ctx->p == '-') {
+ if (ctx->p + 1 >= ctx->pend)
+ return NULL;
+ neg = -1;
+ ctx->p += 1;
+ }
+
+ while (ctx->p < ctx->pend) {
+ if (is_number(*ctx->p)) {
+ number = (number * 10) + (*ctx->p - '0');
+ } else {
+ break;
+ }
+ ctx->p += 1;
+ }
+
+ return heim_number_create(number * neg);
+}
+
+static heim_string_t
+parse_string(struct parse_ctx *ctx)
+{
+ const uint8_t *start;
+ int quote = 0;
+
+ if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) {
+ ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding "
+ "not yet supported");
+ return NULL;
+ }
+
+ if (*ctx->p != '"') {
+ ctx->error = heim_error_create(EINVAL, "Expected a JSON string but "
+ "found something else at line %lu",
+ ctx->lineno);
+ return NULL;
+ }
+ start = ++ctx->p;
+
+ while (ctx->p < ctx->pend) {
+ if (*ctx->p == '\n') {
+ ctx->lineno++;
+ } else if (*ctx->p == '\\') {
+ if (ctx->p + 1 == ctx->pend)
+ goto out;
+ ctx->p++;
+ quote = 1;
+ } else if (*ctx->p == '"') {
+ heim_object_t o;
+
+ if (quote) {
+ char *p0, *p;
+ p = p0 = malloc(ctx->p - start);
+ if (p == NULL)
+ goto out;
+ while (start < ctx->p) {
+ if (*start == '\\') {
+ start++;
+ /* XXX validate quoted char */
+ }
+ *p++ = *start++;
+ }
+ o = heim_string_create_with_bytes(p0, p - p0);
+ free(p0);
+ } else {
+ o = heim_string_create_with_bytes(start, ctx->p - start);
+ if (o == NULL) {
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ /* If we can decode as base64, then let's */
+ if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) {
+ void *buf;
+ size_t len;
+ const char *s;
+
+ s = heim_string_get_utf8(o);
+ len = strlen(s);
+
+ if (len >= 4 && strspn(s, base64_chars) >= len - 2) {
+ buf = malloc(len);
+ if (buf == NULL) {
+ heim_release(o);
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+ len = rk_base64_decode(s, buf);
+ if (len == -1) {
+ free(buf);
+ return o;
+ }
+ heim_release(o);
+ o = heim_data_ref_create(buf, len, free);
+ }
+ }
+ }
+ ctx->p += 1;
+
+ return o;
+ }
+ ctx->p += 1;
+ }
+ out:
+ ctx->error = heim_error_create(EINVAL, "ran out of string");
+ return NULL;
+}
+
+static int
+parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
+{
+ heim_string_t key;
+ heim_object_t value;
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == '}') {
+ ctx->p++;
+ return 0;
+ }
+
+ if (ctx->flags & HEIM_JSON_F_STRICT_DICT)
+ /* JSON allows only string keys */
+ key = parse_string(ctx);
+ else
+ /* heim_dict_t allows any heim_object_t as key */
+ key = parse_value(ctx);
+ if (key == NULL)
+ /* Even heim_dict_t does not allow C NULLs as keys though! */
+ return -1;
+
+ if (white_spaces(ctx)) {
+ heim_release(key);
+ return -1;
+ }
+
+ if (*ctx->p != ':') {
+ heim_release(key);
+ return -1;
+ }
+
+ ctx->p += 1; /* safe because we call white_spaces() next */
+
+ if (white_spaces(ctx)) {
+ heim_release(key);
+ return -1;
+ }
+
+ value = parse_value(ctx);
+ if (value == NULL &&
+ (ctx->error != NULL || (ctx->flags & HEIM_JSON_F_NO_C_NULL))) {
+ if (ctx->error == NULL)
+ ctx->error = heim_error_create(EINVAL, "Invalid JSON encoding");
+ heim_release(key);
+ return -1;
+ }
+ heim_dict_set_value(dict, key, value);
+ heim_release(key);
+ heim_release(value);
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == '}') {
+ /*
+ * Return 1 but don't consume the '}' so we can count the one
+ * pair in a one-pair dict
+ */
+ return 1;
+ } else if (*ctx->p == ',') {
+ ctx->p++;
+ return 1;
+ }
+ return -1;
+}
+
+static heim_dict_t
+parse_dict(struct parse_ctx *ctx)
+{
+ heim_dict_t dict;
+ size_t count = 0;
+ int ret;
+
+ heim_assert(*ctx->p == '{', "string doesn't start with {");
+
+ dict = heim_dict_create(11);
+ if (dict == NULL) {
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */
+
+ while ((ret = parse_pair(dict, ctx)) > 0)
+ count++;
+ if (ret < 0) {
+ heim_release(dict);
+ return NULL;
+ }
+ if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) {
+ heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key);
+
+ /*
+ * Binary data encoded as a dict with a single magic key with
+ * base64-encoded value? Decode as heim_data_t.
+ */
+ if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) {
+ void *buf;
+ size_t len;
+
+ buf = malloc(strlen(heim_string_get_utf8(v)));
+ if (buf == NULL) {
+ heim_release(dict);
+ heim_release(v);
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+ len = rk_base64_decode(heim_string_get_utf8(v), buf);
+ heim_release(v);
+ if (len == -1) {
+ free(buf);
+ return dict; /* assume aliasing accident */
+ }
+ heim_release(dict);
+ return (heim_dict_t)heim_data_ref_create(buf, len, free);
+ }
+ }
+ return dict;
+}
+
+static int
+parse_item(heim_array_t array, struct parse_ctx *ctx)
+{
+ heim_object_t value;
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == ']') {
+ ctx->p++; /* safe because parse_value() calls white_spaces() first */
+ return 0;
+ }
+
+ value = parse_value(ctx);
+ if (value == NULL &&
+ (ctx->error || (ctx->flags & HEIM_JSON_F_NO_C_NULL)))
+ return -1;
+
+ heim_array_append_value(array, value);
+ heim_release(value);
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == ']') {
+ ctx->p++;
+ return 0;
+ } else if (*ctx->p == ',') {
+ ctx->p++;
+ return 1;
+ }
+ return -1;
+}
+
+static heim_array_t
+parse_array(struct parse_ctx *ctx)
+{
+ heim_array_t array = heim_array_create();
+ int ret;
+
+ heim_assert(*ctx->p == '[', "array doesn't start with [");
+ ctx->p += 1;
+
+ while ((ret = parse_item(array, ctx)) > 0)
+ ;
+ if (ret < 0) {
+ heim_release(array);
+ return NULL;
+ }
+ return array;
+}
+
+static heim_object_t
+parse_value(struct parse_ctx *ctx)
+{
+ size_t len;
+ heim_object_t o;
+
+ if (white_spaces(ctx))
+ return NULL;
+
+ if (*ctx->p == '"') {
+ return parse_string(ctx);
+ } else if (*ctx->p == '{') {
+ if (ctx->depth-- == 1) {
+ ctx->error = heim_error_create(EINVAL, "JSON object too deep");
+ return NULL;
+ }
+ o = parse_dict(ctx);
+ ctx->depth++;
+ return o;
+ } else if (*ctx->p == '[') {
+ if (ctx->depth-- == 1) {
+ ctx->error = heim_error_create(EINVAL, "JSON object too deep");
+ return NULL;
+ }
+ o = parse_array(ctx);
+ ctx->depth++;
+ return o;
+ } else if (is_number(*ctx->p) || *ctx->p == '-') {
+ return parse_number(ctx);
+ }
+
+ len = ctx->pend - ctx->p;
+
+ if ((ctx->flags & HEIM_JSON_F_NO_C_NULL) == 0 &&
+ len >= 6 && memcmp(ctx->p, "<NULL>", 6) == 0) {
+ ctx->p += 6;
+ return heim_null_create();
+ } else if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
+ ctx->p += 4;
+ return heim_null_create();
+ } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) {
+ ctx->p += 4;
+ return heim_bool_create(1);
+ } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) {
+ ctx->p += 5;
+ return heim_bool_create(0);
+ }
+
+ ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu",
+ (char)*ctx->p,
+ (unsigned long)(ctx->p - ctx->pstart),
+ ctx->lineno);
+ return NULL;
+}
+
+
+heim_object_t
+heim_json_create(const char *string, size_t max_depth, heim_json_flags_t flags,
+ heim_error_t *error)
+{
+ return heim_json_create_with_bytes(string, strlen(string), max_depth, flags,
+ error);
+}
+
+heim_object_t
+heim_json_create_with_bytes(const void *data, size_t length, size_t max_depth,
+ heim_json_flags_t flags, heim_error_t *error)
+{
+ struct parse_ctx ctx;
+ heim_object_t o;
+
+ heim_base_once_f(&heim_json_once, NULL, json_init_once);
+
+ ctx.lineno = 1;
+ ctx.p = data;
+ ctx.pstart = data;
+ ctx.pend = ((uint8_t *)data) + length;
+ ctx.error = NULL;
+ ctx.flags = flags;
+ ctx.depth = max_depth;
+
+ o = parse_value(&ctx);
+
+ if (o == NULL && error) {
+ *error = ctx.error;
+ } else if (ctx.error) {
+ heim_release(ctx.error);
+ }
+
+ return o;
+}
+
+
+static void
+show_printf(void *ctx, const char *str)
+{
+ if (str == NULL)
+ return;
+ fprintf(ctx, "%s", str);
+}
+
+/**
+ * Dump a heimbase object to stderr (useful from the debugger!)
+ *
+ * @param obj object to dump using JSON or JSON-like format
+ *
+ * @addtogroup heimbase
+ */
+void
+heim_show(heim_object_t obj)
+{
+ heim_base2json(obj, stderr, HEIM_JSON_F_NO_DATA_DICT, show_printf);
+}
+
+static void
+strbuf_add(void *ctx, const char *str)
+{
+ struct heim_strbuf *strbuf = ctx;
+ size_t len;
+
+ if (strbuf->enomem)
+ return;
+
+ if (str == NULL) {
+ /*
+ * Eat the last '\n'; this is used when formatting dict pairs
+ * and array items so that the ',' separating them is never
+ * preceded by a '\n'.
+ */
+ if (strbuf->len > 0 && strbuf->str[strbuf->len - 1] == '\n')
+ strbuf->len--;
+ return;
+ }
+
+ len = strlen(str);
+ if ((len + 1) > (strbuf->alloced - strbuf->len)) {
+ size_t new_len = strbuf->alloced + (strbuf->alloced >> 2) + len + 1;
+ char *s;
+
+ s = realloc(strbuf->str, new_len);
+ if (s == NULL) {
+ strbuf->enomem = 1;
+ return;
+ }
+ strbuf->str = s;
+ strbuf->alloced = new_len;
+ }
+ /* +1 so we copy the NUL */
+ (void) memcpy(strbuf->str + strbuf->len, str, len + 1);
+ strbuf->len += len;
+ if (strbuf->str[strbuf->len - 1] == '\n' &&
+ strbuf->flags & HEIM_JSON_F_ONE_LINE)
+ strbuf->len--;
+}
+
+#define STRBUF_INIT_SZ 64
+
+heim_string_t
+heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_t *error)
+{
+ heim_string_t str;
+ struct heim_strbuf strbuf;
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ memset(&strbuf, 0, sizeof (strbuf));
+ strbuf.str = malloc(STRBUF_INIT_SZ);
+ if (strbuf.str == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ strbuf.len = 0;
+ strbuf.alloced = STRBUF_INIT_SZ;
+ strbuf.str[0] = '\0';
+ strbuf.flags = flags;
+
+ ret = heim_base2json(obj, &strbuf, flags, strbuf_add);
+ if (ret || strbuf.enomem) {
+ if (error) {
+ if (strbuf.enomem || ret == ENOMEM)
+ *error = heim_error_create_enomem();
+ else
+ *error = heim_error_create(1, "Impossible to JSON-encode "
+ "object");
+ }
+ free(strbuf.str);
+ return NULL;
+ }
+ if (flags & HEIM_JSON_F_ONE_LINE) {
+ strbuf.flags &= ~HEIM_JSON_F_ONE_LINE;
+ strbuf_add(&strbuf, "\n");
+ }
+ str = heim_string_ref_create(strbuf.str, free);
+ if (str == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ free(strbuf.str);
+ }
+ return str;
+}
diff --git a/third_party/heimdal/lib/base/log.c b/third_party/heimdal/lib/base/log.c
new file mode 100644
index 0000000..24295b5
--- /dev/null
+++ b/third_party/heimdal/lib/base/log.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 1997-2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include "heim_threads.h"
+#include "heimbase.h"
+#include "heimbase-svc.h"
+#include <assert.h>
+#include <stdarg.h>
+#include <vis.h>
+#include <base64.h>
+
+struct heim_log_facility_internal {
+ int min;
+ int max;
+ heim_log_log_func_t log_func;
+ heim_log_close_func_t close_func;
+ void *data;
+};
+
+struct heim_log_facility_s {
+ char *program;
+ size_t refs;
+ size_t len;
+ struct heim_log_facility_internal *val;
+};
+
+typedef struct heim_pcontext_s *heim_pcontext;
+typedef struct heim_pconfig *heim_pconfig;
+struct heim_svc_req_desc_common_s {
+ HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS;
+};
+
+static struct heim_log_facility_internal *
+log_realloc(heim_log_facility *f)
+{
+ struct heim_log_facility_internal *fp;
+ fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
+ if (fp == NULL)
+ return NULL;
+ f->len++;
+ f->val = fp;
+ fp += f->len - 1;
+ return fp;
+}
+
+struct s2i {
+ const char *s;
+ int val;
+};
+
+#define L(X) { #X, LOG_ ## X }
+
+static struct s2i syslogvals[] = {
+ L(EMERG),
+ L(ALERT),
+ L(CRIT),
+ L(ERR),
+ L(WARNING),
+ L(NOTICE),
+ L(INFO),
+ L(DEBUG),
+
+ L(AUTH),
+#ifdef LOG_AUTHPRIV
+ L(AUTHPRIV),
+#endif
+#ifdef LOG_CRON
+ L(CRON),
+#endif
+ L(DAEMON),
+#ifdef LOG_FTP
+ L(FTP),
+#endif
+ L(KERN),
+ L(LPR),
+ L(MAIL),
+#ifdef LOG_NEWS
+ L(NEWS),
+#endif
+ L(SYSLOG),
+ L(USER),
+#ifdef LOG_UUCP
+ L(UUCP),
+#endif
+ L(LOCAL0),
+ L(LOCAL1),
+ L(LOCAL2),
+ L(LOCAL3),
+ L(LOCAL4),
+ L(LOCAL5),
+ L(LOCAL6),
+ L(LOCAL7),
+ { NULL, -1 }
+};
+
+static int
+find_value(const char *s, struct s2i *table)
+{
+ while (table->s && strcasecmp(table->s, s) != 0)
+ table++;
+ return table->val;
+}
+
+heim_error_code
+heim_initlog(heim_context context,
+ const char *program,
+ heim_log_facility **fac)
+{
+ heim_log_facility *f = calloc(1, sizeof(*f));
+ if (f == NULL)
+ return heim_enomem(context);
+ f->refs = 1;
+ f->program = strdup(program);
+ if (f->program == NULL) {
+ free(f);
+ return heim_enomem(context);
+ }
+ *fac = f;
+ return 0;
+}
+
+heim_log_facility *
+heim_log_ref(heim_log_facility *fac)
+{
+ if (fac)
+ fac->refs++;
+ return fac;
+}
+
+heim_error_code
+heim_addlog_func(heim_context context,
+ heim_log_facility *fac,
+ int min,
+ int max,
+ heim_log_log_func_t log_func,
+ heim_log_close_func_t close_func,
+ void *data)
+{
+ struct heim_log_facility_internal *fp = log_realloc(fac);
+ if (fp == NULL)
+ return heim_enomem(context);
+ fp->min = min;
+ fp->max = max;
+ fp->log_func = log_func;
+ fp->close_func = close_func;
+ fp->data = data;
+ return 0;
+}
+
+
+struct _heimdal_syslog_data{
+ int priority;
+};
+
+static void HEIM_CALLCONV
+log_syslog(heim_context context, const char *timestr,
+ const char *msg, void *data)
+{
+ struct _heimdal_syslog_data *s = data;
+ syslog(s->priority, "%s", msg);
+}
+
+static void HEIM_CALLCONV
+close_syslog(void *data)
+{
+ free(data);
+ closelog();
+}
+
+static heim_error_code
+open_syslog(heim_context context,
+ heim_log_facility *facility, int min, int max,
+ const char *sev, const char *fac)
+{
+ struct _heimdal_syslog_data *sd;
+ heim_error_code ret;
+ int i;
+
+ if (facility == NULL)
+ return EINVAL;
+ if ((sd = calloc(1, sizeof(*sd))) == NULL)
+ return heim_enomem(context);
+ i = find_value(sev, syslogvals);
+ if (i == -1)
+ i = LOG_ERR;
+ sd->priority = i;
+ i = find_value(fac, syslogvals);
+ if (i == -1)
+ i = LOG_AUTH;
+ sd->priority |= i;
+ roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
+ ret = heim_addlog_func(context, facility, min, max, log_syslog,
+ close_syslog, sd);
+ if (ret)
+ free(sd);
+ return ret;
+}
+
+struct file_data {
+ char *filename;
+ const char *mode;
+ struct timeval tv;
+ FILE *fd;
+ int disp;
+#define FILEDISP_KEEPOPEN 0x1
+#define FILEDISP_REOPEN 0x2
+#define FILEDISP_IFEXISTS 0x4
+};
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+static void HEIM_CALLCONV
+log_file(heim_context context, const char *timestr, const char *msg, void *data)
+{
+ struct timeval tv;
+ struct file_data *f = data;
+ FILE *logf = f->fd;
+ char *msgclean;
+ size_t i = 0;
+ size_t j;
+
+ if (f->filename && (logf == NULL || (f->disp & FILEDISP_REOPEN))) {
+ int flags = O_WRONLY|O_APPEND;
+ int fd;
+
+ if (f->mode[0] == 'e') {
+ flags |= O_CLOEXEC;
+ i = 1;
+ }
+ if (f->mode[i] == 'w')
+ flags |= O_TRUNC;
+ if (f->mode[i + 1] == '+')
+ flags |= O_RDWR;
+
+ if (f->disp & FILEDISP_IFEXISTS) {
+ /* Cache failure for 1s */
+ gettimeofday(&tv, NULL);
+ if (tv.tv_sec == f->tv.tv_sec)
+ return;
+ } else {
+ flags |= O_CREAT;
+ }
+
+ fd = open(f->filename, flags, 0666); /* umask best be set */
+ if (fd == -1) {
+ if (f->disp & FILEDISP_IFEXISTS)
+ gettimeofday(&f->tv, NULL);
+ return;
+ }
+ rk_cloexec(fd);
+ logf = fdopen(fd, f->mode);
+ }
+ if (f->fd == NULL && (f->disp & FILEDISP_KEEPOPEN))
+ f->fd = logf;
+ if (logf == NULL)
+ return;
+ /*
+ * make sure the log doesn't contain special chars:
+ * we used to use strvisx(3) to encode the log, but this is
+ * inconsistent with our syslog(3) code which does not do this.
+ * It also makes it inelegant to write data which has already
+ * been quoted such as what krb5_unparse_principal() gives us.
+ * So, we change here to eat the special characters, instead.
+ */
+ if (msg && (msgclean = strdup(msg))) {
+ for (i = 0, j = 0; msg[i]; i++)
+ if (msg[i] >= 32 || msg[i] == '\t')
+ msgclean[j++] = msg[i];
+ fprintf(logf, "%s %s\n", timestr ? timestr : "", msgclean);
+ free(msgclean);
+ }
+ if (logf != f->fd)
+ fclose(logf);
+}
+
+static void HEIM_CALLCONV
+close_file(void *data)
+{
+ struct file_data *f = data;
+ if (f->fd && f->fd != stdout && f->fd != stderr)
+ fclose(f->fd);
+ free(f->filename);
+ free(data);
+}
+
+static heim_error_code
+open_file(heim_context context, heim_log_facility *fac, int min, int max,
+ const char *filename, const char *mode, FILE *f, int disp,
+ int exp_tokens)
+{
+ heim_error_code ret = 0;
+ struct file_data *fd;
+
+ if ((fd = calloc(1, sizeof(*fd))) == NULL)
+ return heim_enomem(context);
+
+ fd->filename = NULL;
+ fd->mode = mode;
+ fd->fd = f;
+ fd->disp = disp;
+
+ if (filename) {
+ if (exp_tokens)
+ ret = heim_expand_path_tokens(context, filename, 1, &fd->filename, NULL);
+ else if ((fd->filename = strdup(filename)) == NULL)
+ ret = heim_enomem(context);
+ }
+ if (ret == 0)
+ ret = heim_addlog_func(context, fac, min, max, log_file, close_file, fd);
+ if (ret) {
+ free(fd->filename);
+ free(fd);
+ } else if (disp & FILEDISP_KEEPOPEN) {
+ log_file(context, NULL, NULL, fd);
+ }
+ return ret;
+}
+
+heim_error_code
+heim_addlog_dest(heim_context context, heim_log_facility *f, const char *orig)
+{
+ heim_error_code ret = 0;
+ int min = 0, max = 3, n;
+ char c;
+ const char *p = orig;
+#ifdef _WIN32
+ const char *q;
+#endif
+
+ n = sscanf(p, "%d%c%d/", &min, &c, &max);
+ if (n == 2) {
+ if (ISPATHSEP(c)) {
+ if (min < 0) {
+ max = -min;
+ min = 0;
+ } else {
+ max = min;
+ }
+ }
+ if (c == '-')
+ max = -1;
+ }
+ if (n) {
+#ifdef _WIN32
+ q = strrchr(p, '\\');
+ if (q != NULL)
+ p = q;
+ else
+#endif
+ p = strchr(p, '/');
+ if (p == NULL) {
+ heim_set_error_message(context, EINVAL /*XXX HEIM_ERR_LOG_PARSE*/,
+ N_("failed to parse \"%s\"", ""), orig);
+ return EINVAL /*XXX HEIM_ERR_LOG_PARSE*/;
+ }
+ p++;
+ }
+ if (strcmp(p, "STDERR") == 0) {
+ ret = open_file(context, f, min, max, NULL, "a", stderr,
+ FILEDISP_KEEPOPEN, 0);
+ } else if (strcmp(p, "CONSOLE") == 0) {
+ /* XXX WIN32 */
+ ret = open_file(context, f, min, max, "/dev/console", "w", NULL,
+ FILEDISP_KEEPOPEN, 0);
+ } else if (strncmp(p, "EFILE:", 5) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("EFILE:") - 1, "a",
+ NULL, FILEDISP_IFEXISTS | FILEDISP_REOPEN, 1);
+ } else if (strncmp(p, "EFILE=", 5) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("EFILE=") - 1, "a",
+ NULL, FILEDISP_IFEXISTS | FILEDISP_KEEPOPEN, 1);
+ } else if (strncmp(p, "FILE:", sizeof("FILE:") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("FILE:") - 1, "a",
+ NULL, FILEDISP_REOPEN, 1);
+ } else if (strncmp(p, "FILE=", sizeof("FILE=") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("FILE=") - 1, "a",
+ NULL, FILEDISP_KEEPOPEN, 1);
+ } else if (strncmp(p, "DEVICE:", sizeof("DEVICE:") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("DEVICE:") - 1, "a",
+ NULL, FILEDISP_REOPEN, 0);
+ } else if (strncmp(p, "DEVICE=", sizeof("DEVICE=") - 1) == 0) {
+ ret = open_file(context, f, min, max, p + sizeof("DEVICE=") - 1, "a",
+ NULL, FILEDISP_KEEPOPEN, 0);
+ } else if (strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')) {
+ char severity[128] = "";
+ char facility[128] = "";
+ p += 6;
+ if (*p != '\0')
+ p++;
+ if (strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
+ strsep_copy(&p, ":", facility, sizeof(facility));
+ if (*severity == '\0')
+ strlcpy(severity, "ERR", sizeof(severity));
+ if (*facility == '\0')
+ strlcpy(facility, "AUTH", sizeof(facility));
+ ret = open_syslog(context, f, min, max, severity, facility);
+ } else {
+ ret = EINVAL; /*XXX HEIM_ERR_LOG_PARSE*/
+ heim_set_error_message(context, ret,
+ N_("unknown log type: %s", ""), p);
+ }
+ return ret;
+}
+
+heim_error_code
+heim_openlog(heim_context context,
+ const char *program,
+ const char **specs,
+ heim_log_facility **fac)
+{
+ heim_error_code ret;
+
+ ret = heim_initlog(context, program, fac);
+ if (ret)
+ return ret;
+
+ if (specs) {
+ size_t i;
+ for (i = 0; specs[i] && ret == 0; i++)
+ ret = heim_addlog_dest(context, *fac, specs[i]);
+ } else {
+ ret = heim_addlog_dest(context, *fac, "SYSLOG");
+ }
+ return ret;
+}
+
+void
+heim_closelog(heim_context context, heim_log_facility *fac)
+{
+ int i;
+
+ if (!fac || --(fac->refs))
+ return;
+ for (i = 0; i < fac->len; i++)
+ (*fac->val[i].close_func)(fac->val[i].data);
+ free(fac->val);
+ free(fac->program);
+ fac->val = NULL;
+ fac->len = 0;
+ fac->program = NULL;
+ free(fac);
+ return;
+}
+
+static void
+format_time(heim_context context, time_t t, char *s, size_t len)
+{
+ struct tm *tm = heim_context_get_log_utc(context) ?
+ gmtime(&t) : localtime(&t);
+ if (tm && strftime(s, len, heim_context_get_time_fmt(context), tm))
+ return;
+ snprintf(s, len, "%ld", (long)t);
+}
+
+#undef __attribute__
+#define __attribute__(X)
+
+heim_error_code
+heim_vlog_msg(heim_context context,
+ heim_log_facility *fac,
+ char **reply,
+ int level,
+ const char *fmt,
+ va_list ap)
+__attribute__ ((__format__ (__printf__, 5, 0)))
+{
+
+ char *msg = NULL;
+ const char *actual = NULL;
+ char buf[64];
+ time_t t = 0;
+ int i;
+
+ if (!fac)
+ fac = context->log_dest;
+ for (i = 0; fac && i < fac->len; i++)
+ if (fac->val[i].min <= level &&
+ (fac->val[i].max < 0 || fac->val[i].max >= level)) {
+ if (t == 0) {
+ t = time(NULL);
+ format_time(context, t, buf, sizeof(buf));
+ }
+ if (actual == NULL) {
+ int ret = vasprintf(&msg, fmt, ap);
+ if (ret < 0 || msg == NULL)
+ actual = fmt;
+ else
+ actual = msg;
+ }
+ (*fac->val[i].log_func)(context, buf, actual, fac->val[i].data);
+ }
+ if (reply == NULL)
+ free(msg);
+ else
+ *reply = msg;
+ return 0;
+}
+
+heim_error_code
+heim_vlog(heim_context context,
+ heim_log_facility *fac,
+ int level,
+ const char *fmt,
+ va_list ap)
+__attribute__ ((__format__ (__printf__, 4, 0)))
+{
+ return heim_vlog_msg(context, fac, NULL, level, fmt, ap);
+}
+
+heim_error_code
+heim_log_msg(heim_context context,
+ heim_log_facility *fac,
+ int level,
+ char **reply,
+ const char *fmt,
+ ...)
+__attribute__ ((__format__ (__printf__, 5, 6)))
+{
+ va_list ap;
+ heim_error_code ret;
+
+ va_start(ap, fmt);
+ ret = heim_vlog_msg(context, fac, reply, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+heim_error_code
+heim_log(heim_context context,
+ heim_log_facility *fac,
+ int level,
+ const char *fmt,
+ ...)
+__attribute__ ((__format__ (__printf__, 4, 5)))
+{
+ va_list ap;
+ heim_error_code ret;
+
+ va_start(ap, fmt);
+ ret = heim_vlog(context, fac, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+void
+heim_debug(heim_context context,
+ int level,
+ const char *fmt,
+ ...)
+__attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ heim_log_facility *fac;
+ va_list ap;
+
+ if (context == NULL ||
+ (fac = heim_get_debug_dest(context)) == NULL)
+ return;
+
+ va_start(ap, fmt);
+ heim_vlog(context, fac, level, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_vdebug(heim_context context,
+ int level,
+ const char *fmt,
+ va_list ap)
+__attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ heim_log_facility *fac;
+
+ if (context == NULL ||
+ (fac = heim_get_debug_dest(context)) == NULL)
+ return;
+
+ heim_vlog(context, fac, level, fmt, ap);
+}
+
+heim_error_code
+heim_have_debug(heim_context context, int level)
+{
+ return (context != NULL && heim_get_debug_dest(context) != NULL);
+}
+
+heim_error_code
+heim_add_warn_dest(heim_context context, const char *program,
+ const char *log_spec)
+{
+ heim_log_facility *fac;
+
+ heim_error_code ret;
+
+ if ((fac = heim_get_warn_dest(context)) == NULL) {
+ ret = heim_initlog(context, program, &fac);
+ if (ret)
+ return ret;
+ heim_set_warn_dest(context, fac);
+ }
+
+ ret = heim_addlog_dest(context, fac, log_spec);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+heim_error_code
+heim_add_debug_dest(heim_context context, const char *program,
+ const char *log_spec)
+{
+ heim_log_facility *fac;
+ heim_error_code ret;
+
+ if ((fac = heim_get_debug_dest(context)) == NULL) {
+ ret = heim_initlog(context, program, &fac);
+ if (ret)
+ return ret;
+ heim_set_debug_dest(context, fac);
+ }
+
+ ret = heim_addlog_dest(context, fac, log_spec);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+struct heim_audit_kv_tuple {
+ heim_string_t key;
+ heim_object_t value;
+};
+
+static struct heim_audit_kv_tuple zero_tuple;
+
+static struct heim_audit_kv_tuple
+fmtkv(int flags, const char *k, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ size_t i;
+ ssize_t j;
+ struct heim_audit_kv_tuple kv;
+ char *value;
+ char *value_vis;
+
+ j = vasprintf(&value, fmt, ap);
+ if (j < 0 || value == NULL)
+ return zero_tuple;
+
+ /* We optionally eat the whitespace. */
+
+ if (flags & HEIM_SVC_AUDIT_EATWHITE) {
+ for (i=0, j=0; value[i]; i++)
+ if (value[i] != ' ' && value[i] != '\t')
+ value[j++] = value[i];
+ value[j] = '\0';
+ }
+
+ if (flags & (HEIM_SVC_AUDIT_VIS | HEIM_SVC_AUDIT_VISLAST)) {
+ int vis_flags = VIS_CSTYLE | VIS_OCTAL | VIS_NL;
+
+ if (flags & HEIM_SVC_AUDIT_VIS)
+ vis_flags |= VIS_WHITE;
+ value_vis = malloc((j + 1) * 4 + 1);
+ if (value_vis)
+ strvisx(value_vis, value, j, vis_flags);
+ free(value);
+ if (value_vis == NULL)
+ return zero_tuple;
+ } else
+ value_vis = value;
+
+ if (k)
+ kv.key = heim_string_create(k);
+ else
+ kv.key = NULL;
+ kv.value = heim_string_ref_create(value_vis, free);
+
+ return kv;
+}
+
+void
+heim_audit_vaddreason(heim_svc_req_desc r, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 2, 0)))
+{
+ struct heim_audit_kv_tuple kv;
+
+ kv = fmtkv(HEIM_SVC_AUDIT_VISLAST, NULL, fmt, ap);
+ if (kv.value == NULL) {
+ heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddreason: "
+ "failed to add reason (out of memory)");
+ return;
+ }
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddreason(): "
+ "adding reason %s", heim_string_get_utf8(kv.value));
+ if (r->reason) {
+ heim_string_t str2;
+
+ str2 = heim_string_create_with_format("%s: %s",
+ heim_string_get_utf8(kv.value),
+ heim_string_get_utf8(r->reason));
+ if (str2) {
+ heim_release(kv.value);
+ kv.value = str2;
+ }
+ }
+ heim_release(r->reason);
+ r->reason = kv.value;
+}
+
+void
+heim_audit_addreason(heim_svc_req_desc r, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ heim_audit_vaddreason(r, fmt, ap);
+ va_end(ap);
+}
+
+size_t
+addkv(heim_svc_req_desc r, heim_object_t key, heim_object_t value)
+{
+ size_t index;
+ heim_object_t obj;
+
+ obj = heim_dict_get_value(r->kv, key);
+ if (obj) {
+ if (heim_get_tid(obj) == HEIM_TID_ARRAY) {
+ index = heim_array_get_length(obj);
+ heim_array_append_value(obj, value);
+ } else {
+ heim_array_t array = heim_array_create();
+
+ index = 1;
+ heim_array_append_value(array, obj);
+ heim_array_append_value(array, value);
+ heim_dict_set_value(r->kv, key, array);
+ heim_release(array); /* retained by r->kv */
+ }
+ } else {
+ index = 0;
+ heim_dict_set_value(r->kv, key, value);
+ }
+
+ return index;
+}
+
+/*
+ * add a key-value token. if the key already exists, the value is
+ * promoted to an array of values.
+ */
+
+void
+heim_audit_vaddkv(heim_svc_req_desc r, int flags, const char *k,
+ const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 4, 0)))
+{
+ struct heim_audit_kv_tuple kv;
+ size_t index;
+
+ kv = fmtkv(flags, k, fmt, ap);
+ if (kv.key == NULL || kv.value == NULL) {
+ heim_log(r->hcontext, r->logf, 1, "heim_audit_vaddkv: "
+ "failed to add kv pair (out of memory)");
+ heim_release(kv.key);
+ heim_release(kv.value);
+ return;
+ }
+
+ index = addkv(r, kv.key, kv.value);
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_vaddkv(): "
+ "kv pair[%zu] %s=%s", index,
+ heim_string_get_utf8(kv.key), heim_string_get_utf8(kv.value));
+
+ heim_release(kv.key);
+ heim_release(kv.value);
+}
+
+void
+heim_audit_addkv(heim_svc_req_desc r, int flags, const char *k,
+ const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ heim_audit_vaddkv(r, flags, k, fmt, ap);
+ va_end(ap);
+}
+
+void
+heim_audit_addkv_timediff(heim_svc_req_desc r, const char *k,
+ const struct timeval *start,
+ const struct timeval *end)
+{
+ time_t sec;
+ int usec;
+ const char *sign = "";
+
+ if (end->tv_sec > start->tv_sec ||
+ (end->tv_sec == start->tv_sec && end->tv_usec >= start->tv_usec)) {
+ sec = end->tv_sec - start->tv_sec;
+ usec = end->tv_usec - start->tv_usec;
+ } else {
+ sec = start->tv_sec - end->tv_sec;
+ usec = start->tv_usec - end->tv_usec;
+ sign = "-";
+ }
+
+ if (usec < 0) {
+ usec += 1000000;
+ sec -= 1;
+ }
+
+ heim_audit_addkv(r, 0, k, "%s%ld.%06d", sign, (long)sec, usec);
+}
+
+void
+heim_audit_setkv_bool(heim_svc_req_desc r, const char *k, int v)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_number_t value;
+
+ if (key == NULL)
+ return;
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_bool(): "
+ "setting kv pair %s=%s", k, v ? "true" : "false");
+
+ value = heim_bool_create(v);
+ heim_dict_set_value(r->kv, key, value);
+ heim_release(key);
+ heim_release(value);
+}
+
+void
+heim_audit_addkv_number(heim_svc_req_desc r, const char *k, int64_t v)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_number_t value;
+
+ if (key == NULL)
+ return;
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_number(): "
+ "adding kv pair %s=%lld", k, (long long)v);
+
+ value = heim_number_create(v);
+ addkv(r, key, value);
+ heim_release(key);
+ heim_release(value);
+}
+
+void
+heim_audit_setkv_number(heim_svc_req_desc r, const char *k, int64_t v)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_number_t value;
+
+ if (key == NULL)
+ return;
+
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_number(): "
+ "setting kv pair %s=%lld", k, (long long)v);
+
+ value = heim_number_create(v);
+ heim_dict_set_value(r->kv, key, value);
+ heim_release(key);
+ heim_release(value);
+}
+
+void
+heim_audit_addkv_object(heim_svc_req_desc r, const char *k, heim_object_t value)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_string_t descr;
+
+ if (key == NULL)
+ return;
+
+ descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL);
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_addkv_object(): "
+ "adding kv pair %s=%s",
+ k, descr ? heim_string_get_utf8(descr) : "<unprintable>");
+ addkv(r, key, value);
+ heim_release(key);
+ heim_release(descr);
+}
+
+void
+heim_audit_setkv_object(heim_svc_req_desc r, const char *k, heim_object_t value)
+{
+ heim_string_t key = heim_string_create(k);
+ heim_string_t descr;
+
+ if (key == NULL)
+ return;
+
+ descr = heim_json_copy_serialize(value, HEIM_JSON_F_NO_DATA_DICT, NULL);
+ heim_log(r->hcontext, r->logf, 7, "heim_audit_setkv_object(): "
+ "setting kv pair %s=%s",
+ k, descr ? heim_string_get_utf8(descr) : "<unprintable>");
+ heim_dict_set_value(r->kv, key, value);
+ heim_release(key);
+ heim_release(descr);
+}
+
+heim_object_t
+heim_audit_getkv(heim_svc_req_desc r, const char *k)
+{
+ heim_string_t key;
+ heim_object_t value;
+
+ key = heim_string_create(k);
+ if (key == NULL)
+ return NULL;
+
+ value = heim_dict_get_value(r->kv, key);
+ heim_release(key);
+ return value;
+}
+
+struct heim_audit_kv_buf {
+ char buf[1024];
+ size_t pos;
+ heim_object_t iter;
+};
+
+static void
+audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg);
+
+static void
+audit_trail_iterator_array(heim_object_t value, void *arg, int *stop)
+{
+ struct heim_audit_kv_buf *kvb = arg;
+
+ audit_trail_iterator(kvb->iter, value, kvb);
+}
+
+static void
+audit_trail_iterator(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct heim_audit_kv_buf *kvb = arg;
+ char num[32];
+ const char *k = heim_string_get_utf8(key), *v = NULL;
+ char *b64 = NULL;
+
+ if (k == NULL || *k == '#') /* # keys are hidden */
+ return;
+
+ switch (heim_get_tid(value)) {
+ case HEIM_TID_STRING:
+ v = heim_string_get_utf8(value);
+ break;
+ case HEIM_TID_NUMBER:
+ snprintf(num, sizeof(num), "%lld", (long long)heim_number_get_long(value));
+ v = num;
+ break;
+ case HEIM_TID_NULL:
+ v = "null";
+ break;
+ case HEIM_TID_BOOL:
+ v = heim_bool_val(value) ? "true" : "false";
+ break;
+ case HEIM_TID_ARRAY:
+ if (kvb->iter)
+ break; /* arrays cannot be nested */
+
+ kvb->iter = key;
+ heim_array_iterate_f(value, kvb, audit_trail_iterator_array);
+ kvb->iter = NULL;
+ break;
+ case HEIM_TID_DATA: {
+ const heim_octet_string *data = heim_data_get_data(value);
+ if (rk_base64_encode(data->data, data->length, &b64) >= 0)
+ v = b64;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (v == NULL)
+ return;
+
+ if (kvb->pos < sizeof(kvb->buf) - 1)
+ kvb->buf[kvb->pos++] = ' ';
+ for (; *k && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++)
+ kvb->buf[kvb->pos] = *k++;
+ if (kvb->pos < sizeof(kvb->buf) - 1)
+ kvb->buf[kvb->pos++] = '=';
+ for (; *v && kvb->pos < sizeof(kvb->buf) - 1; kvb->pos++)
+ kvb->buf[kvb->pos] = *v++;
+
+ free(b64);
+}
+
+void
+heim_audit_trail(heim_svc_req_desc r, heim_error_code ret, const char *retname)
+{
+ const char *retval;
+ struct heim_audit_kv_buf kvb;
+ char retvalbuf[30]; /* Enough for UNKNOWN-%d */
+
+#define CASE(x) case x : retval = #x; break
+ if (retname) {
+ retval = retname;
+ } else switch (ret ? ret : r->error_code) {
+ CASE(ENOMEM);
+ CASE(ENOENT);
+ CASE(EACCES);
+ case 0:
+ retval = "SUCCESS";
+ break;
+ default:
+ /* Wish we had a com_err number->symbolic name function */
+ (void) snprintf(retvalbuf, sizeof(retvalbuf), "UNKNOWN-%d", ret);
+ retval = retvalbuf;
+ break;
+ }
+
+ heim_audit_addkv_timediff(r, "elapsed", &r->tv_start, &r->tv_end);
+ if (r->e_text && r->kv)
+ heim_audit_addkv(r, HEIM_SVC_AUDIT_VIS, "e-text", "%s", r->e_text);
+
+ memset(&kvb, 0, sizeof(kvb));
+ if (r->kv)
+ heim_dict_iterate_f(r->kv, &kvb, audit_trail_iterator);
+ kvb.buf[kvb.pos] = '\0';
+
+ heim_log(r->hcontext, r->logf, 3, "%s %s %s %s %s%s%s%s",
+ r->reqtype, retval, r->from,
+ r->cname ? r->cname : "<unknown>",
+ r->sname ? r->sname : "<unknown>",
+ kvb.buf, r->reason ? " reason=" : "",
+ r->reason ? heim_string_get_utf8(r->reason) : "");
+}
diff --git a/third_party/heimdal/lib/base/null.c b/third_party/heimdal/lib/base/null.c
new file mode 100644
index 0000000..43bde96
--- /dev/null
+++ b/third_party/heimdal/lib/base/null.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct heim_type_data _heim_null_object = {
+ HEIM_TID_NULL,
+ "null-object",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_null_t
+heim_null_create(void)
+{
+ return heim_base_make_tagged_object(0, HEIM_TID_NULL);
+}
diff --git a/third_party/heimdal/lib/base/number.c b/third_party/heimdal/lib/base/number.c
new file mode 100644
index 0000000..8833c8b
--- /dev/null
+++ b/third_party/heimdal/lib/base/number.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+static void HEIM_CALLCONV
+number_dealloc(void *ptr)
+{
+}
+
+static int
+number_cmp(void *a, void *b)
+{
+ int na, nb;
+
+ if (heim_base_is_tagged_object(a))
+ na = heim_base_tagged_object_value(a);
+ else
+ na = *(int *)a;
+
+ if (heim_base_is_tagged_object(b))
+ nb = heim_base_tagged_object_value(b);
+ else
+ nb = *(int *)b;
+
+ return na - nb;
+}
+
+static uintptr_t
+number_hash(void *ptr)
+{
+ if (heim_base_is_tagged_object(ptr))
+ return heim_base_tagged_object_value(ptr);
+ return (uintptr_t)*(int64_t *)ptr;
+}
+
+struct heim_type_data _heim_number_object = {
+ HEIM_TID_NUMBER,
+ "number-object",
+ NULL,
+ number_dealloc,
+ NULL,
+ number_cmp,
+ number_hash,
+ NULL
+};
+
+/**
+ * Create a number object
+ *
+ * @param the number to contain in the object
+ *
+ * @return a number object
+ */
+
+heim_number_t
+heim_number_create(int64_t number)
+{
+ heim_number_t n;
+
+ if (number < 0xffffff && number >= 0)
+ return heim_base_make_tagged_object(number, HEIM_TID_NUMBER);
+
+ n = _heim_alloc_object(&_heim_number_object, sizeof(int64_t));
+ if (n)
+ *((int64_t *)n) = number;
+ return n;
+}
+
+/**
+ * Return the type ID of number objects
+ *
+ * @return type id of number objects
+ */
+
+heim_tid_t
+heim_number_get_type_id(void)
+{
+ return HEIM_TID_NUMBER;
+}
+
+/**
+ * Get the int value of the content
+ *
+ * @param number the number object to get the value from
+ *
+ * @return an int
+ */
+
+int
+heim_number_get_int(heim_number_t number)
+{
+ if (heim_base_is_tagged_object(number))
+ return heim_base_tagged_object_value(number);
+ return (int)(*(int64_t *)number);
+}
+
+int64_t
+heim_number_get_long(heim_number_t number)
+{
+ if (heim_base_is_tagged_object(number))
+ return heim_base_tagged_object_value(number);
+ return *(int64_t *)number;
+}
diff --git a/third_party/heimdal/lib/base/plugin.c b/third_party/heimdal/lib/base/plugin.c
new file mode 100644
index 0000000..355306d
--- /dev/null
+++ b/third_party/heimdal/lib/base/plugin.c
@@ -0,0 +1,785 @@
+/*
+ * Copyright (c) 2006 - 2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2018 AuriStor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include "common_plugin.h"
+
+/*
+ * Documentation for the Heimdal plugin system is in lib/krb5/plugin.c and
+ * lib/krb5/krb5-plugin.7.
+ */
+
+/*
+ * Definitions:
+ *
+ * module - a category of plugin module, identified by subsystem
+ * (e.g., "krb5")
+ * dso - a library for a module containing a map of plugin
+ * types to plugins (e.g. "service_locator")
+ * plugin - a set of callbacks and state that follows the
+ * common plugin module definition (version, init, fini)
+ *
+ * Obviously it would have been clearer to use the term "module" rather than
+ * "DSO" given there is an internal "DSO", but "module" was already taken...
+ *
+ * modules := { module: dsos }
+ * dsos := { path, dsohandle, plugins-by-name }
+ * plugins-by-name := { plugin-name: [plug] }
+ * plug := { ftable, ctx }
+ */
+
+/* global module use, use copy_modules() accessor to access */
+static heim_dict_t __modules;
+
+static HEIMDAL_MUTEX modules_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+static void
+copy_modules_once(void *context)
+{
+ heim_dict_t *modules = (heim_dict_t *)context;
+
+ *modules = heim_dict_create(11);
+ heim_assert(*modules, "plugin modules array allocation failure");
+}
+
+/* returns global modules list, refcount +1 */
+static heim_dict_t
+copy_modules(void)
+{
+ static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT;
+
+ heim_base_once_f(&modules_once, &__modules, copy_modules_once);
+
+ return heim_retain(__modules);
+}
+
+/* returns named module, refcount +1 */
+static heim_dict_t
+copy_module(const char *name)
+{
+ heim_string_t module_name = heim_string_create(name);
+ heim_dict_t modules = copy_modules();
+ heim_dict_t module;
+
+ module = heim_dict_copy_value(modules, module_name);
+ if (module == NULL) {
+ module = heim_dict_create(11);
+ heim_dict_set_value(modules, module_name, module);
+ }
+
+ heim_release(modules);
+ heim_release(module_name);
+
+ return module;
+}
+
+/* DSO helpers */
+struct heim_dso {
+ heim_string_t path;
+ heim_dict_t plugins_by_name;
+ void *dsohandle;
+};
+
+static void HEIM_CALLCONV
+dso_dealloc(void *ptr)
+{
+ struct heim_dso *p = ptr;
+
+ heim_release(p->path);
+ heim_release(p->plugins_by_name);
+#ifdef HAVE_DLOPEN
+ if (p->dsohandle)
+ dlclose(p->dsohandle);
+#endif
+}
+
+/* returns internal "DSO" for name, refcount +1 */
+static struct heim_dso *
+copy_internal_dso(const char *name)
+{
+ heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__");
+ heim_dict_t module = copy_module(name);
+ struct heim_dso *dso;
+
+ if (module == NULL)
+ return NULL;
+
+ dso = heim_dict_copy_value(module, dso_name);
+ if (dso == NULL) {
+ dso = heim_alloc(sizeof(*dso), "heim-dso", dso_dealloc);
+
+ dso->path = dso_name;
+ dso->plugins_by_name = heim_dict_create(11);
+
+ heim_dict_set_value(module, dso_name, dso);
+ }
+
+ heim_release(module);
+
+ return dso;
+}
+
+struct heim_plugin {
+ heim_plugin_common_ftable_p ftable;
+ void *ctx;
+};
+
+static void HEIM_CALLCONV
+plugin_free(void *ptr)
+{
+ struct heim_plugin *pl = ptr;
+
+ if (pl->ftable && pl->ftable->fini)
+ pl->ftable->fini(pl->ctx);
+}
+
+struct heim_plugin_register_ctx {
+ void *symbol;
+ int is_dup;
+};
+
+static void
+plugin_register_check_dup(heim_object_t value, void *ctx, int *stop)
+{
+ struct heim_plugin_register_ctx *pc = ctx;
+ struct heim_plugin *pl = value;
+
+ if (pl->ftable == pc->symbol) {
+ pc->is_dup = 1;
+ *stop = 1;
+ }
+}
+
+/**
+ * Register a plugin symbol name of specific type.
+ * @param context a Keberos context
+ * @param module name of plugin module (e.g., "krb5")
+ * @param name name of plugin symbol (e.g., "krb5_plugin_kuserok")
+ * @param ftable a pointer to a function pointer table
+ * @return In case of error a non zero error com_err error is returned
+ * and the Kerberos error string is set.
+ *
+ * @ingroup heim_support
+ */
+
+heim_error_code
+heim_plugin_register(heim_context context,
+ heim_pcontext pcontext,
+ const char *module,
+ const char *name,
+ void *ftable)
+{
+ heim_error_code ret;
+ heim_array_t plugins;
+ heim_string_t hname;
+ struct heim_dso *dso;
+ struct heim_plugin_register_ctx ctx;
+
+ ctx.symbol = ftable;
+ ctx.is_dup = 0;
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ dso = copy_internal_dso(module);
+ hname = heim_string_create(name);
+ plugins = heim_dict_copy_value(dso->plugins_by_name, hname);
+ if (plugins != NULL)
+ heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup);
+ else {
+ plugins = heim_array_create();
+ heim_dict_set_value(dso->plugins_by_name, hname, plugins);
+ }
+
+ ret = 0;
+ if (!ctx.is_dup) {
+ /* Note: refactored plugin API only supports common plugin layout */
+ struct heim_plugin *pl;
+
+ pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
+ if (pl == NULL) {
+ ret = heim_enomem(context);
+ } else {
+ pl->ftable = ftable;
+ ret = pl->ftable->init(pcontext, &pl->ctx);
+ if (ret == 0) {
+ heim_array_append_value(plugins, pl);
+ heim_debug(context, 5, "Registered %s plugin", name);
+ }
+ heim_release(pl);
+ }
+ }
+
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+
+ heim_release(dso);
+ heim_release(hname);
+ heim_release(plugins);
+
+ return ret;
+}
+
+#ifdef HAVE_DLOPEN
+
+static char *
+resolve_origin(const char *di, const char *module)
+{
+#ifdef HAVE_DLADDR
+ Dl_info dl_info;
+ const char *dname;
+ char *path, *p;
+
+ if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 &&
+ strcmp(di, "$ORIGIN") != 0)
+ return strdup(di);
+
+ di += sizeof("$ORIGIN") - 1;
+
+ if (dladdr(heim_plugin_register, &dl_info) == 0) {
+ char *s = NULL;
+
+ /* dladdr() failed */
+ if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
+ return NULL;
+ return s;
+ }
+
+ dname = dl_info.dli_fname;
+#ifdef _WIN32
+ p = strrchr(dname, '\\');
+ if (p == NULL)
+#endif
+ p = strrchr(dname, '/');
+ if (p) {
+ if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1)
+ return NULL;
+ } else {
+ if (asprintf(&path, "%s%s", dname, di) == -1)
+ return NULL;
+ }
+
+ return path;
+#else
+ char *s = NULL;
+
+ if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) != 0 &&
+ strcmp(di, "$ORIGIN") != 0)
+ return strdup(di);
+ if (asprintf(&s, LIBDIR "/plugin/%s", module) == -1)
+ return NULL;
+ return s;
+#endif /* HAVE_DLADDR */
+}
+
+#endif /* HAVE_DLOPEN */
+
+/**
+ * Load plugins (new system) for the given module @module from the given
+ * directory @paths.
+ *
+ * Inputs:
+ *
+ * @context A heim_context
+ * @module Name of plugin module (typically "krb5")
+ * @paths Array of directory paths where to look
+ */
+void
+heim_load_plugins(heim_context context,
+ const char *module,
+ const char **paths)
+{
+#ifdef HAVE_DLOPEN
+ heim_string_t s = heim_string_create(module);
+ heim_dict_t mod, modules;
+ struct dirent *entry;
+ heim_error_code ret;
+ const char **di;
+ char *dirname = NULL;
+ DIR *d;
+#ifdef _WIN32
+ char *plugin_prefix;
+ size_t plugin_prefix_len;
+
+ if (asprintf(&plugin_prefix, "plugin_%s_", module) == -1)
+ return;
+ plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
+#endif
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ modules = copy_modules();
+
+ mod = heim_dict_copy_value(modules, s);
+ if (mod == NULL) {
+ mod = heim_dict_create(11);
+ if (mod == NULL) {
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+ heim_release(s);
+ heim_release(modules);
+ heim_debug(context, 5, "Load plugins for module %s failed", module);
+ return;
+ }
+ heim_dict_set_value(modules, s, mod);
+ }
+ heim_release(s);
+ heim_release(modules);
+
+ for (di = paths; *di != NULL; di++) {
+ free(dirname);
+ dirname = resolve_origin(*di, module);
+ if (dirname == NULL) {
+ heim_debug(context, 10, "Could not resolve %s", *di);
+ continue;
+ }
+ d = opendir(dirname);
+ if (d == NULL) {
+ heim_debug(context, 10, "No such directory %s", dirname);
+ continue;
+ }
+ rk_cloexec_dir(d);
+
+ heim_debug(context, 10, "Load plugins for module %s; search %s (%s)",
+ module, *di, dirname);
+
+ while ((entry = readdir(d)) != NULL) {
+ char *n = entry->d_name;
+ char *path = NULL;
+ heim_string_t spath;
+ struct heim_dso *p;
+
+ /* skip . and .. */
+ if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
+ continue;
+
+ ret = 0;
+#ifdef _WIN32
+ /*
+ * On Windows, plugins must be loaded from the same directory as
+ * heimdal.dll (typically the assembly directory) and must have
+ * the name form "plugin_<module>_<name>.dll".
+ */
+ {
+ char *ext;
+
+ if (strnicmp(n, plugin_prefix, plugin_prefix_len) != 0)
+ continue;
+ ext = strrchr(n, '.');
+ if (ext == NULL || stricmp(ext, ".dll") != 0)
+ continue;
+
+ ret = asprintf(&path, "%s\\%s", dirname, n);
+ if (ret < 0 || path == NULL)
+ continue;
+ }
+#endif
+#ifdef __APPLE__
+ { /* support loading bundles on MacOS */
+ size_t len = strlen(n);
+ if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
+ ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n);
+ }
+#endif
+ if (ret < 0 || path == NULL)
+ ret = asprintf(&path, "%s/%s", dirname, n);
+
+ if (ret < 0 || path == NULL)
+ continue;
+
+ spath = heim_string_create(n);
+ if (spath == NULL) {
+ free(path);
+ continue;
+ }
+
+ /* check if already cached */
+ p = heim_dict_copy_value(mod, spath);
+ if (p == NULL) {
+ p = heim_alloc(sizeof(*p), "heim-dso", dso_dealloc);
+ if (p)
+ p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP);
+ if (p && p->dsohandle) {
+ p->path = heim_retain(spath);
+ p->plugins_by_name = heim_dict_create(11);
+ heim_dict_set_value(mod, spath, p);
+ heim_debug(context, 10, "Load plugins for module %s; "
+ "found DSO %s", module, path);
+ }
+ }
+ heim_release(p);
+ heim_release(spath);
+ free(path);
+ }
+ closedir(d);
+ }
+ free(dirname);
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+ heim_release(mod);
+#ifdef _WIN32
+ if (plugin_prefix)
+ free(plugin_prefix);
+#endif
+#endif /* HAVE_DLOPEN */
+}
+
+/**
+ * Unload plugins of the given @module name.
+ *
+ * Params:
+ *
+ * @module Name of module whose plusins to unload.
+ */
+void
+heim_unload_plugins(heim_context context, const char *module)
+{
+ heim_string_t sname = heim_string_create(module);
+ heim_dict_t modules;
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ modules = copy_modules();
+ heim_dict_delete_key(modules, sname);
+
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+
+ heim_release(modules);
+ heim_release(sname);
+}
+
+struct iter_ctx {
+ heim_context context;
+ heim_pcontext pcontext;
+ heim_string_t n;
+ struct heim_plugin_data *caller;
+ int flags;
+ heim_array_t result;
+ int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *);
+ void *userctx;
+ int32_t ret;
+ int32_t plugin_no_handle_retval;
+};
+
+#ifdef HAVE_DLOPEN
+/*
+ * Add plugin from a DSO that exports the plugin structure directly. This is
+ * provided for backwards compatibility with prior versions of Heimdal, but it
+ * does not allow a module to export multiple plugins, nor does it allow
+ * instance validation.
+ */
+static heim_array_t
+add_dso_plugin_struct(heim_context context,
+ heim_pcontext pcontext,
+ const char *dsopath,
+ void *dsohandle,
+ const char *name)
+{
+ heim_error_code ret;
+ heim_plugin_common_ftable_p cpm;
+ struct heim_plugin *pl;
+ heim_array_t plugins;
+
+ if (dsohandle == NULL)
+ return NULL;
+
+ /* suppress error here because we may be looking for a different plugin type */
+ cpm = (heim_plugin_common_ftable_p)dlsym(dsohandle, name);
+ if (cpm == NULL) {
+ heim_debug(context, 15, "Symbol %s not found in %s", name, dsopath);
+ return NULL;
+ }
+
+ heim_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
+
+ pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
+
+ ret = cpm->init(pcontext, &pl->ctx);
+ if (ret) {
+ heim_warn(context, ret, "plugin %s failed to initialize", dsopath);
+ heim_release(pl);
+ return NULL;
+ }
+
+ pl->ftable = cpm;
+
+ plugins = heim_array_create();
+ heim_array_append_value(plugins, pl);
+ heim_release(pl);
+
+ return plugins;
+}
+
+static int
+validate_plugin_deps(heim_context context,
+ struct heim_plugin_data *caller,
+ const char *dsopath,
+ heim_get_instance_func_t get_instance)
+{
+ size_t i;
+
+ if (get_instance == NULL) {
+ heim_warnx(context, "plugin %s omitted instance callback",
+ dsopath);
+ return FALSE;
+ }
+
+ for (i = 0; caller->deps[i] != NULL; i++) {
+ uintptr_t heim_instance, plugin_instance;
+
+ heim_instance = caller->get_instance(caller->deps[i]);
+ plugin_instance = get_instance(caller->deps[i]);
+
+ if (heim_instance == 0 || plugin_instance == 0)
+ continue;
+
+ if (heim_instance != plugin_instance) {
+ heim_warnx(context, "plugin %s library %s linked against different "
+ "instance of Heimdal (got %zu, us %zu)",
+ dsopath, caller->deps[i],
+ plugin_instance, heim_instance);
+ return FALSE;
+ }
+ heim_debug(context, 10, "Validated plugin library dependency %s for %s",
+ caller->deps[i], dsopath);
+ }
+
+ return TRUE;
+}
+
+/*
+ * New interface from Heimdal 8 where a DSO can export a load function
+ * that can return both a Heimdal instance identifier along with an
+ * array of plugins.
+ */
+static heim_array_t
+add_dso_plugins_load_fn(heim_context context,
+ heim_pcontext pcontext,
+ struct heim_plugin_data *caller,
+ const char *dsopath,
+ void *dsohandle)
+{
+ heim_error_code ret;
+ heim_array_t plugins;
+ heim_plugin_load_t load_fn;
+ char *sym = NULL;
+ size_t i;
+ heim_get_instance_func_t get_instance;
+ size_t n_ftables;
+ heim_plugin_common_ftable_cp *ftables;
+
+ if (asprintf(&sym, "%s_plugin_load", caller->name) == -1 || sym == NULL)
+ return NULL;
+
+ /* suppress error here because we may be looking for a different plugin type */
+ load_fn = (heim_plugin_load_t)dlsym(dsohandle, sym);
+ if (load_fn == NULL) {
+ heim_debug(context, 15, "Symbol %s not found in %s", sym, dsopath);
+ free(sym);
+ return NULL;
+ }
+
+ ret = load_fn(pcontext, &get_instance, &n_ftables, &ftables);
+ if (ret) {
+ heim_warn(context, ret, "plugin %s failed to load", dsopath);
+ free(sym);
+
+ /* fallback to loading structure directly */
+ return add_dso_plugin_struct(context, pcontext, dsopath,
+ dsohandle, caller->name);
+ }
+
+ if (!validate_plugin_deps(context, caller, dsopath, get_instance)) {
+ free(sym);
+ return NULL;
+ }
+
+ plugins = heim_array_create();
+
+ for (i = 0; i < n_ftables; i++) {
+ heim_plugin_common_ftable_cp cpm = ftables[i];
+ struct heim_plugin *pl;
+
+ pl = heim_alloc(sizeof(*pl), "heim-plugin", plugin_free);
+
+ ret = cpm->init(pcontext, &pl->ctx);
+ if (ret) {
+ heim_warn(context, ret, "plugin %s[%zu] failed to initialize",
+ dsopath, i);
+ } else {
+ pl->ftable = rk_UNCONST(cpm);
+ heim_array_append_value(plugins, pl);
+ }
+ heim_release(pl);
+ }
+
+ heim_debug(context, 15, "DSO %s loaded (%s)", dsopath, sym);
+ free(sym);
+ return plugins;
+}
+#endif /* HAVE_DLOPEN */
+
+static void
+reduce_by_version(heim_object_t value, void *ctx, int *stop)
+{
+ struct iter_ctx *s = ctx;
+ struct heim_plugin *pl = value;
+
+ if (pl->ftable && pl->ftable->minor_version >= s->caller->min_version)
+ heim_array_append_value(s->result, pl);
+}
+
+static void
+search_modules(heim_object_t key, heim_object_t value, void *ctx)
+{
+ struct iter_ctx *s = ctx;
+ struct heim_dso *p = value;
+ heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n);
+
+#ifdef HAVE_DLOPEN
+ if (plugins == NULL && p->dsohandle) {
+ const char *path = heim_string_get_utf8(p->path);
+
+ plugins = add_dso_plugins_load_fn(s->context,
+ s->pcontext,
+ s->caller,
+ path,
+ p->dsohandle);
+ if (plugins) {
+ heim_dict_set_value(p->plugins_by_name, s->n, plugins);
+ heim_debug(s->context, 5, "Loaded %zu %s %s plugin%s from %s",
+ heim_array_get_length(plugins),
+ s->caller->module, s->caller->name,
+ heim_array_get_length(plugins) > 1 ? "s" : "",
+ path);
+ }
+ }
+#endif /* HAVE_DLOPEN */
+
+ if (plugins) {
+ heim_array_iterate_f(plugins, s, reduce_by_version);
+ heim_release(plugins);
+ }
+}
+
+static void
+eval_results(heim_object_t value, void *ctx, int *stop)
+{
+ struct heim_plugin *pl = value;
+ struct iter_ctx *s = ctx;
+
+ if (s->ret != s->plugin_no_handle_retval)
+ return;
+
+ s->ret = s->func(s->pcontext, pl->ftable, pl->ctx, s->userctx);
+ if (s->ret != s->plugin_no_handle_retval
+ && !(s->flags & HEIM_PLUGIN_INVOKE_ALL))
+ *stop = 1;
+}
+
+/**
+ * Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
+ * "kuserok"). Specifically, the @func is invoked once per-plugin with
+ * four arguments: the @context, the plugin symbol value (a pointer to a
+ * struct whose first three fields are the same as common_plugin_ftable),
+ * a context value produced by the plugin's init method, and @userctx.
+ *
+ * @func should unpack arguments for a plugin function and invoke it
+ * with arguments taken from @userctx. @func should save plugin
+ * outputs, if any, in @userctx.
+ *
+ * All loaded and registered plugins are invoked via @func until @func
+ * returns something other than @nohandle. Plugins that have nothing to
+ * do for the given arguments should return the same value as @nohandle.
+ *
+ * Inputs:
+ *
+ * @context A heim_context
+ * @pcontext A context for the plugin, such as a krb5_context
+ * @module Name of module (typically "krb5")
+ * @name Name of pluggable interface (e.g., "kuserok")
+ * @min_version Lowest acceptable plugin minor version number
+ * @flags Flags (none defined at this time)
+ * @nohandle Flags (none defined at this time)
+ * @userctx Callback data for the callback function @func
+ * @func A callback function, invoked once per-plugin
+ *
+ * Outputs: None, other than the return value and such outputs as are
+ * gathered by @func.
+ */
+heim_error_code
+heim_plugin_run_f(heim_context context,
+ heim_pcontext pcontext,
+ struct heim_plugin_data *caller,
+ int flags,
+ int32_t nohandle,
+ void *userctx,
+ int32_t (HEIM_LIB_CALL *func)(void *, const void *, void *, void *))
+{
+ heim_string_t m = heim_string_create(caller->module);
+ heim_dict_t modules, dict = NULL;
+ struct iter_ctx s;
+
+ s.context = context;
+ s.pcontext = pcontext;
+ s.caller = caller;
+ s.n = heim_string_create(caller->name);
+ s.flags = flags;
+ s.result = heim_array_create();
+ s.func = func;
+ s.userctx = userctx;
+ s.plugin_no_handle_retval = nohandle;
+ s.ret = nohandle;
+
+ HEIMDAL_MUTEX_lock(&modules_mutex);
+
+ /* Get loaded plugins */
+ modules = copy_modules();
+ dict = heim_dict_copy_value(modules, m);
+
+ /* Add loaded plugins to s.result array */
+ if (dict)
+ heim_dict_iterate_f(dict, &s, search_modules);
+
+ /* We don't need to hold modules_mutex during plugin invocation */
+ HEIMDAL_MUTEX_unlock(&modules_mutex);
+
+ /* Invoke loaded plugins */
+ heim_array_iterate_f(s.result, &s, eval_results);
+
+ heim_release(s.result);
+ heim_release(s.n);
+ heim_release(dict);
+ heim_release(m);
+ heim_release(modules);
+
+ return s.ret;
+}
diff --git a/third_party/heimdal/lib/base/roken_rename.h b/third_party/heimdal/lib/base/roken_rename.h
new file mode 100644
index 0000000..ea72098
--- /dev/null
+++ b/third_party/heimdal/lib/base/roken_rename.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __heimbase_roken_rename_h__
+#define __heimbase_roken_rename_h__
+
+#ifndef HAVE_VSNPRINTF
+#define rk_vsnprintf heimbase_vsnprintf
+#endif
+#ifndef HAVE_ASPRINTF
+#define rk_asprintf heimbase_asprintf
+#endif
+#ifndef HAVE_ASNPRINTF
+#define rk_asnprintf heimbase_asnprintf
+#endif
+#ifndef HAVE_VASPRINTF
+#define rk_vasprintf heimbase_vasprintf
+#endif
+#ifndef HAVE_VASNPRINTF
+#define rk_vasnprintf heimbase_vasnprintf
+#endif
+#ifndef HAVE_STRDUP
+#define rk_strdup heimbase_strdup
+#endif
+#ifndef HAVE_STRNDUP
+#define rk_strndup heimbase_strndup
+#endif
+
+#endif /* __heimbase_roken_rename_h__ */
diff --git a/third_party/heimdal/lib/base/string.c b/third_party/heimdal/lib/base/string.c
new file mode 100644
index 0000000..f942447
--- /dev/null
+++ b/third_party/heimdal/lib/base/string.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <string.h>
+
+static void HEIM_CALLCONV
+string_dealloc(void *ptr)
+{
+ heim_string_t s = ptr;
+ heim_string_free_f_t *deallocp;
+ heim_string_free_f_t dealloc;
+
+ if (*(const char *)ptr != '\0')
+ return;
+
+ /* Possible string ref */
+ deallocp = _heim_get_isaextra(s, 0);
+ dealloc = *deallocp;
+ if (dealloc != NULL) {
+ char **strp = _heim_get_isaextra(s, 1);
+ dealloc(*strp);
+ }
+}
+
+static int
+string_cmp(void *a, void *b)
+{
+ if (*(char *)a == '\0') {
+ char **strp = _heim_get_isaextra(a, 1);
+
+ if (*strp != NULL)
+ a = *strp; /* a is a string ref */
+ }
+ if (*(char *)b == '\0') {
+ char **strp = _heim_get_isaextra(b, 1);
+
+ if (*strp != NULL)
+ b = *strp; /* b is a string ref */
+ }
+ return strcmp(a, b);
+}
+
+static uintptr_t
+string_hash(void *ptr)
+{
+ const char *s = ptr;
+ uintptr_t n;
+
+ for (n = 0; *s; ++s)
+ n += *s;
+ return n;
+}
+
+struct heim_type_data _heim_string_object = {
+ HEIM_TID_STRING,
+ "string-object",
+ NULL,
+ string_dealloc,
+ NULL,
+ string_cmp,
+ string_hash,
+ NULL
+};
+
+/**
+ * Create a string object
+ *
+ * @param string the string to create, must be an utf8 string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create(const char *string)
+{
+ return heim_string_create_with_bytes(string, strlen(string));
+}
+
+/**
+ * Create a string object without copying the source.
+ *
+ * @param string the string to referenced, must be UTF-8
+ * @param dealloc the function to use to release the referece to the string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_ref_create(const char *string, heim_string_free_f_t dealloc)
+{
+ heim_string_t s;
+ heim_string_free_f_t *deallocp;
+
+ s = _heim_alloc_object(&_heim_string_object, 1);
+ if (s) {
+ const char **strp;
+
+ ((char *)s)[0] = '\0';
+ deallocp = _heim_get_isaextra(s, 0);
+ *deallocp = dealloc;
+ strp = _heim_get_isaextra(s, 1);
+ *strp = string;
+ }
+ return s;
+}
+
+/**
+ * Create a string object
+ *
+ * @param string the string to create, must be an utf8 string
+ * @param len the length of the string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create_with_bytes(const void *data, size_t len)
+{
+ heim_string_t s;
+
+ s = _heim_alloc_object(&_heim_string_object, len + 1);
+ if (s) {
+ memcpy(s, data, len);
+ ((char *)s)[len] = '\0';
+ }
+ return s;
+}
+
+/**
+ * Create a string object using a format string
+ *
+ * @param fmt format string
+ * @param ...
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create_with_format(const char *fmt, ...)
+{
+ heim_string_t s;
+ char *str = NULL;
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret < 0 || str == NULL)
+ return NULL;
+
+ s = heim_string_ref_create(str, free);
+ if (s == NULL)
+ free(str);
+ return s;
+}
+
+/**
+ * Return the type ID of string objects
+ *
+ * @return type id of string objects
+ */
+
+heim_tid_t
+heim_string_get_type_id(void)
+{
+ return HEIM_TID_STRING;
+}
+
+/**
+ * Get the string value of the content.
+ *
+ * @param string the string object to get the value from
+ *
+ * @return a utf8 string
+ */
+
+const char *
+heim_string_get_utf8(heim_string_t string)
+{
+ if (*(const char *)string == '\0') {
+ const char **strp;
+
+ /* String ref */
+ strp = _heim_get_isaextra(string, 1);
+ if (*strp != NULL)
+ return *strp;
+ }
+ return (const char *)string;
+}
+
+/*
+ *
+ */
+
+static void
+init_string(void *ptr)
+{
+ heim_dict_t *dict = ptr;
+ *dict = heim_dict_create(101);
+ heim_assert(*dict != NULL, "__heim_string_constant");
+}
+
+heim_string_t
+__heim_string_constant(const char *_str)
+{
+ static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
+ static heim_base_once_t once;
+ static heim_dict_t dict = NULL;
+ heim_string_t s, s2;
+
+ heim_base_once_f(&once, &dict, init_string);
+ s = heim_string_create(_str);
+
+ HEIMDAL_MUTEX_lock(&mutex);
+ s2 = heim_dict_get_value(dict, s);
+ if (s2) {
+ heim_release(s);
+ s = s2;
+ } else {
+ _heim_make_permanent(s);
+ heim_dict_set_value(dict, s, s);
+ }
+ HEIMDAL_MUTEX_unlock(&mutex);
+
+ return s;
+}
diff --git a/third_party/heimdal/lib/base/test_base.c b/third_party/heimdal/lib/base/test_base.c
new file mode 100644
index 0000000..be6c860
--- /dev/null
+++ b/third_party/heimdal/lib/base/test_base.c
@@ -0,0 +1,962 @@
+/*
+ * Copyright (c) 2010-2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This is a test of libheimbase functionality. If you make any changes
+ * to libheimbase or to this test you should run it under valgrind with
+ * the following options:
+ *
+ * -v --track-fds=yes --num-callers=30 --leak-check=full
+ *
+ * and make sure that there are no leaks that don't have
+ * __heim_string_constant() or heim_db_register() in their stack trace.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef WIN32
+#include <sys/file.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "baselocl.h"
+
+static void HEIM_CALLCONV
+memory_free(heim_object_t obj)
+{
+}
+
+static int
+test_memory(void)
+{
+ void *ptr;
+
+ ptr = heim_alloc(10, "memory", memory_free);
+
+ heim_retain(ptr);
+ heim_release(ptr);
+
+ heim_retain(ptr);
+ heim_release(ptr);
+
+ heim_release(ptr);
+
+ ptr = heim_alloc(10, "memory", NULL);
+ heim_release(ptr);
+
+ return 0;
+}
+
+static int
+test_mutex(void)
+{
+ HEIMDAL_MUTEX m = HEIMDAL_MUTEX_INITIALIZER;
+
+ HEIMDAL_MUTEX_lock(&m);
+ HEIMDAL_MUTEX_unlock(&m);
+ HEIMDAL_MUTEX_destroy(&m);
+
+ HEIMDAL_MUTEX_init(&m);
+ HEIMDAL_MUTEX_lock(&m);
+ HEIMDAL_MUTEX_unlock(&m);
+ HEIMDAL_MUTEX_destroy(&m);
+
+ return 0;
+}
+
+static int
+test_rwlock(void)
+{
+ HEIMDAL_RWLOCK l = HEIMDAL_RWLOCK_INITIALIZER;
+
+ HEIMDAL_RWLOCK_rdlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_wrlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_trywrlock(&l) != 0)
+ err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_tryrdlock(&l))
+ err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_destroy(&l);
+
+ HEIMDAL_RWLOCK_init(&l);
+ HEIMDAL_RWLOCK_rdlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_wrlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_trywrlock(&l))
+ err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_tryrdlock(&l))
+ err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_destroy(&l);
+
+ return 0;
+}
+
+static int
+test_dict(void)
+{
+ heim_dict_t dict;
+ heim_number_t a1 = heim_number_create(1);
+ heim_string_t a2 = heim_string_create("hejsan");
+ heim_number_t a3 = heim_number_create(3);
+ heim_string_t a4 = heim_string_create("foosan");
+
+ dict = heim_dict_create(10);
+
+ heim_dict_set_value(dict, a1, a2);
+ heim_dict_set_value(dict, a3, a4);
+
+ heim_dict_delete_key(dict, a3);
+ heim_dict_delete_key(dict, a1);
+
+ heim_release(a1);
+ heim_release(a2);
+ heim_release(a3);
+ heim_release(a4);
+
+ heim_release(dict);
+
+ return 0;
+}
+
+static int
+test_auto_release(void)
+{
+ heim_auto_release_t ar1, ar2;
+ heim_number_t n1;
+ heim_string_t s1;
+
+ ar1 = heim_auto_release_create();
+
+ s1 = heim_string_create("hejsan");
+ heim_auto_release(s1);
+
+ n1 = heim_number_create(1);
+ heim_auto_release(n1);
+
+ ar2 = heim_auto_release_create();
+
+ n1 = heim_number_create(1);
+ heim_auto_release(n1);
+
+ heim_release(ar2);
+ heim_release(ar1);
+
+ return 0;
+}
+
+static int
+test_string(void)
+{
+ heim_string_t s1, s2;
+ const char *string = "hejsan";
+
+ s1 = heim_string_create(string);
+ s2 = heim_string_create(string);
+
+ if (heim_cmp(s1, s2) != 0) {
+ printf("the same string is not the same\n");
+ exit(1);
+ }
+
+ heim_release(s1);
+ heim_release(s2);
+
+ return 0;
+}
+
+static int
+test_error(void)
+{
+ heim_error_t e;
+ heim_string_t s;
+
+ e = heim_error_create(10, "foo: %s", "bar");
+ heim_assert(heim_error_get_code(e) == 10, "error_code != 10");
+
+ s = heim_error_copy_string(e);
+ heim_assert(strcmp(heim_string_get_utf8(s), "foo: bar") == 0, "msg wrong");
+
+ heim_release(s);
+ heim_release(e);
+
+ return 0;
+}
+
+static int
+test_json(void)
+{
+ static char *j[] = {
+ "{ \"k1\" : \"s1\", \"k2\" : \"s2\" }",
+ "{ \"k1\" : [\"s1\", \"s2\", \"s3\"], \"k2\" : \"s3\" }",
+ "{ \"k1\" : {\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"}, \"k5\" : \"s4\" }",
+ ("[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, "
+ "null, true, false, 123456789, \"\"]"),
+ " -1"
+ };
+ char *s;
+ size_t i, k;
+ heim_object_t o, o2;
+ heim_string_t k1 = heim_string_create("k1");
+
+ o = heim_json_create("\"string\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
+ heim_release(o);
+
+ o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
+ heim_release(o);
+
+ o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o);
+
+ o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
+ "{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o);
+
+ o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
+ "{ \"k3\" : \"s4\" } : -1 }", 10,
+ HEIM_JSON_F_STRICT_DICT, NULL);
+ heim_assert(o == NULL, "dict");
+
+ o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("-10", 10, 0, NULL);
+ heim_assert(o != NULL, "number");
+ heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ heim_release(o);
+
+ o = heim_json_create("99", 10, 0, NULL);
+ heim_assert(o != NULL, "number");
+ heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ heim_release(o);
+
+ o = heim_json_create(" [ 1 ]", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ heim_release(o);
+
+ o = heim_json_create(" [ -1 ]", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ heim_release(o);
+
+ for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
+ o = heim_json_create(j[i], 10, 0, NULL);
+ if (o == NULL) {
+ fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
+ return 1;
+ }
+ heim_release(o);
+ /* Simple fuzz test */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ o = heim_json_create_with_bytes(j[i], k, 10, 0, NULL);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %.*s\n", (int)k, j[i]);
+ return EINVAL;
+ }
+ }
+ /* Again, but this time make it so valgrind can find invalid accesses */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ s = strndup(j[i], k);
+ if (s == NULL)
+ return ENOMEM;
+ o = heim_json_create(s, 10, 0, NULL);
+ free(s);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
+ return EINVAL;
+ }
+ }
+ /* Again, but with no NUL termination */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ s = malloc(k);
+ if (s == NULL)
+ return ENOMEM;
+ memcpy(s, j[i], k);
+ o = heim_json_create_with_bytes(s, k, 10, 0, NULL);
+ free(s);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
+ return EINVAL;
+ }
+ }
+ }
+
+ heim_release(k1);
+
+ return 0;
+}
+
+static int
+test_path(void)
+{
+ heim_dict_t dict = heim_dict_create(11);
+ heim_string_t p1 = heim_string_create("abc");
+ heim_string_t p2a = heim_string_create("def");
+ heim_string_t p2b = heim_string_create("DEF");
+ heim_number_t p3 = heim_number_create(0);
+ heim_string_t p4a = heim_string_create("ghi");
+ heim_string_t p4b = heim_string_create("GHI");
+ heim_array_t a = heim_array_create();
+ heim_number_t l1 = heim_number_create(42);
+ heim_number_t l2 = heim_number_create(813);
+ heim_number_t l3 = heim_number_create(1234);
+ heim_string_t k1 = heim_string_create("k1");
+ heim_string_t k2 = heim_string_create("k2");
+ heim_string_t k3 = heim_string_create("k3");
+ heim_string_t k2_1 = heim_string_create("k2-1");
+ heim_string_t k2_2 = heim_string_create("k2-2");
+ heim_string_t k2_3 = heim_string_create("k2-3");
+ heim_string_t k2_4 = heim_string_create("k2-4");
+ heim_string_t k2_5 = heim_string_create("k2-5");
+ heim_string_t k2_5_1 = heim_string_create("k2-5-1");
+ heim_object_t o;
+ heim_object_t neg_num;
+ int ret;
+
+ if (!dict || !p1 || !p2a || !p2b || !p4a || !p4b)
+ return ENOMEM;
+
+ ret = heim_path_create(dict, 11, a, NULL, p1, p2a, NULL);
+ heim_release(a);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l3, NULL, p1, p2b, NULL);
+ if (ret)
+ return ret;
+ o = heim_path_get(dict, NULL, p1, p2b, NULL);
+ if (o != l3)
+ return 1;
+ ret = heim_path_create(dict, 11, NULL, NULL, p1, p2a, p3, NULL);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l1, NULL, p1, p2a, p3, p4a, NULL);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l2, NULL, p1, p2a, p3, p4b, NULL);
+ if (ret)
+ return ret;
+
+ o = heim_path_get(dict, NULL, p1, p2a, p3, p4a, NULL);
+ if (o != l1)
+ return 1;
+ o = heim_path_get(dict, NULL, p1, p2a, p3, p4b, NULL);
+ if (o != l2)
+ return 1;
+
+ heim_release(dict);
+
+ /* Test that JSON parsing works right by using heim_path_get() */
+ dict = heim_json_create("{\"k1\":1,"
+ "\"k2\":{\"k2-1\":21,"
+ "\"k2-2\":null,"
+ "\"k2-3\":true,"
+ "\"k2-4\":false,"
+ "\"k2-5\":[1,2,3,{\"k2-5-1\":-1},-2]},"
+ "\"k3\":[true,false,0,42]}", 10, 0, NULL);
+ heim_assert(dict != NULL, "dict");
+ o = heim_path_get(dict, NULL, k1, NULL);
+ if (heim_cmp(o, heim_number_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, NULL);
+ if (heim_get_tid(o) != heim_dict_get_type_id()) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_1, NULL);
+ if (heim_cmp(o, heim_number_create(21))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_2, NULL);
+ if (heim_cmp(o, heim_null_create())) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_3, NULL);
+ if (heim_cmp(o, heim_bool_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_4, NULL);
+ if (heim_cmp(o, heim_bool_create(0))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, NULL);
+ if (heim_get_tid(o) != heim_array_get_type_id()) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(0), NULL);
+ if (heim_cmp(o, heim_number_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(1), NULL);
+ if (heim_cmp(o, heim_number_create(2))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(3), k2_5_1, NULL);
+ if (heim_cmp(o, neg_num = heim_number_create(-1))) return 1;
+ heim_release(neg_num);
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(4), NULL);
+ if (heim_cmp(o, neg_num = heim_number_create(-2))) return 1;
+ heim_release(neg_num);
+ o = heim_path_get(dict, NULL, k3, heim_number_create(3), NULL);
+ if (heim_cmp(o, heim_number_create(42))) return 1;
+
+ heim_release(dict);
+ heim_release(p1);
+ heim_release(p2a);
+ heim_release(p2b);
+ heim_release(p4a);
+ heim_release(p4b);
+ heim_release(k1);
+ heim_release(k2);
+ heim_release(k3);
+ heim_release(k2_1);
+ heim_release(k2_2);
+ heim_release(k2_3);
+ heim_release(k2_4);
+ heim_release(k2_5);
+ heim_release(k2_5_1);
+
+ return 0;
+}
+
+typedef struct dict_db {
+ heim_dict_t dict;
+ int locked;
+} *dict_db_t;
+
+static int
+dict_db_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ dict_db_t dictdb;
+ heim_dict_t contents = NULL;
+
+ if (error)
+ *error = NULL;
+ if (dbtype && *dbtype && strcmp(dbtype, "dictdb") != 0)
+ return EINVAL;
+ if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0)
+ return EINVAL;
+ dictdb = heim_alloc(sizeof (*dictdb), "dict_db", NULL);
+ if (dictdb == NULL)
+ return ENOMEM;
+
+ if (contents != NULL)
+ dictdb->dict = contents;
+ else {
+ dictdb->dict = heim_dict_create(29);
+ if (dictdb->dict == NULL) {
+ heim_release(dictdb);
+ return ENOMEM;
+ }
+ }
+
+ *db = dictdb;
+ return 0;
+}
+
+static int
+dict_db_close(void *db, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ heim_release(dictdb->dict);
+ heim_release(dictdb);
+ return 0;
+}
+
+static int
+dict_db_lock(void *db, int read_only, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ if (dictdb->locked)
+ return EWOULDBLOCK;
+ dictdb->locked = 1;
+ return 0;
+}
+
+static int
+dict_db_unlock(void *db, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ dictdb->locked = 0;
+ return 0;
+}
+
+static heim_data_t
+dict_db_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ return heim_retain(heim_path_get(dictdb->dict, error, table, key, NULL));
+}
+
+static int
+dict_db_set_value(void *db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ return heim_path_create(dictdb->dict, 29, value, error, table, key, NULL);
+}
+
+static int
+dict_db_del_key(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ heim_path_delete(dictdb->dict, error, table, key, NULL);
+ return 0;
+}
+
+struct dict_db_iter_ctx {
+ heim_db_iterator_f_t iter_f;
+ void *iter_ctx;
+};
+
+static void dict_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct dict_db_iter_ctx *ctx = arg;
+
+ ctx->iter_f((heim_object_t)key, (heim_object_t)value, ctx->iter_ctx);
+}
+
+static void
+dict_db_iter(void *db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+ struct dict_db_iter_ctx ctx;
+ heim_dict_t table_dict;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ table_dict = heim_dict_copy_value(dictdb->dict, table);
+ if (table_dict == NULL)
+ return;
+
+ ctx.iter_ctx = iter_data;
+ ctx.iter_f = iter_f;
+
+ heim_dict_iterate_f(table_dict, &ctx, dict_db_iter_f);
+ heim_release(table_dict);
+}
+
+static void
+test_db_iter(heim_data_t k, heim_data_t v, void *arg)
+{
+ int *ret = arg;
+ const void *kptr, *vptr;
+ size_t klen, vlen;
+
+ heim_assert(heim_get_tid(k) == heim_data_get_type_id(), "...");
+
+ kptr = heim_data_get_ptr(k);
+ klen = heim_data_get_length(k);
+ vptr = heim_data_get_ptr(v);
+ vlen = heim_data_get_length(v);
+
+ if (klen == strlen("msg") && strncmp(kptr, "msg", strlen("msg")) == 0 &&
+ vlen == strlen("abc") && strncmp(vptr, "abc", strlen("abc")) == 0)
+ *ret &= ~(1);
+ else if (klen == strlen("msg2") &&
+ strncmp(kptr, "msg2", strlen("msg2")) == 0 &&
+ vlen == strlen("FooBar") &&
+ strncmp(vptr, "FooBar", strlen("FooBar")) == 0)
+ *ret &= ~(2);
+ else
+ *ret |= 4;
+}
+
+static struct heim_db_type dbt = {
+ 1, dict_db_open, NULL, dict_db_close,
+ dict_db_lock, dict_db_unlock, NULL, NULL, NULL, NULL,
+ dict_db_copy_value, dict_db_set_value,
+ dict_db_del_key, dict_db_iter
+};
+
+static int
+test_db(const char *dbtype, const char *dbname)
+{
+ heim_data_t k1, k2, v, v1, v2, v3;
+ heim_db_t db;
+ int ret;
+
+ if (dbtype == NULL) {
+ ret = heim_db_register("dictdb", NULL, &dbt);
+ heim_assert(!ret, "...");
+ db = heim_db_create("dictdb", "foo", NULL, NULL);
+ heim_assert(!db, "...");
+ db = heim_db_create("foobar", "MEMORY", NULL, NULL);
+ heim_assert(!db, "...");
+ db = heim_db_create("dictdb", "MEMORY", NULL, NULL);
+ heim_assert(db, "...");
+ } else {
+ heim_dict_t options;
+
+ options = heim_dict_create(11);
+ if (options == NULL) return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("journal-filename"),
+ HSTR("json-journal")))
+ return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("create"), heim_null_create()))
+ return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("truncate"), heim_null_create()))
+ return ENOMEM;
+ db = heim_db_create(dbtype, dbname, options, NULL);
+ heim_assert(db, "...");
+ heim_release(options);
+ }
+
+ k1 = heim_data_create("msg", strlen("msg"));
+ k2 = heim_data_create("msg2", strlen("msg2"));
+ v1 = heim_data_create("Hello world!", strlen("Hello world!"));
+ v2 = heim_data_create("FooBar", strlen("FooBar"));
+ v3 = heim_data_create("abc", strlen("abc"));
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_set_value(db, NULL, k2, v2, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k2, NULL);
+ heim_assert(v && !heim_cmp(v, v2), "...");
+ heim_release(v);
+
+ ret = heim_db_set_value(db, NULL, k1, v3, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v3), "...");
+ heim_release(v);
+
+ ret = 3;
+ heim_db_iterate_f(db, NULL, &ret, test_db_iter, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_commit(db, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v3), "...");
+ heim_release(v);
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_commit(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_delete_key(db, NULL, k1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v == NULL, "...");
+ heim_release(v);
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ if (dbtype != NULL) {
+ heim_data_t k3 = heim_data_create("value-is-a-dict", strlen("value-is-a-dict"));
+ heim_dict_t vdict = heim_dict_create(11);
+ heim_db_t db2;
+
+ heim_assert(k3 && vdict, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k1"), heim_number_create(11));
+ heim_assert(!ret, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k2"), heim_null_create());
+ heim_assert(!ret, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k3"), HSTR("a value"));
+ heim_assert(!ret, "...");
+ ret = heim_db_set_value(db, NULL, k3, (heim_data_t)vdict, NULL);
+ heim_assert(!ret, "...");
+
+ heim_release(vdict);
+
+ db2 = heim_db_create(dbtype, dbname, NULL, NULL);
+ heim_assert(db2, "...");
+
+ vdict = (heim_dict_t)heim_db_copy_value(db2, NULL, k3, NULL);
+ heim_release(db2);
+ heim_release(k3);
+ heim_assert(vdict, "...");
+ heim_assert(heim_get_tid(vdict) == heim_dict_get_type_id(), "...");
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k1"));
+ heim_assert(v && !heim_cmp(v, heim_number_create(11)), "...");
+ heim_release(v);
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k2"));
+ heim_assert(v && !heim_cmp(v, heim_null_create()), "...");
+ heim_release(v);
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k3"));
+ heim_assert(v && !heim_cmp(v, HSTR("a value")), "...");
+ heim_release(v);
+
+ heim_release(vdict);
+ }
+
+ heim_release(db);
+ heim_release(k1);
+ heim_release(k2);
+ heim_release(v1);
+ heim_release(v2);
+ heim_release(v3);
+
+ return 0;
+}
+
+struct test_array_iter_ctx {
+ char buf[256];
+};
+
+static void test_array_iter(heim_object_t elt, void *arg, int *stop)
+{
+ struct test_array_iter_ctx *iter_ctx = arg;
+
+ strcat(iter_ctx->buf, heim_string_get_utf8((heim_string_t)elt));
+}
+
+static int
+test_array()
+{
+ struct test_array_iter_ctx iter_ctx;
+ heim_string_t s1 = heim_string_create("abc");
+ heim_string_t s2 = heim_string_create("def");
+ heim_string_t s3 = heim_string_create("ghi");
+ heim_string_t s4 = heim_string_create("jkl");
+ heim_string_t s5 = heim_string_create("mno");
+ heim_string_t s6 = heim_string_create("pqr");
+ heim_array_t a = heim_array_create();
+
+ if (!s1 || !s2 || !s3 || !s4 || !s5 || !s6 || !a)
+ return ENOMEM;
+
+ heim_array_append_value(a, s4);
+ heim_array_append_value(a, s5);
+ heim_array_insert_value(a, 0, s3);
+ heim_array_insert_value(a, 0, s2);
+ heim_array_append_value(a, s6);
+ heim_array_insert_value(a, 0, s1);
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefghijklmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefjklmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 0);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defmno") != 0)
+ return 1;
+
+ heim_array_insert_value(a, 0, s1);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefmno") != 0)
+ return 1;
+
+ heim_array_insert_value(a, 0, s2);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmno") != 0)
+ return 1;
+
+ heim_array_append_value(a, s3);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmnoghi") != 0)
+ return 1;
+
+ heim_array_append_value(a, s6);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmnoghipqr") != 0)
+ return 1;
+
+ heim_release(s1);
+ heim_release(s2);
+ heim_release(s3);
+ heim_release(s4);
+ heim_release(s5);
+ heim_release(s6);
+ heim_release(a);
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int res = 0;
+
+ res |= test_memory();
+ res |= test_mutex();
+ res |= test_rwlock();
+ res |= test_dict();
+ res |= test_auto_release();
+ res |= test_string();
+ res |= test_error();
+ res |= test_json();
+ res |= test_path();
+ res |= test_db(NULL, NULL);
+ res |= test_db("json", argc > 1 ? argv[1] : "test_db.json");
+ res |= test_array();
+
+ return res ? 1 : 0;
+}
diff --git a/third_party/heimdal/lib/base/version-script.map b/third_party/heimdal/lib/base/version-script.map
new file mode 100644
index 0000000..928e861
--- /dev/null
+++ b/third_party/heimdal/lib/base/version-script.map
@@ -0,0 +1,208 @@
+HEIMDAL_BASE_1.0 {
+ global:
+ _bsearch_file;
+ _bsearch_file_close;
+ _bsearch_file_info;
+ _bsearch_file_open;
+ _bsearch_text;
+ DllMain;
+ et_heim_error_table;
+ heim_abort;
+ heim_abortv;
+ heim_add_debug_dest;
+ heim_add_et_list;
+ heim_addlog_dest;
+ heim_addlog_func;
+ heim_add_warn_dest;
+ heim_alloc;
+ _heim_alloc_object;
+ heim_array_append_value;
+ heim_array_copy_value;
+ heim_array_create;
+ heim_array_delete_value;
+ heim_array_filter_f;
+ heim_array_get_length;
+ heim_array_get_type_id;
+ heim_array_get_value;
+ heim_array_insert_value;
+ heim_array_iterate_f;
+ heim_array_iterate_reverse_f;
+ heim_array_set_value;
+ heim_audit_addkv;
+ heim_audit_addkv_number;
+ heim_audit_addkv_object;
+ heim_audit_addkv_timediff;
+ heim_audit_setkv_bool;
+ heim_audit_setkv_number;
+ heim_audit_setkv_object;
+ heim_audit_addreason;
+ heim_audit_getkv;
+ heim_audit_trail;
+ heim_audit_vaddkv;
+ heim_audit_vaddreason;
+ heim_auto_release;
+ heim_auto_release_create;
+ heim_auto_release_drain;
+ heim_base_once_f;
+ heim_bool_create;
+ heim_bool_val;
+ heim_clear_error_message;
+ heim_closelog;
+ heim_cmp;
+ heim_config_copy;
+ heim_config_file_free;
+ heim_config_free_strings;
+ heim_config_get;
+ heim_config_get_bool;
+ heim_config_get_bool_default;
+ heim_config_get_entry;
+ heim_config_get_int;
+ heim_config_get_int_default;
+ heim_config_get_list;
+ heim_config_get_next;
+ heim_config_get_string;
+ heim_config_get_string_default;
+ heim_config_get_strings;
+ heim_config_get_time;
+ heim_config_get_time_default;
+ heim_config_parse_dir_multi;
+ heim_config_parse_file;
+ heim_config_parse_file_multi;
+ heim_config_parse_string_multi;
+ heim_config_vget;
+ heim_config_vget_bool;
+ heim_config_vget_bool_default;
+ heim_config_vget_int;
+ heim_config_vget_int_default;
+ heim_config_vget_list;
+ heim_config_vget_next;
+ heim_config_vget_string;
+ heim_config_vget_string_default;
+ heim_config_vget_strings;
+ heim_config_vget_time;
+ heim_config_vget_time_default;
+ heim_context_free;
+ heim_context_get_homedir_access;
+ heim_context_get_log_utc;
+ heim_context_get_time_fmt;
+ heim_context_init;
+ heim_context_set_homedir_access;
+ heim_context_set_log_utc;
+ heim_context_set_time_fmt;
+ _heim_create_type;
+ heim_data_create;
+ heim_data_get_data;
+ heim_data_get_length;
+ heim_data_get_ptr;
+ heim_data_get_type_id;
+ heim_data_ref_create;
+ heim_data_ref_get_type_id;
+ heim_db_begin;
+ heim_db_clone;
+ heim_db_commit;
+ heim_db_copy_value;
+ heim_db_create;
+ heim_db_delete_key;
+ heim_db_get_type_id;
+ _heim_db_get_value;
+ heim_db_iterate_f;
+ heim_db_register;
+ heim_db_rollback;
+ heim_db_set_value;
+ heim_debug;
+ heim_description;
+ heim_dict_copy_value;
+ heim_dict_create;
+ heim_dict_delete_key;
+ heim_dict_get_type_id;
+ heim_dict_get_value;
+ heim_dict_iterate_f;
+ heim_dict_set_value;
+ heim_enomem;
+ heim_error_append;
+ heim_error_copy_string;
+ heim_error_create;
+ heim_error_create_enomem;
+ heim_error_create_opt;
+ heim_error_createv;
+ heim_error_get_code;
+ heim_expand_path_tokens;
+ heim_expand_path_tokensv;
+ heim_free_config_files;
+ heim_free_error_message;
+ heim_get_debug_dest;
+ heim_get_default_config_files;
+ heim_get_error_message;
+ heim_get_error_string;
+ heim_get_hash;
+ _heim_get_isa;
+ _heim_get_isaextra;
+ heim_get_log_dest;
+ heim_get_tid;
+ heim_get_warn_dest;
+ heim_have_debug;
+ heim_have_error_string;
+ heim_initlog;
+ heim_json_copy_serialize;
+ heim_json_create;
+ heim_json_create_with_bytes;
+ heim_load_plugins;
+ heim_log;
+ heim_log_msg;
+ _heim_make_permanent;
+ heim_null_create;
+ heim_number_create;
+ heim_number_get_int;
+ heim_number_get_long;
+ heim_number_get_type_id;
+ heim_openlog;
+ heim_path_copy;
+ heim_path_create;
+ heim_path_delete;
+ heim_path_get;
+ heim_path_vcopy;
+ heim_path_vcreate;
+ heim_path_vdelete;
+ heim_path_vget;
+ heim_plugin_register;
+ heim_plugin_run_f;
+ heim_prepend_config_files;
+ heim_prepend_config_files_default;
+ heim_prepend_error_message;
+ heim_release;
+ heim_retain;
+ heim_set_config_files;
+ heim_set_debug_dest;
+ heim_set_error_message;
+ heim_set_log_dest;
+ heim_set_warn_dest;
+ heim_show;
+ heim_sorted_text_file_dbtype;
+ __heim_string_constant;
+ heim_string_create;
+ heim_string_create_with_bytes;
+ heim_string_create_with_format;
+ heim_string_get_type_id;
+ heim_string_get_utf8;
+ heim_string_ref_create;
+ _heim_type_get_tid;
+ heim_unload_plugins;
+ heim_vdebug;
+ heim_vlog;
+ heim_vlog_msg;
+ heim_vprepend_error_message;
+ heim_vset_error_message;
+ heim_vwarn;
+ heim_vwarnx;
+ heim_w32_delete_key;
+ heim_w32_getspecific;
+ heim_w32_key_create;
+ heim_w32_service_thread_detach;
+ heim_w32_setspecific;
+ heim_warn;
+ heim_warnx;
+ initialize_heim_error_table;
+ initialize_heim_error_table_r;
+ local:
+ *;
+};
diff --git a/third_party/heimdal/lib/base/warn.c b/third_party/heimdal/lib/base/warn.c
new file mode 100644
index 0000000..2376a51
--- /dev/null
+++ b/third_party/heimdal/lib/base/warn.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 1997 - 2020 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER)
+# pragma warning(disable: 4646)
+# pragma warning(disable: 4716)
+#endif
+
+#include "baselocl.h"
+#include <err.h>
+
+static heim_error_code _warnerr(heim_context context, int do_errtext,
+ heim_error_code code, int level, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 5, 0)));
+
+static heim_error_code
+_warnerr(heim_context context, int do_errtext,
+ heim_error_code code, int level, const char *fmt, va_list ap)
+{
+ char xfmt[7] = "";
+ const char *args[2], **arg;
+ char *msg = NULL;
+ const char *err_str = NULL;
+ heim_error_code ret;
+
+ args[0] = args[1] = NULL;
+ arg = args;
+ if(fmt){
+ strlcat(xfmt, "%s", sizeof(xfmt));
+ if(do_errtext)
+ strlcat(xfmt, ": ", sizeof(xfmt));
+ ret = vasprintf(&msg, fmt, ap);
+ if(ret < 0 || msg == NULL)
+ return ENOMEM;
+ *arg++ = msg;
+ }
+ if (context && do_errtext) {
+ strlcat(xfmt, "%s", sizeof(xfmt));
+
+ err_str = heim_get_error_message(context, code);
+ if (err_str != NULL) {
+ *arg = err_str;
+ } else {
+ *arg= "<unknown error>";
+ }
+ }
+
+ if (context && heim_get_warn_dest(context))
+ heim_log(context, heim_get_warn_dest(context), level, xfmt, args[0],
+ args[1]);
+ else
+ warnx(xfmt, args[0], args[1]);
+ free(msg);
+ heim_free_error_message(context, err_str);
+ return 0;
+}
+
+#define FUNC(ETEXT, CODE, LEVEL) \
+ heim_error_code ret; \
+ va_list ap; \
+ va_start(ap, fmt); \
+ ret = _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
+ va_end(ap);
+
+#undef __attribute__
+#define __attribute__(X)
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_vwarn(heim_context context, heim_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ return _warnerr(context, 1, code, 1, fmt, ap);
+}
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_warn(heim_context context, heim_error_code code, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ FUNC(1, code, 1);
+ return ret;
+}
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_vwarnx(heim_context context, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 2, 0)))
+{
+ return _warnerr(context, 0, 0, 1, fmt, ap);
+}
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ *
+ * @ingroup heim_error
+ */
+
+heim_error_code
+heim_warnx(heim_context context, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)))
+{
+ FUNC(0, 0, 1);
+ return ret;
+}