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