From a0b0e0d76a7afd9c1009f7bd5e955eaca318a5e0 Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Wed, 13 Nov 2019 19:11:35 +0200 Subject: [PATCH 1/5] Add a small rough XML interface based on libxml2 --- gmp/CMakeLists.txt | 3 +- osp/CMakeLists.txt | 3 +- util/CMakeLists.txt | 10 ++- util/xmlutils.c | 159 ++++++++++++++++++++++++++++++++++++++++++++ util/xmlutils.h | 48 +++++++++++++ 5 files changed, 219 insertions(+), 4 deletions(-) diff --git a/gmp/CMakeLists.txt b/gmp/CMakeLists.txt index 43be5cdd1..47d32df82 100644 --- a/gmp/CMakeLists.txt +++ b/gmp/CMakeLists.txt @@ -27,8 +27,9 @@ endif (NOT PKG_CONFIG_FOUND) ## Dependency checks pkg_check_modules (GLIB REQUIRED glib-2.0>=2.42) +pkg_check_modules (LIBXML2 REQUIRED libxml-2.0>=2.0) -include_directories (${GLIB_INCLUDE_DIRS}) +include_directories (${GLIB_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIRS}) set (FILES gmp.c) diff --git a/osp/CMakeLists.txt b/osp/CMakeLists.txt index 45af4df6f..225cdbe8c 100644 --- a/osp/CMakeLists.txt +++ b/osp/CMakeLists.txt @@ -27,8 +27,9 @@ endif (NOT PKG_CONFIG_FOUND) ## Dependency checks pkg_check_modules (GLIB REQUIRED glib-2.0>=2.42) +pkg_check_modules (LIBXML2 REQUIRED libxml-2.0>=2.0) -include_directories (${GLIB_INCLUDE_DIRS}) +include_directories (${GLIB_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIRS}) set (FILES osp.c) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index af5e363e7..8de8c327d 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -24,6 +24,8 @@ if (NOT PKG_CONFIG_FOUND) message(FATAL_ERROR "pkg-config executable not found. Aborting.") endif (NOT PKG_CONFIG_FOUND) +set (CMAKE_VERBOSE_MAKEFILE ON) + ## Dependency checks # for all modules we need glib @@ -47,6 +49,9 @@ pkg_check_modules (LIBSSH REQUIRED libssh>=0.6.0) # for kb we need libhiredis pkg_check_modules (REDIS REQUIRED hiredis>=0.10.1) +# for fast XML we need libxml2 +pkg_check_modules (LIBXML2 REQUIRED libxml-2.0>=2.0) + # Set NVTICACHE name with the version set (NVTICACHE_STR "nvticache${PROJECT_VERSION}") add_definitions (-DNVTICACHE_STR="${NVTICACHE_STR}") @@ -131,7 +136,7 @@ if (BUILD_WITH_LDAP) endif (NOT LIBLDAP) endif (BUILD_WITH_LDAP) -include_directories (${GLIB_INCLUDE_DIRS} ${GPGME_INCLUDE_DIRS} ${GCRYPT_INCLUDE_DIRS}) +include_directories (${GLIB_INCLUDE_DIRS} ${GPGME_INCLUDE_DIRS} ${GCRYPT_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIRS}) set (FILES authutils.c compressutils.c fileutils.c gpgmeutils.c kb.c ldaputils.c nvticache.c radiusutils.c serverutils.c sshutils.c uuidutils.c @@ -160,7 +165,8 @@ if (BUILD_SHARED) ${GIO_LDFLAGS} ${GPGME_LDFLAGS} ${ZLIB_LDFLAGS} ${RADIUS_LDFLAGS} ${LIBSSH_LDFLAGS} ${GNUTLS_LDFLAGS} ${GCRYPT_LDFLAGS} ${LDAP_LDFLAGS} ${REDIS_LDFLAGS} - ${UUID_LDFLAGS} ${LINKER_HARDENING_FLAGS}) + ${LIBXML2_LDFLAGS} ${UUID_LDFLAGS} + ${LINKER_HARDENING_FLAGS}) endif (BUILD_SHARED) ## Install diff --git a/util/xmlutils.c b/util/xmlutils.c index a2308ba8e..3e2df8e57 100644 --- a/util/xmlutils.c +++ b/util/xmlutils.c @@ -1087,6 +1087,165 @@ read_entity_c (gvm_connection_t *connection, entity_t *entity) return try_read_entity_c (connection, 0, entity); } +/** + * @brief Get the name an entity. + * + * @param[in] entity Entity. + * + * @return Entity name, which is freed by free_entity2. + */ +void +xml_doc_free (xml_doc_t doc) +{ + xmlFreeDoc (doc); +} + +/** + * @brief Read an XML entity tree from a string. + * + * @param[in] string Input string. + * @param[out] entity Pointer to an entity tree. + * + * @return 0 success, -1 read error, -2 parse error, -3 XML ended prematurely. + */ +int +parse_entity2 (const char *string, xml_doc_t *xml_doc, entity2_t *entity) +{ + LIBXML_TEST_VERSION + + *xml_doc = xmlReadMemory (string, strlen (string), "noname.xml", NULL, 0); + if (*xml_doc == NULL) + return -2; + //xmlFreeDoc(doc); + + *entity = xmlDocGetRootElement (*xml_doc); + return 0; +} + +/** + * @brief Get the name an entity. + * + * @param[in] entity Entity. + * + * @return Entity name, which is freed by free_entity2. + */ +const char * +entity2_name (entity2_t entity) +{ + if (entity + && (entity->type == XML_ELEMENT_NODE)) + return (const char *) entity->name; + + // FIX because we have text nodes now too + // FIX maybe should skip over in entity2_children and entity2_next + return ""; +} + +/** + * @brief Get children of an entity. + * + * @param[in] entity Entity. + * + * @return Children if found, else NULL. + */ +entities2_t +entity2_children (entity2_t entity) +{ + if (!entity) + return NULL; + return entity->children; +} + +/** + * @brief Get a child of an entity. + * + * @param[in] entity Entity. + * @param[in] name Name of the child. + * + * @return Entity if found, else NULL. + */ +entity2_t +entity2_child (entity2_t entity, const char *name) +{ + if (!entity) + return NULL; + + for (xmlNode *node = entity->children; node; node = node->next) + if (xmlStrcmp (node->name, (const xmlChar *) name) == 0) + return node; + + return NULL; +} + +/** + * @brief Get a child of an entity. + * + * @param[in] entity Entity. + * @param[in] name Name of the child. + * + * @return Entity if found, else NULL. + */ +char * +entity2_text (xml_doc_t doc, entity2_t entity) +{ + if (!entity) + return NULL; + + return (char *) xmlNodeListGetString (doc, entity->xmlChildrenNode, 1); +} + +/** + * @brief Get a child of an entity. + * + * @param[in] entity Entity. + * @param[in] name Name of the child. + * + * @return Entity if found, else NULL. + */ +char * +entity2_attribute (entity2_t entity, const char *name) +{ + if (!entity) + return NULL; + + return (char *) xmlGetProp (entity, (const xmlChar *) name); +} + +void +xml_string_free (xml_string_t string) +{ + if (string) + xmlFree (string); +} + +/** + * @brief Get the name an entity. + * + * @param[in] entity Entity. + * + * @return Entity name, which is freed by free_entity2. + */ +entities2_t +first_entity2 (entities2_t entities) +{ + return entities; +} + +/** + * @brief Get the name an entity. + * + * @param[in] entity Entity. + * + * @return Entity name, which is freed by free_entity2. + */ +entities2_t +next_entities2 (entities2_t entities) +{ + if (entities) + return entities->children; + return NULL; +} + /** * @brief Read an XML entity tree from a string. * diff --git a/util/xmlutils.h b/util/xmlutils.h index f468fafdc..4ecec9547 100644 --- a/util/xmlutils.h +++ b/util/xmlutils.h @@ -29,6 +29,9 @@ #include #include +// FIX should be in .c only +#include +#include #include /** @@ -54,6 +57,51 @@ xml_handle_end_element (context_data_t *, const gchar *); void xml_handle_text (context_data_t *, const gchar *, gsize); +/** + * @brief XML doc. + */ +typedef xmlDoc *xml_doc_t; + +typedef xmlNode *entities2_t; +/* equivalent now, kept for compat. */ +typedef xmlNode *entity2_t; + +typedef xmlChar *xml_string_t; +typedef const xmlChar *const_xml_string_t; + +void +xml_doc_free (xml_doc_t); + +int +parse_entity2 (const char *, xml_doc_t *, entity2_t *); + +void +xml_string_free (xml_string_t); + +entities2_t +xml_doc_root (xml_doc_t); + +const char * +entity2_name (entities2_t); + +char * +entity2_attribute (entity2_t, const char *); + +char * +entity2_text (xml_doc_t, entities2_t); + +entity2_t +entity2_child (entity2_t, const char *); + +entities2_t +entity2_children (entities2_t); + +entities2_t +first_entity2 (entities2_t); + +entities2_t +next_entities2 (entities2_t); + /** * @brief Entities. */ From 9d80f878f7e0998bf001bef69e575a8d81f48e20 Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Thu, 14 Nov 2019 18:00:36 +0200 Subject: [PATCH 2/5] Fix some issues with the new XML interface --- util/CMakeLists.txt | 20 +++ util/xmlutils.c | 96 +++++++++-- util/xmlutils_tests.c | 377 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 483 insertions(+), 10 deletions(-) create mode 100644 util/xmlutils_tests.c diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 8de8c327d..ea75f8406 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -169,6 +169,26 @@ if (BUILD_SHARED) ${LINKER_HARDENING_FLAGS}) endif (BUILD_SHARED) + +## Tests + +add_executable (xmlutils-test + EXCLUDE_FROM_ALL + xmlutils_tests.c) + +add_test (xmlutils-test xmlutils-test) + +target_link_libraries (xmlutils-test cgreen + ${GLIB_LDFLAGS} ${GIO_LDFLAGS} ${GPGME_LDFLAGS} ${ZLIB_LDFLAGS} + ${RADIUS_LDFLAGS} ${LIBSSH_LDFLAGS} ${GNUTLS_LDFLAGS} + ${GCRYPT_LDFLAGS} ${LDAP_LDFLAGS} ${REDIS_LDFLAGS} + ${LIBXML2_LDFLAGS} ${UUID_LDFLAGS} + ${LINKER_HARDENING_FLAGS}) + +add_custom_target (tests-xmlutils + DEPENDS xmlutils-test) + + ## Install configure_file (libgvm_util.pc.in ${CMAKE_BINARY_DIR}/libgvm_util.pc @ONLY) diff --git a/util/xmlutils.c b/util/xmlutils.c index 3e2df8e57..8bf22c4ef 100644 --- a/util/xmlutils.c +++ b/util/xmlutils.c @@ -1137,7 +1137,7 @@ entity2_name (entity2_t entity) return (const char *) entity->name; // FIX because we have text nodes now too - // FIX maybe should skip over in entity2_children and entity2_next + // FIX maybe should skip over in entity2_children and next_entities2 return ""; } @@ -1156,6 +1156,15 @@ entity2_children (entity2_t entity) return entity->children; } +static entity2_t +find_child (entity2_t entity, const char *name) +{ + for (xmlNode *node = entity->children; node; node = node->next) + if (xmlStrcmp (node->name, (const xmlChar *) name) == 0) + return node; + return NULL; +} + /** * @brief Get a child of an entity. * @@ -1167,31 +1176,67 @@ entity2_children (entity2_t entity) entity2_t entity2_child (entity2_t entity, const char *name) { + const char *stripped_name; + if (!entity) return NULL; - for (xmlNode *node = entity->children; node; node = node->next) - if (xmlStrcmp (node->name, (const xmlChar *) name) == 0) - return node; + stripped_name = strchr (name, ':'); + if (stripped_name) + { + entity2_t child; - return NULL; + /* There was a namespace in the name. + * + * First try without the namespace, because libxml2 doesn't consider the + * namespace in the name when the namespace is defined. */ + + stripped_name++; + + if (*stripped_name == '\0') + /* Don't search for child with empty stripped name, because we'll + * find text nodes. But search with just the namespace for glib + * compatibility. */ + return find_child (entity, name); + + child = find_child (entity, stripped_name); + if (child) + return child; + + /* Didn't find anything. */ + } + + /* There was no namespace, or we didn't find anything without the namespace. + * + * Try with the full name. */ + + return find_child (entity, name); } /** - * @brief Get a child of an entity. + * @brief Get text of an entity. + * + * If entity is not NULL then the return is guaranteed to be a string. + * So if the caller has NULL checked entity then there is no need for + * the caller to NULL check the return. * + * @param[in] doc XML doc. * @param[in] entity Entity. - * @param[in] name Name of the child. * - * @return Entity if found, else NULL. + * @return NULL if entity is NULL, else the text. */ char * entity2_text (xml_doc_t doc, entity2_t entity) { + char *string; + if (!entity) return NULL; - return (char *) xmlNodeListGetString (doc, entity->xmlChildrenNode, 1); + string = (char *) xmlNodeListGetString (doc, entity->xmlChildrenNode, 1); + if (string) + return string; + return ""; } /** @@ -1208,6 +1253,37 @@ entity2_attribute (entity2_t entity, const char *name) if (!entity) return NULL; + const char *stripped_name; + + stripped_name = strchr (name, ':'); + if (stripped_name) + { + char *attribute; + + /* There was a namespace in the name. + * + * First try without the namespace, because libxml2 doesn't consider the + * namespace in the name when the namespace is defined. */ + + stripped_name++; + + if (*stripped_name == '\0') + /* Don't search for child with empty stripped name, because we'll + * find text nodes. But search with just the namespace for glib + * compatibility. */ + return (char *) xmlGetProp (entity, (const xmlChar *) name); + + attribute = (char *) xmlGetProp (entity, (const xmlChar *) stripped_name); + if (attribute) + return attribute; + + /* Didn't find anything. */ + } + + /* There was no namespace, or we didn't find anything without the namespace. + * + * Try with the full name. */ + return (char *) xmlGetProp (entity, (const xmlChar *) name); } @@ -1242,7 +1318,7 @@ entities2_t next_entities2 (entities2_t entities) { if (entities) - return entities->children; + return entities->next; return NULL; } diff --git a/util/xmlutils_tests.c b/util/xmlutils_tests.c new file mode 100644 index 000000000..db4334100 --- /dev/null +++ b/util/xmlutils_tests.c @@ -0,0 +1,377 @@ +/* Copyright (C) 2019 Greenbone Networks GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 + * of the License, or (at your option) any later version. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "xmlutils.c" + +#include +#include + +Describe (xmlutils); +BeforeEach (xmlutils) +{ +} +AfterEach (xmlutils) +{ +} + +/* parse_entity */ + +Ensure (xmlutils, parse_entity_parses_simple_xml) +{ + entity_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity (xml, &entity), is_equal_to (0)); + + assert_that (entity_name (entity), is_equal_to_string ("a")); + + b = entity_child (entity, "b"); + assert_that (entity_name (b), is_equal_to_string ("b")); + + assert_that (entity_text (b), is_equal_to_string ("1")); +} + +Ensure (xmlutils, parse_entity_parses_xml_with_attributes) +{ + entity_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity (xml, &entity), is_equal_to (0)); + + b = entity_child (entity, "b"); + + assert_that (entity_attribute (b, "ba1"), is_equal_to_string ("test")); +} + +Ensure (xmlutils, parse_entity_handles_declaration) +{ + entity_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity (xml, &entity), is_equal_to (0)); + + assert_that (entity_name (entity), is_equal_to_string ("a")); + + b = entity_child (entity, "b"); + assert_that (entity_name (b), is_equal_to_string ("b")); + + assert_that (entity_text (b), is_equal_to_string ("1")); +} + +Ensure (xmlutils, parse_entity_handles_namespace) +{ + entity_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity (xml, &entity), is_equal_to (0)); + + assert_that (entity_name (entity), is_equal_to_string ("a")); + + b = entity_child (entity, "n:b"); + assert_that (entity_name (b), is_equal_to_string ("n:b")); + + assert_that (entity_text (b), is_equal_to_string ("1")); +} + +Ensure (xmlutils, parse_entity_oval_timestamp) +{ + gchar *generator_name; + entity_t generator; + entity_t timestamp; + entity_t entity; + + const gchar *xml = "" \ +"" \ +" " \ +" The OVAL Repository" \ +" 5.10" \ +" 2015-08-20T10:09:07.183-04:00" \ +" " \ +""; + + assert_that (parse_entity (xml, &entity), is_equal_to (0)); + + assert_that (entity_name (entity), is_equal_to_string ("oval_definitions")); + generator_name = g_strdup ("generator"); + generator = entity_child (entity, generator_name); + g_free (generator_name); + assert_that (generator, is_not_null); + timestamp = entity_child (generator, "oval:timestamp"); + assert_that (timestamp, is_not_null); + assert_that (entity_text (timestamp), is_equal_to_string ("2015-08-20T10:09:07.183-04:00")); +} + +/* next_entities. */ + +Ensure (xmlutils, next_entities_handles_multiple_children) +{ + entity_t entity, child; + entities_t children; + + const gchar *xml = "13"; + + assert_that (parse_entity (xml, &entity), is_equal_to (0)); + + assert_that (entity_name (entity), is_equal_to_string ("top")); + + children = entity->entities; + + child = first_entity (children); + assert_that (child, is_not_null); + assert_that (entity_name (child), is_equal_to_string ("a")); + assert_that (entity_text (child), is_equal_to_string ("1")); + children = next_entities (children); + + child = first_entity (children); + assert_that (child, is_not_null); + assert_that (entity_name (child), is_equal_to_string ("b")); + assert_that (entity_text (child), is_equal_to_string ("")); + children = next_entities (children); + + child = first_entity (children); + assert_that (child, is_not_null); + assert_that (entity_name (child), is_equal_to_string ("c")); + assert_that (entity_text (child), is_equal_to_string ("3")); + children = next_entities (children); +} + +/* parse_entity2 */ + +Ensure (xmlutils, parse_entity2_parses_simple_xml) +{ + xml_doc_t doc; + entity2_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + assert_that (entity2_name (entity), is_equal_to_string ("a")); + + b = entity2_child (entity, "b"); + assert_that (entity2_name (b), is_equal_to_string ("b")); + + assert_that (entity2_text (doc, b), is_equal_to_string ("1")); +} + +Ensure (xmlutils, parse_entity2_parses_xml_with_attributes) +{ + xml_doc_t doc; + entity2_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + b = entity2_child (entity, "b"); + + assert_that (entity2_attribute (b, "ba1"), is_equal_to_string ("test")); +} + +Ensure (xmlutils, parse_entity2_handles_declaration) +{ + xml_doc_t doc; + entity2_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + assert_that (entity2_name (entity), is_equal_to_string ("a")); + + b = entity2_child (entity, "b"); + assert_that (entity2_name (b), is_equal_to_string ("b")); + + assert_that (entity2_text (doc, b), is_equal_to_string ("1")); +} + +Ensure (xmlutils, parse_entity2_handles_namespace) +{ + xml_doc_t doc; + entity2_t entity, b; + + const gchar *xml = "1"; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + assert_that (entity2_name (entity), is_equal_to_string ("a")); + + b = entity2_child (entity, "n:b"); + assert_that (entity2_name (b), is_equal_to_string ("n:b")); + + assert_that (entity2_text (doc, b), is_equal_to_string ("1")); + + assert_that (entity2_attribute (b, "n2:ba2"), is_equal_to_string ("test2")); +} + +Ensure (xmlutils, parse_entity2_oval_timestamp) +{ + xml_doc_t doc; + + gchar *generator_name; + entity2_t generator; + entity2_t timestamp; + entity2_t entity; + + const gchar *xml = "" \ +"" \ +" " \ +" The OVAL Repository" \ +" 5.10" \ +" 2015-08-20T10:09:07.183-04:00" \ +" " \ +""; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + assert_that (entity2_name (entity), is_equal_to_string ("oval_definitions")); + generator_name = g_strdup ("generator"); + generator = entity2_child (entity, generator_name); + g_free (generator_name); + assert_that (generator, is_not_null); + timestamp = entity2_child (generator, "oval:timestamp"); + assert_that (timestamp, is_not_null); + assert_that (entity2_text (doc, timestamp), is_equal_to_string ("2015-08-20T10:09:07.183-04:00")); +} + +Ensure (xmlutils, parse_entity2_item_metadata) +{ + xml_doc_t doc; + entity2_t entity, item, meta; + + const gchar *xml = "" \ +" " \ +" $0.99 Kindle Books project $0.99 Kindle Books (aka com.kindle.books.for99) for android 6.0" \ +" " \ +" Product information" \ +" Government Advisory" \ +" " \ +" " \ +" " \ +""; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + assert_that (entity2_name (entity), is_equal_to_string ("cpe-list")); + item = entity2_child (entity, "cpe-item"); + assert_that (item, is_not_null); + meta = entity2_child (item, "meta:item-metadata"); + assert_that (meta, is_not_null); + assert_that (entity2_name (meta), is_equal_to_string ("meta:item-metadata")); +} + +Ensure (xmlutils, parse_entity2_item_metadata_with_namespace) +{ + xml_doc_t doc; + entity2_t entity, item, meta; + + const gchar *xml = "" \ +" " \ +" $0.99 Kindle Books project $0.99 Kindle Books (aka com.kindle.books.for99) for android 6.0" \ +" " \ +" Product information" \ +" Government Advisory" \ +" " \ +" " \ +" " \ +""; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + assert_that (entity2_name (entity), is_equal_to_string ("cpe-list")); + item = entity2_child (entity, "cpe-item"); + assert_that (item, is_not_null); + meta = entity2_child (item, "item-metadata"); + assert_that (meta, is_not_null); + // NB + assert_that (entity2_name (meta), is_equal_to_string ("item-metadata")); + //assert_that (entity2_name (meta), is_equal_to_string ("meta:item-metadata")); +} + +/* next_entities2. */ + +Ensure (xmlutils, next_entities2_handles_multiple_children) +{ + xml_doc_t doc; + entity2_t entity, child; + entities2_t children; + + const gchar *xml = "123"; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + + assert_that (entity2_name (entity), is_equal_to_string ("top")); + + children = entity2_children (entity); + + child = first_entity2 (children); + assert_that (child, is_not_null); + assert_that (entity2_name (child), is_equal_to_string ("a")); + assert_that (entity2_text (doc, child), is_equal_to_string ("1")); + children = next_entities2 (children); + + child = first_entity2 (children); + assert_that (child, is_not_null); + assert_that (entity2_name (child), is_equal_to_string ("b")); + assert_that (entity2_text (doc, child), is_equal_to_string ("2")); + children = next_entities2 (children); + + child = first_entity2 (children); + assert_that (child, is_not_null); + assert_that (entity2_name (child), is_equal_to_string ("c")); + assert_that (entity2_text (doc, child), is_equal_to_string ("3")); + children = next_entities2 (children); +} + +/* Test suite. */ + +int +main (int argc, char **argv) +{ + TestSuite *suite; + + suite = create_test_suite (); + + add_test_with_context (suite, xmlutils, parse_entity_parses_simple_xml); + add_test_with_context (suite, xmlutils, parse_entity_parses_xml_with_attributes); + add_test_with_context (suite, xmlutils, parse_entity_handles_declaration); + add_test_with_context (suite, xmlutils, parse_entity_handles_namespace); + add_test_with_context (suite, xmlutils, parse_entity_oval_timestamp); + + add_test_with_context (suite, xmlutils, next_entities_handles_multiple_children); + + add_test_with_context (suite, xmlutils, parse_entity2_parses_simple_xml); + add_test_with_context (suite, xmlutils, parse_entity2_parses_xml_with_attributes); + add_test_with_context (suite, xmlutils, parse_entity2_handles_declaration); + add_test_with_context (suite, xmlutils, parse_entity2_handles_namespace); + add_test_with_context (suite, xmlutils, parse_entity2_oval_timestamp); + add_test_with_context (suite, xmlutils, parse_entity2_item_metadata); + add_test_with_context (suite, xmlutils, parse_entity2_item_metadata_with_namespace); + + add_test_with_context (suite, xmlutils, next_entities2_handles_multiple_children); + + if (argc > 1) + return run_single_test (suite, argv[1], create_text_reporter ()); + + return run_test_suite (suite, create_text_reporter ()); +} From 6c34a590a0cbf00dde282ab24176f8190bbc40fe Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Fri, 15 Nov 2019 10:54:18 +0200 Subject: [PATCH 3/5] Add a CDATA test --- util/xmlutils.c | 5 ++++- util/xmlutils_tests.c | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/util/xmlutils.c b/util/xmlutils.c index 8bf22c4ef..9757af947 100644 --- a/util/xmlutils.c +++ b/util/xmlutils.c @@ -1113,7 +1113,10 @@ parse_entity2 (const char *string, xml_doc_t *xml_doc, entity2_t *entity) { LIBXML_TEST_VERSION - *xml_doc = xmlReadMemory (string, strlen (string), "noname.xml", NULL, 0); + /* XML_PARSE_NOCDATA makes this skip CDATA to match our glib parser, but + * it makes this extremely slow, so instead I stripped the CDATA out of the + * XML in my feed (for now, so that I can compare the resulting scap dbs). */ + *xml_doc = xmlReadMemory (string, strlen (string), "noname.xml", NULL, /* XML_PARSE_NOCDATA */ 0); if (*xml_doc == NULL) return -2; //xmlFreeDoc(doc); diff --git a/util/xmlutils_tests.c b/util/xmlutils_tests.c index db4334100..6260066e1 100644 --- a/util/xmlutils_tests.c +++ b/util/xmlutils_tests.c @@ -308,6 +308,18 @@ Ensure (xmlutils, parse_entity2_item_metadata_with_namespace) //assert_that (entity2_name (meta), is_equal_to_string ("meta:item-metadata")); } +Ensure (xmlutils, parse_entity2_item_handles_cdata) +{ + xml_doc_t doc; + entity2_t entity; + + const gchar *xml = ""; + + assert_that (parse_entity2 (xml, &doc, &entity), is_equal_to (0)); + assert_that (entity2_name (entity), is_equal_to_string ("description")); + assert_that (entity2_text (doc, entity), is_equal_to_string ("123")); +} + /* next_entities2. */ Ensure (xmlutils, next_entities2_handles_multiple_children) @@ -367,6 +379,7 @@ main (int argc, char **argv) add_test_with_context (suite, xmlutils, parse_entity2_oval_timestamp); add_test_with_context (suite, xmlutils, parse_entity2_item_metadata); add_test_with_context (suite, xmlutils, parse_entity2_item_metadata_with_namespace); + add_test_with_context (suite, xmlutils, parse_entity2_item_handles_cdata); add_test_with_context (suite, xmlutils, next_entities2_handles_multiple_children); From c83e0923400e722a7309bfb1c4d157258b3f263d Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Sun, 17 Nov 2019 21:38:47 +0200 Subject: [PATCH 4/5] Add entity2_text_free --- util/xmlutils.c | 5 +++++ util/xmlutils.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/util/xmlutils.c b/util/xmlutils.c index 9757af947..5a57edf4f 100644 --- a/util/xmlutils.c +++ b/util/xmlutils.c @@ -1242,6 +1242,11 @@ entity2_text (xml_doc_t doc, entity2_t entity) return ""; } +void +entity2_text_free (char *text) +{ + xmlFree (text); +} /** * @brief Get a child of an entity. * diff --git a/util/xmlutils.h b/util/xmlutils.h index 4ecec9547..034f03fc8 100644 --- a/util/xmlutils.h +++ b/util/xmlutils.h @@ -90,6 +90,9 @@ entity2_attribute (entity2_t, const char *); char * entity2_text (xml_doc_t, entities2_t); +void +entity2_text_free (char *); + entity2_t entity2_child (entity2_t, const char *); From 4f904b2d75e4700b20634ed06e309b95a9739c59 Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Sun, 17 Nov 2019 21:40:10 +0200 Subject: [PATCH 5/5] Allocate fallback return in entity2_text --- util/xmlutils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/xmlutils.c b/util/xmlutils.c index 5a57edf4f..cb8d90512 100644 --- a/util/xmlutils.c +++ b/util/xmlutils.c @@ -1239,7 +1239,9 @@ entity2_text (xml_doc_t doc, entity2_t entity) string = (char *) xmlNodeListGetString (doc, entity->xmlChildrenNode, 1); if (string) return string; - return ""; + string = xmlMalloc (1); + string[0] = '\0'; + return string; } void