Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 :
3 : /*
4 : * This file is part of the LibreOffice project.
5 : *
6 : * This Source Code Form is subject to the terms of the Mozilla Public
7 : * License, v. 2.0. If a copy of the MPL was not distributed with this
8 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 : */
10 :
11 : #include <algorithm>
12 : #include <cstdio>
13 : #include <cstring>
14 : #include <list>
15 : #include <map>
16 : #include <vector>
17 : #include <iostream>
18 : #include <libxml/parser.h>
19 : #include <libxml/tree.h>
20 : #include <libxml/xmlIO.h>
21 : #include <libxml/xpath.h>
22 : #include <libxml/xpathInternals.h>
23 : #include <libxml/xmlstring.h>
24 : #include <libxslt/transform.h>
25 : #include <libxslt/xsltutils.h>
26 : #include <libxslt/variables.h>
27 : #include <libxslt/extensions.h>
28 : #include <libexslt/exslt.h>
29 :
30 : #include <cppuhelper/factory.hxx>
31 : #include <cppuhelper/implbase4.hxx>
32 :
33 : #include <osl/module.h>
34 : #include <osl/file.hxx>
35 : #include <osl/process.h>
36 : #include <com/sun/star/lang/XComponent.hpp>
37 : #include <com/sun/star/lang/XInitialization.hpp>
38 : #include <com/sun/star/uno/Any.hxx>
39 : #include <com/sun/star/beans/NamedValue.hpp>
40 : #include <com/sun/star/io/XInputStream.hpp>
41 : #include <com/sun/star/io/XOutputStream.hpp>
42 : #include <com/sun/star/io/XActiveDataSource.hpp>
43 : #include <com/sun/star/io/XActiveDataSink.hpp>
44 : #include <com/sun/star/io/XActiveDataControl.hpp>
45 : #include <com/sun/star/io/XStreamListener.hpp>
46 :
47 : #include <LibXSLTTransformer.hxx>
48 : #include <OleHandler.hxx>
49 : #include <boost/scoped_ptr.hpp>
50 :
51 : using namespace ::cppu;
52 : using namespace ::osl;
53 : using namespace ::com::sun::star::beans;
54 : using namespace ::com::sun::star::io;
55 : using namespace ::com::sun::star::uno;
56 : using namespace ::com::sun::star::lang;
57 : using namespace ::com::sun::star::registry;
58 : using ::std::list;
59 : using ::std::map;
60 : using ::std::pair;
61 :
62 : #define _INPUT_BUFFER_SIZE 4096
63 : #define _OUTPUT_BUFFER_SIZE 4096
64 :
65 : namespace XSLT
66 : {
67 : const char* const LibXSLTTransformer::PARAM_SOURCE_URL = "sourceURL";
68 : const char* const LibXSLTTransformer::PARAM_SOURCE_BASE_URL =
69 : "sourceBaseURL";
70 : const char* const LibXSLTTransformer::PARAM_TARGET_URL = "targetURL";
71 : const char* const LibXSLTTransformer::PARAM_TARGET_BASE_URL =
72 : "targetBaseURL";
73 : const char* const LibXSLTTransformer::PARAM_DOCTYPE_PUBLIC = "publicType";
74 :
75 : const sal_Int32 Reader::OUTPUT_BUFFER_SIZE = _OUTPUT_BUFFER_SIZE;
76 :
77 : const sal_Int32 Reader::INPUT_BUFFER_SIZE = _INPUT_BUFFER_SIZE;
78 :
79 : /**
80 : * ParserInputBufferCallback forwards IO call-backs to libxml stream IO.
81 : */
82 : struct ParserInputBufferCallback
83 : {
84 : static int
85 65 : on_read(void * context, char * buffer, int len)
86 : {
87 65 : Reader * tmp = static_cast<Reader*> (context);
88 65 : return tmp->read(buffer, len);
89 : }
90 : static int
91 4 : on_close(void * )
92 : {
93 4 : return 0;
94 : }
95 : };
96 : /**
97 : * ParserOutputBufferCallback forwards IO call-backs to libxml stream IO.
98 : */
99 : struct ParserOutputBufferCallback
100 : {
101 : static int
102 10 : on_write(void * context, const char * buffer, int len)
103 : {
104 10 : Reader * tmp = static_cast<Reader*> (context);
105 10 : return tmp->write(buffer, len);
106 : }
107 : static int
108 4 : on_close(void * context)
109 : {
110 4 : Reader * tmp = static_cast<Reader*> (context);
111 4 : return tmp->closeOutput();
112 : }
113 : };
114 : /**
115 : * ExtFuncOleCB forwards XPath extension function calls registered with libxslt to the OleHandler instance that actually
116 : * provides the implementation for those functions.
117 : *
118 : * The OLE extension module currently supplies two functions
119 : * insertByName: registers an OLE object to be later inserted into the output tree.
120 : * getByName: reads a previously registered OLE object and returns a base64 encoded string representation.
121 : */
122 : struct ExtFuncOleCB
123 : {
124 : static void *
125 0 : init(xsltTransformContextPtr, const xmlChar*)
126 : {
127 0 : return NULL;
128 : }
129 : static void
130 0 : insertByName(xmlXPathParserContextPtr ctxt, int nargs)
131 : {
132 : xsltTransformContextPtr tctxt;
133 : void *data;
134 0 : if (nargs != 2) {
135 : xsltGenericError(xsltGenericErrorContext,
136 0 : "insertByName: requires exactly 2 arguments\n");
137 0 : return;
138 : }
139 0 : tctxt = xsltXPathGetTransformContext(ctxt);
140 0 : if (tctxt == NULL) {
141 : xsltGenericError(xsltGenericErrorContext,
142 0 : "xsltExtFunctionTest: failed to get the transformation context\n");
143 0 : return;
144 : }
145 : // XXX: someone with better knowledge of libxslt might come up with a better
146 : // idea to pass the OleHandler than by attaching it to tctxt->_private. See also
147 : // below.
148 0 : data = tctxt->_private;
149 0 : if (data == NULL) {
150 : xsltGenericError(xsltGenericErrorContext,
151 0 : "xsltExtFunctionTest: failed to get module data\n");
152 0 : return;
153 : }
154 0 : OleHandler * oh = static_cast<OleHandler*> (data);
155 :
156 0 : xmlXPathObjectPtr value = valuePop(ctxt);
157 0 : value = ensureStringValue(value, ctxt);
158 0 : xmlXPathObjectPtr streamName = valuePop(ctxt);
159 0 : streamName = ensureStringValue(streamName, ctxt);
160 :
161 0 : oh->insertByName(OUString::createFromAscii(reinterpret_cast<char*>(streamName->stringval)), OString(reinterpret_cast<char*>(value->stringval)));
162 0 : valuePush(ctxt, xmlXPathNewCString(""));
163 : }
164 :
165 0 : static xmlXPathObjectPtr ensureStringValue(xmlXPathObjectPtr obj, const xmlXPathParserContextPtr ctxt)
166 : {
167 0 : if (obj->type != XPATH_STRING) {
168 0 : valuePush(ctxt, obj);
169 0 : xmlXPathStringFunction(ctxt, 1);
170 0 : obj = valuePop(ctxt);
171 : }
172 0 : return obj;
173 : }
174 :
175 0 : static void getByName(xmlXPathParserContextPtr ctxt, int nargs)
176 : {
177 : xsltTransformContextPtr tctxt;
178 : void *data;
179 0 : if (nargs != 1) {
180 : xsltGenericError(xsltGenericErrorContext,
181 0 : "getByName: requires exactly 1 argument\n");
182 0 : return;
183 : }
184 :
185 0 : tctxt = xsltXPathGetTransformContext(ctxt);
186 0 : if (tctxt == NULL) {
187 : xsltGenericError(xsltGenericErrorContext,
188 0 : "xsltExtFunctionTest: failed to get the transformation context\n");
189 0 : return;
190 : }
191 : // XXX: someone with better knowledge of libxslt might come up with a better
192 : // idea to pass the OleHandler than by attaching it to tctxt->_private
193 0 : data = tctxt->_private;
194 0 : if (data == NULL) {
195 : xsltGenericError(xsltGenericErrorContext,
196 0 : "xsltExtFunctionTest: failed to get module data\n");
197 0 : return;
198 : }
199 0 : OleHandler * oh = static_cast<OleHandler*> (data);
200 0 : xmlXPathObjectPtr streamName = valuePop(ctxt);
201 0 : streamName = ensureStringValue(streamName, ctxt);
202 0 : const OString content = oh->getByName(OUString::createFromAscii(reinterpret_cast<char*>(streamName->stringval)));
203 0 : valuePush(ctxt, xmlXPathNewCString(content.getStr()));
204 0 : xmlXPathFreeObject(streamName);
205 : }
206 : };
207 :
208 4 : Reader::Reader(LibXSLTTransformer* transformer) :
209 : Thread("LibXSLTTransformer"), m_transformer(transformer),
210 4 : m_readBuf(INPUT_BUFFER_SIZE), m_writeBuf(OUTPUT_BUFFER_SIZE)
211 : {
212 4 : LIBXML_TEST_VERSION;
213 4 : }
214 : ;
215 :
216 : int
217 65 : Reader::read(char * buffer, int len)
218 : {
219 : // const char *ptr = (const char *) context;
220 65 : if (buffer == NULL || len < 0)
221 0 : return (-1);
222 : sal_Int32 n;
223 65 : css::uno::Reference<XInputStream> xis = this->m_transformer->getInputStream();
224 65 : n = xis.get()->readBytes(m_readBuf, len);
225 65 : if (n > 0)
226 : {
227 61 : memcpy(buffer, m_readBuf.getArray(), n);
228 : }
229 65 : return n;
230 : }
231 :
232 : int
233 10 : Reader::write(const char * buffer, int len)
234 : {
235 10 : if (buffer == NULL || len < 0)
236 0 : return -1;
237 10 : if (len > 0)
238 : {
239 6 : css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
240 6 : sal_Int32 writeLen = len;
241 : sal_Int32 bufLen = ::std::min(writeLen,
242 6 : this->OUTPUT_BUFFER_SIZE);
243 : const sal_uInt8* memPtr =
244 6 : reinterpret_cast<const sal_uInt8*> (buffer);
245 18 : while (writeLen > 0)
246 : {
247 6 : sal_Int32 n = ::std::min(writeLen, bufLen);
248 6 : m_writeBuf.realloc(n);
249 6 : memcpy(m_writeBuf.getArray(), memPtr,
250 12 : static_cast<size_t> (n));
251 6 : xos.get()->writeBytes(m_writeBuf);
252 6 : memPtr += n;
253 6 : writeLen -= n;
254 6 : }
255 : }
256 10 : return len;
257 : }
258 :
259 : int
260 8 : Reader::closeOutput()
261 : {
262 8 : css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
263 8 : if (xos.is())
264 : {
265 8 : xos.get()->flush();
266 8 : xos.get()->closeOutput();
267 : }
268 8 : m_transformer->done();
269 8 : return 0;
270 : }
271 :
272 : void
273 4 : Reader::execute()
274 : {
275 : OSL_ASSERT(m_transformer != NULL);
276 : OSL_ASSERT(m_transformer->getInputStream().is());
277 : OSL_ASSERT(m_transformer->getOutputStream().is());
278 : OSL_ASSERT(!m_transformer->getStyleSheetURL().isEmpty());
279 4 : ::std::map<const char*, OString>::iterator pit;
280 4 : ::std::map<const char*, OString> pmap = m_transformer->getParameters();
281 8 : ::std::vector< const char* > params( pmap.size() * 2 + 1 ); // build parameters
282 4 : int paramIndex = 0;
283 18 : for (pit = pmap.begin(); pit != pmap.end(); ++pit)
284 : {
285 14 : params[paramIndex++] = (*pit).first;
286 14 : params[paramIndex++] = (*pit).second.getStr();
287 : }
288 4 : params[paramIndex] = NULL;
289 : xmlDocPtr doc = xmlReadIO(&ParserInputBufferCallback::on_read,
290 : &ParserInputBufferCallback::on_close,
291 4 : static_cast<void*> (this), NULL, NULL, 0);
292 : xsltStylesheetPtr styleSheet = xsltParseStylesheetFile(
293 4 : reinterpret_cast<const xmlChar *>(m_transformer->getStyleSheetURL().getStr()));
294 4 : xmlDocPtr result = NULL;
295 4 : xsltTransformContextPtr tcontext = NULL;
296 4 : exsltRegisterAll();
297 4 : registerExtensionModule();
298 : #if OSL_DEBUG_LEVEL > 1
299 : xsltSetGenericDebugFunc(stderr, NULL);
300 : xsltDebugDumpExtensions(NULL);
301 : #endif
302 8 : boost::scoped_ptr<OleHandler> oh(new OleHandler(m_transformer->getComponentContext()));
303 4 : if (styleSheet)
304 : {
305 4 : tcontext = xsltNewTransformContext(styleSheet, doc);
306 4 : tcontext->_private = static_cast<void *> (oh.get());
307 4 : xsltQuoteUserParams(tcontext, ¶ms[0]);
308 : result = xsltApplyStylesheetUser(styleSheet, doc, 0, 0, 0,
309 4 : tcontext);
310 : }
311 :
312 4 : if (result)
313 : {
314 : xmlCharEncodingHandlerPtr encoder = xmlGetCharEncodingHandler(
315 4 : XML_CHAR_ENCODING_UTF8);
316 4 : xmlOutputBufferPtr outBuf = xmlAllocOutputBuffer(encoder);
317 4 : outBuf->context = static_cast<void *> (this);
318 4 : outBuf->writecallback = &ParserOutputBufferCallback::on_write;
319 4 : outBuf->closecallback = &ParserOutputBufferCallback::on_close;
320 4 : xsltSaveResultTo(outBuf, result, styleSheet);
321 4 : xmlOutputBufferClose(outBuf);
322 : }
323 : else
324 : {
325 0 : xmlErrorPtr lastErr = xmlGetLastError();
326 0 : OUString msg;
327 0 : if (lastErr)
328 0 : msg = OUString::createFromAscii(lastErr->message);
329 : else
330 0 : msg = "Unknown XSLT transformation error";
331 :
332 0 : m_transformer->error(msg);
333 : }
334 4 : closeOutput();
335 4 : oh.reset();
336 4 : xsltFreeStylesheet(styleSheet);
337 4 : xsltFreeTransformContext(tcontext);
338 4 : xmlFreeDoc(doc);
339 8 : xmlFreeDoc(result);
340 4 : }
341 :
342 : void
343 4 : Reader::registerExtensionModule()
344 : {
345 4 : const xmlChar* oleModuleURI = reinterpret_cast<const xmlChar *>(EXT_MODULE_OLE_URI);
346 4 : xsltRegisterExtModule(oleModuleURI, &ExtFuncOleCB::init, NULL);
347 : xsltRegisterExtModuleFunction(
348 : reinterpret_cast<const xmlChar*>("insertByName"),
349 : oleModuleURI,
350 4 : &ExtFuncOleCB::insertByName);
351 : xsltRegisterExtModuleFunction(
352 : reinterpret_cast<const xmlChar*>("getByName"),
353 : oleModuleURI,
354 4 : &ExtFuncOleCB::getByName);
355 :
356 4 : }
357 :
358 8 : Reader::~Reader()
359 : {
360 8 : }
361 :
362 4 : LibXSLTTransformer::LibXSLTTransformer(
363 : const css::uno::Reference<XComponentContext> & rxContext) :
364 4 : m_xContext(rxContext)
365 : {
366 4 : }
367 :
368 : void
369 4 : LibXSLTTransformer::setInputStream(
370 : const css::uno::Reference<XInputStream>& inputStream)
371 : throw (RuntimeException, std::exception)
372 : {
373 4 : m_rInputStream = inputStream;
374 4 : }
375 :
376 : css::uno::Reference<XInputStream>
377 65 : LibXSLTTransformer::getInputStream() throw (RuntimeException, std::exception)
378 : {
379 65 : return m_rInputStream;
380 : }
381 :
382 : void
383 4 : LibXSLTTransformer::setOutputStream(
384 : const css::uno::Reference<XOutputStream>& outputStream)
385 : throw (RuntimeException, std::exception)
386 : {
387 4 : m_rOutputStream = outputStream;
388 4 : }
389 :
390 : css::uno::Reference<XOutputStream>
391 14 : LibXSLTTransformer::getOutputStream() throw (RuntimeException, std::exception)
392 : {
393 14 : return m_rOutputStream;
394 : }
395 :
396 : void
397 4 : LibXSLTTransformer::addListener(const css::uno::Reference<XStreamListener>& listener)
398 : throw (RuntimeException, std::exception)
399 : {
400 4 : m_listeners.insert(m_listeners.begin(), listener);
401 4 : }
402 :
403 : void
404 0 : LibXSLTTransformer::removeListener(
405 : const css::uno::Reference<XStreamListener>& listener)
406 : throw (RuntimeException, std::exception)
407 : {
408 0 : m_listeners.remove(listener);
409 0 : }
410 :
411 : void
412 4 : LibXSLTTransformer::start() throw (RuntimeException, std::exception)
413 : {
414 4 : ListenerList::iterator it;
415 4 : ListenerList* l = &m_listeners;
416 8 : for (it = l->begin(); it != l->end(); ++it)
417 : {
418 4 : css::uno::Reference<XStreamListener> xl = *it;
419 4 : xl.get()->started();
420 4 : }
421 : OSL_ENSURE(!m_Reader.is(), "Somebody forgot to call terminate *and* holds a reference to this LibXSLTTransformer instance");
422 4 : m_Reader = new Reader(this);
423 4 : m_Reader->launch();
424 4 : }
425 :
426 : void
427 0 : LibXSLTTransformer::error(const OUString& msg)
428 : {
429 0 : ListenerList* l = &m_listeners;
430 0 : Any arg;
431 0 : arg <<= Exception(msg, *this);
432 0 : for (ListenerList::iterator it = l->begin(); it != l->end(); ++it)
433 : {
434 0 : css::uno::Reference<XStreamListener> xl = *it;
435 0 : if (xl.is())
436 : {
437 0 : xl.get()->error(arg);
438 : }
439 0 : }
440 0 : }
441 :
442 : void
443 8 : LibXSLTTransformer::done()
444 : {
445 8 : ListenerList* l = &m_listeners;
446 16 : for (ListenerList::iterator it = l->begin(); it != l->end(); ++it)
447 : {
448 8 : css::uno::Reference<XStreamListener> xl = *it;
449 8 : if (xl.is())
450 : {
451 8 : xl.get()->closed();
452 : }
453 8 : }
454 8 : }
455 :
456 : void
457 2 : LibXSLTTransformer::terminate() throw (RuntimeException, std::exception)
458 : {
459 2 : if (m_Reader.is())
460 : {
461 2 : m_Reader->terminate();
462 2 : m_Reader->join();
463 : }
464 2 : m_Reader.clear();
465 2 : m_parameters.clear();
466 2 : }
467 :
468 : void
469 4 : LibXSLTTransformer::initialize(const Sequence<Any>& args)
470 : throw (RuntimeException, std::exception)
471 : {
472 4 : Sequence<Any> params;
473 4 : if (!(args[0] >>= params))
474 : { // backward compatibility for old clients using createInstance
475 1 : params = args;
476 : }
477 4 : xmlSubstituteEntitiesDefault(0);
478 4 : m_parameters.clear();
479 26 : for (int i = 0; i < params.getLength(); i++)
480 : {
481 22 : NamedValue nv;
482 22 : params[i] >>= nv;
483 : OString nameUTF8 = OUStringToOString(nv.Name,
484 44 : RTL_TEXTENCODING_UTF8);
485 44 : OUString value;
486 44 : OString valueUTF8;
487 22 : if (nv.Value >>= value)
488 : {
489 44 : valueUTF8 = OUStringToOString(value,
490 22 : RTL_TEXTENCODING_UTF8);
491 : }
492 : else
493 : {
494 : // ignore non-string parameters
495 0 : continue;
496 : }
497 22 : if (nameUTF8.equals("StylesheetURL"))
498 : {
499 4 : m_styleSheetURL = valueUTF8;
500 : }
501 18 : else if (nameUTF8.equals("SourceURL"))
502 : {
503 : m_parameters.insert(pair<const char*, OString> (
504 2 : PARAM_SOURCE_URL, valueUTF8));
505 : }
506 16 : else if (nameUTF8.equals("SourceBaseURL"))
507 : {
508 : m_parameters.insert(pair<const char*, OString> (
509 2 : PARAM_SOURCE_BASE_URL, valueUTF8));
510 : }
511 14 : else if (nameUTF8.equals("TargetURL"))
512 : {
513 : m_parameters.insert(pair<const char*, OString> (
514 4 : PARAM_TARGET_URL, valueUTF8));
515 : }
516 10 : else if (nameUTF8.equals("TargetBaseURL"))
517 : {
518 : m_parameters.insert(pair<const char*, OString> (
519 4 : PARAM_TARGET_BASE_URL, valueUTF8));
520 : }
521 6 : else if (nameUTF8.equals("DoctypePublic"))
522 : {
523 : m_parameters.insert(pair<const char*, OString> (
524 2 : PARAM_DOCTYPE_PUBLIC, valueUTF8));
525 : }
526 26 : }
527 4 : }
528 :
529 :
530 6 : }
531 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
532 :
|