/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmlsecurity.hxx"

#include <rtl/ustring.hxx>

#include "saxhelper.hxx"
#include "libxml/parserInternals.h"

#ifndef XMLSEC_NO_XSLT
#include "libxslt/xslt.h"
#endif

namespace cssu = com::sun::star::uno;
namespace cssxs = com::sun::star::xml::sax;
namespace cssxcsax = com::sun::star::xml::csax;

/**
 * The return value is NULL terminated. The application has the responsibilty to
 * deallocte the return value.
 */
xmlChar* ous_to_xmlstr( const rtl::OUString& oustr ) 
{
	rtl::OString ostr = rtl::OUStringToOString( oustr , RTL_TEXTENCODING_UTF8 ) ;
	return xmlStrndup( ( xmlChar* )ostr.getStr(), ( int )ostr.getLength() ) ;
}

/**
 * The return value is NULL terminated. The application has the responsibilty to
 * deallocte the return value.
 */
xmlChar* ous_to_nxmlstr( const rtl::OUString& oustr, int& length ) 
{
	rtl::OString ostr = rtl::OUStringToOString( oustr , RTL_TEXTENCODING_UTF8 ) ;
	length = ostr.getLength();

	return xmlStrndup( ( xmlChar* )ostr.getStr(), length ) ;
}

/**
 * The input parameter isn't necessaryly NULL terminated.
 */
rtl::OUString xmlchar_to_ous( const xmlChar* pChar, int length ) 
{
	if( pChar != NULL )
	{
		return rtl::OUString( ( sal_Char* )pChar , length , RTL_TEXTENCODING_UTF8 ) ;
	}
	else
	{
		return rtl::OUString() ;
	}
}

/**
 * The input parameter is NULL terminated
 */
rtl::OUString xmlstr_to_ous( const xmlChar* pStr ) 
{
	if( pStr != NULL ) 
	{
		return xmlchar_to_ous( pStr , xmlStrlen( pStr ) ) ;
	}
	else
	{
		return rtl::OUString() ;
	}
}

/**
 * The return value and the referenced value must be NULL terminated.
 * The application has the responsibilty to deallocte the return value.
 */
const xmlChar** attrlist_to_nxmlstr( const cssu::Sequence< cssxcsax::XMLAttribute >& aAttributes ) 
{
	xmlChar* attname = NULL ;
	xmlChar* attvalue = NULL ;
	const xmlChar** attrs = NULL ;
	rtl::OUString oustr ;
	
	sal_Int32 nLength = aAttributes.getLength();;

	if( nLength != 0 ) 
	{
		attrs = ( const xmlChar** )xmlMalloc( ( nLength * 2 + 2 ) * sizeof( xmlChar* ) ) ;
	}
	else
	{
		return NULL ;
	}

	for( int i = 0 , j = 0 ; j < nLength ; ++j ) 
	{
		attname = ous_to_xmlstr( aAttributes[j].sName ) ;
		attvalue = ous_to_xmlstr( aAttributes[j].sValue ) ;

		if( attname != NULL && attvalue != NULL ) 
		{
			attrs[i++] = attname ;
			attrs[i++] = attvalue ;
			attrs[i] = NULL ;
			attrs[i+1] = NULL ;
		}
		else
		{
			if( attname != NULL )
				xmlFree( attname ) ;
			if( attvalue != NULL )
				xmlFree( attvalue ) ;
		}
	}

	return attrs ;
}

/**
 * Constructor
 *
 * In this constructor, a libxml sax parser context is initialized. a libxml
 * default sax handler is initialized with the context.
 */
