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.ArrayUtil;
033    import com.liferay.portal.kernel.util.FileUtil;
034    import com.liferay.portal.kernel.util.PropsKeys;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.kernel.util.StringUtil;
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 (ArrayUtil.isNotEmpty(repositoryIds)) {
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 (validateFileExtension) {
574                            boolean validFileExtension = false;
575    
576                            String[] fileExtensions = PrefsPropsUtil.getStringArray(
577                                    PropsKeys.DL_FILE_EXTENSIONS, StringPool.COMMA);
578    
579                            for (String fileExtension : fileExtensions) {
580                                    if (StringPool.STAR.equals(fileExtension) ||
581                                            StringUtil.endsWith(fileName, fileExtension)) {
582    
583                                            validFileExtension = true;
584    
585                                            break;
586                                    }
587                            }
588    
589                            if (!validFileExtension) {
590                                    throw new FileExtensionException(fileName);
591                            }
592                    }
593            }
594    
595            @Override
596            public void validate(
597                            String fileName, boolean validateFileExtension, byte[] bytes)
598                    throws PortalException, SystemException {
599    
600                    validate(fileName, validateFileExtension);
601    
602                    if ((PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
603                            ((bytes == null) ||
604                             (bytes.length >
605                                     PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE)))) {
606    
607                            throw new FileSizeException(fileName);
608                    }
609            }
610    
611            @Override
612            public void validate(
613                            String fileName, boolean validateFileExtension, File file)
614                    throws PortalException, SystemException {
615    
616                    validate(fileName, validateFileExtension);
617    
618                    if ((PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
619                            ((file == null) ||
620                             (file.length() >
621                                    PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE)))) {
622    
623                            throw new FileSizeException(fileName);
624                    }
625            }
626    
627            @Override
628            public void validate(
629                            String fileName, boolean validateFileExtension, InputStream is)
630                    throws PortalException, SystemException {
631    
632                    validate(fileName, validateFileExtension);
633    
634                    // LEP-4851
635    
636                    try {
637                            if ((is == null) ||
638                                    ((PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
639                                     (is.available() >
640                                            PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE)))) {
641    
642                                    throw new FileSizeException(fileName);
643                            }
644                    }
645                    catch (IOException ioe) {
646                            throw new FileSizeException(ioe.getMessage());
647                    }
648            }
649    
650            @Override
651            public void validate(
652                            String fileName, String fileExtension, String sourceFileName,
653                            boolean validateFileExtension, File file)
654                    throws PortalException, SystemException {
655    
656                    validate(
657                            fileName, fileExtension, sourceFileName, validateFileExtension);
658    
659                    if ((file != null) &&
660                            (PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
661                            (file.length() >
662                                    PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE))) {
663    
664                            throw new FileSizeException(fileName);
665                    }
666            }
667    
668            @Override
669            public void validate(
670                            String fileName, String fileExtension, String sourceFileName,
671                            boolean validateFileExtension, InputStream is)
672                    throws PortalException, SystemException {
673    
674                    validate(
675                            fileName, fileExtension, sourceFileName, validateFileExtension);
676    
677                    try {
678                            if ((is != null) &&
679                                    (PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE) > 0) &&
680                                    (is.available() >
681                                            PrefsPropsUtil.getLong(PropsKeys.DL_FILE_MAX_SIZE))) {
682    
683                                    throw new FileSizeException(fileName);
684                            }
685                    }
686                    catch (IOException ioe) {
687                            throw new FileSizeException(ioe.getMessage());
688                    }
689            }
690    
691            protected boolean isValidName(String name) {
692                    if ((name == null) ||
693                            name.contains("\\") ||
694                            name.contains("\\\\") ||
695                            name.contains("//") ||
696                            name.contains(":") ||
697                            name.contains("*") ||
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    
708                            return false;
709                    }
710    
711                    return true;
712            }
713    
714            protected void validate(
715                            String fileName, String fileExtension, String sourceFileName,
716                            boolean validateFileExtension)
717                    throws PortalException, SystemException {
718    
719                    String sourceFileExtension = FileUtil.getExtension(sourceFileName);
720    
721                    if (Validator.isNotNull(sourceFileName) &&
722                            PropsValues.DL_FILE_EXTENSIONS_STRICT_CHECK &&
723                            !fileExtension.equals(sourceFileExtension)) {
724    
725                            throw new SourceFileNameException(sourceFileExtension);
726                    }
727    
728                    validate(fileName, validateFileExtension);
729            }
730    
731            @BeanReference(type = GroupLocalService.class)
732            protected GroupLocalService groupLocalService;
733    
734            @BeanReference(type = Store.class)
735            protected Store store;
736    
737            private static final String[] _KEYWORDS_FIELDS = {
738                    Field.ASSET_TAG_NAMES, Field.CONTENT, Field.PROPERTIES
739            };
740    
741    }