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