<commit_before>/*
 * Copyright 2012 Red Hat Inc., Durham, North Carolina.
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Authors:
 *      <NAME> <<EMAIL>>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "public/ds_sds_session.h"
#include "public/scap_ds.h"
#include "public/xccdf_benchmark.h"
#include "public/oval_definitions.h"
#include "public/oscap.h"
#include "public/oscap_text.h"

#include "ds_common.h"
#include "ds_sds_session_priv.h"
#include "sds_priv.h"

#include "common/alloc.h"
#include "common/_error.h"
#include "common/util.h"
#include "common/list.h"
#include "common/oscap_acquire.h"
#include "source/oscap_source_priv.h"

#include <sys/stat.h>
#include <time.h>
#include <libgen.h>

#include <libxml/xmlreader.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#ifndef MAXPATHLEN
#   define MAXPATHLEN 1024
#endif

static const char* datastream_ns_uri = "http://scap.nist.gov/schema/scap/source/1.2";
static const char* xlink_ns_uri = "http://www.w3.org/1999/xlink";
static const char* cat_ns_uri = "urn:oasis:names:tc:entity:xmlns:xml:catalog";
static const char* sce_xccdf_ns_uri = "http://open-scap.org/page/SCE_xccdf_stream";

xmlNodePtr node_get_child_element(xmlNodePtr parent, const char* name)
{
	xmlNodePtr candidate = parent->children;

	for (; candidate != NULL; candidate = candidate->next)
	{
		if (candidate->type != XML_ELEMENT_NODE)
			continue;

		if (name != NULL && strcmp((const char*)(candidate->name), name) != 0)
			continue;

		return candidate;
	}

	return NULL;
}

xmlNode *containter_get_component_ref_by_id(xmlNode *container, const char *component_id)
{
	for (xmlNode *component_ref = container->children; component_ref != NULL; component_ref = component_ref->next)
	{
		if (component_ref->type != XML_ELEMENT_NODE)
			continue;

		if (strcmp((const char*)(component_ref->name), "component-ref") != 0)
			continue;

		xmlChar* cref_id = xmlGetProp(component_ref, BAD_CAST "id");
		// if cref_id is zero we have encountered a fatal error that will be handled
		// in ds_sds_dump_component_ref
		if (component_id && cref_id && strcmp(component_id, (char*)cref_id) != 0)
		{
			xmlFree(cref_id);
			continue;
		}
		xmlFree(cref_id);
		return component_ref;
	}
	return NULL;
}

static xmlNodePtr ds_sds_find_component_ref(xmlNodePtr datastream, const char* id)
{
	/* This searches for a ds:component-ref (XLink) element with a given id.
	 * It returns a first such element in a given ds:data-stream.
	 */
	xmlNodePtr cref_parent = datastream->children;

	for (; cref_parent != NULL; cref_parent = cref_parent->next)
	{
		if (cref_parent->type != XML_ELEMENT_NODE)
			continue;
		xmlNode *component_ref = containter_get_component_ref_by_id(cref_parent, id);
		if (component_ref != NULL) {
			return component_ref;
		}
	}

	return NULL;
}

static xmlNodePtr _lookup_component_in_collection(xmlDocPtr doc, const char *component_id)
{
	xmlNodePtr root = xmlDocGetRootElement(doc);
	xmlNodePtr component = NULL;
	xmlNodePtr candidate = root->children;

	for (; candidate != NULL; candidate = candidate->next)
	{
		if (candidate->type != XML_ELEMENT_NODE)
			continue;

		if ((strcmp((const char*)(candidate->name), "component") != 0) &&
		    (strcmp((const char*)(candidate->name), "extended-component") != 0))
			continue;

		char* candidate_id = (char*)xmlGetProp(candidate, BAD_CAST "id");
		if (strcmp(candidate_id, component_id) == 0)
		{
			component = candidate;
			xmlFree(candidate_id);
			break;
		}
		xmlFree(candidate_id);
	}
	return component;
}

static int ds_sds_dump_component_sce(xmlNode *script_node, const char *component_id, const char *filename)
{
	if (script_node) {
		// TODO: should we check whether the component is extended?
		int fd;
		xmlChar* text_contents = xmlNodeGetContent(script_node);
		if ((fd = open(filename, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0700)) < 0) {
			oscap_seterr(OSCAP_EFAMILY_XML, "Error while creating script component (id='%s') to file '%s'.", component_id, filename);
			xmlFree(text_contents);
			return -1;
		}
		FILE* output_file = fdopen(fd, "w");
		if (output_file == NULL) {
			oscap_seterr(OSCAP_EFAMILY_XML, "Error while dumping script component (id='%s') to file '%s'.", component_id, filename);
			xmlFree(text_contents);
			close(fd);
			return -1;
		}
		// TODO: error checking, fprintf should return strlen((const char*)text_contents)
		fprintf(output_file, "%s", text_contents ? (char*)text_contents : "");
		// NB: This code is for SCE scripts
		if (fchmod(fd, 0700) != 0) {
			oscap_seterr(OSCAP_EFAMILY_XML, "Failed to set executable permission on script (id='%s') that was split to '%s'.", component_id, filename);
		}

		fclose(output_file);
		xmlFree(text_contents);
		return 0;
	}
	else {
		oscap_seterr(OSCAP_EFAMILY_XML, "Error while dumping script component (id='%s') to file '%s'. "
			"The script element was empty!", component_id, filename);
		return -1;
	}
}

static int ds_sds_dump_component(const char* component_id, struct ds_sds_session *session, const char* filename)
{
	xmlDoc *doc = ds_sds_session_get_xmlDoc(session);
	xmlNodePtr component = _lookup_component_in_collection(doc, component_id);
	if (component == NULL)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "Component of given id '%s' was not found in the document.", component_id);
		return -1;
	}

	xmlNodePtr inner_root = node_get_child_element(component, NULL);

	if (inner_root == NULL)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "Found component (id='%s') but it has no element contents, nothing to dump, skipping...", component_id);
		return -1;
	}

	// If the inner root is script, we have to treat it in a special way
	if (strcmp((const char*)inner_root->name, "script") == 0) {
		int ret = ds_sds_dump_component_sce(inner_root->children, component_id, filename);
		if (ret != 0) {
			return ret;
		}
	}
	// Otherwise we create a new XML doc we will dump the contents to.
	// We can't just dump node "innerXML" because namespaces have to be
	// handled.
	else {
		xmlDoc *new_doc = ds_doc_from_foreign_node(inner_root, doc);
		if (new_doc == NULL) {
			return -1;
		}
		struct oscap_source *source = oscap_source_new_from_xmlDoc(new_doc, oscap_acquire_guess_realpath(filename));
		ds_sds_session_register_component_source(session, oscap_source_readable_origin(source), source);
	}

	return 0;
}

