001    /**
002     * Copyright (c) 2000-2013 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.store;
016    
017    import com.liferay.portal.kernel.bean.BeanReference;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.io.ByteArrayFileInputStream;
021    import com.liferay.portal.kernel.search.BooleanClauseOccur;
022    import com.liferay.portal.kernel.search.BooleanQuery;
023    import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
024    import com.liferay.portal.kernel.search.Field;
025    import com.liferay.portal.kernel.search.Hits;
026    import com.liferay.portal.kernel.search.Indexer;
027    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
028    import com.liferay.portal.kernel.search.SearchContext;
029    import com.liferay.portal.kernel.search.SearchEngineUtil;
030    import com.liferay.portal.kernel.search.TermQuery;
031    import com.liferay.portal.kernel.search.TermQueryFactoryUtil;
032    import com.liferay.portal.kernel.util.FileUtil;
033    import com.liferay.portal.kernel.util.PropsKeys;
034    import com.liferay.portal.kernel.util.StringPool;
035    import com.liferay.portal.kernel.util.StringUtil;
036    import com.liferay.portal.kernel.util.TempFileUtil;
037    import com.liferay.portal.kernel.util.Validator;
038    import com.liferay.portal.model.Group;
039    import com.liferay.portal.security.permission.ActionKeys;
040    import com.liferay.portal.security.permission.PermissionChecker;
041    import com.liferay.portal.security.permission.PermissionThreadLocal;
042    import com.liferay.portal.service.GroupLocalService;
043    import com.liferay.portal.util.PrefsPropsUtil;
044    import com.liferay.portal.util.PropsValues;
045    import com.liferay.portlet.documentlibrary.DirectoryNameException;
046    import com.liferay.portlet.documentlibrary.FileExtensionException;
047    import com.liferay.portlet.documentlibrary.FileNameException;
048    import com.liferay.portlet.documentlibrary.FileSizeException;
049    import com.liferay.portlet.documentlibrary.SourceFileNameException;
050    import com.liferay.portlet.documentlibrary.antivirus.AntivirusScannerUtil;
051    import com.liferay.portlet.documentlibrary.model.DLFileEntryConstants;
052    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
053    import com.liferay.portlet.documentlibrary.service.permission.DLFolderPermission;
054    
055    import java.io.File;
056    import java.io.IOException;
057    import java.io.InputStream;
058    
059    /**
060     * @author Brian Wing Shun Chan
061     * @author Alexander Chow
062     * @author Edward Han
063     */
064    public class DLStoreImpl implements DLStore {
065    
066            @Override
067            public void addDirectory(long companyId, long repositoryId, String dirName)
068                    throws PortalException, SystemException {
069    
070                    if (!isValidName(dirName) || dirName.equals("/")) {
071                            throw new DirectoryNameException(dirName);
072                    }
073    
074                    store.addDirectory(companyId, repositoryId, dirName);
075            }
076    
077            @Override
078            public void addFile(
079                            long companyId, long repositoryId, String fileName,
080                            boolean validateFileExtension, byte[] bytes)
081                    throws PortalException, SystemException {
082    
083                    validate(fileName, validateFileExtension, bytes);
084    
085                    if (PropsValues.DL_STORE_ANTIVIRUS_ENABLED) {
086                            AntivirusScannerUtil.scan(bytes);
087                    }
088    
089                    store.addFile(companyId, repositoryId, fileName, bytes);
090            }
091    
092            @Override
093            public void addFile(
094                            long companyId, long repositoryId, String fileName,
095                            boolean validateFileExtension, File file)
096                    throws PortalException, SystemException {
097    
098                    validate(fileName, validateFileExtension, file);
099    
100                    if (PropsValues.DL_STORE_ANTIVIRUS_ENABLED) {
101                            AntivirusScannerUtil.scan(file);
102                    }
103    
104                    store.addFile(companyId, repositoryId, fileName, file);
105            }
106    
107            @Override
108            public void addFile(
109                            long companyId, long repositoryId, String fileName,
110                            boolean validateFileExtension, InputStream is)
111                    throws PortalException, SystemException {
112    
113                    if (is instanceof ByteArrayFileInputStream) {
114                            ByteArrayFileInputStream byteArrayFileInputStream =
115                                    (ByteArrayFileInputStream)is;
116    
117                            File file = byteArrayFileInputStream.getFile();
118    
119                            addFile(
120                                    companyId, repositoryId, fileName, validateFileExtension, file);
121    
122                            return;
123                    }
124    
125                    validate(fileName, validateFileExtension, is);
126    
127                    if (!PropsValues.DL_STORE_ANTIVIRUS_ENABLED ||
128                            !AntivirusScannerUtil.isActive()) {
129    
130                            store.addFile(companyId, repositoryId, fileName, is);
131                    }
132                    else {
133                            File tempFile = null;
134    
135                            try {
136                                    if (is.markSupported()) {
137                                            is.mark(is.available() + 1);
138    
139                                            AntivirusScannerUtil.scan(is);
140    
141                                            is.reset();
142    
143                                            store.addFile(companyId, repositoryId, fileName, is);
144                                    }
145                                    else {
146                                            tempFile = FileUtil.createTempFile();
147    
148                                            FileUtil.write(tempFile, is);
149    
150                                            AntivirusScannerUtil.scan(tempFile);
151    
152                                            store.addFile(companyId, repositoryId, fileName, tempFile);
153                                    }
154                            }
155                            catch (IOException ioe) {
156                                    throw new SystemException(
157                                            "Unable to scan file " + fileName, ioe);
158                            }
159                            finally {
160                                    if (tempFile != null) {
161                                            tempFile.delete();
162                                    }
163                            }
164                    }
165            }
166    
167            @Override
168            public void addFile(
169                            long companyId, long repositoryId, String fileName, byte[] bytes)
170                    throws PortalException, SystemException {
171    
172                    addFile(companyId, repositoryId, fileName, true, bytes);
173            }
174    
175            @Override
176            public void addFile(
177                            long companyId, long repositoryId, String fileName, File file)
178                    throws PortalException, SystemException {
179    
180                    addFile(companyId, repositoryId, fileName, true, file);
181            }
182    
183            @Override
184            public void addFile(
185                            long companyId, long repositoryId, String fileName, InputStream is)
186                    throws PortalException, SystemException {
187    
188                    addFile(companyId, repositoryId, fileName, true, is);
189            }
190    
191            @Override
192            public void checkRoot(long companyId) throws SystemException {
193                    store.checkRoot(companyId);
194            }
195    
196            @Override
197            public void copyFileVersion(
198                            long companyId, long repositoryId, String fileName,
199                            String fromVersionLabel, String toVersionLabel)
200                    throws PortalException, SystemException {
201    
202                    store.copyFileVersion(
203                            companyId, repositoryId, fileName, fromVersionLabel,
204                            toVersionLabel);
205            }
206    
207            @Override
208            public void deleteDirectory(
209                            long companyId, long repositoryId, String dirName)
210                    throws PortalException, SystemException {
211    
212                    store.deleteDirectory(companyId, repositoryId, dirName);
213            }
214    
215            @Override
216            public void deleteFile(long companyId, long repositoryId, String fileName)
217                    throws PortalException, SystemException {
218    
219                    validate(fileName, false);
220    
221                    store.deleteFile(companyId, repositoryId, fileName);
222            }
223    
224            @Override
225            public void deleteFile(
226                            long companyId, long repositoryId, String fileName,
227                            String versionLabel)
228                    throws PortalException, SystemException {
229    
230                    validate(fileName, false);
231    
232                    store.deleteFile(companyId, repositoryId, fileName, versionLabel);
233            }
234    
235            @Override
236            public File getFile(long companyId, long repositoryId, String fileName)
237                    throws PortalException, SystemException {
238    
239                    validate(fileName, false);
240    
241                    return store.getFile(companyId, repositoryId, fileName);
242            }
243    
244            @Override
245            public File getFile(
246                            long companyId, long repositoryId, String fileName,
247                            String versionLabel)
248                    throws PortalException, SystemException {
249    
250                    validate(fileName, false);
251    
252                    return store.getFile(companyId, repositoryId, fileName, versionLabel);
253            }
254    
255            @Override
256            public byte[] getFileAsBytes(
257                            long companyId, long repositoryId, String fileName)
258                    throws PortalException, SystemException {
259    
260                    validate(fileName, false);
261    
262                    return store.getFileAsBytes(companyId, repositoryId, fileName);
263            }
264    
265            @Override
266            public byte[] getFileAsBytes(
267                            long companyId, long repositoryId, String fileName,
268                            String versionLabel)
269                    throws PortalException, SystemException {
270    
271                    validate(fileName, false);
272    
273                    return store.getFileAsBytes(
274                            companyId, repositoryId, fileName, versionLabel);
275            }
276    
277            @Override
278            public InputStream getFileAsStream(
279                            long companyId, long repositoryId, String fileName)
280                    throws PortalException, SystemException {
281    
282                    validate(fileName, false);
283    
284                    return store.getFileAsStream(companyId, repositoryId, fileName);
285            }
286    
287            @Override
288            public InputStream getFileAsStream(
289                            long companyId, long repositoryId, String fileName,
290                            String versionLabel)
291                    throws PortalException, SystemException {
292    
293                    validate(fileName, false);
294    
295                    return store.getFileAsStream(
296                            companyId, repositoryId, fileName, versionLabel);
297            }
298    
299            @Override
300            public String[] getFileNames(
301                            long companyId, long repositoryId, String dirName)
302                    throws PortalException, SystemException {
303    
304                    if (!isValidName(dirName)) {
305                            throw new DirectoryNameException(dirName);
306                    }
307    
308                    return store.getFileNames(companyId, repositoryId, dirName);
309            }
310    
311            @Override
312            public long getFileSize(long companyId, long repositoryId, String fileName)
313                    throws PortalException, SystemException {
314    
315                    validate(fileName, false);
316    
317                    return store.getFileSize(companyId, repositoryId, fileName);
318            }
319    
320            @Override
321            public boolean hasDirectory(
322                            long companyId, long repositoryId, String dirName)
323                    throws PortalException, SystemException {
324    
325                    if (!isValidName(dirName)) {
326                            throw new DirectoryNameException(dirName);
327                    }
328    
329                    return store.hasDirectory(companyId, repositoryId, dirName);
330            }
331    
332            @Override
333            public boolean hasFile(long companyId, long repositoryId, String fileName)
334                    throws PortalException, SystemException {
335    
336                    validate(fileName, false);
337    
338                    return store.hasFile(companyId, repositoryId, fileName);
339            }
340    
341            @Override
342            public boolean hasFile(
343                            long companyId, long repositoryId, String fileName,
344                            String versionLabel)
345                    throws PortalException, SystemException {
346    
347                    validate(fileName, false);
348    
349                    return store.hasFile(companyId, repositoryId, fileName, versionLabel);
350            }
351    
352            @Override
353            public void move(String srcDir, String destDir) throws SystemException {
354                    store.move(srcDir, destDir);
355            }
356    
357            public Hits search(
358                            long companyId, long userId, String portletId, long groupId,
359                            long[] repositoryIds, String keywords, int start, int end)
360                    throws SystemException {
361    
362                    try {
363                            SearchContext searchContext = new SearchContext();
364    
365                            searchContext.setCompanyId(companyId);
366                            searchContext.setEnd(end);
367                            searchContext.setEntryClassNames(
368                                    new String[] {DLFileEntryConstants.getClassName()});
369                            searchContext.setGroupIds(new long[] {groupId});
370    
371                            Indexer indexer = IndexerRegistryUtil.getIndexer(
372                                    DLFileEntryConstants.getClassName());
373    
374                            searchContext.setSearchEngineId(indexer.getSearchEngineId());
375    
376                            searchContext.setStart(start);
377                            searchContext.setUserId(userId);
378    
379                            BooleanQuery contextQuery = BooleanQueryFactoryUtil.create(
380                                    searchContext);
381    
382                            contextQuery.addRequiredTerm(Field.PORTLET_ID, portletId);
383    
384                            if (groupId > 0) {
385                                    Group group = groupLocalService.getGroup(groupId);
386    
387                                    if (group.isLayout()) {
388                                            contextQuery.addRequiredTerm(Field.SCOPE_GROUP_ID, groupId);
389    
390                                            groupId = group.getParentGroupId();
391                                    }
392    
393                                    contextQuery.addRequiredTerm(Field.GROUP_ID, groupId);
394                            }
395    
396                            if ((repositoryIds != null) && (repositoryIds.length > 0)) {
397                                    BooleanQuery repositoryIdsQuery =
398                                            BooleanQueryFactoryUtil.create(searchContext);
399    
400                                    for (long repositoryId : repositoryIds) {
401                                            try {
402                                                    if (userId > 0) {
403                                                            PermissionChecker permissionChecker =
404                                                                    PermissionThreadLocal.getPermissionChecker();
405    
406                                                            DLFolderPermission.check(
407                                                                    permissionChecker, groupId, repositoryId,
408                                                                    ActionKeys.VIEW);
409                                                    }
410    
411                                                    if (repositoryId ==
412                                                                    DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
413    
414                                                            repositoryId = groupId;
415                                                    }
416    
417                                                    TermQuery termQuery = TermQueryFactoryUtil.create(
418                                                            searchContext, "repositoryId", repositoryId);
419    
420                                                    repositoryIdsQuery.add(
421                                                            termQuery, BooleanClauseOccur.SHOULD);
422                                            }
423                                            catch (Exception e) {
424                                            }
425                                    }
426    
427                                    contextQuery.add(repositoryIdsQuery, BooleanClauseOccur.MUST);
428                            }
429    
430                            BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(
431                                    searchContext);
432    
433                            searchQuery.addTerms(_KEYWORDS_FIELDS, keywords);
434    
435                            BooleanQuery fullQuery = BooleanQueryFactoryUtil.create(
436                                    searchContext);
437    
438                            fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
439    
440                            if (searchQuery.clauses().size() > 0) {
441                                    fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
442                            }
443    
444                            return SearchEngineUtil.search(searchContext, fullQuery);
445                    }
446                    catch (Exception e) {
447                            throw new SystemException(e);
448                    }
449            }
450    
451            @Override
452            public void updateFile(
453                            long companyId, long repositoryId, long newRepositoryId,
454                            String fileName)
455                    throws PortalException, SystemException {
456    
457                    store.updateFile(companyId, repositoryId, newRepositoryId, fileName);
458            }
459    
460            @Override
461            public void updateFile(
462                            long companyId, long repositoryId, String fileName,
463                            String newFileName)
464                    throws PortalException, SystemException {
465    
466                    store.updateFile(companyId, repositoryId, fileName, newFileName);
467            }
468    
469            @Override
470            public void updateFile(
471                            long companyId, long repositoryId, String fileName,
472                            String fileExtension, boolean validateFileExtension,
473                            String versionLabel, String sourceFileName, File file)
474                    throws PortalException, SystemException {
475    
476                    validate(
477                            fileName, fileExtension, sourceFileName, validateFileExtension,
478                            file);
479    
480                    if (PropsValues.DL_STORE_ANTIVIRUS_ENABLED) {
481                            AntivirusScannerUtil.scan(file);
482                    }
483    
484                    store.updateFile(companyId, repositoryId, fileName, versionLabel, file);
485            }
486    
487            @Override
488            public void updateFile(
489                            long companyId, long repositoryId, String fileName,
490                            String fileExtension, boolean validateFileExtension,
491                            String versionLabel, String sourceFileName, InputStream is)
492                    throws PortalException, SystemException {
493    
494                    if (is instanceof ByteArrayFileInputStream) {
495                            ByteArrayFileInputStream byteArrayFileInputStream =
496                                    (ByteArrayFileInputStream)is;
497    
498                            File file = byteArrayFileInputStream.getFile();
499    
500                            updateFile(
501                                    companyId, repositoryId, fileName, fileExtension,
502                                    validateFileExtension, versionLabel, sourceFileName, file);
503    
504                            return;
505                    }
506    
507                    validate(
508                            fileName, fileExtension, sourceFileName, validateFileExtension, is);
509    
510                    if (!PropsValues.DL_STORE_ANTIVIRUS_ENABLED ||
511                            !AntivirusScannerUtil.isActive()) {
512    
513                            store.updateFile(
514                                    companyId, repositoryId, fileName, versionLabel, is);
515                    }
516                    else {
517                            File tempFile = null;
518    
519                            try {
520                                    if (is.markSupported()) {
521                                            is.mark(is.available() + 1);
522    
523                                            AntivirusScannerUtil.scan(is);
524    
525                                            is.reset();
526    
527                                            store.updateFile(
528                                                    companyId, repositoryId, fileName, versionLabel, is);
529                                    }
530                                    else {
531                                            tempFile = FileUtil.createTempFile();
532    
533                                            FileUtil.write(tempFile, is);
534    
535                                            AntivirusScannerUtil.scan(tempFile);
536    
537                                            store.updateFile(
538                                                    companyId, repositoryId, fileName, versionLabel,
539                                                    tempFile);
540                                    }
541                            }
542                            catch (IOException ioe) {
543                                    throw new SystemException(
544                                            "Unable to scan file " + fileName, ioe);
545                            }
546                            finally {
547                                    if (tempFile != null) {
548                                            tempFile.delete();
549                                    }
550                            }
551                    }
552            }
553    
554            @Override
555            public void updateFileVersion(
556                            long companyId, long repositoryId, String fileName,
557                            String fromVersionLabel, String toVersionLabel)
558                    throws PortalException, SystemException {
559    
560                    store.updateFileVersion(
561                            companyId, repositoryId, fileName, fromVersionLabel,
562                            toVersionLabel);
563            }
564    
565            @Override
566            public void validate(String fileName, boolean validateFileExtension)
567                    throws PortalException, SystemException {
568    
569                    if (!isValidName(fileName)) {
570                            throw new FileNameException(fileName);
571                    }
572    
573                    if (fileName.endsWith(TempFileUtil.SUFFIX_TEMP_FILE_NAME)) {
574                            fileName = StringUtil.replaceLast(
575                                    fileName, TempFileUtil.SUFFIX_TEMP_FILE_NAME, StringPool.BLANK);
576                    }
577    
578                    if (validateFileExtension) {
579                            boolean validFileExtension = false;
580    
581                            String[] fileExtensions = PrefsPropsUtil.getStringArray(
582                                    PropsKeys.DL_FILE_EXTENSIONS, StringPool.COMMA);
583    
584                            for (String fileExtension : fileExtensions) {
585                                    if (StringPool.STAR.equals(fileExtension) ||
586                                            StringUtil.endsWith(fileName, fileExtension)) {
587    
588                                            validFileExtension = true;
589    
590                                            break;
591                                    }
592                            }
593    
594                            if (!validFileExtension) {
595                                    throw new FileExtensionException(fileName);
596                            }
597                    }
598            }
599    
600            @Override
601            public void validate(
602                            String fileName, boolean validateFileExtension, byte[] bytes)
603                    throws PortalException, SystemException {
604    
605                    validate(fileName, validateFileExtension);
606    
607                    if ((PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
608                            ((bytes == null) ||
609                             (bytes.length >
610                                     PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE)))) {
611    
612                            throw new FileSizeException(fileName);
613                    }
614            }
615    
616            @Override
617            public void validate(
618                            String fileName, boolean validateFileExtension, File file)
619                    throws PortalException, SystemException {
620    
621                    validate(fileName, validateFileExtension);
622    
623                    if ((PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
624                            ((file == null) ||
625                             (file.length() >
626                                    PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE)))) {
627    
628                            throw new FileSizeException(fileName);
629                    }
630            }
631    
632            @Override
633            public void validate(
634                            String fileName, boolean validateFileExtension, InputStream is)
635                    throws PortalException, SystemException {
636    
637                    validate(fileName, validateFileExtension);
638    
639                    // LEP-4851
640    
641                    try {
642                            if ((is == null) ||
643                                    ((PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
644                                     (is.available() >
645                                            PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE)))) {
646    
647                                    throw new FileSizeException(fileName);
648                            }
649                    }
650                    catch (IOException ioe) {
651                            throw new FileSizeException(ioe.getMessage());
652                    }
653            }
654    
655            @Override
656            public void validate(
657                            String fileName, String fileExtension, String sourceFileName,
658                            boolean validateFileExtension, File file)
659                    throws PortalException, SystemException {
660    
661                    validate(
662                            fileName, fileExtension, sourceFileName, validateFileExtension);
663    
664                    if ((file != null) &&
665                            (PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
666                            (file.length() >
667                                    PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE))) {
668    
669                            throw new FileSizeException(fileName);
670                    }
671            }
672    
673            @Override
674            public void validate(
675                            String fileName, String fileExtension, String sourceFileName,
676                            boolean validateFileExtension, InputStream is)
677                    throws PortalException, SystemException {
678    
679                    validate(
680                            fileName, fileExtension, sourceFileName, validateFileExtension);
681    
682                    try {
683                            if ((is != null) &&
684                                    (PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
685                                    (is.available() >
686                                            PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE))) {
687    
688                                    throw new FileSizeException(fileName);
689                            }
690                    }
691                    catch (IOException ioe) {
692                            throw new FileSizeException(ioe.getMessage());
693                    }
694            }
695    
696            protected boolean isValidName(String name) {
697                    if ((name == null) ||
698                            name.contains("\\") ||
699                            name.contains("\\\\") ||
700                            name.contains("//") ||
701                            name.contains(":") ||
702                            name.contains("*") ||
703                            name.contains("?") ||
704                            name.contains("\"") ||
705                            name.contains("<") ||
706                            name.contains(">") ||
707                            name.contains("|") ||
708                            name.contains("[") ||
709                            name.contains("]") ||
710                            name.contains("../") ||
711                            name.contains("/..")) {
712    
713                            return false;
714                    }
715    
716                    return true;
717            }
718    
719            protected void validate(
720                            String fileName, String fileExtension, String sourceFileName,
721                            boolean validateFileExtension)
722                    throws PortalException, SystemException {
723    
724                    String sourceFileExtension = FileUtil.getExtension(sourceFileName);
725    
726                    if (Validator.isNotNull(sourceFileName) &&
727                            PropsValues.DL_FILE_EXTENSIONS_STRICT_CHECK &&
728                            !fileExtension.equals(sourceFileExtension)) {
729    
730                            throw new SourceFileNameException(sourceFileExtension);
731                    }
732    
733                    validate(fileName, validateFileExtension);
734            }
735    
736            @BeanReference(type = GroupLocalService.class)
737            protected GroupLocalService groupLocalService;
738    
739            @BeanReference(type = Store.class)
740            protected Store store;
741    
742            private static final String[] _KEYWORDS_FIELDS = {
743                    Field.ASSET_TAG_NAMES, Field.CONTENT, Field.PROPERTIES
744            };
745    
746    }