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