SAXHelper::SAXHelper( )
	: m_pParserCtxt( NULL ),
	  m_pSaxHandler( NULL )
{
	xmlInitParser() ;
	LIBXML_TEST_VERSION ;
	
	/*
	 * compile error:
	 * xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS ;
	 */
	xmlSubstituteEntitiesDefault( 1 ) ;

#ifndef XMLSEC_NO_XSLT
	xmlIndentTreeOutput = 1 ;
#endif /* XMLSEC_NO_XSLT */

	m_pParserCtxt = xmlNewParserCtxt() ;

	/*
	 * i41748
	 *
	 * mmi : re-initialize the SAX handler to version 1
	 */
	
	xmlSAXVersion(m_pParserCtxt->sax, 1);

	/* end */

	if( m_pParserCtxt->inputTab[0] != NULL ) 
	{
		m_pParserCtxt->inputTab[0] = NULL ;
	}
	
	if( m_pParserCtxt == NULL ) 
	{
#ifndef XMLSEC_NO_XSLT
		xsltCleanupGlobals() ;            
#endif
//      see issue i74334, we cannot call xmlCleanupParser when libxml is still used
//		in other parts of the office.
//		xmlCleanupParser() ;
		throw cssu::RuntimeException() ;
	}
	else if( m_pParserCtxt->sax == NULL ) 
	{
		xmlFreeParserCtxt( m_pParserCtxt ) ;

#ifndef XMLSEC_NO_XSLT
		xsltCleanupGlobals() ;
#endif
//      see issue i74334, we cannot call xmlCleanupParser when libxml is still used
//		in other parts of the office.
//		xmlCleanupParser() ;
		m_pParserCtxt = NULL ;
		throw cssu::RuntimeException() ;
	}
	else
	{
		m_pSaxHandler = m_pParserCtxt->sax ;

		//Adjust the context
		m_pParserCtxt->recovery = 1 ;
	}
}

/**
 * Destructor
 *
 * In this destructor, a libxml sax parser context is desturcted. The XML tree
 * in the context is not deallocated because the tree is bind with a document
 * model by the setTargetDocument method, which delegate the target document to
 * destruct the xml tree.
 */
SAXHelper::~SAXHelper() {
	if( m_pParserCtxt != NULL ) 
	{
		/*
		 * In the situation that no object refer the Document, this destructor
		 * must deallocate the Document memory
		 */
		if( m_pSaxHandler == m_pParserCtxt->sax )
		{
			m_pSaxHandler = NULL ;
		}

		xmlFreeParserCtxt( m_pParserCtxt ) ;
		m_pParserCtxt = NULL ;
	}

	if( m_pSaxHandler != NULL ) 
	{
		xmlFree( m_pSaxHandler ) ;
		m_pSaxHandler = NULL ;
	}
//      see issue i74334, we cannot call xmlCleanupParser when libxml is still used
//		in other parts of the office.
//	xmlCleanupParser() ;
}

xmlNodePtr SAXHelper::getCurrentNode()
{
	return m_pParserCtxt->node;
}

void SAXHelper::setCurrentNode(const xmlNodePtr pNode)
{
	/*
	 * This is really a black trick.
	 * When the current node is replaced, the nodeTab
	 * stack's top has to been replaced with the same
	 * node, in order to make compatibility.
	 */
	m_pParserCtxt->nodeTab[m_pParserCtxt->nodeNr - 1]
		= m_pParserCtxt->node 
			= pNode;
}

xmlDocPtr SAXHelper::getDocument()
{
	return m_pParserCtxt->myDoc;
}

/**
 * XDocumentHandler -- start an xml document
 */
void SAXHelper::startDocument( void )
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	/*
	 * Adjust inputTab
	 */
	xmlParserInputPtr pInput = xmlNewInputStream( m_pParserCtxt ) ;

	if( m_pParserCtxt->inputTab != NULL && m_pParserCtxt->inputMax != 0 ) 
	{
		m_pParserCtxt->inputTab[0] = pInput ;
		m_pParserCtxt->input = pInput ;
	}

	m_pSaxHandler->startDocument( m_pParserCtxt ) ;

	if( m_pParserCtxt == NULL || m_pParserCtxt->myDoc == NULL ) 
	{
		throw cssu::RuntimeException() ;
	}
}

/**
 * XDocumentHandler -- end an xml document
 */
void SAXHelper::endDocument( void ) 
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	m_pSaxHandler->endDocument( m_pParserCtxt ) ;
}

/**
 * XDocumentHandler -- start an xml element
 */