int ds_sds_dump_component_ref_as(xmlNodePtr component_ref, struct ds_sds_session *session, const char* target_dir, const char* filename)
{
	char* cref_id = (char*)xmlGetProp(component_ref, BAD_CAST "id");
	if (!cref_id)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "No or invalid id attribute on given component-ref.");
		xmlFree(cref_id);
		return -1;
	}

	char* xlink_href = (char*)xmlGetNsProp(component_ref, BAD_CAST "href", BAD_CAST xlink_ns_uri);
	if (!xlink_href || strlen(xlink_href) < 2)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "No or invalid xlink:href attribute on given component-ref.");
		xmlFree(cref_id);
		xmlFree(xlink_href);
		return -1;
	}<commit_msg>Plug a memory leak.<commit_after>/*
 * Copyright 2012 Red Hat Inc., Durham, North Carolina.
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Authors:
 *      <NAME> <<EMAIL>>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "public/ds_sds_session.h"
#include "public/scap_ds.h"
#include "public/xccdf_benchmark.h"
#include "public/oval_definitions.h"
#include "public/oscap.h"
#include "public/oscap_text.h"

#include "ds_common.h"
#include "ds_sds_session_priv.h"
#include "sds_priv.h"

#include "common/alloc.h"
#include "common/_error.h"
#include "common/util.h"
#include "common/list.h"
#include "common/oscap_acquire.h"
#include "source/oscap_source_priv.h"

#include <sys/stat.h>
#include <time.h>
#include <libgen.h>

#include <libxml/xmlreader.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#ifndef MAXPATHLEN
#   define MAXPATHLEN 1024
#endif

static const char* datastream_ns_uri = "http://scap.nist.gov/schema/scap/source/1.2";
static const char* xlink_ns_uri = "http://www.w3.org/1999/xlink";
static const char* cat_ns_uri = "urn:oasis:names:tc:entity:xmlns:xml:catalog";
static const char* sce_xccdf_ns_uri = "http://open-scap.org/page/SCE_xccdf_stream";

xmlNodePtr node_get_child_element(xmlNodePtr parent, const char* name)
{
	xmlNodePtr candidate = parent->children;

	for (; candidate != NULL; candidate = candidate->next)
	{
		if (candidate->type != XML_ELEMENT_NODE)
			continue;

		if (name != NULL && strcmp((const char*)(candidate->name), name) != 0)
			continue;

		return candidate;
	}

	return NULL;
}

xmlNode *containter_get_component_ref_by_id(xmlNode *container, const char *component_id)
{
	for (xmlNode *component_ref = container->children; component_ref != NULL; component_ref = component_ref->next)
	{
		if (component_ref->type != XML_ELEMENT_NODE)
			continue;

		if (strcmp((const char*)(component_ref->name), "component-ref") != 0)
			continue;

		xmlChar* cref_id = xmlGetProp(component_ref, BAD_CAST "id");
		// if cref_id is zero we have encountered a fatal error that will be handled
		// in ds_sds_dump_component_ref
		if (component_id && cref_id && strcmp(component_id, (char*)cref_id) != 0)
		{
			xmlFree(cref_id);
			continue;
		}
		xmlFree(cref_id);
		return component_ref;
	}
	return NULL;
}

static xmlNodePtr ds_sds_find_component_ref(xmlNodePtr datastream, const char* id)
{
	/* This searches for a ds:component-ref (XLink) element with a given id.
	 * It returns a first such element in a given ds:data-stream.
	 */
	xmlNodePtr cref_parent = datastream->children;

	for (; cref_parent != NULL; cref_parent = cref_parent->next)
	{
		if (cref_parent->type != XML_ELEMENT_NODE)
			continue;
		xmlNode *component_ref = containter_get_component_ref_by_id(cref_parent, id);
		if (component_ref != NULL) {
			return component_ref;
		}
	}

	return NULL;
}

static xmlNodePtr _lookup_component_in_collection(xmlDocPtr doc, const char *component_id)
{
	xmlNodePtr root = xmlDocGetRootElement(doc);
	xmlNodePtr component = NULL;
	xmlNodePtr candidate = root->children;

	for (; candidate != NULL; candidate = candidate->next)
	{
		if (candidate->type != XML_ELEMENT_NODE)
			continue;

		if ((strcmp((const char*)(candidate->name), "component") != 0) &&
		    (strcmp((const char*)(candidate->name), "extended-component") != 0))
			continue;

		char* candidate_id = (char*)xmlGetProp(candidate, BAD_CAST "id");
		if (strcmp(candidate_id, component_id) == 0)
		{
			component = candidate;
			xmlFree(candidate_id);
			break;
		}
		xmlFree(candidate_id);
	}
	return component;
}

