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