summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2013-12-13 04:35:44 +0100
committerGravatar Nikias Bassen2013-12-13 04:35:44 +0100
commitc168c3c7746e51fef4ec748a3982553833bb0c4e (patch)
tree927567acde881f84bdd3b40caf7dd095952423ad
parent3537f78a98111b88aab5e2d94dda340cf09be5af (diff)
downloadusbmuxd-c168c3c7746e51fef4ec748a3982553833bb0c4e.tar.gz
usbmuxd-c168c3c7746e51fef4ec748a3982553833bb0c4e.tar.bz2
add support for reading and writing config and pair record files
-rw-r--r--configure.ac2
-rw-r--r--src/Makefile.am1
-rw-r--r--src/conf.c499
-rw-r--r--src/conf.h40
-rw-r--r--src/main.c7
-rw-r--r--src/preflight.c11
-rw-r--r--src/utils.c144
-rw-r--r--src/utils.h17
8 files changed, 709 insertions, 12 deletions
diff --git a/configure.ac b/configure.ac
index 9e113d7..6737f6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,7 +57,7 @@ AC_TYPE_UINT8_T
# Checks for library functions.
AC_FUNC_MALLOC
AC_FUNC_REALLOC
-AC_CHECK_FUNCS([strcasecmp strdup strerror strndup])
+AC_CHECK_FUNCS([strcasecmp strdup strerror strndup stpcpy])
# Check for operating system
AC_MSG_CHECKING([whether to enable WIN32 build settings])
diff --git a/src/Makefile.am b/src/Makefile.am
index 883f1d8..38858c4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@ usbmuxd_SOURCES = client.c client.h \
log.c log.h \
usb-linux.c usb.h \
utils.c utils.h \
+ conf.c conf.h \
main.c
usbmuxd_CFLAGS = $(AM_CFLAGS)
usbmuxd_LDFLAGS = $(AM_LDFLAGS)
diff --git a/src/conf.c b/src/conf.c
new file mode 100644
index 0000000..780a7c4
--- /dev/null
+++ b/src/conf.c
@@ -0,0 +1,499 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <dirent.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#ifdef WIN32
+#include <shlobj.h>
+#endif
+
+#include "conf.h"
+#include "utils.h"
+#include "log.h"
+
+#ifdef WIN32
+#define DIR_SEP '\\'
+#define DIR_SEP_S "\\"
+#else
+#define DIR_SEP '/'
+#define DIR_SEP_S "/"
+#endif
+
+#define CONFIG_SYSTEM_BUID_KEY "SystemBUID"
+#define CONFIG_HOST_ID_KEY "HostID"
+
+#define CONFIG_EXT ".plist"
+
+#ifdef WIN32
+#define CONFIG_DIR "Apple"DIR_SEP_S"Lockdown"
+#else
+#define CONFIG_DIR "lockdown"
+#endif
+
+#define CONFIG_FILE "SystemConfiguration"CONFIG_EXT
+
+static char *__config_dir = NULL;
+
+#ifdef WIN32
+static char *config_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written)
+{
+ if (!unistr || (len <= 0)) return NULL;
+ char *outbuf = (char*)malloc(3*(len+1));
+ int p = 0;
+ int i = 0;
+
+ wchar_t wc;
+
+ while (i < len) {
+ wc = unistr[i++];
+ if (wc >= 0x800) {
+ outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF));
+ outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F));
+ outbuf[p++] = (char)(0x80 + (wc & 0x3F));
+ } else if (wc >= 0x80) {
+ outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F));
+ outbuf[p++] = (char)(0x80 + (wc & 0x3F));
+ } else {
+ outbuf[p++] = (char)(wc & 0x7F);
+ }
+ }
+ if (items_read) {
+ *items_read = i;
+ }
+ if (items_written) {
+ *items_written = p;
+ }
+ outbuf[p] = 0;
+
+ return outbuf;
+}
+#endif
+
+const char *config_get_config_dir()
+{
+ char *base_config_dir = NULL;
+
+ if (__config_dir)
+ return __config_dir;
+
+#ifdef WIN32
+ wchar_t path[MAX_PATH+1];
+ HRESULT hr;
+ LPITEMIDLIST pidl = NULL;
+ BOOL b = FALSE;
+
+ hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl);
+ if (hr == S_OK) {
+ b = SHGetPathFromIDListW (pidl, path);
+ if (b) {
+ base_config_dir = config_utf16_to_utf8 (path, wcslen(path), NULL, NULL);
+ CoTaskMemFree (pidl);
+ }
+ }
+#else
+#ifdef __APPLE__
+ base_config_dir = strdup("/var/db");
+#else
+ base_config_dir = strdup("/var/lib");
+#endif
+#endif
+ __config_dir = string_concat(base_config_dir, DIR_SEP_S, CONFIG_DIR, NULL);
+
+ if (__config_dir) {
+ int i = strlen(__config_dir)-1;
+ while ((i > 0) && (__config_dir[i] == DIR_SEP)) {
+ __config_dir[i--] = '\0';
+ }
+ }
+
+ free(base_config_dir);
+
+ usbmuxd_log(LL_DEBUG, "initialized config_dir to %s", __config_dir);
+
+ return __config_dir;
+}
+
+static int __mkdir(const char *dir, int mode)
+{
+#ifdef WIN32
+ return mkdir(dir);
+#else
+ return mkdir(dir, mode);
+#endif
+}
+
+static int mkdir_with_parents(const char *dir, int mode)
+{
+ if (!dir) return -1;
+ if (__mkdir(dir, mode) == 0) {
+ return 0;
+ } else {
+ if (errno == EEXIST) return 0;
+ }
+ int res;
+ char *parent = strdup(dir);
+ char* parentdir = dirname(parent);
+ if (parentdir) {
+ res = mkdir_with_parents(parentdir, mode);
+ } else {
+ res = -1;
+ }
+ free(parent);
+ return res;
+}
+
+/**
+ * Creates a freedesktop compatible configuration directory.
+ */
+static void config_create_config_dir(void)
+{
+ const char *config_path = config_get_config_dir();
+ struct stat st;
+ if (stat(config_path, &st) != 0) {
+ mkdir_with_parents(config_path, 0755);
+ }
+}
+
+static int get_rand(int min, int max)
+{
+ int retval = (rand() % (max - min)) + min;
+ return retval;
+}
+
+static char *config_generate_uuid(int idx)
+{
+ char *uuid = (char *) malloc(sizeof(char) * 37);
+ const char *chars = "ABCDEF0123456789";
+ srand(time(NULL) - idx);
+ int i = 0;
+
+ for (i = 0; i < 36; i++) {
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
+ uuid[i] = '-';
+ continue;
+ } else {
+ uuid[i] = chars[get_rand(0, 16)];
+ }
+ }
+ /* make it a real string */
+ uuid[36] = '\0';
+ return uuid;
+}
+
+/**
+ * Generates a valid BUID for this system (which is actually a UUID).
+ *
+ * @return A null terminated string containing a valid BUID.
+ */
+static char *config_generate_system_buid()
+{
+ return config_generate_uuid(1);
+}
+
+static int internal_set_value(const char *config_file, const char *key, plist_t value)
+{
+ if (!config_file)
+ return 0;
+
+ /* read file into plist */
+ plist_t config = NULL;
+
+ plist_read_from_filename(&config, config_file);
+ if (!config) {
+ config = plist_new_dict();
+ plist_dict_insert_item(config, key, value);
+ } else {
+ plist_t n = plist_dict_get_item(config, key);
+ if (n) {
+ plist_dict_remove_item(config, key);
+ }
+ plist_dict_insert_item(config, key, value);
+ remove(config_file);
+ }
+
+ /* store in config file */
+ char *value_string = NULL;
+ if (plist_get_node_type(value) == PLIST_STRING) {
+ plist_get_string_val(value, &value_string);
+ usbmuxd_log(LL_DEBUG, "setting key %s to %s in config_file %s", key, value_string, config_file);
+ if (value_string)
+ free(value_string);
+ } else {
+ usbmuxd_log(LL_DEBUG, "setting key %s in config_file %s", key, config_file);
+ }
+
+ plist_write_to_filename(config, config_file, PLIST_FORMAT_XML);
+
+ plist_free(config);
+
+ return 1;
+}
+
+static int config_set_value(const char *key, plist_t value)
+{
+ const char *config_path = NULL;
+ char *config_file = NULL;
+
+ /* Make sure config directory exists */
+ config_create_config_dir();
+
+ config_path = config_get_config_dir();
+ config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
+
+ int result = internal_set_value(config_file, key, value);
+
+ free(config_file);
+
+ return result;
+}
+
+static int internal_get_value(const char* config_file, const char *key, plist_t *value)
+{
+ *value = NULL;
+
+ /* now parse file to get the SystemBUID */
+ plist_t config = NULL;
+ if (plist_read_from_filename(&config, config_file)) {
+ usbmuxd_log(LL_DEBUG, "reading key %s from config_file %s", key, config_file);
+ plist_t n = plist_dict_get_item(config, key);
+ if (n) {
+ *value = plist_copy(n);
+ plist_free(n);
+ n = NULL;
+ }
+ }
+ plist_free(config);
+
+ return 1;
+}
+
+static int config_get_value(const char *key, plist_t *value)
+{
+ const char *config_path = NULL;
+ char *config_file = NULL;
+
+ config_path = config_get_config_dir();
+ config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
+
+ int result = internal_get_value(config_file, key, value);
+
+ free(config_file);
+
+ return result;
+}
+
+/**
+ * Store SystemBUID in config file.
+ *
+ * @param system_buid A null terminated string containing a valid SystemBUID.
+ */
+static int config_set_system_buid(const char *system_buid)
+{
+ return config_set_value(CONFIG_SYSTEM_BUID_KEY, plist_new_string(system_buid));
+}
+
+/**
+ * Reads the BUID from a previously generated configuration file.
+ *
+ * @param system_buid pointer to a variable that will be set to point to a
+ * newly allocated string containing the BUID.
+ *
+ * @note It is the responsibility of the calling function to free the returned system_buid
+ */
+void config_get_system_buid(char **system_buid)
+{
+ plist_t value = NULL;
+
+ config_get_value(CONFIG_SYSTEM_BUID_KEY, &value);
+
+ if (value && (plist_get_node_type(value) == PLIST_STRING)) {
+ plist_get_string_val(value, system_buid);
+ usbmuxd_log(LL_DEBUG, "got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid);
+ }
+
+ if (value)
+ plist_free(value);
+
+ if (!*system_buid) {
+ /* no config, generate system_buid */
+ usbmuxd_log(LL_DEBUG, "no previous %s found", CONFIG_SYSTEM_BUID_KEY);
+ *system_buid = config_generate_system_buid();
+ config_set_system_buid(*system_buid);
+ }
+
+ usbmuxd_log(LL_DEBUG, "using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY);
+}
+
+/**
+ * Store a pairing record for the given device identifier.
+ *
+ * @param udid device identifier
+ * @param record_data buffer containing a pairing record
+ * @param record_size size of buffer passed in record_data
+ *
+ * @return 0 on success or a negative errno otherwise.
+ */
+int config_set_device_record(const char *udid, char* record_data, uint64_t record_size)
+{
+ int res = 0;
+
+ if (!udid || record_data || record_size < 8)
+ return -EINVAL;
+
+ plist_t plist = NULL;
+ if (memcmp(record_data, "bplist00", 8) == 0) {
+ plist_from_bin(record_data, record_size, &plist);
+ } else {
+ plist_from_xml(record_data, record_size, &plist);
+ }
+
+ if (!plist || plist_get_node_type(plist) != PLIST_DICT) {
+ if (plist)
+ plist_free(plist);
+ return -EINVAL;
+ }
+
+ /* ensure config directory exists */
+ config_create_config_dir();
+
+ /* build file path */
+ const char *config_path = config_get_config_dir();
+ char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
+
+ remove(device_record_file);
+
+ /* store file */
+ if (!plist_write_to_filename(plist, device_record_file, PLIST_FORMAT_XML)) {
+ usbmuxd_log(LL_DEBUG, "could not open '%s' for writing: %s", device_record_file, strerror(errno));
+ res = -ENOENT;
+ }
+ free(device_record_file);
+ if (plist)
+ plist_free(plist);
+
+ return res;
+}
+
+/**
+ * Retrieve a pairing record for the given device identifier
+ *
+ * @param udid device identifier
+ * @param record_data pointer to a variable that will be set to point to a
+ * newly allocated buffer holding the pairing record
+ * @param record_size pointer to a variable that will be set to the size
+ * of the buffer given in record_data.
+ *
+ * @return 0 on success or a negative errno otherwise.
+ */
+int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size)
+{
+ int res = 0;
+
+ /* ensure config directory exists */
+ config_create_config_dir();
+
+ /* build file path */
+ const char *config_path = config_get_config_dir();
+ char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
+
+ /* read file */
+ buffer_read_from_filename(device_record_file, record_data, record_size); if (!*record_data) {
+ usbmuxd_log(LL_ERROR, "%s: failed to read '%s': %s", __func__, device_record_file, strerror(errno));
+ res = -ENOENT;
+ }
+ free(device_record_file);
+
+ return res;
+}
+
+/**
+ * Remove the pairing record stored for a device from this host.
+ *
+ * @param udid The udid of the device
+ *
+ * @return 0 on success or a negative errno otherwise.
+ */
+int config_remove_device_record(const char *udid)
+{
+ int res = 0;
+
+ /* build file path */
+ const char *config_path = config_get_config_dir();
+ char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
+
+ /* remove file */
+ if (remove(device_record_file) != 0) {
+ res = -errno;
+ usbmuxd_log(LL_DEBUG, "could not remove %s: %s", device_record_file, strerror(errno));
+ }
+
+ free(device_record_file);
+
+ return res;
+}
+
+static int config_device_record_get_value(const char *udid, const char *key, plist_t *value)
+{
+ const char *config_path = NULL;
+ char *config_file = NULL;
+
+ config_path = config_get_config_dir();
+ config_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
+
+ int result = internal_get_value(config_file, key, value);
+
+ free(config_file);
+
+ return result;
+}
+
+void config_device_record_get_host_id(const char *udid, char **host_id)
+{
+ plist_t value = NULL;
+
+ config_device_record_get_value(udid, CONFIG_HOST_ID_KEY, &value);
+
+ if (value && (plist_get_node_type(value) == PLIST_STRING)) {
+ plist_get_string_val(value, host_id);
+ }
+
+ if (value)
+ plist_free(value);
+
+ if (!*host_id) {
+ usbmuxd_log(LL_ERROR, "%s: ERROR couldn't get HostID from pairing record for udid %s\n", __func__, udid);
+ }
+}
diff --git a/src/conf.h b/src/conf.h
new file mode 100644
index 0000000..a104185
--- /dev/null
+++ b/src/conf.h
@@ -0,0 +1,40 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+#ifndef __CONF_H__
+#define __CONF_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <plist/plist.h>
+
+const char *config_get_config_dir();
+
+void config_get_system_buid(char **system_buid);
+
+int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size);
+int config_set_device_record(const char *udid, char* record_data, uint64_t record_size);
+int config_remove_device_record(const char *udid);
+
+void config_device_record_get_host_id(const char *udid, char **host_id);
+
+#endif
diff --git a/src/main.c b/src/main.c
index 15cb5a1..9e2db60 100644
--- a/src/main.c
+++ b/src/main.c
@@ -46,10 +46,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include "usb.h"
#include "device.h"
#include "client.h"
-
-#ifdef HAVE_LIBIMOBILEDEVICE
-extern const char* userpref_get_config_dir();
-#endif
+#include "conf.h"
static const char *socket_path = "/var/run/usbmuxd";
static const char *lockfile = "/var/run/usbmuxd.pid";
@@ -534,7 +531,7 @@ int main(int argc, char *argv[])
goto terminate;
#ifdef HAVE_LIBIMOBILEDEVICE
- const char* userprefdir = userpref_get_config_dir();
+ const char* userprefdir = config_get_config_dir();
struct stat fst;
memset(&fst, '\0', sizeof(struct stat));
if (stat(userprefdir, &fst) < 0) {
diff --git a/src/preflight.c b/src/preflight.c
index def6a82..283c6d9 100644
--- a/src/preflight.c
+++ b/src/preflight.c
@@ -36,6 +36,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include "preflight.h"
#include "client.h"
+#include "conf.h"
#include "log.h"
#ifdef HAVE_LIBIMOBILEDEVICE
@@ -55,12 +56,10 @@ struct cb_data {
int is_device_connected;
};
-extern uint16_t userpref_remove_device_record(const char* udid);
-
static void lockdownd_set_untrusted_host_buid(lockdownd_client_t lockdown)
{
char* system_buid = NULL;
- userpref_get_system_buid(&system_buid);
+ config_get_system_buid(&system_buid);
usbmuxd_log(LL_DEBUG, "%s: Setting UntrustedHostBUID to %s", __func__, system_buid);
lockdownd_set_value(lockdown, NULL, "UntrustedHostBUID", plist_new_string(system_buid));
free(system_buid);
@@ -162,7 +161,7 @@ retry:
int is_device_paired = 0;
char *host_id = NULL;
- userpref_device_record_get_host_id(dev->udid, &host_id);
+ config_device_record_get_host_id(dev->udid, &host_id);
lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
free(host_id);
if (lerr == LOCKDOWN_E_SUCCESS) {
@@ -179,7 +178,7 @@ retry:
break;
case LOCKDOWN_E_SSL_ERROR:
usbmuxd_log(LL_ERROR, "%s: The stored pair record for device %s is invalid. Removing.", __func__, _dev->udid);
- if (userpref_remove_device_record(_dev->udid) == 0) {
+ if (config_remove_device_record(_dev->udid) == 0) {
lockdownd_client_free(lockdown);
lockdown = NULL;
goto retry;
@@ -293,7 +292,7 @@ retry:
}
host_id = NULL;
- userpref_device_record_get_host_id(dev->udid, &host_id);
+ config_device_record_get_host_id(dev->udid, &host_id);
lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
free(host_id);
if (lerr != LOCKDOWN_E_SUCCESS) {
diff --git a/src/utils.c b/src/utils.c
index ceef535..475a921 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -3,6 +3,7 @@
Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+Copyright (c) 2013 Federico Mena Quintero
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -27,6 +28,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include <stdarg.h>
+
#include "utils.h"
#include "log.h"
@@ -118,3 +121,144 @@ int collection_count(struct collection *col)
}
return cnt;
}
+
+#ifndef HAVE_STPCPY
+/**
+ * Copy characters from one string into another
+ *
+ * @note: The strings should not overlap, as the behavior is undefined.
+ *
+ * @s1: The source string.
+ * @s2: The destination string.
+ *
+ * @return a pointer to the terminating `\0' character of @s1,
+ * or NULL if @s1 or @s2 is NULL.
+ */
+char *stpcpy(char * s1, const char * s2)
+{
+ if (s1 == NULL || s2 == NULL)
+ return NULL;
+
+ strcpy(s1, s2);
+
+ return s1 + strlen(s2);
+}
+#endif
+
+/**
+ * Concatenate strings into a newly allocated string
+ *
+ * @note: Specify NULL for the last string in the varargs list
+ *
+ * @str: The first string in the list
+ * @...: Subsequent strings. Use NULL for the last item.
+ *
+ * @return a newly allocated string, or NULL if @str is NULL. This will also
+ * return NULL and set errno to ENOMEM if memory is exhausted.
+ */
+char *string_concat(const char *str, ...)
+{
+ size_t len;
+ va_list args;
+ char *s;
+ char *result;
+ char *dest;
+
+ if (!str)
+ return NULL;
+
+ /* Compute final length */
+
+ len = strlen(str) + 1; /* plus 1 for the null terminator */
+
+ va_start(args, str);
+ s = va_arg(args, char *);
+ while (s) {
+ len += strlen(s);
+ s = va_arg(args, char*);
+ }
+ va_end(args);
+
+ /* Concat each string */
+
+ result = malloc(len);
+ if (!result)
+ return NULL; /* errno remains set */
+
+ dest = result;
+
+ dest = stpcpy(dest, str);
+
+ va_start(args, str);
+ s = va_arg(args, char *);
+ while (s) {
+ dest = stpcpy(dest, s);
+ s = va_arg(args, char *);
+ }
+ va_end(args);
+
+ return result;
+}
+
+void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
+{
+ FILE *f;
+ uint64_t size;
+
+ *length = 0;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ return;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ rewind(f);
+
+ if (size == 0) {
+ fclose(f);
+ return;
+ }
+
+ *buffer = (char*)malloc(sizeof(char)*(size+1));
+ if (fread(*buffer, sizeof(char), size, f) != size) {
+ usbmuxd_log(LL_ERROR, "%s: ERROR: couldn't read %d bytes from %s\n", __func__, (int)size, filename);
+ }
+ fclose(f);
+
+ *length = size;
+}
+
+void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length)
+{
+ FILE *f;
+
+ f = fopen(filename, "wb");
+ if (f) {
+ fwrite(buffer, sizeof(char), length, f);
+ fclose(f);
+ }
+}
+
+int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format)
+{
+ char *buffer = NULL;
+ uint32_t length;
+
+ if (!plist || !filename)
+ return 0;
+
+ if (format == PLIST_FORMAT_XML)
+ plist_to_xml(plist, &buffer, &length);
+ else if (format == PLIST_FORMAT_BINARY)
+ plist_to_bin(plist, &buffer, &length);
+ else
+ return 0;
+
+ buffer_write_to_filename(filename, buffer, length);
+
+ free(buffer);
+
+ return 1;
+}
diff --git a/src/utils.h b/src/utils.h
index 69a4259..730fbad 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#define __UTILS_H__
#include <poll.h>
+#include <plist/plist.h>
enum fdowner {
FD_LISTEN,
@@ -69,4 +70,20 @@ void collection_free(struct collection *col);
} \
} while(0);
+#ifndef HAVE_STPCPY
+char *stpcpy(char * s1, const char * s2);
+#endif
+char *string_concat(const char *str, ...);
+
+void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length);
+void buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length);
+
+enum plist_format_t {
+ PLIST_FORMAT_XML,
+ PLIST_FORMAT_BINARY
+};
+
+int plist_read_from_filename(plist_t *plist, const char *filename);
+int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format);
+
#endif