static int ds_sds_dump_component_sce(xmlNode *script_node, const char *component_id, const char *filename)
{
	if (script_node) {
		// TODO: should we check whether the component is extended?
		int fd;
		xmlChar* text_contents = xmlNodeGetContent(script_node);
		if ((fd = open(filename, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0700)) < 0) {
			oscap_seterr(OSCAP_EFAMILY_XML, "Error while creating script component (id='%s') to file '%s'.", component_id, filename);
			xmlFree(text_contents);
			return -1;
		}
		FILE* output_file = fdopen(fd, "w");
		if (output_file == NULL) {
			oscap_seterr(OSCAP_EFAMILY_XML, "Error while dumping script component (id='%s') to file '%s'.", component_id, filename);
			xmlFree(text_contents);
			close(fd);
			return -1;
		}
		// TODO: error checking, fprintf should return strlen((const char*)text_contents)
		fprintf(output_file, "%s", text_contents ? (char*)text_contents : "");
		// NB: This code is for SCE scripts
		if (fchmod(fd, 0700) != 0) {
			oscap_seterr(OSCAP_EFAMILY_XML, "Failed to set executable permission on script (id='%s') that was split to '%s'.", component_id, filename);
		}

		fclose(output_file);
		xmlFree(text_contents);
		return 0;
	}
	else {
		oscap_seterr(OSCAP_EFAMILY_XML, "Error while dumping script component (id='%s') to file '%s'. "
			"The script element was empty!", component_id, filename);
		return -1;
	}
}

static int ds_sds_dump_component(const char* component_id, struct ds_sds_session *session, const char* filename)
{
	xmlDoc *doc = ds_sds_session_get_xmlDoc(session);
	xmlNodePtr component = _lookup_component_in_collection(doc, component_id);
	if (component == NULL)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "Component of given id '%s' was not found in the document.", component_id);
		return -1;
	}

	xmlNodePtr inner_root = node_get_child_element(component, NULL);

	if (inner_root == NULL)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "Found component (id='%s') but it has no element contents, nothing to dump, skipping...", component_id);
		return -1;
	}

	// If the inner root is script, we have to treat it in a special way
	if (strcmp((const char*)inner_root->name, "script") == 0) {
		int ret = ds_sds_dump_component_sce(inner_root->children, component_id, filename);
		if (ret != 0) {
			return ret;
		}
	}
	// Otherwise we create a new XML doc we will dump the contents to.
	// We can't just dump node "innerXML" because namespaces have to be
	// handled.
	else {
		xmlDoc *new_doc = ds_doc_from_foreign_node(inner_root, doc);
		if (new_doc == NULL) {
			return -1;
		}
		char *realpath = oscap_acquire_guess_realpath(filename);
		struct oscap_source *source = oscap_source_new_from_xmlDoc(new_doc, realpath);
		oscap_free(realpath);
		ds_sds_session_register_component_source(session, oscap_source_readable_origin(source), source);
	}

	return 0;
}

int ds_sds_dump_component_ref_as(xmlNodePtr component_ref, struct ds_sds_session *session, const char* target_dir, const char* filename)
{
	char* cref_id = (char*)xmlGetProp(component_ref, BAD_CAST "id");
	if (!cref_id)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "No or invalid id attribute on given component-ref.");
		xmlFree(cref_id);
		return -1;
	}

	char* xlink_href = (char*)xmlGetNsProp(component_ref, BAD_CAST "href", BAD_CAST xlink_ns_uri);
	if (!xlink_href || strlen(xlink_href) < 2)
	{
		oscap_seterr(OSCAP_EFAMILY_XML, "No or invalid xlink:href attribute on given component-ref.");
		xmlFree(cref_id);
		xmlFree(xlink_href);
		return -1;
	}
{'fraction_non_alphanumeric': 0.12060718158045794, 'fraction_numerical': 0.007653549333503412, 'mean_word_length': 4.781710914454277, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 98}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5951825', 'n_tokens_mistral': 5843, 'n_tokens_neox': 5466, 'n_words': 1467}