001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.documentlibrary.util;
016    
017    import com.artofsolving.jodconverter.DefaultDocumentFormatRegistry;
018    import com.artofsolving.jodconverter.DocumentConverter;
019    import com.artofsolving.jodconverter.DocumentFormat;
020    import com.artofsolving.jodconverter.DocumentFormatRegistry;
021    import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
022    import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
023    import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;
024    import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter;
025    
026    import com.liferay.portal.kernel.configuration.Filter;
027    import com.liferay.portal.kernel.exception.SystemException;
028    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
029    import com.liferay.portal.kernel.log.Log;
030    import com.liferay.portal.kernel.log.LogFactoryUtil;
031    import com.liferay.portal.kernel.util.ArrayUtil;
032    import com.liferay.portal.kernel.util.FileUtil;
033    import com.liferay.portal.kernel.util.PropsKeys;
034    import com.liferay.portal.kernel.util.SortedArrayList;
035    import com.liferay.portal.kernel.util.StringBundler;
036    import com.liferay.portal.kernel.util.StringPool;
037    import com.liferay.portal.kernel.util.SystemProperties;
038    import com.liferay.portal.kernel.util.Validator;
039    import com.liferay.portal.util.PrefsPropsUtil;
040    import com.liferay.portal.util.PropsUtil;
041    import com.liferay.portal.util.PropsValues;
042    
043    import java.io.File;
044    import java.io.IOException;
045    import java.io.InputStream;
046    
047    import java.util.ArrayList;
048    import java.util.HashMap;
049    import java.util.List;
050    import java.util.Map;
051    
052    /**
053     * @author Bruno Farache
054     * @author Alexander Chow
055     */
056    public class DocumentConversionUtil {
057    
058            public static File convert(
059                            String id, InputStream inputStream, String sourceExtension,
060                            String targetExtension)
061                    throws IOException {
062    
063                    return _instance._convert(
064                            id, inputStream, sourceExtension, targetExtension);
065            }
066    
067            public static void disconnect() {
068                    _instance._disconnect();
069            }
070    
071            public static String[] getConversions(String extension) {
072                    return _instance._getConversions(extension);
073            }
074    
075            public static String getFilePath(String id, String targetExtension) {
076                    StringBundler sb = new StringBundler(5);
077    
078                    sb.append(SystemProperties.get(SystemProperties.TMP_DIR));
079                    sb.append("/liferay/document_conversion/");
080                    sb.append(id);
081                    sb.append(StringPool.PERIOD);
082                    sb.append(targetExtension);
083    
084                    return sb.toString();
085            }
086    
087            public static boolean isComparableVersion(String extension) {
088                    boolean enabled = false;
089    
090                    String periodAndExtension = StringPool.PERIOD.concat(extension);
091    
092                    for (int i = 0; i < _COMPARABLE_FILE_EXTENSIONS.length; i++) {
093                            if (StringPool.STAR.equals(_COMPARABLE_FILE_EXTENSIONS[i]) ||
094                                    periodAndExtension.equals(_COMPARABLE_FILE_EXTENSIONS[i])) {
095    
096                                    enabled = true;
097    
098                                    break;
099                            }
100                    }
101    
102                    if (!enabled) {
103                            return false;
104                    }
105    
106                    if (extension.equals("css") || extension.equals("htm") ||
107                            extension.equals("html") || extension.equals("js") ||
108                            extension.equals("txt") || extension.equals("xml")) {
109    
110                            return true;
111                    }
112    
113                    try {
114                            if (isEnabled() && isConvertBeforeCompare(extension)) {
115                                    return true;
116                            }
117                    }
118                    catch (Exception e) {
119                            if (_log.isErrorEnabled()) {
120                                    _log.error(e, e);
121                            }
122                    }
123    
124                    return false;
125            }
126    
127            public static boolean isConvertBeforeCompare(String extension) {
128                    if (extension.equals("txt")) {
129                            return false;
130                    }
131    
132                    String[] conversions = getConversions(extension);
133    
134                    for (int i = 0; i < conversions.length; i++) {
135                            if (conversions[i].equals("txt")) {
136                                    return true;
137                            }
138                    }
139    
140                    return false;
141            }
142    
143            public static boolean isEnabled() {
144                    try {
145                            return PrefsPropsUtil.getBoolean(
146                                    PropsKeys.OPENOFFICE_SERVER_ENABLED,
147                                    PropsValues.OPENOFFICE_SERVER_ENABLED);
148                    }
149                    catch (Exception e) {
150                    }
151    
152                    return false;
153            }
154    
155            private DocumentConversionUtil() {
156                    _populateConversionsMap("drawing");
157                    _populateConversionsMap("presentation");
158                    _populateConversionsMap("spreadsheet");
159                    _populateConversionsMap("text");
160            }
161    
162            private File _convert(
163                            String id, InputStream inputStream, String sourceExtension,
164                            String targetExtension)
165                    throws IOException {
166    
167                    if (!isEnabled()) {
168                            return null;
169                    }
170    
171                    sourceExtension = _fixExtension(sourceExtension);
172                    targetExtension = _fixExtension(targetExtension);
173    
174                    _validate(targetExtension, id);
175    
176                    String fileName = getFilePath(id, targetExtension);
177    
178                    File file = new File(fileName);
179    
180                    if (PropsValues.OPENOFFICE_CACHE_ENABLED && file.exists()) {
181                            return file;
182                    }
183    
184                    DocumentFormatRegistry documentFormatRegistry =
185                            new DefaultDocumentFormatRegistry();
186    
187                    DocumentFormat inputDocumentFormat =
188                            documentFormatRegistry.getFormatByFileExtension(sourceExtension);
189                    DocumentFormat outputDocumentFormat =
190                            documentFormatRegistry.getFormatByFileExtension(targetExtension);
191    
192                    if (inputDocumentFormat == null) {
193                            throw new SystemException(
194                                    "Conversion is not supported from ." + sourceExtension);
195                    }
196                    else if (!inputDocumentFormat.isImportable()) {
197                            throw new SystemException(
198                                    "Conversion is not supported from " +
199                                            inputDocumentFormat.getName());
200                    }
201                    else if (outputDocumentFormat == null) {
202                            throw new SystemException(
203                                    "Conversion is not supported from " +
204                                            inputDocumentFormat.getName() + " to ." + targetExtension);
205                    }
206                    else if (!inputDocumentFormat.isExportableTo(outputDocumentFormat)) {
207                            throw new SystemException(
208                                    "Conversion is not supported from " +
209                                            inputDocumentFormat.getName() + " to " +
210                                                    outputDocumentFormat.getName());
211                    }
212    
213                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
214                            new UnsyncByteArrayOutputStream();
215    
216                    DocumentConverter documentConverter = _getDocumentConverter();
217    
218                    documentConverter.convert(
219                            inputStream, inputDocumentFormat, unsyncByteArrayOutputStream,
220                            outputDocumentFormat);
221    
222                    FileUtil.write(
223                            file, unsyncByteArrayOutputStream.unsafeGetByteArray(), 0,
224                            unsyncByteArrayOutputStream.size());
225    
226                    return file;
227            }
228    
229            private void _disconnect() {
230                    if (_openOfficeConnection != null) {
231                            _openOfficeConnection.disconnect();
232                    }
233            }
234    
235            private String _fixExtension(String extension) {
236                    if (extension.equals("htm")) {
237                            extension = "html";
238                    }
239    
240                    return extension;
241            }
242    
243            private String[] _getConversions(String extension) {
244                    extension = _fixExtension(extension);
245    
246                    String[] conversions = _conversionsMap.get(extension);
247    
248                    if (conversions == null) {
249                            conversions = _DEFAULT_CONVERSIONS;
250                    }
251                    else {
252                            if (ArrayUtil.contains(conversions, extension)) {
253                                    List<String> conversionsList = new ArrayList<>();
254    
255                                    for (int i = 0; i < conversions.length; i++) {
256                                            String conversion = conversions[i];
257    
258                                            if (!conversion.equals(extension)) {
259                                                    conversionsList.add(conversion);
260                                            }
261                                    }
262    
263                                    conversions = conversionsList.toArray(
264                                            new String[conversionsList.size()]);
265                            }
266                    }
267    
268                    return conversions;
269            }
270    
271            private DocumentConverter _getDocumentConverter() {
272                    if ((_openOfficeConnection != null) && (_documentConverter != null)) {
273                            return _documentConverter;
274                    }
275    
276                    String host = PrefsPropsUtil.getString(
277                            PropsKeys.OPENOFFICE_SERVER_HOST);
278                    int port = PrefsPropsUtil.getInteger(
279                            PropsKeys.OPENOFFICE_SERVER_PORT,
280                            PropsValues.OPENOFFICE_SERVER_PORT);
281    
282                    if (_isRemoteOpenOfficeHost(host)) {
283                            _openOfficeConnection = new SocketOpenOfficeConnection(host, port);
284                            _documentConverter = new StreamOpenOfficeDocumentConverter(
285                                    _openOfficeConnection);
286                    }
287                    else {
288                            _openOfficeConnection = new SocketOpenOfficeConnection(port);
289                            _documentConverter = new OpenOfficeDocumentConverter(
290                                    _openOfficeConnection);
291                    }
292    
293                    return _documentConverter;
294            }
295    
296            private boolean _isRemoteOpenOfficeHost(String host) {
297                    if (Validator.isNotNull(host) && !host.equals(_LOCALHOST_IP) &&
298                            !host.startsWith(_LOCALHOST)) {
299    
300                            return true;
301                    }
302                    else {
303                            return false;
304                    }
305            }
306    
307            private void _populateConversionsMap(String documentFamily) {
308                    Filter filter = new Filter(documentFamily);
309    
310                    DocumentFormatRegistry documentFormatRegistry =
311                            new DefaultDocumentFormatRegistry();
312    
313                    String[] sourceExtensions = PropsUtil.getArray(
314                            PropsKeys.OPENOFFICE_CONVERSION_SOURCE_EXTENSIONS, filter);
315                    String[] targetExtensions = PropsUtil.getArray(
316                            PropsKeys.OPENOFFICE_CONVERSION_TARGET_EXTENSIONS, filter);
317    
318                    for (String sourceExtension : sourceExtensions) {
319                            List<String> conversions = new SortedArrayList<>();
320    
321                            DocumentFormat sourceDocumentFormat =
322                                    documentFormatRegistry.getFormatByFileExtension(
323                                            sourceExtension);
324    
325                            if (sourceDocumentFormat == null) {
326                                    if (_log.isWarnEnabled()) {
327                                            _log.warn("Invalid source extension " + sourceExtension);
328                                    }
329    
330                                    continue;
331                            }
332    
333                            for (String targetExtension : targetExtensions) {
334                                    DocumentFormat targetDocumentFormat =
335                                            documentFormatRegistry.getFormatByFileExtension(
336                                                    targetExtension);
337    
338                                    if (targetDocumentFormat == null) {
339                                            if (_log.isWarnEnabled()) {
340                                                    _log.warn(
341                                                            "Invalid target extension " + targetDocumentFormat);
342                                            }
343    
344                                            continue;
345                                    }
346    
347                                    if (sourceDocumentFormat.isExportableTo(targetDocumentFormat)) {
348                                            conversions.add(targetExtension);
349                                    }
350                            }
351    
352                            if (conversions.isEmpty()) {
353                                    if (_log.isInfoEnabled()) {
354                                            _log.info(
355                                                    "There are no conversions supported from " +
356                                                            sourceExtension);
357                                    }
358                            }
359                            else {
360                                    if (_log.isInfoEnabled()) {
361                                            _log.info(
362                                                    "Conversions supported from " + sourceExtension +
363                                                            " to " + conversions);
364                                    }
365    
366                                    _conversionsMap.put(
367                                            sourceExtension,
368                                            conversions.toArray(new String[conversions.size()]));
369                            }
370                    }
371            }
372    
373            private void _validate(String targetExtension, String id) {
374                    if (!Validator.isFileExtension(targetExtension)) {
375                            throw new SystemException("Invalid extension: " + targetExtension);
376                    }
377    
378                    if (!Validator.isFileName(id)) {
379                            throw new SystemException("Invalid file name: " + id);
380                    }
381            }
382    
383            private static final String[] _COMPARABLE_FILE_EXTENSIONS =
384                    PropsValues.DL_COMPARABLE_FILE_EXTENSIONS;
385    
386            private static final String[] _DEFAULT_CONVERSIONS = new String[0];
387    
388            private static final String _LOCALHOST = "localhost";
389    
390            private static final String _LOCALHOST_IP = "127.0.0.1";
391    
392            private static final Log _log = LogFactoryUtil.getLog(
393                    DocumentConversionUtil.class);
394    
395            private static final DocumentConversionUtil _instance =
396                    new DocumentConversionUtil();
397    
398            private final Map<String, String[]> _conversionsMap = new HashMap<>();
399            private DocumentConverter _documentConverter;
400            private OpenOfficeConnection _openOfficeConnection;
401    
402    }