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