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