1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include <documentbuilder.hxx>
25 
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 
30 #include <libxml/xmlerror.h>
31 #include <libxml/tree.h>
32 
33 #include <boost/shared_ptr.hpp>
34 
35 #include <rtl/alloc.h>
36 #include <rtl/memory.h>
37 #include <rtl/ustrbuf.hxx>
38 
39 #include <cppuhelper/implbase1.hxx>
40 
41 #include <com/sun/star/xml/sax/SAXParseException.hpp>
42 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
43 #include <com/sun/star/task/XInteractionHandler.hpp>
44 
45 #include <ucbhelper/content.hxx>
46 #include <ucbhelper/commandenvironment.hxx>
47 
48 #include <node.hxx>
49 #include <document.hxx>
50 
51 
52 using ::rtl::OUStringBuffer;
53 using ::rtl::OString;
54 using ::com::sun::star::xml::sax::InputSource;
55 using namespace ucbhelper;
56 using namespace ::com::sun::star::ucb;
57 using ::com::sun::star::task::XInteractionHandler;
58 
59 
60 namespace DOM
61 {
62 
63 	class CDefaultEntityResolver : public cppu::WeakImplHelper1< XEntityResolver >
64 	{
65 	public:
resolveEntity(const OUString & sPublicId,const OUString & sSystemId)66 	    virtual InputSource SAL_CALL resolveEntity( const OUString& sPublicId, const OUString& sSystemId )
67 			throw (::com::sun::star::uno::RuntimeException)
68 		{
69 			InputSource is;
70 			is.sPublicId = sPublicId;
71 			is.sSystemId = sSystemId;
72 			is.sEncoding = OUString();
73 
74 			try {
75 				Reference< XCommandEnvironment > aEnvironment(
76 					new CommandEnvironment(Reference< XInteractionHandler >(),
77 										   Reference< XProgressHandler >() ));
78 				Content aContent(sSystemId, aEnvironment);
79 
80 				is.aInputStream = aContent.openStream();
81 			} catch (com::sun::star::uno::Exception) {
82 				OSL_ENSURE(sal_False, "exception in default entity resolver");
83 				is.aInputStream = Reference< XInputStream >();
84 			}
85 			return is;
86 		}
87 
88 	};
89 
CDocumentBuilder(Reference<XMultiServiceFactory> const & xFactory)90     CDocumentBuilder::CDocumentBuilder(
91             Reference< XMultiServiceFactory > const& xFactory)
92         : m_xFactory(xFactory)
93         , m_xEntityResolver(new CDefaultEntityResolver())
94     {
95         // init libxml. libxml will protect itself against multiple
96         // initializations so there is no problem here if this gets
97         // called multiple times.
98         xmlInitParser();
99     }
100 
_getInstance(const Reference<XMultiServiceFactory> & rSMgr)101     Reference< XInterface > CDocumentBuilder::_getInstance(const Reference< XMultiServiceFactory >& rSMgr)
102     {
103         return static_cast< XDocumentBuilder* >(new CDocumentBuilder(rSMgr));
104     }
105 
106     const char* CDocumentBuilder::aImplementationName = "com.sun.star.comp.xml.dom.DocumentBuilder";
107     const char* CDocumentBuilder::aSupportedServiceNames[] = {
108         "com.sun.star.xml.dom.DocumentBuilder",
109         NULL
110     };
111 
_getImplementationName()112     OUString CDocumentBuilder::_getImplementationName()
113     {
114 	    return OUString::createFromAscii(aImplementationName);
115     }
_getSupportedServiceNames()116     Sequence<OUString> CDocumentBuilder::_getSupportedServiceNames()
117     {
118 	    Sequence<OUString> aSequence;
119 	    for (int i=0; aSupportedServiceNames[i]!=NULL; i++) {
120 		    aSequence.realloc(i+1);
121 		    aSequence[i]=(OUString::createFromAscii(aSupportedServiceNames[i]));
122 	    }
123 	    return aSequence;
124     }
125 
getSupportedServiceNames()126     Sequence< OUString > SAL_CALL CDocumentBuilder::getSupportedServiceNames()
127         throw (RuntimeException)
128     {
129         return CDocumentBuilder::_getSupportedServiceNames();
130     }
131 
getImplementationName()132     OUString SAL_CALL CDocumentBuilder::getImplementationName()
133         throw (RuntimeException)
134     {
135         return CDocumentBuilder::_getImplementationName();
136     }
137 
supportsService(const OUString & aServiceName)138     sal_Bool SAL_CALL CDocumentBuilder::supportsService(const OUString& aServiceName)
139         throw (RuntimeException)
140     {
141         Sequence< OUString > supported = CDocumentBuilder::_getSupportedServiceNames();
142         for (sal_Int32 i=0; i<supported.getLength(); i++)
143         {
144             if (supported[i] == aServiceName) return sal_True;
145         }
146         return sal_False;
147     }
148 
getDOMImplementation()149     Reference< XDOMImplementation > SAL_CALL CDocumentBuilder::getDOMImplementation()
150         throw (RuntimeException)
151     {
152 
153         return Reference< XDOMImplementation >();
154     }
155 
isNamespaceAware()156     sal_Bool SAL_CALL CDocumentBuilder::isNamespaceAware()
157         throw (RuntimeException)
158     {
159         return sal_True;
160     }
161 
isValidating()162     sal_Bool SAL_CALL CDocumentBuilder::isValidating()
163         throw (RuntimeException)
164     {
165         return sal_False;
166     }
167 
newDocument()168     Reference< XDocument > SAL_CALL CDocumentBuilder::newDocument()
169         throw (RuntimeException)
170     {
171         ::osl::MutexGuard const g(m_Mutex);
172 
173         // create a new document
174         xmlDocPtr pDocument = xmlNewDoc((const xmlChar*)"1.0");
175         Reference< XDocument > const xRet(
176                 CDocument::CreateCDocument(pDocument).get());
177         return xRet;
178     }
179 
make_error_message(xmlParserCtxtPtr ctxt)180 	static OUString make_error_message(xmlParserCtxtPtr ctxt)
181 	{
182 		OUStringBuffer buf;
183 		buf.appendAscii(ctxt->lastError.message);
184 		buf.appendAscii("Line: ");
185 		buf.append(static_cast<sal_Int32>(ctxt->lastError.line));
186 		buf.appendAscii("\nColumn: ");
187 		buf.append(static_cast<sal_Int32>(ctxt->lastError.int2));
188 		OUString msg = buf.makeStringAndClear();
189 		return msg;
190 	}
191 
192 	// -- callbacks and context struct for parsing from stream
193 	// -- c-linkage, so the callbacks can be used by libxml
194 	extern "C" {
195 
196 	// context struct passed to IO functions
197 	typedef struct context {
198 		CDocumentBuilder *pBuilder;
199 		Reference< XInputStream > rInputStream;
200 		bool close;
201 		bool freeOnClose;
202 	} context_t;
203 
xmlIO_read_func(void * context,char * buffer,int len)204     static int xmlIO_read_func( void *context, char *buffer, int len)
205     {
206 		// get the context...
207         context_t *pctx = static_cast<context_t*>(context);
208 		if (!pctx->rInputStream.is())
209 			return -1;
210 		try {
211 			// try to read the requested number of bytes
212             Sequence< sal_Int8 > chunk(len);
213             int nread = pctx->rInputStream->readBytes(chunk, len);
214 
215             // copy bytes to the provided buffer
216             rtl_copyMemory(buffer, chunk.getConstArray(), nread);
217             return nread;
218 		} catch (com::sun::star::uno::Exception& ex) {
219             (void) ex;
220             OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
221             return -1;
222         }
223     }
224 
xmlIO_close_func(void * context)225     static int xmlIO_close_func(void* context)
226 	{
227 		// get the context...
228 		context_t *pctx = static_cast<context_t*>(context);
229 		if (!pctx->rInputStream.is())
230 			return 0;
231         try
232         {
233 			if (pctx->close)
234 				pctx->rInputStream->closeInput();
235 			if (pctx->freeOnClose)
236 				delete pctx;
237             return 0;
238 		} catch (com::sun::star::uno::Exception& ex) {
239             (void) ex;
240             OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
241             return -1;
242         }
243     }
244 
resolve_func(void * ctx,const xmlChar * publicId,const xmlChar * systemId)245 	static xmlParserInputPtr resolve_func(void *ctx,
246                                 const xmlChar *publicId,
247                                 const xmlChar *systemId)
248 	{
249 		// get the CDocumentBuilder object
250 		xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
251 		CDocumentBuilder *builder = static_cast< CDocumentBuilder* >(ctxt->_private);
252 		Reference< XEntityResolver > resolver = builder->getEntityResolver();
253 		OUString sysid;
254 		if (systemId != 0)
255 			sysid = OUString((sal_Char*)systemId, strlen((char*)systemId), RTL_TEXTENCODING_UTF8);
256 		OUString pubid;
257 		if (publicId != 0)
258 			pubid = OUString((sal_Char*)publicId, strlen((char*)publicId), RTL_TEXTENCODING_UTF8);
259 
260 		// resolve the entity
261 		InputSource src = resolver->resolveEntity(pubid, sysid);
262 
263 		// create IO context on heap because this call will no longer be on the stack
264 		// when IO is actually performed through the callbacks. The close function must
265 		// free the memory which is indicated by the freeOnClose field in the context struct
266 		context_t *c = new context_t;
267 		c->pBuilder = builder;
268 		c->rInputStream = src.aInputStream;
269 		c->close = true;
270 		c->freeOnClose = true;
271 
272 		// set up the inputBuffer and inputPtr for libxml
273 		xmlParserInputBufferPtr pBuffer =
274 			xmlParserInputBufferCreateIO(xmlIO_read_func, xmlIO_close_func, c, XML_CHAR_ENCODING_NONE);
275 		xmlParserInputPtr pInput =
276 					xmlNewIOInputStream(ctxt, pBuffer, XML_CHAR_ENCODING_NONE);
277 		return pInput;
278 	}
279 
280 #if 0
281 	static xmlParserInputPtr external_entity_loader(const char *URL, const char * /*ID*/, xmlParserCtxtPtr ctxt)
282 	{
283 		// just call our resolver function using the URL as systemId
284 		return resolve_func(ctxt, 0, (const xmlChar*)URL);
285 	}
286 #endif
287 
288 	// default warning handler triggers assertion
warning_func(void * ctx,const char *,...)289 	static void warning_func(void * ctx, const char * /*msg*/, ...)
290 	{
291 		OUStringBuffer buf(OUString::createFromAscii("libxml2 warning\n"));
292 		buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
293 		OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
294 		OSL_ENSURE(sal_False, msg.getStr());
295 	}
296 
297 	// default error handler triggers assertion
error_func(void * ctx,const char *,...)298 	static void error_func(void * ctx, const char * /*msg*/, ...)
299 	{
300 		OUStringBuffer buf(OUString::createFromAscii("libxml2 error\n"));
301 		buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
302 		OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
303 		OSL_ENSURE(sal_False, msg.getStr());
304 	}
305 
306 	} // extern "C"
307 
throwEx(xmlParserCtxtPtr ctxt)308     void throwEx(xmlParserCtxtPtr ctxt) {
309         OUString msg = make_error_message(ctxt);
310         com::sun::star::xml::sax::SAXParseException saxex;
311         saxex.Message = msg;
312         saxex.LineNumber = static_cast<sal_Int32>(ctxt->lastError.line);
313         saxex.ColumnNumber = static_cast<sal_Int32>(ctxt->lastError.int2);
314         throw saxex;
315     }
316 
parse(const Reference<XInputStream> & is)317     Reference< XDocument > SAL_CALL CDocumentBuilder::parse(const Reference< XInputStream >& is)
318         throw (RuntimeException, SAXParseException, IOException)
319     {
320         if (!is.is()) {
321             throw RuntimeException();
322         }
323 
324         ::osl::MutexGuard const g(m_Mutex);
325 
326 		// encoding...
327 		/*
328 		xmlChar *encstr = (xmlChar*) OUStringToOString(src.sEncoding, RTL_TEXTENCODING_UTF8).getStr();
329 		xmlCharEncoding enc = xmlParseCharEncoding(encstr);
330 		*/
331 
332         ::boost::shared_ptr<xmlParserCtxt> const pContext(
333                 xmlNewParserCtxt(), xmlFreeParserCtxt);
334 
335 		// register error functions to prevent errors being printed
336 		// on the console
337         pContext->_private = this;
338         pContext->sax->error = error_func;
339         pContext->sax->warning = warning_func;
340         pContext->sax->resolveEntity = resolve_func;
341 
342 		// IO context struct
343 		context_t c;
344 		c.pBuilder = this;
345 		c.rInputStream = is;
346 		// we did not open the stream, thus we do not close it.
347 		c.close = false;
348 		c.freeOnClose = false;
349         xmlDocPtr const pDoc = xmlCtxtReadIO(pContext.get(),
350                 xmlIO_read_func, xmlIO_close_func, &c, 0, 0, 0);
351 
352 		if (pDoc == 0) {
353             throwEx(pContext.get());
354         }
355         Reference< XDocument > const xRet(
356                 CDocument::CreateCDocument(pDoc).get());
357         return xRet;
358     }
359 
parseURI(const OUString & sUri)360 	Reference< XDocument > SAL_CALL CDocumentBuilder::parseURI(const OUString& sUri)
361 		throw (RuntimeException, SAXParseException, IOException)
362 	{
363         ::osl::MutexGuard const g(m_Mutex);
364 
365         ::boost::shared_ptr<xmlParserCtxt> const pContext(
366                 xmlNewParserCtxt(), xmlFreeParserCtxt);
367         pContext->_private = this;
368         pContext->sax->error = error_func;
369         pContext->sax->warning = warning_func;
370         pContext->sax->resolveEntity = resolve_func;
371 		// xmlSetExternalEntityLoader(external_entity_loader);
372 		OString oUri = OUStringToOString(sUri, RTL_TEXTENCODING_UTF8);
373 		char *uri = (char*) oUri.getStr();
374         xmlDocPtr pDoc = xmlCtxtReadFile(pContext.get(), uri, 0, 0);
375 		if (pDoc == 0) {
376             throwEx(pContext.get());
377         }
378         Reference< XDocument > const xRet(
379                 CDocument::CreateCDocument(pDoc).get());
380         return xRet;
381 	}
382 
383     void SAL_CALL
setEntityResolver(Reference<XEntityResolver> const & xER)384     CDocumentBuilder::setEntityResolver(Reference< XEntityResolver > const& xER)
385 		throw (RuntimeException)
386 	{
387         ::osl::MutexGuard const g(m_Mutex);
388 
389         m_xEntityResolver = xER;
390     }
391 
getEntityResolver()392 	Reference< XEntityResolver > SAL_CALL CDocumentBuilder::getEntityResolver()
393 		throw (RuntimeException)
394     {
395         ::osl::MutexGuard const g(m_Mutex);
396 
397         return m_xEntityResolver;
398     }
399 
400     void SAL_CALL
setErrorHandler(Reference<XErrorHandler> const & xEH)401     CDocumentBuilder::setErrorHandler(Reference< XErrorHandler > const& xEH)
402 		throw (RuntimeException)
403     {
404         ::osl::MutexGuard const g(m_Mutex);
405 
406         m_xErrorHandler = xEH;
407     }
408 }
409