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                            _log.error(e, e);
120                    }
121    
122                    return false;
123            }
124    
125            public static boolean isConvertBeforeCompare(String extension) {
126                    if (extension.equals("txt")) {
127                            return false;
128                    }
129    
130                    String[] conversions = getConversions(extension);
131    
132                    for (int i = 0; i < conversions.length; i++) {
133                            if (conversions[i].equals("txt")) {
134                                    return true;
135                            }
136                    }
137    
138                    return false;
139            }
140    
141            public static boolean isEnabled() {
142                    try {
143                            return PrefsPropsUtil.getBoolean(
144                                    PropsKeys.OPENOFFICE_SERVER_ENABLED,
145                                    PropsValues.OPENOFFICE_SERVER_ENABLED);
146                    }
147                    catch (Exception e) {
148                    }
149    
150                    return false;
151            }
152    
153            private DocumentConversionUtil() {
154                    _populateConversionsMap("drawing");
155                    _populateConversionsMap("presentation");
156                    _populateConversionsMap("spreadsheet");
157                    _populateConversionsMap("text");
158            }
159    
160            private File _convert(
161                            String id, InputStream inputStream, String sourceExtension,
162                            String targetExtension)
163                    throws IOException {
164    
165                    if (!isEnabled()) {
166                            return null;
167                    }
168    
169                    sourceExtension = _fixExtension(sourceExtension);
170                    targetExtension = _fixExtension(targetExtension);
171    
172                    _validate(targetExtension, id);
173    
174                    String fileName = getFilePath(id, targetExtension);
175    
176                    File file = new File(fileName);
177    
178                    if (PropsValues.OPENOFFICE_CACHE_ENABLED && file.exists()) {
179                            return file;
180                    }
181    
182                    DocumentFormatRegistry documentFormatRegistry =
183                            new DefaultDocumentFormatRegistry();
184    
185                    DocumentFormat inputDocumentFormat =
186                            documentFormatRegistry.getFormatByFileExtension(sourceExtension);
187                    DocumentFormat outputDocumentFormat =
188                            documentFormatRegistry.getFormatByFileExtension(targetExtension);
189    
190                    if (inputDocumentFormat == null) {
191                            throw new SystemException(
192                                    "Conversion is not supported from ." + sourceExtension);
193                    }
194                    else if (!inputDocumentFormat.isImportable()) {
195                            throw new SystemException(
196                                    "Conversion is not supported from " +
197                                            inputDocumentFormat.getName());
198                    }
199                    else if (outputDocumentFormat == null) {
200                            throw new SystemException(
201                                    "Conversion is not supported from " +
202                                            inputDocumentFormat.getName() + " to ." + targetExtension);
203                    }
204                    else if (!inputDocumentFormat.isExportableTo(outputDocumentFormat)) {
205                            throw new SystemException(
206                                    "Conversion is not supported from " +
207                                            inputDocumentFormat.getName() + " to " +
208                                                    outputDocumentFormat.getName());
209                    }
210    
211                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
212                            new UnsyncByteArrayOutputStream();
213    
214                    DocumentConverter documentConverter = _getDocumentConverter();
215    
216                    documentConverter.convert(
217                            inputStream, inputDocumentFormat, unsyncByteArrayOutputStream,
218                            outputDocumentFormat);
219    
220                    FileUtil.write(
221                            file, unsyncByteArrayOutputStream.unsafeGetByteArray(), 0,
222                            unsyncByteArrayOutputStream.size());
223    
224                    return file;
225            }
226    
227            private void _disconnect() {
228                    if (_openOfficeConnection != null) {
229                            _openOfficeConnection.disconnect();
230                    }
231            }
232    
233            private String _fixExtension(String extension) {
234                    if (extension.equals("htm")) {
235                            extension = "html";
236                    }
237    
238                    return extension;
239            }
240    
241            private String[] _getConversions(String extension) {
242                    extension = _fixExtension(extension);
243    
244                    String[] conversions = _conversionsMap.get(extension);
245    
246                    if (conversions == null) {
247                            conversions = _DEFAULT_CONVERSIONS;
248                    }
249                    else {
250                            if (ArrayUtil.contains(conversions, extension)) {
251                                    List<String> conversionsList = new ArrayList<>();
252    
253                                    for (int i = 0; i < conversions.length; i++) {
254                                            String conversion = conversions[i];
255    
256                                            if (!conversion.equals(extension)) {
257                                                    conversionsList.add(conversion);
258                                            }
259                                    }
260    
261                                    conversions = conversionsList.toArray(
262                                            new String[conversionsList.size()]);
263                            }
264                    }
265    
266                    return conversions;
267            }
268    
269            private DocumentConverter _getDocumentConverter() {
270                    if ((_openOfficeConnection != null) && (_documentConverter != null)) {
271                            return _documentConverter;
272                    }
273    
274                    String host = PrefsPropsUtil.getString(
275                            PropsKeys.OPENOFFICE_SERVER_HOST);
276                    int port = PrefsPropsUtil.getInteger(
277                            PropsKeys.OPENOFFICE_SERVER_PORT,
278                            PropsValues.OPENOFFICE_SERVER_PORT);
279    
280                    if (_isRemoteOpenOfficeHost(host)) {
281                            _openOfficeConnection = new SocketOpenOfficeConnection(host, port);
282                            _documentConverter = new StreamOpenOfficeDocumentConverter(
283                                    _openOfficeConnection);
284                    }
285                    else {
286                            _openOfficeConnection = new SocketOpenOfficeConnection(port);
287                            _documentConverter = new OpenOfficeDocumentConverter(
288                                    _openOfficeConnection);
289                    }
290    
291                    return _documentConverter;
292            }
293    
294            private boolean _isRemoteOpenOfficeHost(String host) {
295                    if (Validator.isNotNull(host) && !host.equals(_LOCALHOST_IP) &&
296                            !host.startsWith(_LOCALHOST)) {
297    
298                            return true;
299                    }
300                    else {
301                            return false;
302                    }
303            }
304    
305            private void _populateConversionsMap(String documentFamily) {
306                    Filter filter = new Filter(documentFamily);
307    
308                    DocumentFormatRegistry documentFormatRegistry =
309                            new DefaultDocumentFormatRegistry();
310    
311                    String[] sourceExtensions = PropsUtil.getArray(
312                            PropsKeys.OPENOFFICE_CONVERSION_SOURCE_EXTENSIONS, filter);
313                    String[] targetExtensions = PropsUtil.getArray(
314                            PropsKeys.OPENOFFICE_CONVERSION_TARGET_EXTENSIONS, filter);
315    
316                    for (String sourceExtension : sourceExtensions) {
317                            List<String> conversions = new SortedArrayList<>();
318    
319                            DocumentFormat sourceDocumentFormat =
320                                    documentFormatRegistry.getFormatByFileExtension(
321                                            sourceExtension);
322    
323                            if (sourceDocumentFormat == null) {
324                                    if (_log.isWarnEnabled()) {
325                                            _log.warn("Invalid source extension " + sourceExtension);
326                                    }
327    
328                                    continue;
329                            }
330    
331                            for (String targetExtension : targetExtensions) {
332                                    DocumentFormat targetDocumentFormat =
333                                            documentFormatRegistry.getFormatByFileExtension(
334                                                    targetExtension);
335    
336                                    if (targetDocumentFormat == null) {
337                                            if (_log.isWarnEnabled()) {
338                                                    _log.warn(
339                                                            "Invalid target extension " + targetDocumentFormat);
340                                            }
341    
342                                            continue;
343                                    }
344    
345                                    if (sourceDocumentFormat.isExportableTo(targetDocumentFormat)) {
346                                            conversions.add(targetExtension);
347                                    }
348                            }
349    
350                            if (conversions.isEmpty()) {
351                                    if (_log.isInfoEnabled()) {
352                                            _log.info(
353                                                    "There are no conversions supported from " +
354                                                            sourceExtension);
355                                    }
356                            }
357                            else {
358                                    if (_log.isInfoEnabled()) {
359                                            _log.info(
360                                                    "Conversions supported from " + sourceExtension +
361                                                            " to " + conversions);
362                                    }
363    
364                                    _conversionsMap.put(
365                                            sourceExtension,
366                                            conversions.toArray(new String[conversions.size()]));
367                            }
368                    }
369            }
370    
371            private void _validate(String targetExtension, String id) {
372                    if (!Validator.isFileExtension(targetExtension)) {
373                            throw new SystemException("Invalid extension: " + targetExtension);
374                    }
375    
376                    if (!Validator.isFileName(id)) {
377                            throw new SystemException("Invalid file name: " + id);
378                    }
379            }
380    
381            private static final String[] _COMPARABLE_FILE_EXTENSIONS =
382                    PropsValues.DL_COMPARABLE_FILE_EXTENSIONS;
383    
384            private static final String[] _DEFAULT_CONVERSIONS = new String[0];
385    
386            private static final String _LOCALHOST = "localhost";
387    
388            private static final String _LOCALHOST_IP = "127.0.0.1";
389    
390            private static final Log _log = LogFactoryUtil.getLog(
391                    DocumentConversionUtil.class);
392    
393            private static final DocumentConversionUtil _instance =
394                    new DocumentConversionUtil();
395    
396            private final Map<String, String[]> _conversionsMap = new HashMap<>();
397            private DocumentConverter _documentConverter;
398            private OpenOfficeConnection _openOfficeConnection;
399    
400    }