Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
L
libplist
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
pwn
libplist
Commits
392135c7
Commit
392135c7
authored
Oct 22, 2016
by
Nikias Bassen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove libxml2 dependency in favor of custom XML parsing
parent
a3263ad3
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
798 additions
and
407 deletions
+798
-407
configure.ac
configure.ac
+18
-9
plist.h
include/plist/plist.h
+0
-13
Makefile.am
src/Makefile.am
+3
-2
base64.c
src/base64.c
+11
-11
base64.h
src/base64.h
+1
-1
bplist.c
src/bplist.c
+1
-2
bytearray.c
src/bytearray.c
+8
-4
plist.c
src/plist.c
+71
-15
strbuf.h
src/strbuf.h
+33
-0
xplist.c
src/xplist.c
+652
-350
No files found.
configure.ac
View file @
392135c7
...
@@ -19,11 +19,6 @@ LIBPLIST_SO_VERSION=3:0:0
...
@@ -19,11 +19,6 @@ LIBPLIST_SO_VERSION=3:0:0
AC_SUBST(LIBPLIST_SO_VERSION)
AC_SUBST(LIBPLIST_SO_VERSION)
dnl Minimum package versions
LIBXML2_VERSION=2.7.8
AC_SUBST(LIBXML2_VERSION)
# Checks for programs.
# Checks for programs.
AC_PROG_CC
AC_PROG_CC
AC_PROG_CXX
AC_PROG_CXX
...
@@ -40,9 +35,6 @@ AC_LANG_POP
...
@@ -40,9 +35,6 @@ AC_LANG_POP
AM_PROG_CC_C_O
AM_PROG_CC_C_O
AC_PROG_LIBTOOL
AC_PROG_LIBTOOL
# Checks for libraries.
PKG_CHECK_MODULES(libxml2, libxml-2.0 >= $LIBXML2_VERSION)
# Checks for header files.
# Checks for header files.
AC_HEADER_STDC
AC_HEADER_STDC
AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
...
@@ -139,6 +131,22 @@ AM_CONDITIONAL([HAVE_CYTHON],[test "x$CYTHON_SUB" = "xcython"])
...
@@ -139,6 +131,22 @@ AM_CONDITIONAL([HAVE_CYTHON],[test "x$CYTHON_SUB" = "xcython"])
AC_SUBST([CYTHON_SUB])
AC_SUBST([CYTHON_SUB])
AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fvisibility=hidden")
AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fvisibility=hidden")
AC_ARG_ENABLE(debug,
AS_HELP_STRING([--enable-debug],
[enable debugging, default: no]),
[case "${enableval}" in
yes) debug=yes ;;
no) debug=no ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;;
esac],
[debug=no])
if (test "x$debug" = "xyes"); then
AC_DEFINE(DEBUG, 1, [Define if debug code should be enabled.])
GLOBAL_CFLAGS+=" -g"
fi
AC_SUBST(GLOBAL_CFLAGS)
AC_SUBST(GLOBAL_CFLAGS)
case "$GLOBAL_CFLAGS" in
case "$GLOBAL_CFLAGS" in
...
@@ -165,7 +173,8 @@ echo "
...
@@ -165,7 +173,8 @@ echo "
Configuration for $PACKAGE $VERSION:
Configuration for $PACKAGE $VERSION:
-------------------------------------------
-------------------------------------------
Install prefix: .........: $prefix
Install prefix ..........: $prefix
Debug code ..............: $debug
Python bindings .........: $cython_python_bindings
Python bindings .........: $cython_python_bindings
Now type 'make' to build $PACKAGE $VERSION,
Now type 'make' to build $PACKAGE $VERSION,
...
...
include/plist/plist.h
View file @
392135c7
...
@@ -111,19 +111,6 @@ extern "C"
...
@@ -111,19 +111,6 @@ extern "C"
}
plist_type
;
}
plist_type
;
/********************************************
* *
* Library Initialization & Cleanup *
* *
********************************************/
/**
* Frees memory used globally by listplist, in
* particular the libxml parser
*/
void
plist_cleanup
(
void
);
/********************************************
/********************************************
* *
* *
* Creation & Destruction *
* Creation & Destruction *
...
...
src/Makefile.am
View file @
392135c7
AM_CPPFLAGS
=
-I
$(top_srcdir)
/include
-I
$(top_srcdir)
-I
$(top_srcdir)
/libcnary/include
AM_CPPFLAGS
=
-I
$(top_srcdir)
/include
-I
$(top_srcdir)
-I
$(top_srcdir)
/libcnary/include
AM_CFLAGS
=
$(GLOBAL_CFLAGS)
$(libxml2_CFLAGS)
AM_CFLAGS
=
$(GLOBAL_CFLAGS)
AM_LDFLAGS
=
$(libxml2_LIBS)
AM_LDFLAGS
=
lib_LTLIBRARIES
=
libplist.la libplist++.la
lib_LTLIBRARIES
=
libplist.la libplist++.la
libplist_la_LIBADD
=
$(top_builddir)
/libcnary/libcnary.la
libplist_la_LIBADD
=
$(top_builddir)
/libcnary/libcnary.la
libplist_la_LDFLAGS
=
$(AM_LDFLAGS)
-version-info
$(LIBPLIST_SO_VERSION)
-no-undefined
libplist_la_LDFLAGS
=
$(AM_LDFLAGS)
-version-info
$(LIBPLIST_SO_VERSION)
-no-undefined
libplist_la_SOURCES
=
base64.c base64.h
\
libplist_la_SOURCES
=
base64.c base64.h
\
bytearray.c bytearray.h
\
bytearray.c bytearray.h
\
strbuf.h
\
hashtable.c hashtable.h
\
hashtable.c hashtable.h
\
ptrarray.c ptrarray.h
\
ptrarray.c ptrarray.h
\
time64.c time64.h time64_limits.h
\
time64.c time64.h time64_limits.h
\
...
...
src/base64.c
View file @
392135c7
...
@@ -43,32 +43,32 @@ static const signed char base64_table[256] = {
...
@@ -43,32 +43,32 @@ static const signed char base64_table[256] = {
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
};
};
char
*
base64encode
(
const
unsigned
char
*
buf
,
size_t
*
size
)
size_t
base64encode
(
char
*
outbuf
,
const
unsigned
char
*
buf
,
size_t
size
)
{
{
if
(
!
buf
||
!
size
||
!
(
*
size
>
0
))
return
NULL
;
if
(
!
outbuf
||
!
buf
||
(
size
<=
0
))
{
int
outlen
=
(
*
size
/
3
)
*
4
;
return
0
;
char
*
outbuf
=
(
char
*
)
malloc
(
outlen
+
5
);
// 4 spare bytes + 1 for '\0'
}
size_t
n
=
0
;
size_t
n
=
0
;
size_t
m
=
0
;
size_t
m
=
0
;
unsigned
char
input
[
3
];
unsigned
char
input
[
3
];
unsigned
int
output
[
4
];
unsigned
int
output
[
4
];
while
(
n
<
*
size
)
{
while
(
n
<
size
)
{
input
[
0
]
=
buf
[
n
];
input
[
0
]
=
buf
[
n
];
input
[
1
]
=
(
n
+
1
<
*
size
)
?
buf
[
n
+
1
]
:
0
;
input
[
1
]
=
(
n
+
1
<
size
)
?
buf
[
n
+
1
]
:
0
;
input
[
2
]
=
(
n
+
2
<
*
size
)
?
buf
[
n
+
2
]
:
0
;
input
[
2
]
=
(
n
+
2
<
size
)
?
buf
[
n
+
2
]
:
0
;
output
[
0
]
=
input
[
0
]
>>
2
;
output
[
0
]
=
input
[
0
]
>>
2
;
output
[
1
]
=
((
input
[
0
]
&
3
)
<<
4
)
+
(
input
[
1
]
>>
4
);
output
[
1
]
=
((
input
[
0
]
&
3
)
<<
4
)
+
(
input
[
1
]
>>
4
);
output
[
2
]
=
((
input
[
1
]
&
15
)
<<
2
)
+
(
input
[
2
]
>>
6
);
output
[
2
]
=
((
input
[
1
]
&
15
)
<<
2
)
+
(
input
[
2
]
>>
6
);
output
[
3
]
=
input
[
2
]
&
63
;
output
[
3
]
=
input
[
2
]
&
63
;
outbuf
[
m
++
]
=
base64_str
[(
int
)
output
[
0
]];
outbuf
[
m
++
]
=
base64_str
[(
int
)
output
[
0
]];
outbuf
[
m
++
]
=
base64_str
[(
int
)
output
[
1
]];
outbuf
[
m
++
]
=
base64_str
[(
int
)
output
[
1
]];
outbuf
[
m
++
]
=
(
n
+
1
<
*
size
)
?
base64_str
[(
int
)
output
[
2
]]
:
base64_pad
;
outbuf
[
m
++
]
=
(
n
+
1
<
size
)
?
base64_str
[(
int
)
output
[
2
]]
:
base64_pad
;
outbuf
[
m
++
]
=
(
n
+
2
<
*
size
)
?
base64_str
[(
int
)
output
[
3
]]
:
base64_pad
;
outbuf
[
m
++
]
=
(
n
+
2
<
size
)
?
base64_str
[(
int
)
output
[
3
]]
:
base64_pad
;
n
+=
3
;
n
+=
3
;
}
}
outbuf
[
m
]
=
0
;
// 0-termination!
outbuf
[
m
]
=
0
;
// 0-termination!
*
size
=
m
;
return
m
;
return
outbuf
;
}
}
static
int
base64decode_block
(
unsigned
char
*
target
,
const
char
*
data
,
size_t
data_size
)
static
int
base64decode_block
(
unsigned
char
*
target
,
const
char
*
data
,
size_t
data_size
)
...
...
src/base64.h
View file @
392135c7
...
@@ -22,7 +22,7 @@
...
@@ -22,7 +22,7 @@
#define BASE64_H
#define BASE64_H
#include <stdlib.h>
#include <stdlib.h>
char
*
base64encode
(
const
unsigned
char
*
buf
,
size_t
*
size
);
size_t
base64encode
(
char
*
outbuf
,
const
unsigned
char
*
buf
,
size_t
size
);
unsigned
char
*
base64decode
(
const
char
*
buf
,
size_t
*
size
);
unsigned
char
*
base64decode
(
const
char
*
buf
,
size_t
*
size
);
#endif
#endif
src/bplist.c
View file @
392135c7
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
* bplist.c
* bplist.c
* Binary plist implementation
* Binary plist implementation
*
*
* Copyright (c) 2011-201
5
Nikias Bassen, All Rights Reserved.
* Copyright (c) 2011-201
6
Nikias Bassen, All Rights Reserved.
* Copyright (c) 2008-2010 Jonathan Beck, All Rights Reserved.
* Copyright (c) 2008-2010 Jonathan Beck, All Rights Reserved.
*
*
* This library is free software; you can redistribute it and/or
* This library is free software; you can redistribute it and/or
...
@@ -28,7 +28,6 @@
...
@@ -28,7 +28,6 @@
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <libxml/encoding.h>
#include <ctype.h>
#include <ctype.h>
#include <plist/plist.h>
#include <plist/plist.h>
...
...
src/bytearray.c
View file @
392135c7
...
@@ -21,12 +21,14 @@
...
@@ -21,12 +21,14 @@
#include <string.h>
#include <string.h>
#include "bytearray.h"
#include "bytearray.h"
#define PAGE_SIZE 4096
bytearray_t
*
byte_array_new
()
bytearray_t
*
byte_array_new
()
{
{
bytearray_t
*
a
=
(
bytearray_t
*
)
malloc
(
sizeof
(
bytearray_t
));
bytearray_t
*
a
=
(
bytearray_t
*
)
malloc
(
sizeof
(
bytearray_t
));
a
->
data
=
malloc
(
256
);
a
->
capacity
=
PAGE_SIZE
*
8
;
a
->
data
=
malloc
(
a
->
capacity
);
a
->
len
=
0
;
a
->
len
=
0
;
a
->
capacity
=
256
;
return
a
;
return
a
;
}
}
...
@@ -44,8 +46,10 @@ void byte_array_append(bytearray_t *ba, void *buf, size_t len)
...
@@ -44,8 +46,10 @@ void byte_array_append(bytearray_t *ba, void *buf, size_t len)
if
(
!
ba
||
!
ba
->
data
||
(
len
<=
0
))
return
;
if
(
!
ba
||
!
ba
->
data
||
(
len
<=
0
))
return
;
size_t
remaining
=
ba
->
capacity
-
ba
->
len
;
size_t
remaining
=
ba
->
capacity
-
ba
->
len
;
if
(
len
>
remaining
)
{
if
(
len
>
remaining
)
{
ba
->
data
=
realloc
(
ba
->
data
,
ba
->
capacity
+
(
len
-
remaining
));
size_t
needed
=
len
-
remaining
;
ba
->
capacity
+=
(
len
-
remaining
);
size_t
increase
=
(
needed
>
PAGE_SIZE
)
?
(
needed
+
(
PAGE_SIZE
-
1
))
&
(
~
(
PAGE_SIZE
-
1
))
:
PAGE_SIZE
;
ba
->
data
=
realloc
(
ba
->
data
,
ba
->
capacity
+
increase
);
ba
->
capacity
+=
increase
;
}
}
memcpy
(((
char
*
)
ba
->
data
)
+
ba
->
len
,
buf
,
len
);
memcpy
(((
char
*
)
ba
->
data
)
+
ba
->
len
,
buf
,
len
);
ba
->
len
+=
len
;
ba
->
len
+=
len
;
...
...
src/plist.c
View file @
392135c7
...
@@ -2,8 +2,8 @@
...
@@ -2,8 +2,8 @@
* plist.c
* plist.c
* Builds plist XML structures
* Builds plist XML structures
*
*
* Copyright (c) 2009-2016 Nikias Bassen All Rights Reserved.
* Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
* Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
* Copyright (c) 2009-2014 Nikias Bassen All Rights Reserved.
* Copyright (c) 2008 Zach C. All Rights Reserved.
* Copyright (c) 2008 Zach C. All Rights Reserved.
*
*
* This library is free software; you can redistribute it and/or
* This library is free software; you can redistribute it and/or
...
@@ -29,27 +29,83 @@
...
@@ -29,27 +29,83 @@
#include <stdio.h>
#include <stdio.h>
#include <math.h>
#include <math.h>
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <node.h>
#include <node.h>
#include <node_iterator.h>
#include <node_iterator.h>
#include <libxml/encoding.h>
extern
void
plist_xml_init
(
void
);
#include <libxml/dict.h>
extern
void
plist_xml_deinit
(
void
);
#include <libxml/xmlerror.h>
#include <libxml/globals.h>
static
void
internal_plist_init
(
void
)
#include <libxml/threads.h>
{
#include <libxml/xmlmemory.h>
plist_xml_init
();
}
static
void
internal_plist_deinit
(
void
)
{
plist_xml_deinit
();
}
#ifdef WIN32
typedef
volatile
struct
{
LONG
lock
;
int
state
;
}
thread_once_t
;
static
thread_once_t
init_once
=
{
0
,
0
};
static
thread_once_t
deinit_once
=
{
0
,
0
};
void
thread_once
(
thread_once_t
*
once_control
,
void
(
*
init_routine
)(
void
))
{
while
(
InterlockedExchange
(
&
(
once_control
->
lock
),
1
)
!=
0
)
{
Sleep
(
1
);
}
if
(
!
once_control
->
state
)
{
once_control
->
state
=
1
;
init_routine
();
}
InterlockedExchange
(
&
(
once_control
->
lock
),
0
);
}
BOOL
WINAPI
DllMain
(
HINSTANCE
hModule
,
DWORD
dwReason
,
LPVOID
lpReserved
)
{
switch
(
dwReason
)
{
case
DLL_PROCESS_ATTACH
:
thread_once
(
&
init_once
,
internal_plist_init
);
break
;
case
DLL_PROCESS_DETACH
:
thread_once
(
&
deinit_once
,
internal_plist_deinit
);
break
;
default:
break
;
}
return
1
;
}
PLIST_API
void
plist_cleanup
(
void
)
#else
static
pthread_once_t
init_once
=
PTHREAD_ONCE_INIT
;
static
pthread_once_t
deinit_once
=
PTHREAD_ONCE_INIT
;
static
void
__attribute__
((
constructor
))
libplist_initialize
(
void
)
{
{
/* free memory from parser initialization */
pthread_once
(
&
init_once
,
internal_plist_init
);
xmlCleanupCharEncodingHandlers
();
xmlDictCleanup
();
xmlResetLastError
();
xmlCleanupGlobals
();
xmlCleanupThreads
();
xmlCleanupMemory
();
}
}
static
void
__attribute__
((
destructor
))
libplist_deinitialize
(
void
)
{
pthread_once
(
&
deinit_once
,
internal_plist_deinit
);
}
#endif
PLIST_API
int
plist_is_binary
(
const
char
*
plist_data
,
uint32_t
length
)
PLIST_API
int
plist_is_binary
(
const
char
*
plist_data
,
uint32_t
length
)
{
{
if
(
length
<
8
)
{
if
(
length
<
8
)
{
...
...
src/strbuf.h
0 → 100644
View file @
392135c7
/*
* strbuf.h
* header file for simple string buffer, using the bytearray as underlying
* structure.
*
* Copyright (c) 2016 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STRBUF_H
#define STRBUF_H
#include <stdlib.h>
#include "bytearray.h"
typedef
struct
bytearray_t
strbuf_t
;
#define str_buf_new() byte_array_new()
#define str_buf_free(__ba) byte_array_free(__ba)
#define str_buf_append(__ba, __str, __len) byte_array_append(__ba, (void*)(__str), __len)
#endif
src/xplist.c
View file @
392135c7
...
@@ -2,8 +2,8 @@
...
@@ -2,8 +2,8 @@
* xplist.c
* xplist.c
* XML plist implementation
* XML plist implementation
*
*
* Copyright (c) 2010-2016 Nikias Bassen All Rights Reserved.
* Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
* Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
* Copyright (c) 2010-2014 Nikias Bassen All Rights Reserved.
* Copyright (c) 2008 Jonathan Beck All Rights Reserved.
* Copyright (c) 2008 Jonathan Beck All Rights Reserved.
*
*
* This library is free software; you can redistribute it and/or
* This library is free software; you can redistribute it and/or
...
@@ -21,6 +21,9 @@
...
@@ -21,6 +21,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <string.h>
#include <assert.h>
#include <assert.h>
...
@@ -31,121 +34,55 @@
...
@@ -31,121 +34,55 @@
#include <inttypes.h>
#include <inttypes.h>
#include <math.h>
#include <math.h>
#include <libxml/xmlIO.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <node.h>
#include <node.h>
#include <node_list.h>
#include <node_list.h>
#include <node_iterator.h>
#include <node_iterator.h>
#include "plist.h"
#include "plist.h"
#include "base64.h"
#include "base64.h"
#include "strbuf.h"
#include "time64.h"
#include "time64.h"
#define XPLIST_TEXT
BAD_CAST("text")
#define XPLIST_TEXT
"text"
#define XPLIST_KEY
BAD_CAST("key")
#define XPLIST_KEY
"key"
#define XPLIST_FALSE
BAD_CAST("false")
#define XPLIST_FALSE
"false"
#define XPLIST_TRUE
BAD_CAST("true")
#define XPLIST_TRUE
"true"
#define XPLIST_INT
BAD_CAST("integer")
#define XPLIST_INT
"integer"
#define XPLIST_REAL
BAD_CAST("real")
#define XPLIST_REAL
"real"
#define XPLIST_DATE
BAD_CAST("date")
#define XPLIST_DATE
"date"
#define XPLIST_DATA
BAD_CAST("data")
#define XPLIST_DATA
"data"
#define XPLIST_STRING
BAD_CAST("string")
#define XPLIST_STRING
"string"
#define XPLIST_ARRAY
BAD_CAST("array")
#define XPLIST_ARRAY
"array"
#define XPLIST_DICT
BAD_CAST("dict")
#define XPLIST_DICT
"dict"
#define MAC_EPOCH 978307200
#define MAC_EPOCH 978307200
static
const
char
*
plist_base
=
"<?xml version=
\"
1.0
\"
encoding=
\"
UTF-8
\"
?>
\n
\
static
const
char
XML_PLIST_PROLOG
[]
=
"<?xml version=
\"
1.0
\"
encoding=
\"
UTF-8
\"
?>
\n
\
<!DOCTYPE plist PUBLIC
\"
-//Apple//DTD PLIST 1.0//EN
\"
\"
http://www.apple.com/DTDs/PropertyList-1.0.dtd
\"
>
\n
\
<!DOCTYPE plist PUBLIC
\"
-//Apple//DTD PLIST 1.0//EN
\"
\"
http://www.apple.com/DTDs/PropertyList-1.0.dtd
\"
>
\n
\
<plist version=
\"
1.0
\"
>
\n
\
<plist version=
\"
1.0
\"
>
\n
"
;
</plist>
\0
"
;
static
const
char
XML_PLIST_EPILOG
[]
=
"</plist>
\n
"
;
#ifdef DEBUG
static
int
plist_xml_debug
=
0
;
#define PLIST_XML_ERR(...) if (plist_xml_debug) { fprintf(stderr, "libplist[xmlparser] ERROR: " __VA_ARGS__); }
#else
#define PLIST_XML_ERR(...)
#endif
/** Formats a block of text to be a given indentation and width.
void
plist_xml_init
(
void
)
*
* The total width of the return string will be depth + cols.
*
* @param buf The string to format.
* @param cols The number of text columns for returned block of text.
* @param depth The number of tabs to indent the returned block of text.
*
* @return The formatted string.
*/
static
char
*
format_string
(
const
char
*
buf
,
size_t
len
,
int
cols
,
int
depth
)
{
{
if
(
!
buf
||
!
(
len
>
0
))
return
NULL
;
/* init XML stuff */
int
colw
=
depth
+
cols
+
1
;
#ifdef DEBUG
int
nlines
=
len
/
cols
+
1
;
char
*
env_debug
=
getenv
(
"PLIST_XML_DEBUG"
);
char
*
new_buf
=
NULL
;
if
(
env_debug
&&
!
strcmp
(
env_debug
,
"1"
))
{
int
i
=
0
;
plist_xml_debug
=
1
;
int
j
=
0
;
assert
(
cols
>=
0
);
assert
(
depth
>=
0
);
new_buf
=
(
char
*
)
malloc
(
nlines
*
colw
+
depth
+
1
);
assert
(
new_buf
!=
0
);
memset
(
new_buf
,
0
,
nlines
*
colw
+
depth
+
1
);
// Inserts new lines and tabs at appropriate locations
for
(
i
=
0
;
i
<
nlines
;
i
++
)
{
new_buf
[
i
*
colw
]
=
'\n'
;
for
(
j
=
0
;
j
<
depth
;
j
++
)
new_buf
[
i
*
colw
+
1
+
j
]
=
'\t'
;
memcpy
(
new_buf
+
i
*
colw
+
1
+
depth
,
buf
+
i
*
cols
,
(
size_t
)(
i
+
1
)
*
cols
<=
len
?
(
size_t
)
cols
:
len
-
i
*
cols
);
}
}
new_buf
[
len
+
(
1
+
depth
)
*
nlines
]
=
'\n'
;
#endif
// Inserts final row of indentation and termination character
for
(
j
=
0
;
j
<
depth
;
j
++
)
new_buf
[
len
+
(
1
+
depth
)
*
nlines
+
1
+
j
]
=
'\t'
;
new_buf
[
len
+
(
1
+
depth
)
*
nlines
+
depth
+
1
]
=
'\0'
;
return
new_buf
;
}
struct
xml_node
{
xmlNodePtr
xml
;
uint32_t
depth
;
};
/** Creates a new plist XML document.
*
* @return The plist XML document.
*/
static
xmlDocPtr
new_xml_plist
(
void
)
{
char
*
plist
=
strdup
(
plist_base
);
xmlDocPtr
plist_xml
=
xmlParseMemory
(
plist
,
strlen
(
plist
));
free
(
plist
);
return
plist_xml
;
}
static
struct
node_t
*
new_key_node
(
const
char
*
name
)
{
plist_data_t
data
=
plist_new_plist_data
();
data
->
type
=
PLIST_KEY
;
int
size
=
strlen
(
name
);
data
->
strval
=
strdup
(
name
);
data
->
length
=
size
;
return
node_create
(
NULL
,
data
);
}
}
static
struct
node_t
*
new_uint_node
(
uint64_t
value
)
void
plist_xml_deinit
(
void
)
{
{
plist_data_t
data
=
plist_new_plist_data
();
/* deinit XML stuff */
data
->
type
=
PLIST_UINT
;
data
->
intval
=
value
;
data
->
length
=
sizeof
(
uint64_t
);
return
node_create
(
NULL
,
data
);
}
}
static
void
dtostr
(
char
*
buf
,
size_t
bufsize
,
double
realval
)
static
void
dtostr
(
char
*
buf
,
size_t
bufsize
,
double
realval
)
...
@@ -178,27 +115,21 @@ static void dtostr(char *buf, size_t bufsize, double realval)
...
@@ -178,27 +115,21 @@ static void dtostr(char *buf, size_t bufsize, double realval)
buf
[
p
]
=
'\0'
;
buf
[
p
]
=
'\0'
;
}
}
static
void
node_to_xml
(
node_t
*
node
,
void
*
xml_struct
)
static
void
node_to_xml
(
node_t
*
node
,
bytearray_t
**
outbuf
,
uint32_t
depth
)
{
{
struct
xml_node
*
xstruct
=
NULL
;
plist_data_t
node_data
=
NULL
;
plist_data_t
node_data
=
NULL
;
xmlNodePtr
child_node
=
NULL
;
char
isStruct
=
FALSE
;
char
isStruct
=
FALSE
;
char
isUIDNode
=
FALSE
;
char
tagOpen
=
FALSE
;
const
xmlC
har
*
tag
=
NULL
;
const
c
har
*
tag
=
NULL
;
char
*
val
=
NULL
;
char
*
val
=
NULL
;
//for base64
char
*
valtmp
=
NULL
;
uint32_t
i
=
0
;
uint32_t
i
=
0
;
if
(
!
node
)
if
(
!
node
)
return
;
return
;
xstruct
=
(
struct
xml_node
*
)
xml_struct
;
node_data
=
plist_get_data
(
node
);
node_data
=
plist_get_data
(
node
);
switch
(
node_data
->
type
)
switch
(
node_data
->
type
)
...
@@ -230,23 +161,17 @@ static void node_to_xml(node_t* node, void *xml_struct)
...
@@ -230,23 +161,17 @@ static void node_to_xml(node_t* node, void *xml_struct)
case
PLIST_STRING
:
case
PLIST_STRING
:
tag
=
XPLIST_STRING
;
tag
=
XPLIST_STRING
;
val
=
strdup
((
char
*
)
node_data
->
strval
);
/* contents processed directly below */
break
;
break
;
case
PLIST_KEY
:
case
PLIST_KEY
:
tag
=
XPLIST_KEY
;
tag
=
XPLIST_KEY
;
val
=
strdup
((
char
*
)
node_data
->
strval
);
/* contents processed directly below */
break
;
break
;
case
PLIST_DATA
:
case
PLIST_DATA
:
tag
=
XPLIST_DATA
;
tag
=
XPLIST_DATA
;
if
(
node_data
->
length
)
/* contents processed directly below */
{
size_t
len
=
node_data
->
length
;
valtmp
=
base64encode
(
node_data
->
buff
,
&
len
);
val
=
format_string
(
valtmp
,
len
,
68
,
xstruct
->
depth
);
free
(
valtmp
);
}
break
;
break
;
case
PLIST_ARRAY
:
case
PLIST_ARRAY
:
tag
=
XPLIST_ARRAY
;
tag
=
XPLIST_ARRAY
;
...
@@ -275,77 +200,161 @@ static void node_to_xml(node_t* node, void *xml_struct)
...
@@ -275,77 +200,161 @@ static void node_to_xml(node_t* node, void *xml_struct)
}
}
break
;
break
;
case
PLIST_UID
:
case
PLIST_UID
:
// special case for keyed encoding
tag
=
XPLIST_DICT
;
tag
=
XPLIST_DICT
;
isStruct
=
TRUE
;
val
=
(
char
*
)
malloc
(
64
);
isUIDNode
=
TRUE
;
if
(
node_data
->
length
==
16
)
{
node_data
->
type
=
PLIST_DICT
;
(
void
)
snprintf
(
val
,
64
,
"%"
PRIu64
,
node_data
->
intval
);
node_attach
(
node
,
new_key_node
(
"CF$UID"
));
}
else
{
node_attach
(
node
,
new_uint_node
(
node_data
->
intval
));
(
void
)
snprintf
(
val
,
64
,
"%"
PRIi64
,
node_data
->
intval
);
}
break
;
break
;
default:
default:
break
;
break
;
}
}
for
(
i
=
0
;
i
<
xstruct
->
depth
;
i
++
)
for
(
i
=
0
;
i
<
depth
;
i
++
)
{
{
str_buf_append
(
*
outbuf
,
"
\t
"
,
1
);
xmlNodeAddContent
(
xstruct
->
xml
,
BAD_CAST
(
"
\t
"
));
}
}
/* append tag */
str_buf_append
(
*
outbuf
,
"<"
,
1
);
str_buf_append
(
*
outbuf
,
tag
,
strlen
(
tag
));
if
(
node_data
->
type
==
PLIST_STRING
||
node_data
->
type
==
PLIST_KEY
)
{
if
(
node_data
->
type
==
PLIST_STRING
||
node_data
->
type
==
PLIST_KEY
)
{
size_t
j
;
size_t
len
;
off_t
start
=
0
;
off_t
cur
=
0
;
str_buf_append
(
*
outbuf
,
">"
,
1
);
tagOpen
=
TRUE
;
/* make sure we convert the following predefined xml entities */
/* make sure we convert the following predefined xml entities */
/* < = < > = > ' = ' " = " & = & */
/* < = < > = > ' = ' " = " & = & */
child_node
=
xmlNewTextChild
(
xstruct
->
xml
,
NULL
,
tag
,
BAD_CAST
(
val
));
len
=
strlen
(
node_data
->
strval
);
}
else
for
(
j
=
0
;
j
<
len
;
j
++
)
{
child_node
=
xmlNewChild
(
xstruct
->
xml
,
NULL
,
tag
,
BAD_CAST
(
val
));
switch
(
node_data
->
strval
[
j
])
{
xmlNodeAddContent
(
xstruct
->
xml
,
BAD_CAST
(
"
\n
"
));
case
'<'
:
if
(
val
)
{
str_buf_append
(
*
outbuf
,
node_data
->
strval
+
start
,
cur
-
start
);
free
(
val
);
str_buf_append
(
*
outbuf
,
"<"
,
4
);
}
start
=
cur
+
1
;
break
;
case
'>'
:
str_buf_append
(
*
outbuf
,
node_data
->
strval
+
start
,
cur
-
start
);
str_buf_append
(
*
outbuf
,
">"
,
4
);
start
=
cur
+
1
;
break
;
case
'\''
:
str_buf_append
(
*
outbuf
,
node_data
->
strval
+
start
,
cur
-
start
);
str_buf_append
(
*
outbuf
,
"'"
,
6
);
start
=
cur
+
1
;
break
;
case
'"'
:
str_buf_append
(
*
outbuf
,
node_data
->
strval
+
start
,
cur
-
start
);
str_buf_append
(
*
outbuf
,
"""
,
6
);
start
=
cur
+
1
;
break
;
case
'&'
:
str_buf_append
(
*
outbuf
,
node_data
->
strval
+
start
,
cur
-
start
);
str_buf_append
(
*
outbuf
,
"&"
,
5
);
start
=
cur
+
1
;
break
;
default:
break
;
}
cur
++
;
}
str_buf_append
(
*
outbuf
,
node_data
->
strval
+
start
,
cur
-
start
);
}
else
if
(
node_data
->
type
==
PLIST_DATA
)
{
str_buf_append
(
*
outbuf
,
">"
,
1
);
tagOpen
=
TRUE
;
str_buf_append
(
*
outbuf
,
"
\n
"
,
1
);
if
(
node_data
->
length
>
0
)
{
char
*
buf
=
malloc
(
80
);
uint32_t
j
=
0
;
uint32_t
indent
=
(
depth
>
8
)
?
8
:
depth
;
uint32_t
maxread
=
((
76
-
indent
*
8
)
/
4
)
*
3
;
size_t
count
=
0
;
size_t
b64count
=
0
;
while
(
j
<
node_data
->
length
)
{
for
(
i
=
0
;
i
<
indent
;
i
++
)
{
str_buf_append
(
*
outbuf
,
"
\t
"
,
1
);
}
count
=
(
node_data
->
length
-
j
<
maxread
)
?
node_data
->
length
-
j
:
maxread
;
b64count
=
base64encode
(
buf
,
node_data
->
buff
+
j
,
count
);
str_buf_append
(
*
outbuf
,
buf
,
b64count
);
str_buf_append
(
*
outbuf
,
"
\n
"
,
1
);
j
+=
count
;
}
free
(
buf
);
}
for
(
i
=
0
;
i
<
depth
;
i
++
)
{
str_buf_append
(
*
outbuf
,
"
\t
"
,
1
);
}
}
else
if
(
node_data
->
type
==
PLIST_UID
)
{
/* special case for UID nodes: create a DICT */
str_buf_append
(
*
outbuf
,
">"
,
1
);
tagOpen
=
TRUE
;
str_buf_append
(
*
outbuf
,
"
\n
"
,
1
);
/* add CF$UID key */
for
(
i
=
0
;
i
<
depth
+
1
;
i
++
)
{
str_buf_append
(
*
outbuf
,
"
\t
"
,
1
);
}
str_buf_append
(
*
outbuf
,
"<key>CF$UID</key>"
,
17
);
str_buf_append
(
*
outbuf
,
"
\n
"
,
1
);
//add return for structured types
/* add UID value */
if
(
node_data
->
type
==
PLIST_ARRAY
||
node_data
->
type
==
PLIST_DICT
)
for
(
i
=
0
;
i
<
depth
+
1
;
i
++
)
{
xmlNodeAddContent
(
child_node
,
BAD_CAST
(
"
\n
"
));
str_buf_append
(
*
outbuf
,
"
\t
"
,
1
);
}
str_buf_append
(
*
outbuf
,
"<integer>"
,
9
);
str_buf_append
(
*
outbuf
,
val
,
strlen
(
val
));
str_buf_append
(
*
outbuf
,
"</integer>"
,
10
);
str_buf_append
(
*
outbuf
,
"
\n
"
,
1
);
//make sure we don't produce <data/> if it's empty
for
(
i
=
0
;
i
<
depth
;
i
++
)
{
if
((
node_data
->
type
==
PLIST_DATA
)
&&
!
val
)
{
str_buf_append
(
*
outbuf
,
"
\t
"
,
1
);
xmlNodeAddContent
(
child_node
,
BAD_CAST
(
"
\n
"
));
for
(
i
=
0
;
i
<
xstruct
->
depth
;
i
++
)
{
xmlNodeAddContent
(
child_node
,
BAD_CAST
(
"
\t
"
));
}
}
}
else
if
(
val
)
{
str_buf_append
(
*
outbuf
,
">"
,
1
);
tagOpen
=
TRUE
;
str_buf_append
(
*
outbuf
,
val
,
strlen
(
val
));
}
else
if
(
isStruct
)
{
tagOpen
=
TRUE
;
str_buf_append
(
*
outbuf
,
">"
,
1
);
}
else
{
tagOpen
=
FALSE
;
str_buf_append
(
*
outbuf
,
"/>"
,
2
);
}
}
free
(
val
);
if
(
isStruct
)
/* add return for structured types */
{
if
(
node_data
->
type
==
PLIST_ARRAY
||
node_data
->
type
==
PLIST_DICT
)
struct
xml_node
child
=
{
child_node
,
xstruct
->
depth
+
1
};
str_buf_append
(
*
outbuf
,
"
\n
"
,
1
);
if
(
isStruct
)
{
node_iterator_t
*
ni
=
node_iterator_create
(
node
->
children
);
node_iterator_t
*
ni
=
node_iterator_create
(
node
->
children
);
node_t
*
ch
;
node_t
*
ch
;
while
((
ch
=
node_iterator_next
(
ni
)))
{
while
((
ch
=
node_iterator_next
(
ni
)))
{
node_to_xml
(
ch
,
&
child
);
node_to_xml
(
ch
,
outbuf
,
depth
+
1
);
}
}
node_iterator_destroy
(
ni
);
node_iterator_destroy
(
ni
);
}
}
//fix indent for structured types
if
(
node_data
->
type
==
PLIST_ARRAY
||
node_data
->
type
==
PLIST_DICT
)
{
for
(
i
=
0
;
i
<
xstruct
->
depth
;
i
++
)
/* fix indent for structured types */
{
if
(
node_data
->
type
==
PLIST_ARRAY
||
node_data
->
type
==
PLIST_DICT
)
{
xmlNodeAddContent
(
child_node
,
BAD_CAST
(
"
\t
"
));
for
(
i
=
0
;
i
<
depth
;
i
++
)
{
str_buf_append
(
*
outbuf
,
"
\t
"
,
1
);
}
}
}
}
if
(
isUIDNode
)
{
if
(
tagOpen
)
{
unsigned
int
num
=
node_n_children
(
node
);
/* add closing tag */
unsigned
int
j
;
str_buf_append
(
*
outbuf
,
"</"
,
2
);
for
(
j
=
num
;
j
>
0
;
j
--
)
{
str_buf_append
(
*
outbuf
,
tag
,
strlen
(
tag
));
node_t
*
ch
=
node_nth_child
(
node
,
j
-
1
);
str_buf_append
(
*
outbuf
,
">"
,
1
);
node_detach
(
node
,
ch
);
plist_free_data
((
plist_data_t
)((
node_t
*
)
ch
)
->
data
);
node_destroy
(
ch
);
}
node_data
->
type
=
PLIST_UID
;
}
}
str_buf_append
(
*
outbuf
,
"
\n
"
,
1
);
return
;
return
;
}
}
...
@@ -370,233 +379,526 @@ static void parse_date(const char *strval, struct TM *btime)
...
@@ -370,233 +379,526 @@ static void parse_date(const char *strval, struct TM *btime)
btime
->
tm_isdst
=
0
;
btime
->
tm_isdst
=
0
;
}
}
static
void
xml_to_node
(
xmlNodePtr
xml_node
,
plist_t
*
plist_node
)
PLIST_API
void
plist_to_xml
(
plist_t
plist
,
char
**
plist_xml
,
uint32_t
*
length
)
{
{
xmlNodePtr
node
=
NULL
;
strbuf_t
*
outbuf
=
str_buf_new
();
plist_data_t
data
=
NULL
;
plist_t
subnode
=
NULL
;
//for string
str_buf_append
(
outbuf
,
XML_PLIST_PROLOG
,
sizeof
(
XML_PLIST_PROLOG
)
-
1
);
long
len
=
0
;
int
type
=
0
;
if
(
!
xml_node
)
node_to_xml
(
plist
,
&
outbuf
,
0
);
return
;
for
(
node
=
xml_node
->
children
;
node
;
node
=
node
->
next
)
str_buf_append
(
outbuf
,
XML_PLIST_EPILOG
,
sizeof
(
XML_PLIST_EPILOG
));
{
while
(
node
&&
!
xmlStrcmp
(
node
->
name
,
XPLIST_TEXT
))
node
=
node
->
next
;
if
(
!
node
)
break
;
if
(
!
xmlStrcmp
(
node
->
name
,
BAD_CAST
(
"comment"
)))
{
*
plist_xml
=
outbuf
->
data
;
continue
;
*
length
=
outbuf
->
len
-
1
;
}
data
=
plist_new_plist_data
();
outbuf
->
data
=
NULL
;
subnode
=
plist_new_node
(
data
);
str_buf_free
(
outbuf
);
if
(
*
plist_node
)
}
node_attach
(
*
plist_node
,
subnode
);
else
*
plist_node
=
subnode
;
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_TRUE
))
struct
_parse_ctx
{
{
const
char
*
pos
;
data
->
boolval
=
TRUE
;
const
char
*
end
;
data
->
type
=
PLIST_BOOLEAN
;
int
err
;
data
->
length
=
1
;
};
continue
;
typedef
struct
_parse_ctx
*
parse_ctx
;
}
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_FALSE
))
static
void
parse_skip_ws
(
parse_ctx
ctx
)
{
{
data
->
boolval
=
FALSE
;
while
(
ctx
->
pos
<
ctx
->
end
&&
((
*
(
ctx
->
pos
)
==
' '
)
||
(
*
(
ctx
->
pos
)
==
'\t'
)
||
(
*
(
ctx
->
pos
)
==
'\r'
)
||
(
*
(
ctx
->
pos
)
==
'\n'
)))
{
data
->
type
=
PLIST_BOOLEAN
;
ctx
->
pos
++
;
data
->
length
=
1
;
}
continue
;
}
}
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_INT
))
static
void
find_char
(
parse_ctx
ctx
,
char
c
,
int
skip_quotes
)
{
{
xmlChar
*
strval
=
xmlNodeGetContent
(
node
);
while
(
ctx
->
pos
<
ctx
->
end
&&
(
*
(
ctx
->
pos
)
!=
c
))
{
int
is_negative
=
0
;
if
(
skip_quotes
&&
(
c
!=
'"'
)
&&
(
*
(
ctx
->
pos
)
==
'"'
))
{
char
*
str
=
(
char
*
)
strval
;
ctx
->
pos
++
;
if
((
str
[
0
]
==
'-'
)
||
(
str
[
0
]
==
'+'
))
{
find_char
(
ctx
,
'"'
,
0
);
if
(
str
[
0
]
==
'-'
)
{
if
(
*
(
ctx
->
pos
)
!=
'"'
)
{
is_negative
=
1
;
PLIST_XML_ERR
(
"Unmatched double quote
\n
"
);
}
return
;
str
++
;
}
char
*
endp
=
NULL
;
data
->
intval
=
strtoull
((
char
*
)
str
,
&
endp
,
0
);
if
((
endp
!=
NULL
)
&&
(
strlen
(
endp
)
>
0
))
{
fprintf
(
stderr
,
"%s: integer parse error: string contains invalid characters: '%s'
\n
"
,
__func__
,
endp
);
}
if
(
is_negative
||
(
data
->
intval
<=
INT64_MAX
))
{
int64_t
v
=
data
->
intval
;
if
(
is_negative
)
{
v
=
-
v
;
}
data
->
intval
=
(
uint64_t
)
v
;
data
->
length
=
8
;
}
else
{
data
->
length
=
16
;
}
}
data
->
type
=
PLIST_UINT
;
xmlFree
(
strval
);
continue
;
}
}
ctx
->
pos
++
;
}
}
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_REAL
))
static
void
find_str
(
parse_ctx
ctx
,
const
char
*
str
,
int
skip_quotes
)
{
{
xmlChar
*
strval
=
xmlNodeGetContent
(
node
);
size_t
len
=
strlen
(
str
);
data
->
realval
=
atof
((
char
*
)
strval
);
while
(
ctx
->
pos
<
(
ctx
->
end
-
len
))
{
data
->
type
=
PLIST_REAL
;
if
(
!
strncmp
(
ctx
->
pos
,
str
,
len
))
{
data
->
length
=
8
;
break
;
xmlFree
(
strval
);
continue
;
}
}
if
(
skip_quotes
&&
(
*
(
ctx
->
pos
)
==
'"'
))
{
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_DATE
))
ctx
->
pos
++
;
{
find_char
(
ctx
,
'"'
,
0
);
xmlChar
*
strval
=
xmlNodeGetContent
(
node
);
if
(
*
(
ctx
->
pos
)
!=
'"'
)
{
Time64_T
timev
=
0
;
PLIST_XML_ERR
(
"Unmatched double quote
\n
"
);
if
(
strlen
((
const
char
*
)
strval
)
>=
11
)
{
return
;
struct
TM
btime
;
parse_date
((
const
char
*
)
strval
,
&
btime
);
timev
=
timegm64
(
&
btime
);
}
}
data
->
realval
=
(
double
)(
timev
-
MAC_EPOCH
);
data
->
type
=
PLIST_DATE
;
data
->
length
=
sizeof
(
double
);
xmlFree
(
strval
);
continue
;
}
}
ctx
->
pos
++
;
}
}
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_STRING
)
)
static
void
find_next
(
parse_ctx
ctx
,
const
char
*
nextchars
,
int
skip_quotes
)
{
{
xmlChar
*
strval
=
xmlNodeGetContent
(
node
);
int
numchars
=
strlen
(
nextchars
);
len
=
strlen
((
char
*
)
strval
)
;
int
i
=
0
;
type
=
xmlDetectCharEncoding
(
strval
,
len
);
while
(
ctx
->
pos
<
ctx
->
end
)
{
if
(
skip_quotes
&&
(
*
(
ctx
->
pos
)
==
'"'
))
{
if
(
XML_CHAR_ENCODING_UTF8
==
type
||
XML_CHAR_ENCODING_ASCII
==
type
||
XML_CHAR_ENCODING_NONE
==
type
)
ctx
->
pos
++
;
{
find_char
(
ctx
,
'"'
,
0
);
data
->
strval
=
strdup
((
char
*
)
strval
);
if
(
*
(
ctx
->
pos
)
!=
'"'
)
{
data
->
type
=
PLIST_STRING
;
PLIST_XML_ERR
(
"Unmatched double quote
\n
"
)
;
data
->
length
=
strlen
(
data
->
strval
)
;
return
;
}
}
xmlFree
(
strval
);
continue
;
}
}
for
(
i
=
0
;
i
<
numchars
;
i
++
)
{
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_KEY
))
if
(
*
(
ctx
->
pos
)
==
nextchars
[
i
])
{
{
return
;
xmlChar
*
strval
=
xmlNodeGetContent
(
node
);
len
=
strlen
((
char
*
)
strval
);
type
=
xmlDetectCharEncoding
(
strval
,
len
);
if
(
XML_CHAR_ENCODING_UTF8
==
type
||
XML_CHAR_ENCODING_ASCII
==
type
||
XML_CHAR_ENCODING_NONE
==
type
)
{
data
->
strval
=
strdup
((
char
*
)
strval
);
data
->
type
=
PLIST_KEY
;
data
->
length
=
strlen
(
data
->
strval
);
}
}
xmlFree
(
strval
);
continue
;
}
}
ctx
->
pos
++
;
}
}
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_DATA
))
static
char
*
get_text_content
(
parse_ctx
ctx
,
const
char
*
tag
,
int
skip_ws
,
int
unescape_entities
)
{
{
xmlChar
*
strval
=
xmlNodeGetContent
(
node
);
const
char
*
p
;
size_t
size
=
0
;
const
char
*
q
;
unsigned
char
*
dec
=
base64decode
((
char
*
)
strval
,
&
size
);
int
taglen
;
data
->
buff
=
(
uint8_t
*
)
malloc
(
size
*
sizeof
(
uint8_t
));
char
*
str
;
memcpy
(
data
->
buff
,
dec
,
size
*
sizeof
(
uint8_t
));
int
i
=
0
;
free
(
dec
);
data
->
length
=
size
;
data
->
type
=
PLIST_DATA
;
xmlFree
(
strval
);
continue
;
}
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_ARRAY
))
if
(
skip_ws
)
{
{
parse_skip_ws
(
ctx
);
data
->
type
=
PLIST_ARRAY
;
}
xml_to_node
(
node
,
&
subnode
);
p
=
ctx
->
pos
;
continue
;
find_char
(
ctx
,
'<'
,
1
);
}
if
(
*
ctx
->
pos
!=
'<'
)
{
PLIST_XML_ERR
(
"didn't find <
\n
"
);
return
NULL
;
}
q
=
ctx
->
pos
;
ctx
->
pos
++
;
if
(
ctx
->
pos
>=
ctx
->
end
||
*
ctx
->
pos
!=
'/'
)
{
PLIST_XML_ERR
(
"EOF or empty tag while parsing '%s'
\n
"
,
p
);
return
NULL
;
}
ctx
->
pos
++
;
taglen
=
strlen
(
tag
);
if
(
ctx
->
pos
>=
ctx
->
end
-
taglen
||
strncmp
(
ctx
->
pos
,
tag
,
taglen
))
{
PLIST_XML_ERR
(
"EOF or end tag mismatch
\n
"
);
return
NULL
;}
ctx
->
pos
+=
taglen
;
if
(
ctx
->
pos
>=
ctx
->
end
||
*
ctx
->
pos
!=
'>'
)
{
PLIST_XML_ERR
(
"EOF or no '>' after tag name
\n
"
);
return
NULL
;}
ctx
->
pos
++
;
int
len
=
q
-
p
;
if
(
len
<
0
)
{
PLIST_XML_ERR
(
"Couldn't find matching '%s' end tag
\n
"
,
tag
);
return
NULL
;
}
str
=
malloc
(
len
+
1
);
strncpy
(
str
,
p
,
len
);
str
[
len
]
=
0
;
if
(
!
xmlStrcmp
(
node
->
name
,
XPLIST_DICT
))
if
(
!
unescape_entities
)
{
{
return
str
;
data
->
type
=
PLIST_DICT
;
}
xml_to_node
(
node
,
&
subnode
);
if
(
plist_get_node_type
(
subnode
)
==
PLIST_DICT
)
{
/* unescape entities */
if
(
plist_dict_get_size
(
subnode
)
==
1
)
{
while
(
i
<
len
-
1
)
{
plist_t
uid
=
plist_dict_get_item
(
subnode
,
"CF$UID"
);
if
(
str
[
i
]
==
'&'
)
{
if
(
uid
)
{
char
*
entp
=
str
+
i
+
1
;
uint64_t
val
=
0
;
while
(
i
<
len
&&
str
[
i
]
!=
';'
)
{
plist_get_uint_val
(
uid
,
&
val
);
i
++
;
plist_dict_remove_item
(
subnode
,
"CF$UID"
);
}
plist_data_t
nodedata
=
plist_get_data
((
node_t
*
)
subnode
);
if
(
i
>=
len
)
{
free
(
nodedata
->
buff
);
break
;
nodedata
->
type
=
PLIST_UID
;
}
nodedata
->
length
=
sizeof
(
uint64_t
);
if
(
str
+
i
>
entp
+
1
)
{
nodedata
->
intval
=
val
;
int
entlen
=
str
+
i
-
entp
;
}
if
(
!
strncmp
(
entp
,
"amp"
,
3
))
{
/* the '&' is already there */
}
else
if
(
!
strncmp
(
entp
,
"apos"
,
4
))
{
*
(
entp
-
1
)
=
'\''
;
}
else
if
(
!
strncmp
(
entp
,
"quot"
,
4
))
{
*
(
entp
-
1
)
=
'"'
;
}
else
if
(
!
strncmp
(
entp
,
"lt"
,
2
))
{
*
(
entp
-
1
)
=
'<'
;
}
else
if
(
!
strncmp
(
entp
,
"gt"
,
2
))
{
*
(
entp
-
1
)
=
'>'
;
}
else
{
/* unexpected entity, replace with ? */
*
(
entp
-
1
)
=
'?'
;
}
}
memmove
(
entp
,
str
+
i
+
1
,
len
-
i
);
i
-=
entlen
;
continue
;
}
}
continue
;
}
}
i
++
;
}
}
return
str
;
}
}
PLIST_API
void
plist_to_xml
(
plist_t
plist
,
char
**
plist_xml
,
uint32_t
*
length
)
static
void
skip_text_content
(
parse_ctx
ctx
,
const
char
*
tag
)
{
{
xmlDocPtr
plist_doc
=
NULL
;
int
taglen
;
xmlNodePtr
root_node
=
NULL
;
find_char
(
ctx
,
'<'
,
1
);
struct
xml_node
root
=
{
NULL
,
0
};
if
(
*
ctx
->
pos
!=
'<'
)
return
;
int
size
=
0
;
ctx
->
pos
++
;
taglen
=
strlen
(
tag
);
if
(
ctx
->
pos
>=
ctx
->
end
-
taglen
||
strncmp
(
ctx
->
pos
,
tag
,
taglen
))
return
;
ctx
->
pos
+=
taglen
;
if
(
ctx
->
pos
>=
ctx
->
end
||
*
ctx
->
pos
!=
'>'
)
return
;
ctx
->
pos
++
;
}
if
(
!
plist
||
!
plist_xml
||
*
plist_xml
)
static
void
node_from_xml
(
parse_ctx
ctx
,
plist_t
*
plist
)
return
;
{
plist_doc
=
new_xml_plist
();
char
*
keyname
=
NULL
;
root_node
=
xmlDocGetRootElement
(
plist_doc
);
while
(
ctx
->
pos
<
ctx
->
end
&&
!
ctx
->
err
)
{
root
.
xml
=
root_node
;
#ifdef DEBUG
const
char
*
start
=
ctx
->
pos
;
#endif
parse_skip_ws
(
ctx
);
if
(
ctx
->
pos
>=
ctx
->
end
)
{
break
;
}
if
(
*
ctx
->
pos
!=
'<'
)
{
PLIST_XML_ERR
(
"Failed to parse XML. Expected: opening tag, found: '%s', pos: %s
\n
"
,
start
,
ctx
->
pos
);
ctx
->
pos
=
ctx
->
end
;
break
;
}
ctx
->
pos
++
;
if
(
ctx
->
pos
>=
ctx
->
end
)
{
break
;
}
node_to_xml
(
plist
,
&
root
);
if
(
*
(
ctx
->
pos
)
==
'?'
)
{
find_str
(
ctx
,
"?>"
,
1
);
if
(
ctx
->
pos
>=
ctx
->
end
)
{
break
;
}
if
(
strncmp
(
ctx
->
pos
,
"?>"
,
2
))
{
PLIST_XML_ERR
(
"Couldn't find <? tag closing marker
\n
"
);
ctx
->
pos
=
ctx
->
end
;
return
;
}
ctx
->
pos
+=
2
;
continue
;
}
else
if
(
*
(
ctx
->
pos
)
==
'!'
)
{
/* comment or DTD */
if
(((
ctx
->
end
-
ctx
->
pos
)
>
3
)
&&
!
strncmp
(
ctx
->
pos
,
"!--"
,
3
))
{
ctx
->
pos
+=
3
;
find_str
(
ctx
,
"-->"
,
0
);
if
(
strncmp
(
ctx
->
pos
,
"-->"
,
3
))
{
PLIST_XML_ERR
(
"Couldn't find end of comment
\n
"
);
ctx
->
pos
=
ctx
->
end
;
return
;
}
ctx
->
pos
+=
3
;
}
else
if
(((
ctx
->
end
-
ctx
->
pos
)
>
8
)
&&
!
strncmp
(
ctx
->
pos
,
"!DOCTYPE"
,
8
))
{
int
embedded_dtd
=
0
;
ctx
->
pos
+=
8
;
while
(
ctx
->
pos
<
ctx
->
end
)
{
find_next
(
ctx
,
"
\t\r\n
[>"
,
1
);
if
(
*
ctx
->
pos
==
'['
)
{
embedded_dtd
=
1
;
break
;
}
else
if
(
*
ctx
->
pos
==
'>'
)
{
/* end of DOCTYPE found already */
ctx
->
pos
++
;
break
;
}
else
{
parse_skip_ws
(
ctx
);
}
}
if
(
embedded_dtd
)
{
find_str
(
ctx
,
"]>"
,
1
);
if
(
strncmp
(
ctx
->
pos
,
"]>"
,
2
))
{
PLIST_XML_ERR
(
"Couldn't find end of DOCTYPE
\n
"
);
ctx
->
pos
=
ctx
->
end
;
return
;
}
ctx
->
pos
+=
2
;
}
}
else
{
PLIST_XML_ERR
(
"Unknown ! special tag encountered
\n
"
);
ctx
->
err
++
;
}
continue
;
}
else
{
int
is_empty
=
0
;
int
closing_tag
=
0
;
const
char
*
p
=
ctx
->
pos
;
find_next
(
ctx
,
"
\r\n\t
<>"
,
0
);
if
(
ctx
->
pos
>=
ctx
->
end
)
{
PLIST_XML_ERR
(
"Unexpected EOF while parsing XML
\n
"
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
keyname
);
return
;
}
int
taglen
=
ctx
->
pos
-
p
;
char
*
tag
=
malloc
(
taglen
+
1
);
strncpy
(
tag
,
p
,
taglen
);
tag
[
taglen
]
=
'\0'
;
if
(
*
ctx
->
pos
!=
'>'
)
{
find_next
(
ctx
,
"<>"
,
1
);
}
if
(
ctx
->
pos
>=
ctx
->
end
)
{
PLIST_XML_ERR
(
"Unexpected EOF while parsing XML
\n
"
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
tag
);
free
(
keyname
);
return
;
}
if
(
*
ctx
->
pos
!=
'>'
)
{
PLIST_XML_ERR
(
"Missing '>' for tag '%s'
\n
"
,
tag
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
tag
);
free
(
keyname
);
return
;
}
if
(
*
(
ctx
->
pos
-
1
)
==
'/'
)
{
tag
[
ctx
->
pos
-
p
-
1
]
=
'\0'
;
is_empty
=
1
;
}
ctx
->
pos
++
;
if
(
!
strcmp
(
tag
,
"plist"
))
{
free
(
tag
);
if
(
is_empty
)
{
return
;
}
if
(
!*
plist
)
{
/* only process first plist node found */
node_from_xml
(
ctx
,
plist
);
}
continue
;
}
xmlChar
*
tmp
=
NULL
;
plist_data_t
data
=
plist_new_plist_data
();
xmlDocDumpMemory
(
plist_doc
,
&
tmp
,
&
size
);
plist_t
subnode
=
plist_new_node
(
data
);
if
(
size
>=
0
&&
tmp
)
{
if
(
!
strcmp
(
tag
,
XPLIST_DICT
))
{
/* make sure to copy the terminating 0-byte */
data
->
type
=
PLIST_DICT
;
*
plist_xml
=
(
char
*
)
malloc
((
size
+
1
)
*
sizeof
(
char
));
}
else
if
(
!
strcmp
(
tag
,
XPLIST_ARRAY
))
{
memcpy
(
*
plist_xml
,
tmp
,
size
+
1
);
data
->
type
=
PLIST_ARRAY
;
*
length
=
size
;
}
else
if
(
!
strcmp
(
tag
,
XPLIST_INT
))
{
xmlFree
(
tmp
);
if
(
!
is_empty
)
{
tmp
=
NULL
;
char
*
str_content
=
get_text_content
(
ctx
,
tag
,
1
,
0
);
if
(
!
str_content
)
{
PLIST_XML_ERR
(
"Couldn't find end tag for '%s'
\n
"
,
tag
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
tag
);
free
(
keyname
);
return
;
}
char
*
str
=
str_content
;
int
is_negative
=
0
;
if
((
str
[
0
]
==
'-'
)
||
(
str
[
0
]
==
'+'
))
{
if
(
str
[
0
]
==
'-'
)
{
is_negative
=
1
;
}
str
++
;
}
char
*
endp
=
NULL
;
data
->
intval
=
strtoull
((
char
*
)
str
,
&
endp
,
0
);
if
((
endp
!=
NULL
)
&&
(
strlen
(
endp
)
>
0
))
{
PLIST_XML_ERR
(
"integer parse error: string contains invalid characters: '%s'
\n
"
,
endp
);
}
if
(
is_negative
||
(
data
->
intval
<=
INT64_MAX
))
{
int64_t
v
=
data
->
intval
;
if
(
is_negative
)
{
v
=
-
v
;
}
data
->
intval
=
(
uint64_t
)
v
;
data
->
length
=
8
;
}
else
{
data
->
length
=
16
;
}
free
(
str_content
);
}
else
{
data
->
intval
=
0
;
data
->
length
=
8
;
}
data
->
type
=
PLIST_UINT
;
}
else
if
(
!
strcmp
(
tag
,
XPLIST_REAL
))
{
if
(
!
is_empty
)
{
char
*
strval
=
get_text_content
(
ctx
,
tag
,
1
,
0
);
data
->
realval
=
atof
((
char
*
)
strval
);
free
(
strval
);
}
data
->
type
=
PLIST_REAL
;
data
->
length
=
8
;
}
else
if
(
!
strcmp
(
tag
,
XPLIST_TRUE
))
{
if
(
!
is_empty
)
{
skip_text_content
(
ctx
,
tag
);
}
data
->
type
=
PLIST_BOOLEAN
;
data
->
boolval
=
1
;
data
->
length
=
1
;
}
else
if
(
!
strcmp
(
tag
,
XPLIST_FALSE
))
{
if
(
!
is_empty
)
{
skip_text_content
(
ctx
,
tag
);
}
data
->
type
=
PLIST_BOOLEAN
;
data
->
boolval
=
0
;
data
->
length
=
1
;
}
else
if
(
!
strcmp
(
tag
,
XPLIST_STRING
)
||
!
strcmp
(
tag
,
XPLIST_KEY
))
{
if
(
!
is_empty
)
{
char
*
str
=
get_text_content
(
ctx
,
tag
,
0
,
1
);
if
(
!
str
)
{
PLIST_XML_ERR
(
"Couldn't get text content for '%s' node
\n
"
,
tag
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
tag
);
free
(
keyname
);
return
;
}
if
(
!
strcmp
(
tag
,
"key"
)
&&
!
keyname
&&
*
plist
&&
(
plist_get_node_type
(
*
plist
)
==
PLIST_DICT
))
{
keyname
=
str
;
free
(
tag
);
plist_free
(
subnode
);
subnode
=
NULL
;
ctx
->
pos
++
;
continue
;
}
else
{
data
->
strval
=
str
;
data
->
length
=
strlen
(
str
);
}
}
else
{
data
->
strval
=
strdup
(
""
);
data
->
length
=
0
;
}
data
->
type
=
PLIST_STRING
;
}
else
if
(
!
strcmp
(
tag
,
XPLIST_DATA
))
{
if
(
!
is_empty
)
{
char
*
strval
=
get_text_content
(
ctx
,
tag
,
1
,
0
);
if
(
!
strval
)
{
PLIST_XML_ERR
(
"Couldn't get text content for '%s' node
\n
"
,
tag
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
tag
);
free
(
keyname
);
return
;
}
size_t
size
=
0
;
data
->
buff
=
base64decode
((
char
*
)
strval
,
&
size
);
free
(
strval
);
data
->
length
=
size
;
}
data
->
type
=
PLIST_DATA
;
}
else
if
(
!
strcmp
(
tag
,
XPLIST_DATE
))
{
if
(
!
is_empty
)
{
char
*
strval
=
get_text_content
(
ctx
,
tag
,
1
,
0
);
if
(
!
strval
)
{
PLIST_XML_ERR
(
"Couldn't get text content for '%s' node
\n
"
,
tag
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
tag
);
free
(
keyname
);
return
;
}
Time64_T
timev
=
0
;
if
(
strlen
((
const
char
*
)
strval
)
>=
11
)
{
struct
TM
btime
;
parse_date
((
const
char
*
)
strval
,
&
btime
);
timev
=
timegm64
(
&
btime
);
}
data
->
realval
=
(
double
)(
timev
-
MAC_EPOCH
);
free
(
strval
);
}
data
->
length
=
sizeof
(
double
);
data
->
type
=
PLIST_DATE
;
}
else
if
(
tag
[
0
]
==
'/'
)
{
closing_tag
=
1
;
}
else
{
PLIST_XML_ERR
(
"Unexpected tag '<%s%s>' encountered while parsing XML
\n
"
,
tag
,
(
is_empty
)
?
"/"
:
""
);
ctx
->
pos
=
ctx
->
end
;
ctx
->
err
++
;
free
(
tag
);
free
(
keyname
);
return
;
}
free
(
tag
);
if
(
subnode
&&
!
closing_tag
)
{
/* parse sub nodes for structured types */
if
(
data
->
type
==
PLIST_DICT
||
data
->
type
==
PLIST_ARRAY
)
{
if
(
!
is_empty
)
{
/* only if not empty */
node_from_xml
(
ctx
,
&
subnode
);
if
((
data
->
type
==
PLIST_DICT
)
&&
(
plist_dict_get_size
(
subnode
)
==
1
))
{
/* convert XML CF$UID dictionaries to PLIST_UID nodes */
plist_t
uid
=
plist_dict_get_item
(
subnode
,
"CF$UID"
);
if
(
uid
)
{
uint64_t
val
=
0
;
plist_get_uint_val
(
uid
,
&
val
);
plist_dict_remove_item
(
subnode
,
"CF$UID"
);
plist_data_t
nodedata
=
plist_get_data
((
node_t
*
)
subnode
);
free
(
nodedata
->
buff
);
nodedata
->
type
=
PLIST_UID
;
nodedata
->
length
=
sizeof
(
uint64_t
);
nodedata
->
intval
=
val
;
}
}
}
}
if
(
!*
plist
)
{
/* no parent? make this node the new parent node */
*
plist
=
subnode
;
subnode
=
NULL
;
}
else
{
switch
(
plist_get_node_type
(
*
plist
))
{
case
PLIST_DICT
:
if
(
!
keyname
||
*
keyname
==
'\0'
)
{
PLIST_XML_ERR
(
"missing or empty key name while adding dict item
\n
"
);
ctx
->
err
++
;
break
;
}
plist_dict_set_item
(
*
plist
,
keyname
,
subnode
);
subnode
=
NULL
;
break
;
case
PLIST_ARRAY
:
plist_array_append_item
(
*
plist
,
subnode
);
subnode
=
NULL
;
break
;
default:
/* should not happen */
PLIST_XML_ERR
(
"while parsing XML plist: parent is not a structered node.
\n
"
);
ctx
->
err
++
;
break
;
}
}
}
else
if
(
closing_tag
&&
*
plist
&&
plist_get_node_type
(
*
plist
)
==
PLIST_DICT
&&
keyname
)
{
PLIST_XML_ERR
(
"missing value node in dict
\n
"
);
ctx
->
err
++
;
}
free
(
keyname
);
keyname
=
NULL
;
plist_free
(
subnode
);
if
(
closing_tag
)
{
break
;
}
}
ctx
->
pos
++
;
}
if
(
ctx
->
err
)
{
plist_free
(
*
plist
);
*
plist
=
NULL
;
}
}
xmlFreeDoc
(
plist_doc
);
}
static
xmlParserInputPtr
plist_xml_external_entity_loader
(
const
char
*
URL
,
const
char
*
ID
,
xmlParserCtxtPtr
ctxt
)
{
return
NULL
;
}
}
PLIST_API
void
plist_from_xml
(
const
char
*
plist_xml
,
uint32_t
length
,
plist_t
*
plist
)
PLIST_API
void
plist_from_xml
(
const
char
*
plist_xml
,
uint32_t
length
,
plist_t
*
plist
)
{
{
/* CVE-2013-0339: disable external entity loading to prevent XXE vulnerability */
if
(
!
plist_xml
||
(
length
==
0
))
{
xmlSetExternalEntityLoader
(
plist_xml_external_entity_loader
);
*
plist
=
NULL
;
return
;
}
/* read XML from memory and disable network access for security reasons */
struct
_parse_ctx
ctx
=
{
plist_xml
,
plist_xml
+
length
,
0
};
xmlDocPtr
plist_doc
=
xmlReadMemory
(
plist_xml
,
length
,
"plist_from_xml:memory"
,
NULL
,
XML_PARSE_NONET
);
if
(
plist_doc
)
{
xmlNodePtr
root_node
=
xmlDocGetRootElement
(
plist_doc
);
xml_to_node
(
root_node
,
plist
);
node_from_xml
(
&
ctx
,
plist
);
xmlFreeDoc
(
plist_doc
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment