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