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 package com.sun.star.comp.xsltfilter;
23 
24 
25 
26 //Standard Java classes
27 import java.io.FileWriter;
28 import java.util.zip.Inflater;
29 import java.util.zip.Deflater;
30 
31 //StarOffice Interfaces and UNO
32 import com.sun.star.bridge.XBridgeFactory;
33 import com.sun.star.bridge.XBridge;
34 import com.sun.star.connection.XConnector;
35 import com.sun.star.connection.XConnection;
36 import com.sun.star.container.XNameContainer;
37 import com.sun.star.embed.XTransactedObject;
38 import com.sun.star.io.XStream;
39 import com.sun.star.io.XSeekable;
40 import com.sun.star.io.XInputStream;
41 import com.sun.star.io.XOutputStream;
42 import com.sun.star.lang.XMultiServiceFactory;
43 import com.sun.star.lang.XComponent;
44 import com.sun.star.uno.XComponentContext;
45 import com.sun.star.uno.UnoRuntime;
46 
47 /** This class is an xalan extension class. It provider 2 elements
48  *  and 2 functions to used in xslt script. With this elements and functions
49  *  we can convert between oledata between Wordml and OOo flat.
50  *  To use it, we need a running OOo. There are two ways to get the XMultiServiceFactory.
51  *  When called by OOo xslt filter, an XMultiServiceFactory will be add to the transformer
52  *  by setParameter(), then we can get it using getParameter(). Another way is using an
53  *  XConnection to connect to a running OOo. We connect to a running OOo, we need know the
54  *  uno url. It can be set in the xslt script. The default uno url is:
55  *  "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager"
56  *  see XSLTXalanOLEExtracter.java
57  */
58 public class XSLTFilterOLEExtracter {
59 
60     protected XMultiServiceFactory m_xMSF;
61     protected XNameContainer m_Storage;
62     protected XStream m_RootStream;
63     protected XConnection m_Connection;
64     protected String sConnectionString;
65     private static final String UNO_URL = "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager";
66 
XSLTFilterOLEExtracter()67     public XSLTFilterOLEExtracter() {
68     }
69 
init(String unoUrl)70     public void init(String unoUrl) {
71         if (unoUrl == null || unoUrl.equals("")) {
72             unoUrl = UNO_URL;
73         }
74         debugln("Init with uno url=" + unoUrl);
75         if (null == m_xMSF) {
76             try {
77                 m_xMSF = connectAwareGetServiceFactory();
78             } catch (Exception ex) {
79                 System.err.println("Could not connect to the office '" + unoUrl + "'\n" + ex.getMessage());
80             }
81         }
82     }
83 
exit()84     public void exit() {
85         m_Storage = null;
86         m_xMSF = null;
87         if (null != m_Connection) {
88             try {
89                 m_Connection.close();
90             } catch (Exception ex) {
91                 System.err.println("Could not close connection to the office.\n" + ex.getMessage());
92             }
93         }
94     }
95     //If aName = "oledata.mso" then we load the root storage from the given base64 string
96     //Otherwise we compress the stream and add it to the root storage under the name of aName
insertByName(String aName, String aBase64)97     public void insertByName(String aName, String aBase64) {
98         debugln("insertByName(" + aName + " : " + aBase64 + ")");
99         if (aName.equals("oledata.mso")) {
100             loadRootStorageFromBase64(aBase64);
101         } else {
102             ensureCreateRootStorage();
103             insertSubStorage(aName, aBase64);
104         }
105     }
106     //If aName = "oledata.mso" then we return the base64 encoded string of the root storage
107     //Otherwise we return the base64 encoded string of the sub stream under the name of aName
getByName(String aName)108     public String getByName(String aName) {
109         if (aName.equals("oledata.mso")) {
110             try {
111                 //get the length and seek to 0
112                 XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, m_RootStream);
113                 int oleLength = (int) xSeek.getLength();
114                 xSeek.seek(0);
115                 xSeek = null;
116                 //read all bytes
117                 XInputStream xInput = m_RootStream.getInputStream();
118                 byte oledata[][] = new byte[1][oleLength];
119                 xInput.readBytes(oledata, oleLength);
120                 //return the base64 encoded string
121                 return Base64.encodeBytes(oledata[0]);
122             } catch (Exception ex) {
123                 ex.printStackTrace();
124             }
125         } else {
126             return getEncodedSubStorage(aName);
127         }
128         return "";
129     }
130     //get the sub stream which name = aName, decompress it and return the base64 encoded string
getEncodedSubStorage(String aName)131     public String getEncodedSubStorage(String aName) {
132         debugln("getByName(" + aName + ")");
133         try {
134             if (!m_Storage.hasByName(aName)) {
135                 return "Not Found:" + aName;
136             }
137             Object oSubStream = m_Storage.getByName(aName);
138             if (oSubStream == null) {
139                 return "Not Found:" + aName;
140             }
141             XInputStream xSubStream = (XInputStream) UnoRuntime.queryInterface(XInputStream.class,
142                     oSubStream);
143             if (xSubStream == null) {
144                 return "Not Found:" + aName;
145             }
146             //The first four byte are the length of the uncompressed data
147             byte pLength[][] = new byte[1][4];
148             XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xSubStream);
149             xSeek.seek(0);
150             xSeek = null;
151             //Get the uncompressed length
152             int readbytes = xSubStream.readBytes(pLength, 4);
153             if (4 != readbytes) {
154                 System.out.println("readbytes:" + readbytes);
155                 return "Can not read the length.";
156             }
157             int oleLength = (pLength[0][0] << 0) + (pLength[0][1] << 8) + (pLength[0][2] << 16) + (pLength[0][3] << 24);
158             byte pContents[][] = new byte[1][oleLength];
159             //Read all bytes. The compressed length should less then the uncompressed length
160             readbytes = xSubStream.readBytes(pContents, oleLength);
161             if (oleLength < readbytes) {
162                 return "oleLength :" + oleLength + " readbytes: " + readbytes;
163             }
164 
165             // Decompress the bytes
166             Inflater decompresser = new Inflater();
167             decompresser.setInput(pContents[0], 0, readbytes);
168             byte[] result = new byte[oleLength];
169             int resultLength = decompresser.inflate(result);
170             decompresser.end();
171 
172             //return the base64 string of the uncompressed data
173             return Base64.encodeBytes(result);
174         } catch (Exception ex) {
175             ex.printStackTrace();
176         }
177         return "";
178     }
179 
CreateTempFileStream(XMultiServiceFactory xMSF)180     public XStream CreateTempFileStream(XMultiServiceFactory xMSF) {
181         // try to get temporary file representation
182         XStream xTempFileStream = null;
183         try {
184             Object oTempFile = xMSF.createInstance("com.sun.star.io.TempFile");
185             xTempFileStream = (XStream) UnoRuntime.queryInterface(XStream.class, oTempFile);
186         } catch (Exception e) {
187         }
188 
189         if (xTempFileStream == null) {
190             System.out.println("Can't create temporary file!");
191         }
192 
193         return xTempFileStream;
194     }
195     //decode the base64 string and create an com.sun.star.embed.OLESimpleStorage from it
loadRootStorageFromBase64(String aBase64)196     public void loadRootStorageFromBase64(String aBase64) {
197         try {
198             //Decode and write the data to an temp stream
199             byte[] oledata = Base64.decode(aBase64);
200             m_RootStream = CreateTempFileStream(m_xMSF);
201             XOutputStream xOutput = m_RootStream.getOutputStream();
202             xOutput.writeBytes(oledata);
203             xOutput.flush();
204             //Get the input stream and seek to begin
205             XInputStream xInput = m_RootStream.getInputStream();
206             XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xInput);
207             xSeek.seek(0);
208             oledata = null;
209             xSeek = null;
210 
211             //create an com.sun.star.embed.OLESimpleStorage from the temp stream
212             Object pArgs[] = new Object[1];
213             pArgs[0] = (Object) xInput;
214             Object oTempStorage = m_xMSF.createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", pArgs);
215             pArgs = null;
216 
217             m_Storage = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, oTempStorage);
218         } catch (Exception e) {
219             e.printStackTrace();
220         }
221     }
222     //Create a empty OLESimpleStorage if there is not one
ensureCreateRootStorage()223     public void ensureCreateRootStorage() {
224         if (null == m_RootStream || null == m_Storage) {
225             try {
226                 m_RootStream = CreateTempFileStream(m_xMSF);
227 
228                 Object pArgs[] = new Object[1];
229                 pArgs[0] = (Object) m_RootStream;
230                 Object oTempStorage = m_xMSF.createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", pArgs);
231                 pArgs = null;
232 
233                 m_Storage = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, oTempStorage);
234             } catch (Exception e) {
235                 e.printStackTrace();
236             }
237         }
238     }
239     //decode the base64 string and insert the length and the compressed data of it to
240     //the root storage as a sub stream under aName
insertSubStorage(String aName, String aBase64)241     public void insertSubStorage(String aName, String aBase64) {
242         try {
243             //decode the base64 string
244             byte[] oledata = Base64.decode(aBase64);
245             //create a temp stream to write data to
246             XStream subStream = CreateTempFileStream(m_xMSF);
247             XInputStream xInput = subStream.getInputStream();
248             XOutputStream xOutput = subStream.getOutputStream();
249             //write the length to the temp stream
250             byte oleHead[] = new byte[4];
251             oleHead[0] = (byte) ((oledata.length >>> 0) & 0xFF);
252             oleHead[1] = (byte) ((oledata.length >>> 8) & 0xFF);
253             oleHead[2] = (byte) ((oledata.length >>> 16) & 0xFF);
254             oleHead[3] = (byte) ((oledata.length >>> 24) & 0xFF);
255             xOutput.writeBytes(oleHead);
256 
257             // Compress the bytes
258             byte[] output = new byte[oledata.length];
259             Deflater compresser = new Deflater();
260             compresser.setInput(oledata);
261             compresser.finish();
262             int compressedDataLength = compresser.deflate(output);
263             //realloc the data length
264             byte[] compressedBytes = new byte[compressedDataLength];
265             for (int i = 0; i < compressedDataLength; i++) {
266                 compressedBytes[i] = output[i];
267             }
268 
269             //write the compressed data to the temp stream
270             xOutput.writeBytes(compressedBytes);
271             //seek to 0
272             XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xInput);
273             xSeek.seek(0);
274             xSeek = null;
275             oledata = null;
276 
277             //insert the temp stream as a sub stream and use an XTransactedObject to commit it immediately
278             XTransactedObject xTransact = (XTransactedObject) UnoRuntime.queryInterface(XTransactedObject.class, m_Storage);
279             m_Storage.insertByName(aName, xInput);
280             xTransact.commit();
281             xTransact = null;
282 
283         } catch (Exception e) {
284             e.printStackTrace();
285         }
286     }
287 
288     /** separtates the uno-url into 3 different parts.
289      */
parseUnoUrl(String url)290     protected static String[] parseUnoUrl(String url) {
291         String[] aRet = new String[3];
292 
293         if (!url.startsWith("uno:")) {
294             return null;
295         }
296 
297         int semicolon = url.indexOf(';');
298         if (semicolon == -1) {
299             return null;
300         }
301 
302         aRet[0] = url.substring(4, semicolon);
303         int nextSemicolon = url.indexOf(';', semicolon + 1);
304 
305         if (semicolon == -1) {
306             return null;
307         }
308         aRet[1] = url.substring(semicolon + 1, nextSemicolon);
309 
310         aRet[2] = url.substring(nextSemicolon + 1);
311         return aRet;
312     }
313     //connect to running OOo and keep an XConnection object so that we can disconnect from OOo as we wish
connectAwareGetServiceFactory()314     protected XMultiServiceFactory connectAwareGetServiceFactory() throws com.sun.star.uno.Exception,
315             com.sun.star.uno.RuntimeException,
316             Exception {
317 
318         // Get component context
319         XComponentContext xComponentContext =
320                 com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);
321 
322         // instantiate connector service
323         Object x = xComponentContext.getServiceManager().createInstanceWithContext(
324                 "com.sun.star.connection.Connector", xComponentContext);
325 
326         XConnector xConnector = (XConnector) UnoRuntime.queryInterface(XConnector.class, x);
327 
328         String a[] = parseUnoUrl(sConnectionString);
329         if (null == a) {
330             throw new com.sun.star.uno.Exception("Couldn't parse uno-url " + sConnectionString);
331         }
332 
333         // connect using the connection string part of the uno-url only.
334         m_Connection = xConnector.connect(a[0]);
335 
336         x = xComponentContext.getServiceManager().createInstanceWithContext(
337                 "com.sun.star.bridge.BridgeFactory", xComponentContext);
338 
339         XBridgeFactory xBridgeFactory = (XBridgeFactory) UnoRuntime.queryInterface(
340                 XBridgeFactory.class, x);
341 
342         // create a nameless bridge with no instance provider
343         // using the middle part of the uno-url
344         XBridge bridge = xBridgeFactory.createBridge("", a[1], m_Connection, null);
345 
346         // query for the XComponent interface and add this as event listener
347         XComponent xComponent = (XComponent) UnoRuntime.queryInterface(
348                 XComponent.class, bridge);
349 
350         // get the remote instance
351         x = bridge.getInstance(a[2]);
352 
353         // Did the remote server export this object ?
354         if (null == x) {
355             throw new com.sun.star.uno.Exception(
356                     "Server didn't provide an instance for" + a[2], null);
357         }
358 
359         XMultiServiceFactory xFac = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, x);
360         return xFac;
361     }
362     protected static boolean DEBUG = false;
363     protected static boolean DEBUGCHK = false;
364     protected static String debugfile;
365 
debugln(String s)366     protected static void debugln(String s) {
367         debug(s + "\n");
368     }
369 
debug(String s)370     protected static void debug(String s) {
371         if (!DEBUGCHK) {
372             if (System.getProperty("xsltfilter.debug") == null) {
373                 DEBUGCHK = true;
374                 return;
375             } else {
376                 debugfile = System.getProperty("xsltfilter.debug");
377                 DEBUG = true;
378             }
379         }
380         if (!DEBUG) {
381             return;
382         }
383         try {
384             FileWriter dbgwriter = new FileWriter(debugfile, true);
385             dbgwriter.write(s);
386             dbgwriter.close();
387         } catch (Exception e) {
388             e.printStackTrace();
389         }
390     }
391 }
392