/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::cppu; using namespace ::osl; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::io; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::registry; using ::std::list; using ::std::map; using ::std::pair; #define _INPUT_BUFFER_SIZE 4096 #define _OUTPUT_BUFFER_SIZE 4096 namespace XSLT { const char* const LibXSLTTransformer::PARAM_SOURCE_URL = "sourceURL"; const char* const LibXSLTTransformer::PARAM_SOURCE_BASE_URL = "sourceBaseURL"; const char* const LibXSLTTransformer::PARAM_TARGET_URL = "targetURL"; const char* const LibXSLTTransformer::PARAM_TARGET_BASE_URL = "targetBaseURL"; const char* const LibXSLTTransformer::PARAM_DOCTYPE_PUBLIC = "publicType"; const sal_Int32 Reader::OUTPUT_BUFFER_SIZE = _OUTPUT_BUFFER_SIZE; const sal_Int32 Reader::INPUT_BUFFER_SIZE = _INPUT_BUFFER_SIZE; /** * ParserInputBufferCallback forwards IO call-backs to libxml stream IO. */ struct ParserInputBufferCallback { static int on_read(void * context, char * buffer, int len) { Reader * tmp = static_cast (context); return tmp->read(buffer, len); } static int on_close(void * ) { return 0; } }; /** * ParserOutputBufferCallback forwards IO call-backs to libxml stream IO. */ struct ParserOutputBufferCallback { static int on_write(void * context, const char * buffer, int len) { Reader * tmp = static_cast (context); return tmp->write(buffer, len); } static int on_close(void * context) { Reader * tmp = static_cast (context); return tmp->closeOutput(); } }; /** * ExtFuncOleCB forwards XPath extension function calls registered with libxslt to the OleHandler instance that actually * provides the implementation for those functions. * * The OLE extension module currently supplies two functions * insertByName: registers an OLE object to be later inserted into the output tree. * getByName: reads a previously registered OLE object and returns a base64 encoded string representation. */ struct ExtFuncOleCB { static void * init(xsltTransformContextPtr, const xmlChar*) { return NULL; } static void insertByName(xmlXPathParserContextPtr ctxt, int nargs) { xsltTransformContextPtr tctxt; void *data; if (nargs != 2) { xsltGenericError(xsltGenericErrorContext, "insertByName: requires exactly 2 arguments\n"); return; } tctxt = xsltXPathGetTransformContext(ctxt); if (tctxt == NULL) { xsltGenericError(xsltGenericErrorContext, "xsltExtFunctionTest: failed to get the transformation context\n"); return; } // XXX: someone with better knowledge of libxslt might come up with a better // idea to pass the OleHandler than by attaching it to tctxt->_private. See also // below. data = tctxt->_private; if (data == NULL) { xsltGenericError(xsltGenericErrorContext, "xsltExtFunctionTest: failed to get module data\n"); return; } OleHandler * oh = static_cast (data); xmlXPathObjectPtr value = valuePop(ctxt); value = ensureStringValue(value, ctxt); xmlXPathObjectPtr streamName = valuePop(ctxt); streamName = ensureStringValue(streamName, ctxt); oh->insertByName(OUString::createFromAscii(reinterpret_cast(streamName->stringval)), OString(reinterpret_cast(value->stringval))); valuePush(ctxt, xmlXPathNewCString("")); } static xmlXPathObjectPtr ensureStringValue(xmlXPathObjectPtr obj, const xmlXPathParserContextPtr ctxt) { if (obj->type != XPATH_STRING) { valuePush(ctxt, obj); xmlXPathStringFunction(ctxt, 1); obj = valuePop(ctxt); } return obj; } static void getByName(xmlXPathParserContextPtr ctxt, int nargs) { xsltTransformContextPtr tctxt; void *data; if (nargs != 1) { xsltGenericError(xsltGenericErrorContext, "getByName: requires exactly 1 argument\n"); return; } tctxt = xsltXPathGetTransformContext(ctxt); if (tctxt == NULL) { xsltGenericError(xsltGenericErrorContext, "xsltExtFunctionTest: failed to get the transformation context\n"); return; } // XXX: someone with better knowledge of libxslt might come up with a better // idea to pass the OleHandler than by attaching it to tctxt->_private data = tctxt->_private; if (data == NULL) { xsltGenericError(xsltGenericErrorContext, "xsltExtFunctionTest: failed to get module data\n"); return; } OleHandler * oh = static_cast (data); xmlXPathObjectPtr streamName = valuePop(ctxt); streamName = ensureStringValue(streamName, ctxt); const OString content = oh->getByName(OUString::createFromAscii(reinterpret_cast(streamName->stringval))); valuePush(ctxt, xmlXPathNewCString(content.getStr())); xmlXPathFreeObject(streamName); } }; Reader::Reader(LibXSLTTransformer* transformer) : Thread("LibXSLTTransformer"), m_transformer(transformer), m_readBuf(INPUT_BUFFER_SIZE), m_writeBuf(OUTPUT_BUFFER_SIZE) { LIBXML_TEST_VERSION; } ; int Reader::read(char * buffer, int len) { // const char *ptr = (const char *) context; if (buffer == NULL || len < 0) return (-1); sal_Int32 n; css::uno::Reference xis = this->m_transformer->getInputStream(); n = xis.get()->readBytes(m_readBuf, len); if (n > 0) { memcpy(buffer, m_readBuf.getArray(), n); } return n; } int Reader::write(const char * buffer, int len) { if (buffer == NULL || len < 0) return -1; if (len > 0) { css::uno::Reference xos = m_transformer->getOutputStream(); sal_Int32 writeLen = len; sal_Int32 bufLen = ::std::min(writeLen, this->OUTPUT_BUFFER_SIZE); const sal_uInt8* memPtr = reinterpret_cast (buffer); while (writeLen > 0) { sal_Int32 n = ::std::min(writeLen, bufLen); m_writeBuf.realloc(n); memcpy(m_writeBuf.getArray(), memPtr, static_cast (n)); xos.get()->writeBytes(m_writeBuf); memPtr += n; writeLen -= n; } } return len; } int Reader::closeOutput() { css::uno::Reference xos = m_transformer->getOutputStream(); if (xos.is()) { xos.get()->flush(); xos.get()->closeOutput(); } m_transformer->done(); return 0; } void Reader::execute() { OSL_ASSERT(m_transformer != NULL); OSL_ASSERT(m_transformer->getInputStream().is()); OSL_ASSERT(m_transformer->getOutputStream().is()); OSL_ASSERT(!m_transformer->getStyleSheetURL().isEmpty()); ::std::map::iterator pit; ::std::map pmap = m_transformer->getParameters(); ::std::vector< const char* > params( pmap.size() * 2 + 1 ); // build parameters int paramIndex = 0; for (pit = pmap.begin(); pit != pmap.end(); ++pit) { params[paramIndex++] = (*pit).first; params[paramIndex++] = (*pit).second.getStr(); } params[paramIndex] = NULL; xmlDocPtr doc = xmlReadIO(&ParserInputBufferCallback::on_read, &ParserInputBufferCallback::on_close, static_cast (this), NULL, NULL, 0); xsltStylesheetPtr styleSheet = xsltParseStylesheetFile( reinterpret_cast(m_transformer->getStyleSheetURL().getStr())); xmlDocPtr result = NULL; xsltTransformContextPtr tcontext = NULL; exsltRegisterAll(); registerExtensionModule(); #if OSL_DEBUG_LEVEL > 1 xsltSetGenericDebugFunc(stderr, NULL); xsltDebugDumpExtensions(NULL); #endif std::unique_ptr oh(new OleHandler(m_transformer->getComponentContext())); if (styleSheet) { tcontext = xsltNewTransformContext(styleSheet, doc); tcontext->_private = static_cast (oh.get()); xsltQuoteUserParams(tcontext, ¶ms[0]); result = xsltApplyStylesheetUser(styleSheet, doc, 0, 0, 0, tcontext); } if (result) { xmlCharEncodingHandlerPtr encoder = xmlGetCharEncodingHandler( XML_CHAR_ENCODING_UTF8); xmlOutputBufferPtr outBuf = xmlAllocOutputBuffer(encoder); outBuf->context = static_cast (this); outBuf->writecallback = &ParserOutputBufferCallback::on_write; outBuf->closecallback = &ParserOutputBufferCallback::on_close; xsltSaveResultTo(outBuf, result, styleSheet); xmlOutputBufferClose(outBuf); } else { xmlErrorPtr lastErr = xmlGetLastError(); OUString msg; if (lastErr) msg = OUString::createFromAscii(lastErr->message); else msg = "Unknown XSLT transformation error"; m_transformer->error(msg); } closeOutput(); oh.reset(); xsltFreeStylesheet(styleSheet); xsltFreeTransformContext(tcontext); xmlFreeDoc(doc); xmlFreeDoc(result); } void Reader::registerExtensionModule() { const xmlChar* oleModuleURI = reinterpret_cast(EXT_MODULE_OLE_URI); xsltRegisterExtModule(oleModuleURI, &ExtFuncOleCB::init, NULL); xsltRegisterExtModuleFunction( reinterpret_cast("insertByName"), oleModuleURI, &ExtFuncOleCB::insertByName); xsltRegisterExtModuleFunction( reinterpret_cast("getByName"), oleModuleURI, &ExtFuncOleCB::getByName); } Reader::~Reader() { } LibXSLTTransformer::LibXSLTTransformer( const css::uno::Reference & rxContext) : m_xContext(rxContext) { } void LibXSLTTransformer::setInputStream( const css::uno::Reference& inputStream) throw (RuntimeException, std::exception) { m_rInputStream = inputStream; } css::uno::Reference LibXSLTTransformer::getInputStream() throw (RuntimeException, std::exception) { return m_rInputStream; } void LibXSLTTransformer::setOutputStream( const css::uno::Reference& outputStream) throw (RuntimeException, std::exception) { m_rOutputStream = outputStream; } css::uno::Reference LibXSLTTransformer::getOutputStream() throw (RuntimeException, std::exception) { return m_rOutputStream; } void LibXSLTTransformer::addListener(const css::uno::Reference& listener) throw (RuntimeException, std::exception) { m_listeners.insert(m_listeners.begin(), listener); } void LibXSLTTransformer::removeListener( const css::uno::Reference& listener) throw (RuntimeException, std::exception) { m_listeners.remove(listener); } void LibXSLTTransformer::start() throw (RuntimeException, std::exception) { ListenerList::iterator it; ListenerList* l = &m_listeners; for (it = l->begin(); it != l->end(); ++it) { css::uno::Reference xl = *it; xl.get()->started(); } OSL_ENSURE(!m_Reader.is(), "Somebody forgot to call terminate *and* holds a reference to this LibXSLTTransformer instance"); m_Reader = new Reader(this); m_Reader->launch(); } void LibXSLTTransformer::error(const OUString& msg) { ListenerList* l = &m_listeners; Any arg; arg <<= Exception(msg, *this); for (ListenerList::iterator it = l->begin(); it != l->end(); ++it) { css::uno::Reference xl = *it; if (xl.is()) { xl.get()->error(arg); } } } void LibXSLTTransformer::done() { ListenerList* l = &m_listeners; for (ListenerList::iterator it = l->begin(); it != l->end(); ++it) { css::uno::Reference xl = *it; if (xl.is()) { xl.get()->closed(); } } } void LibXSLTTransformer::terminate() throw (RuntimeException, std::exception) { if (m_Reader.is()) { m_Reader->terminate(); m_Reader->join(); } m_Reader.clear(); m_parameters.clear(); } void LibXSLTTransformer::initialize(const Sequence& args) throw (RuntimeException, std::exception) { Sequence params; if (!(args[0] >>= params)) { // backward compatibility for old clients using createInstance params = args; } xmlSubstituteEntitiesDefault(0); m_parameters.clear(); for (int i = 0; i < params.getLength(); i++) { NamedValue nv; params[i] >>= nv; OString nameUTF8 = OUStringToOString(nv.Name, RTL_TEXTENCODING_UTF8); OUString value; OString valueUTF8; if (nv.Value >>= value) { valueUTF8 = OUStringToOString(value, RTL_TEXTENCODING_UTF8); } else { // ignore non-string parameters continue; } if (nameUTF8.equals("StylesheetURL")) { m_styleSheetURL = valueUTF8; } else if (nameUTF8.equals("SourceURL")) { m_parameters.insert(pair ( PARAM_SOURCE_URL, valueUTF8)); } else if (nameUTF8.equals("SourceBaseURL")) { m_parameters.insert(pair ( PARAM_SOURCE_BASE_URL, valueUTF8)); } else if (nameUTF8.equals("TargetURL")) { m_parameters.insert(pair ( PARAM_TARGET_URL, valueUTF8)); } else if (nameUTF8.equals("TargetBaseURL")) { m_parameters.insert(pair ( PARAM_TARGET_BASE_URL, valueUTF8)); } else if (nameUTF8.equals("DoctypePublic")) { m_parameters.insert(pair ( PARAM_DOCTYPE_PUBLIC, valueUTF8)); } } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */