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