void SAXHelper::startElement(
	const rtl::OUString& aName,
	const cssu::Sequence< cssxcsax::XMLAttribute >& aAttributes )
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	const xmlChar* fullName = NULL ;
	const xmlChar** attrs = NULL ;

	fullName = ous_to_xmlstr( aName ) ;
	attrs = attrlist_to_nxmlstr( aAttributes ) ;
	
	if( fullName != NULL || attrs != NULL )
	{
		m_pSaxHandler->startElement( m_pParserCtxt , fullName , attrs ) ;
	}

	if( fullName != NULL ) 
	{
		xmlFree( ( xmlChar* )fullName ) ;
		fullName = NULL ;
	}
	
	if( attrs != NULL ) 
	{
		for( int i = 0 ; attrs[i] != NULL ; ++i ) 
		{
			xmlFree( ( xmlChar* )attrs[i] ) ;
			attrs[i] = NULL ;
		}
		
		xmlFree( ( void* ) attrs ) ;
		attrs = NULL ;
	}
}

/**
 * XDocumentHandler -- end an xml element
 */
void SAXHelper::endElement( const rtl::OUString& aName )
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	xmlChar* fullname = NULL ;

	fullname = ous_to_xmlstr( aName ) ;
	m_pSaxHandler->endElement( m_pParserCtxt , fullname ) ;

	if( fullname != NULL ) 
	{
		xmlFree( ( xmlChar* )fullname ) ;
		fullname = NULL ;
	}
}

/**
 * XDocumentHandler -- an xml element or cdata characters
 */
void SAXHelper::characters( const rtl::OUString& aChars )
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	const xmlChar* chars = NULL ;
	int length = 0 ;

	chars = ous_to_nxmlstr( aChars, length ) ;
	m_pSaxHandler->characters( m_pParserCtxt , chars , length ) ;

	if( chars != NULL ) 
	{
		xmlFree( ( xmlChar* )chars ) ;
	}
}

/**
 * XDocumentHandler -- ignorable xml white space
 */
void SAXHelper::ignorableWhitespace( const rtl::OUString& aWhitespaces ) 
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	const xmlChar* chars = NULL ;
	int length = 0 ;

	chars = ous_to_nxmlstr( aWhitespaces, length ) ;
	m_pSaxHandler->ignorableWhitespace( m_pParserCtxt , chars , length ) ;

	if( chars != NULL ) 
	{
		xmlFree( ( xmlChar* )chars ) ;
	}
}

/**
 * XDocumentHandler -- preaorocessing instruction
 */
void SAXHelper::processingInstruction(
	const rtl::OUString& aTarget,
	const rtl::OUString& aData )
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	xmlChar* target = NULL ;
	xmlChar* data = NULL ;

	target = ous_to_xmlstr( aTarget ) ;
	data = ous_to_xmlstr( aData ) ;

	m_pSaxHandler->processingInstruction( m_pParserCtxt , target , data ) ;

	if( target != NULL ) 
	{
		xmlFree( ( xmlChar* )target ) ;
		target = NULL ;
	}
	
	if( data != NULL ) 
	{
		xmlFree( ( xmlChar* )data ) ;
		data = NULL ;
	}
}

/**
 * XDocumentHandler -- set document locator
 * In this case, locator is useless.
 */
void SAXHelper::setDocumentLocator(
	const cssu::Reference< cssxs::XLocator > &)
	throw( cssxs::SAXException , cssu::RuntimeException ) 
{
	//--Pseudo code if necessary
	//--m_pSaxLocator is a member defined as xmlSAXHabdlerPtr
	//--m_pSaxLocatorHdl is a member defined as Sax_Locator

	//if( m_pSaxLocator != NULL ) {
	//	//Deallocate the memory
	//}
	//if( m_pSaxLocatorHdl != NULL ) {
	//	//Deallocate the memory
	//}

	//m_pSaxLocatorHdl = new Sax_Locator( xLocator ) ;
	//m_pSaxLocator = { m_pSaxLocatorHdl->getPublicId , m_pSaxLocatorHdl->getSystemId , m_pSaxLocatorHdl->getLineNumber , m_pSaxLocatorHdl->getColumnNumber } ;

	//m_pSaxHandler->setDocumentLocator( m_pParserCtxt , m_pSaxLocator ) ;
}

